android - Loader unable to retain itself during certain configuration change -


according http://developer.android.com/guide/components/loaders.html, 1 of nice thing loader that, able retain data during configuration change.

they automatically reconnect last loader's cursor when being recreated after configuration change. thus, don't need re-query data.

however, doesn't work in scenarios.

i take following simple example. fragmentactivity, hosting fragment. fragment owns asynctaskloader.

the following 3 scenarios work pretty well.

during first launched (ok)

1 loader created, , loadinbackground executed once.

during simple rotation (ok)

no new loader being created , loadinbackground not being triggered.

a child activity launched, , button pressed (ok)

no new loader being created , loadinbackground not being triggered.

however, in following scenario.

a child activity launched -> rotation -> button pressed (wrong)

at time, old loader's onreset called. old loader destroyed. new loader created , new loader's loadinbackground triggered again.

the correct behavior i'm expecting is, no new loader created.

the loader related code follow. run code under android 4.1 emulator.

package com.example.bug;  import android.content.context; import android.os.bundle; import android.support.v4.app.fragment; import android.support.v4.app.loadermanager; import android.support.v4.content.asynctaskloader; import android.support.v4.content.loader; import android.util.log; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup;  public class mainfragment extends fragment implements loadermanager.loadercallbacks<integer> {     private static class integerarrayloader extends asynctaskloader<integer> {          private integer result = null;          public integerarrayloader(context context) {             super(context);             log.i("cheok", "integerarrayloader created!");         }          @override         public integer loadinbackground() {             log.i("cheok", "time consuming loadinbackground!");             this.result = 123456;             return result;         }          /**          * handles request cancel load.          */         @override          public void oncanceled(integer integer) {             super.oncanceled(integer);         }          /**          * handles request stop loader.          * automatically called loadermanager via stoploading.          */         @override          protected void onstoploading() {             // attempt cancel current load task if possible.             cancelload();         }          /**          * handles request start loader.          * automatically called loadermanager via startloading.          */         @override                 protected void onstartloading() {             if (this.result != null) {                 deliverresult(this.result);             }              if (takecontentchanged() || this.result == null) {                 forceload();             }         }          /**          * handles request reset loader.          * automatically called loadermanager via reset.          */         @override          protected void onreset() {             super.onreset();              // ensure loader stopped             onstoploading();              // @ point can release resources associated 'apps'             // if needed.             this.result = null;         }             }      @override     public loader<integer> oncreateloader(int arg0, bundle arg1) {         log.i("cheok", "oncreateloader being called");         return new integerarrayloader(this.getactivity());     }      @override     public void onloadfinished(loader<integer> arg0, integer arg1) {         result = arg1;      }      @override     public void onloaderreset(loader<integer> arg0) {         // todo auto-generated method stub      }      public view oncreateview(layoutinflater inflater, viewgroup container,             bundle savedinstancestate) {         view v = inflater.inflate(r.layout.fragment_main, container, false);         return v;     }      // http://stackoverflow.com/questions/11293441/android-loadercallbacks-onloadfinished-called-twice     @override     public void onresume()     {         super.onresume();          if (result == null) {             // prepare loader.  either re-connect existing one,             // or start new one.             getloadermanager().initloader(0, null, this);         } else {             // restore previous state. perhaps through long pressed home             // button.         }     }          private integer result; } 

complete source code can downloaded https://www.dropbox.com/s/n2jee3v7cpwvedv/loader_bug.zip

this might related 1 unsolved android bug : https://code.google.com/p/android/issues/detail?id=20791&can=5&colspec=id%20type%20status%20owner%20summary%20stars

https://groups.google.com/forum/?fromgroups=#!topic/android-developers/dbkl6pvyhli

i wondering, there workaround on bug?

my answer quite straight forward actually. don't use asynctaskloaders. because few bugs regarding asynctaskloaders knew now.

a combination retainable (setretaininstance(true) in onactivitycreated()) fragment asynctask. works same way. have restructure code bit.


message op

although author doesn't provide code example, closest workable solution. not use author proposed solution. instead, still rely on asynctaskloader necessary loading task. workaround that, rely on additional retained fragment, determine whether should reconnect/create loader. skeleton code on whole idea. works pretty far long can tell.

@override public void onactivitycreated(bundle savedinstancestate) {     ...      dataretainedfragment = (dataretainedfragment)fm.findfragmentbytag(date_retained_fragment);     // dataretainedfragment can null still... }  @override public void onresume() {     ...     if (this.data == null) {         if (dataretainedfragment != null) {             // re-use!             onloadfinished(null, dataretainedfragment);         } else {             // prepare loader.  either re-connect existing one,             // or start new one.             getloadermanager().initloader(0, null, this);         }     } else {     } }  @override public void onloadfinished(loader<data> arg0, data data) {     this.data = data;      if (this.dataretainedfragment == null) {         this.dataretainedfragment = dataretainedfragment.newinstance(this.data);         fragmentmanager fm = getfragmentmanager();         fm.begintransaction().add(this.dataretainedfragment, date_retained_fragment).commitallowingstateloss();                 } 

Comments

Popular posts from this blog

android - getbluetoothservice() called with no bluetoothmanagercallback -

sql - ASP.NET SqlDataSource, like on SelectCommand -

ios - Undefined symbols for architecture armv7: "_OBJC_CLASS_$_SSZipArchive" -