AsyncTaskLoader endless onCanceled call - android

I am creating an AsyncTaskLoader based on http://developer.android.com/reference/android/content/AsyncTaskLoader.html. When I run my app, according to my logging, the app endlessly oscillates between loadInBackground and onCanceled. Does anyone know why this error might happen? My BroadcastReceiver is based on Proper notification of AsyncTaskLoader about data changes from background thread.
Here is my loadInBackground method:
#Override
public List<MyItem> loadInBackground() {
List<MyItem> items = createDummyData();
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent(RECEIVER_FILTER_STRING));
Log.d(TAG,”custom loader send broadcast from background and send items: "+items.size());
return items;
}
Here is my onStartLoading
#Override
protected void onStartLoading() {
Log.d(TAG, "items loader onStartLoading");
if (null != mData) {
Log.d(TAG, "items loader onStartLoading mData not null");
//someone is calling to start the loader, so if we have data, deliver it now
deliverResult(mData);
}
if (null == mReceiver) {
Log.d(TAG, "items loader onStartLoading register receiver");
mReceiver = new LoaderBroadcastReceiver(this);
LocalBroadcastManager.getInstance(getContext()).
registerReceiver(mReceiver, new IntentFilter(RECEIVER_FILTER_STRING));
}
if (takeContentChanged() || null == mData) {
//if data has changed since the last time it was loaded or is not available, then:
Log.d(TAG, "items loader onStartLoading onChange forceLoad");
forceLoad();
}
}
Here is my Receiver
class LoaderBroadcastReceiver extends BroadcastReceiver {
private Loader loader;
public LoaderBroadcastReceiver(Loader loader) {
this.loader = loader;
}
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "loader receiver informing of oonContentChagned");
loader.onContentChanged();
}
}

When it loads in background, it sends the Broadcast, and the Broadcast called onContentChanged() that's why the current Loader is cancelled and rerun again.
You should simply remove the BroadcastReceiver, or the BroadcastReceiver should not call onContentChanged().
From the link you quoted, the BroadcastReceiver is used for something like this, e.g. you loader load the file list in a folder, and for some reason you know that new files is added, so you send the Broadcast (not from your loader) and force the loader to rerun and get new content.
public void onContentChanged () Added in API level 11
Called when Loader.ForceLoadContentObserver detects a change. The
default implementation checks to see if the loader is currently
started; if so, it simply calls forceLoad();

Related

When and why deliverResult is called in AsyncTaskLoader?

I want to establish a good understanding of the AsyncTaskLoader lifecycle. I checked several resources, everything is clear but the usage of deliverResult. According to this picture from the internet (available here):
onStartLoading will be called, then if there is data already loaded, deliverResult is called, then it deliver the result to onLoadFinished. However, if there is no data foceLoad will be called, then loadInBackground, then deliverResult, then onLoadFinished.
I did the same way croworc answer suggests here: What does AsyncTaskLoader.deliverResult() actually do?
This is the code:
public class WeatherLoader extends AsyncTaskLoader<List<Weather>> {
List <Weather> receivedData;
/** Tag for log messages */
private String mUrl;
public WeatherLoader(Context context, String url) {
super(context);
mUrl = url;
}
#Override
protected void onStartLoading() {
if (receivedData == null){
Log.i ("loader ", "No data yet");
forceLoad();
} else {
deliverResult(receivedData);
Log.i ("loader ", "data is available no reload");
}
}
#Override
public void deliverResult(List<Weather> data) {
receivedData = data;
super.deliverResult(data);
Log.i ("loader ", "deliver result");
}
#Override
public List<Weather> loadInBackground() {
Log.i ("loader ", "load in background");
if (mUrl == null) {
return null;
}
// Perform the network request, parse the response, and extract a list of earthquakes.
List<Weather> weather = getweatherData(mUrl);
return weather;
}
}
But this is the sequence of the callbacks I'm getting when I initialize the loader or restart it:
onCreatLoader
No data yet
load in background
onLoaderFinish
deliver result
What really confuses me is that deliverResult is called after onLoaderFinished which also I think contradicts with this page of this book:
available here
The check for the availability of the data used in onStartLoading which calls deliverResult only gets called when the activity is stopped and restarted, like if I navigate to another activity then get back to it. Here is what gets printed in the logcat in this case:
deliver result
data is available no reload
Even onLoadFinished doesn't get called in this case. However, if I do the same behavior of navigating to another activity and getting back to the first one with having deliverResult with its original behavior (where I only call the super version of it), onStart gets called, then loadInBackground, then onLoadFinished, then DeliverResult. So, a new load happens
Can anyone please clarify why this behavior of callbacks is taking place? Does this mean that the image that shows the lifecycle is inaccurate?
Thanks.
Put the log calls before calling super and check the flow sequence again.
onLoadFinished is called during the call to super.deliverResult.
#Override
public void deliverResult(List<Weather> data) {
Log.i ("loader ", "deliver result");
receivedData = data;
super.deliverResult(data);
}

