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.
Related
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();
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.
I have developed an app with start, pause, resume and finish buttons.
It works properly in the activity using thread and handler.
If the user clicks on the start button a thread is started and displays textviewHH:MM:SS time and the rest of the buttons work correctly as well.
Problem:
If the activity goes to background then how do I update the textview time? I have made services for this task but, how do I take the response from services to UI?
Please, could you give me any idea of how to do it or any other possible solution?
You can create CustomBroadcast
Here is sample code.
Try this, it will work..
In YourService.Java
public static final String BROADCAST_ACTION = "com.example.tracking.updateprogress";
intent = new Intent(BROADCAST_ACTION);
sendBroadcast(intent);
In YourActivity.Java
registerReceiver(broadcastReceiver, new IntentFilter(YourService.BROADCAST_ACTION));
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Update Your UI here..
updateUI();
}
}
You can also pass data in Intent.
If you create a simple new Thread(new Runnable() {...}) within the Activity you can run UI manipulations with the runOnUiThread(new Runnable() { // your UI modify method }) Acitvity method. If the Activity go to the background the Thread is still running.
I am trying to update my UI in FirstActivity when I receive a notification but is confused by runOnUiThread , Runnable and Handler. Here is what I have: I am running FirstActivity and NotificationService. When NotificationService reeives a notification, it will update FirstActivity UI.
I also have another service AlarmService running.
First Activity
#Override
public void onResume() {
super.onResume();
//some other code for alarm service
}
NotificationService
//on receiving notification
private void showNotification(String text) {
//Get activity
Class<?> activityClass = null;
try {
activityClass = Class.forName("com.pakage.FirstActivity");
contextActivity = (Activity) activityClass.newInstance();
//Update UI on FirstActivity not working
contextActivity.runOnUiThread(new Runnable() {
public void run()
{
Looper.prepare();
TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
Looper.loop();
}
});
} catch (Exception e) {
e.printStackTrace();
}
//Shows the notification
Notification n = new Notification();
//... etc
}
I keep getting looper.prepare error. Do I need to put extra codes in my FirstActivity?
My 1st instinct is that you should instead have the Activity bind to your service and handle the UI update on its side instead of the Service directly modifying the Activity.
See more info here:
http://developer.android.com/reference/android/app/Service.html#LocalServiceSample
And an example here:
Example: Communication between Activity and Service using Messaging
I've always just had the service fire off a Broadcast and then in my Activity I have a BroadcastReciever listening for the Broadcast. It's an approach that is much simpler than the one you outlined above.
I have no idea why you are putting a Looper in
contextActivity.runOnUiThread(new Runnable() {
public void run()
{
Looper.prepare();
TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
Looper.loop();
}
});
because the UI (main) thread already has a Looper/Handler etc..
Even if it did work Looper.loop() is going to block and since you are running it on the UI thread, it will block the UI thread which is not what you want.
What you really want to do is
contextActivity.runOnUiThread(new Runnable() {
public void run()
{
TextView tv = (TextView ) contextActivity.findViewById(R.id.notifyTest);
tv.setText("do something that must be on UI thread") // or whatever
}
});
You don't really need to do all this fancy stuff to get the Activity
activityClass = Class.forName("com.pakage.FirstActivity");
contextActivity = (Activity) activityClass.newInstance();
assuming the Service and Activity are both running in the same process you can just save a reference to the Activity but be careful to update the reference when the Activity gets destroyed.
I have a service which sends continously values to an activity through some custom event listeners.
Here everything works fine. Certain values are displayed in my activity as expected, but some others make the application to crash. This is because some of the incoming data is calculated inside a normal thread (that I cannot have access for changing it), and I know I have to use a handler here, but as far as I tried the app still crashing.
more graphically I would like to do the following
onValuesChanged(float val) {
myTextView.setText( Float.toString(val) )
}
where val is calculated in a normal thread, but of course it makes crash the app when doing the setText.
Any suggestions?
Use AsyncTask instead of Thread and in the onPostExecute() you can update the UI.
or use Activity.runOnUiThread(new Runnable() {
void run() {
// do something interesting.
}
});
hey u can send a custom broadcast from your service like this
Intent mintent = new Intent();
mintent.setAction("com.action");
mintent.putExtra("name", "value");
sendBroadcast(mintent);
and register a receiver in your activity which will get the value from incoming intent and then call the handler like this to update the UI ..plese parse the int to string at receiving
myTextView.post(new Runnable() {
public void run() {
myTextView.setText( Float.toString(val) )
}
});
Every time you send a broadcast to your activity and it will update the ui ..
However the above mentioned way is also right but if you have to stay with service then go for this way else above......