How to load a ListView using AsyncTask in android

I have a method called fetchData() to fetch some data from the database and load those to a ListView. But when the activity starts there is a small lag because of this. So I need to load the data in background. I was wondering if anyone could tell me how to do this using AsyncTask.
This is my fetchData() method.
public void fetchData() {
database = helper.getReadableDatabase();
Cursor c;
Date cDate = new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
final String fDate = sdf.format(cDate);
int thisMonth=Integer.parseInt(fDate.split("-")[1]);
Month mn=new Month();
String month=mn.getMonth(thisMonth);
Calendar cal=Calendar.getInstance();
int today=Integer.parseInt(fDate.split("-")[2]);
int curTab=position;
String whereClause="";
String sort="";
if(curTab==0){
whereClause=null;
sort=Database.NAME;
}
else if(curTab==1){
whereClause=Database.MONTH+" = '"+month+"' and "+Database.DAY+" ="+today;
sort=Database.NAME;
}
else if(curTab==2){
cal.add(Calendar.DAY_OF_MONTH, 1);
int monthn=cal.get(Calendar.MONTH)+1;
Month mnN=new Month();
String monthTomorrow=mnN.getMonth(monthn);
int tomorrow=cal.get(Calendar.DAY_OF_MONTH);
whereClause=Database.MONTH+" = '"+monthTomorrow+"' and "+Database.DAY+" ="+tomorrow;
sort=Database.DAY;
}
else if(curTab==3){
whereClause=Database.MONTH+" = '"+month+"'";
sort=Database.DAY;
}
if(DrawerMain.pos==1){
if(curTab==0){
whereClause=Database.TYPE+"='birthday'";
}
else{
whereClause=whereClause+" and "+Database.TYPE+"='birthday'";
}
}
else if(DrawerMain.pos==2){
if(curTab==0){
whereClause=Database.TYPE+"='anniversary'";
}
else{
whereClause=whereClause+" and "+Database.TYPE+"='anniversary'";
}
}
c = database.query(Database.TABLE_EVENT, null, whereClause, null, null, null, sort);
String[] fromDB={Database.NAME,Database.MONTH,Database.DAY};
int[] toView={R.id.tvName_lv,R.id.tv_month_lv,R.id.tv_day_lv};
CustomCursorAdapter adapter=new CustomCursorAdapter(getActivity(), c, 0, R.layout.events_list_item,fromDB,toView);
lv.setAdapter(adapter);
database.close();
}
You should consider using AsyncTaskLoader instead. AsyncLoaders will handle orientation changes better than AsyncTasks.
You can find a tutorial here: http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html
Code (copied directly from the tutorial)
public class SampleLoader extends AsyncTaskLoader<List<SampleItem>> {
// We hold a reference to the Loader’s data here.
private List<SampleItem> mData;
public SampleLoader(Context ctx) {
// Loaders may be used across multiple Activitys (assuming they aren't
// bound to the LoaderManager), so NEVER hold a reference to the context
// directly. Doing so will cause you to leak an entire Activity's context.
// The superclass constructor will store a reference to the Application
// Context instead, and can be retrieved with a call to getContext().
super(ctx);
}
/****************************************************/
/** (1) A task that performs the asynchronous load **/
/****************************************************/
#Override
public List<SampleItem> loadInBackground() {
// This method is called on a background thread and should generate a
// new set of data to be delivered back to the client.
List<SampleItem> data = new ArrayList<SampleItem>();
// TODO: Perform the query here and add the results to 'data'.
return data;
}
/********************************************************/
/** (2) Deliver the results to the registered listener **/
/********************************************************/
#Override
public void deliverResult(List<SampleItem> data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
}
// Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
List<SampleItem> oldData = mData;
mData = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
/*********************************************************/
/** (3) Implement the Loader’s state-dependent behavior **/
/*********************************************************/
#Override
protected void onStartLoading() {
if (mData != null) {
// Deliver any previously loaded data immediately.
deliverResult(mData);
}
// Begin monitoring the underlying data source.
if (mObserver == null) {
mObserver = new SampleObserver();
// TODO: register the observer
}
if (takeContentChanged() || mData == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
#Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
#Override
protected void onReset() {
// Ensure the loader has been stopped.
onStopLoading();
// At this point we can release the resources associated with 'mData'.
if (mData != null) {
releaseResources(mData);
mData = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
if (mObserver != null) {
// TODO: unregister the observer
mObserver = null;
}
}
#Override
public void onCanceled(List<SampleItem> data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
private void releaseResources(List<SampleItem> data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
/*********************************************************************/
/** (4) Observer which receives notifications when the data changes **/
/*********************************************************************/
// NOTE: Implementing an observer is outside the scope of this post (this example
// uses a made-up "SampleObserver" to illustrate when/where the observer should
// be initialized).
// The observer could be anything so long as it is able to detect content changes
// and report them to the loader with a call to onContentChanged(). For example,
// if you were writing a Loader which loads a list of all installed applications
// on the device, the observer could be a BroadcastReceiver that listens for the
// ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular
// Loader whenever the receiver detects that a new application has been installed.
// Please don’t hesitate to leave a comment if you still find this confusing! :)
private SampleObserver mObserver;
}
Put your fetchData() method in loadInBackground(). Close your Cursor in the releaseResources() method. In your onCreate() call
getLoaderManager().initLoader(0, null, this);

java.lang.IllegalStateException even updating from UI thread through broadcast

I wanna understand why im getting this error and my app crashes, cause im using notifyDataSetChanged correctly from main ui thread through broadcast.
10-24 14:13:36.563: E/AndroidRuntime(24830): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131099732, class android.widget.ListView) with Adapter(class com.example.irclient2.adapter.ConversaAdapter)]
When i receive a message (and i need to update the ui about it), i send a broadcast for it after adding the message to the data set;
public void receiveChannelUserMessage(User user, String msg,
CategoriaMSG category) {
// creates the message
Mensagem mensagem = new Mensagem(user.createSnapshot(),
Colors.removeFormattingAndColors(msg), category, nickcolor);
// add the message
canalConversa.addMessage(mensagem);
// inform UI from broadcast
Intent it = new Intent(ChatFragment.ACTION_CONVERSASETCHANGED);
it.addCategory(MyService.CANAL);
LocalBroadcastManager.getInstance(MyService.this).sendBroadcast(it);
}
This is the receiver in the fragment where the listview is in:
private BroadcastReceiver BR_notifyData = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (listview != null) {
((ConversaAdapter) listview.getAdapter())
.notifyDataSetChanged();
}
}
};
The receiver is registered at onResume() and unregistered at onPause();
What shoud i do about all of it?
Thanks in advance.

AsyncLoaderTask stops loading randomly (loadInBackground() stops getting called)

I have a class that extends AsyncTaskLoader and which frequently receives updates. Now when the app initially starts everything works fine, the UI (a SherlockListFragment (so I am using the compatability library) that implements LoaderManager.LoaderCallbacks<List<Item>>) updates accordingly as updates are received. At some random point however the UI simply stops updating. So far I haven't noticed any type of pattern to ascertain when it will stop updating; it can happen when very few updates are occurring or when many updates are occurring.
Below is my custom AsyncTaskLoader (I simply edited class and variable names to be highly generic so as to hopefully make the code slightly simpler to understand):
public class CustomLoader extends AsyncTaskLoader<List<Item>> {
private static final String LOG_TAG = "CustomLoader";
private List<Item> items;
public CustomLoader(Context context) {
super(context);
}
#Override
public List<Item> loadInBackground() {
return ItemModel.getItemList();
}
#Override
public void deliverResult(List<Item> data) {
if (isReset()) {
if (data != null) {
Log.w(LOG_TAG, "Warning! An async query came in while the Loader was reset!");
releaseResources(data);
return;
}
}
// Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
List<Item> oldItems = items;
items = data;
if (isStarted()) {
Log.i(LOG_TAG, "Delivering results to the LoaderManager.");
// If the Loader is in a started state, have the superclass deliver the
// results to the client.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldItems != null && oldItems != data) {
Log.i(LOG_TAG, "Releasing any old data associated with this Loader.");
releaseResources(oldItems);
}
}
#Override
protected void onStartLoading() {
Log.i(LOG_TAG, "onStartLoading() called!");
if (items != null) {
// Deliver any previously loaded data immediately.
Log.i(LOG_TAG, "Delivering previously loaded data to the client");
deliverResult(items);
}
//Initialises the loader within the model
ItemModel.registerLoader(this);
if (takeContentChanged()) {
forceLoad();
}
else if (items == null) {
// If the current data is null... then we should make it non-null! :)
forceLoad();
}
}
#Override
protected void onStopLoading() {
Log.i(LOG_TAG, "onStopLoading() called!");
// The Loader has been put in a stopped state, so we should attempt to
// cancel the current load (if there is one).
cancelLoad();
}
#Override
protected void onReset() {
Log.i(LOG_TAG, "onReset() called!");
super.onReset();
// Ensure the loader is stopped.
onStopLoading();
// At this point we can release the resources associated.
if (items != null) {
releaseResources(items);
items = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
// We do this by making the loader instance null
ItemModel.deregisterLoader();
}
#Override
public void onCanceled(List<Item> data) {
Log.i(LOG_TAG, "onCanceled() called!");
/**
* So... we were having problems with the loader sometimes simply not refreshing. It was found
* that when receiving two updates in quick succession, the loader would call onCanceled() after
* the second update (in order to try to stop the previous load). Whenever onCanceled() was called,
* the loader would stop refreshing.
*
* And the reason for this?? The support library version of Loader does not support onCanceled() !!!
* Thanks to this answer on stack overflow for bringing up the issue - http://stackoverflow.com/a/15449553
* By examining the API for the support library and the API 11 versions of Loader, it is clear that
* we shouldn't be receiving onCanceled() calls here, but we still do!
*
* Also important to note is that even on Android 3.0 and up, the framework will still use the
* support library methods for Loader.
*
* So we simply swallow this onCanceled() call and don't call the super method. This seems to fix
* the issue - it may also work if we simply remove onCanceled() completely, but not 100% sure.
*/
// Attempt to cancel the current asynchronous load.
//super.onCanceled(data);
// The load has been canceled, so we should release the associated resources
//Uncommenting this line of code does not resolve my issue
//releaseResources(data);
}
#Override
public void forceLoad() {
Log.i(LOG_TAG, "forceLoad() called!");
super.forceLoad();
}
private void releaseResources(List<Item> data) {
// All resources associated with the Loader should be released here.
if (data != null) {
data.clear();
data = null;
}
}
}
Now, while the UI is still updating properly the Logs show the following sequence of events:
03-03 17:23:33.859: I/CustomLoader(20663): forceLoad() called!
03-03 17:23:33.859: I/CustomLoader(20663): Load in background called...
03-03 17:23:33.864: I/CustomLoader(20663): Delivering results to the LoaderManager.
03-03 17:23:33.864: D/CustomFragment(20663): onLoadFinished() for loader_id 0
03-03 17:23:33.869: I/CustomLoader(20663): Releasing any old data associated with this Loader.
whenever the data is updated.
At the point that the UI stops updating it seems as though forceLoad() keeps on getting called every time the data changes it doesn't seem to actually accomplish anything (i.e. loadInBackground() doesn't get called). I have done a lot of research, looking at other implementations of AsyncTaskLoader and the overall logic of my implementation is similar to everything I've found so I'm at a bit of a loss here.

LocalBroadcastManager and the Activity lifecycle

So I'm looking into the feasibility of changing from callback interfaces to local broadcasts for some long-running network operations. Since the Activity lifecycle creates all sorts of complication for asynchronous requests that need to modify the UI (disconnect the Activity from the callback in onDestroy(), don't modify FragmentTransactions after onSaveInstanceState(), etc.), I'm thinking that using local broadcasts makes more sense, and we can just register/unregister the receiver at the lifecycle events.
However, when the Activity is destroyed and recreated during a configuration change, there's this small window of time when the broadcast receiver would not be registered (in between onPause()/onResume() for example). So if, for example, we start an asynchronous request in onCreate() if savedInstanceState == null (e.g. for the first launch of the Activity), isn't it possible that the broadcast sent upon completion would be lost if the user changes their device orientation right before the operation completes? (i.e. the receiver is unregistered on onPause(), then the operation completes, then the receiver is re-registered in onResume())
If that's the case, then it adds a lot of extra complexity we would need to add support for, and it's probably not worth the switch. I've looked into other things such as the Otto EventBus library but I'm not sure whether or not it has the same concerns to worry about.
As documented in the onRetainNonConfigurationInstance() method of the Activity, the system disables the message queue processing in the main thread while the Activity is in the process of being restarted. This ensures that events posted to the main thread will always be delivered at a stable point in the lifecycle of the Activity.
However, there seems to be a design flaw in the sendBroadcast() method of LocalBroadcastManager, in that it evaluates the registered BroadcastReceivers from the posting thread before queuing the broadcast to be delivered on the main thread, instead of evaluating them on the main thread at the time of broadcast delivery. While this enables it to report the success or failure of the delivery, it does not provide the proper semantics to allow BroadcastReceivers to be safely unregistered temporarily from the main thread without the possibility of losing potential broadcasts.
The solution to this is to use a Handler to post the broadcasts from the main thread, using the sendBroadcastSync() method so that the broadcasts are delivered immediately instead of being reposted. Here's a sample utility class implementing this:
public class LocalBroadcastUtils extends Handler {
private final LocalBroadcastManager manager;
private LocalBroadcastUtils(Context context) {
super(context.getMainLooper());
manager = LocalBroadcastManager.getInstance(context);
}
#Override
public void handleMessage(Message msg) {
manager.sendBroadcastSync((Intent) msg.obj);
}
private static LocalBroadcastUtils instance;
public static void sendBroadcast(Context context, Intent intent) {
if (Looper.myLooper() == context.getMainLooper()) {
// If this is called from the main thread, we can retain the
// "optimization" provided by the LocalBroadcastManager semantics.
// Or we could just revert to evaluating matching BroadcastReceivers
// at the time of delivery consistently for all cases.
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
} else {
synchronized (LocalBroadcastUtils.class) {
if (instance == null) {
instance = new LocalBroadcastUtils(context);
}
}
instance.sendMessage(instance.obtainMessage(0, intent));
}
}
}
To overcome this issue you need a component which stays alive even when activity gets re-created on configuration change. You can either use Application singleton or a retained Fragment.
If you use Otto or EventBus, then you can create an instance of event bus as a field of Application, and it will stay decoupled from device configuration changes like orientation change. Your activity will need to register event listener in onStart() and it will receive latest events.
If you use a retained Fragment, then fragment will stay alive until activity is not finished. Configuration changes will not release the instance of retained fragment either. It is also good practice to make retained Fragment invisible (return null from onCreateView() method). In onStart() of your activity you can always puck up latest state from that Fragment.
You can use LocalBroadcastManager with one of these approaches, but it doesn't really addresses the issue. It's just like any other event bus, but with ugly and inconvenient API ;)
I found android loaders is extremly helpful in this case.
In my case I need to receive broadcasts from another application and manage fragment transitions in my application.
So i did like below.
/**
* LoaderManager callbacks
*/
private LoaderManager.LoaderCallbacks<Intent> mLoaderCallbacks =
new LoaderManager.LoaderCallbacks<Intent>() {
#Override
public Loader<Intent> onCreateLoader(int id, Bundle args) {
Logger.v(SUB_TAG + " onCreateLoader");
return new MyLoader(MyActivity.this);
}
#Override
public void onLoadFinished(Loader<Intent> loader, Intent intent) {
Logger.i(SUB_TAG + " onLoadFinished");
// Display our data
if (intent.getAction().equals(INTENT_CHANGE_SCREEN)) {
if (false == isFinishing()) {
// handle fragment transaction
handleChangeScreen(intent.getExtras());
}
} else if (intent.getAction().equals(INTENT_CLOSE_SCREEN)) {
finishActivity();
}
}
#Override
public void onLoaderReset(Loader<Intent> loader) {
Logger.i(SUB_TAG + " onLoaderReset");
}
};
/**
* Listening to change screen commands. We use Loader here because
* it works well with activity life cycle.
* eg, like when the activity is paused and we receive command, it
* will be delivered to activity only after activity comes back.
* LoaderManager handles this.
*/
private static class MyLoader extends Loader<Intent> {
private Intent mIntent;
BroadcastReceiver mCommadListner;
public MyLoader(Context context) {
super(context);
Logger.i(SUB_TAG + " MyLoader");
}
private void registerMyListner() {
if (mCommadListner != null) {
return;
}
Logger.i(SUB_TAG + " registerMyListner");
mCommadListner = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null || action.isEmpty()) {
Logger.i(SUB_TAG + " intent action null/empty returning: ");
return;
}
Logger.i(SUB_TAG + " intent action: " + action);
mIntent = intent;
deliverResult(mIntent);
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(INTENT_CHANGE_SCREEN);
getContext().registerReceiver(mCommadListner, filter);
}
#Override
protected void onStartLoading() {
Logger.i(SUB_TAG + " onStartLoading");
if (mIntent != null) {
deliverResult(mIntent);
}
registerMyListner();
}
#Override
protected void onReset() {
Logger.i(SUB_TAG + "Loader onReset");
if (mCommadListner != null) {
getContext().unregisterReceiver(mCommadListner);
mCommadListner = null;
}
}
}
Activity#onCreate or Fragment#onActivityCreated()
#Override
protected void onCreate(Bundle savedInstanceState) {
// Listening to change screen commands from broadcast listner. We use Loader here because
// it works well with activity life cycle.
// eg, like when the activity is paused and we receive intent from broadcast, it will delivered
// to activity only after activity comes back. LoaderManager handles this.
getLoaderManager().initLoader(0, null, mLoaderCallbacks);
}
Normal broadcast will be lost if your activity will be paused or recreated. You can use sticky broadcast but it doesn't work with LocalBroadcastManager and you have to remember to manually remove sticky broadcast by calling Context.removeStickyBroadcast(). Sticky broadcast will be kept by system(even if your activity is paused) until you decide to delete it.
EventBus offer postSticky() method which works similar to sticky broadcast.

Categories

Resources