Reputation: 986
I have an activity on an Android Application that starts a Sync Process with my server. This process consumes a lot of memory and processing and takes some time do be completed.
After the process completes, a AlertDialog is presented to the user, showing if the process was completed Successfully or not.
If I leave the activity foreground, everything always works as expected. But, sometimes, when I leave the application, the background process continues its job, but, when I return to the application, it crash with the following error:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@4086ea48 is not valid; is your activity running?
Could this error be caused because the activity was destroyed by the OS? If yes, how could the process (that is running on another thread) continues running? And, how could I handle this scenario, avoid the crash and displaying the dialog to the user, even, if the we confirm that the issue is being caused by the Activities' destruction?
Here is some code...
public class ActSincronizacao extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sincronizacao);
//Prepare so starts the process
[...]
// Defines a Handler that will be called when the process gets finished
oSocketEngine.addProgressUpdateFinishListener(new ProgressUpdateFinishListener() {
@Override
public void ProgressUpdateFinishOccurred(final ProgressUpdateFinish evt) {
//Do some processing that doesn´t envolve UI
[...]
// Process that envolves UI
runOnUiThread(new Runnable() {
public void run() {
if ((boolean)evt.getArgs().getResult()) {
//If the process gets Success
AlertDialog.Builder builder = new AlertDialog.Builder(ActSincronizacao.this);
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setTitle("Informação");
builder.setMessage("Sincronização concluida com sucesso.");
// Defines a button handler
builder.setNeutralButton("OK", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//Go to another Activity
[...]
}
});
//Call the Dialog
builder.create().show();
} else {
//If something wrong hapenned
StringBuilder stb = new StringBuilder();
stb.append("Some error ocurrend while running the sync process.");
if (evt.getArgs().getMessage() != null) {
stb.append("\n\n");
stb.append(evt.getArgs().getMessage());
} else {
stb.append("\n\n");
stb.append("Try again...");
}
AlertDialog.Builder builder = new AlertDialog.Builder(ActSincronizacao.this);
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle("Error");
builder.setMessage(stb.toString());
builder.setNeutralButton("OK", null);
builder.create().show(); //<<<<<<<<<<<<<<<<< Crash is reported here
}
}
});
}
});
//Starts the process in backgroud (Thread)
oSocketEngine.StartCommunication(oDeviceConfig);
}
}
Here is the full stacktrace logged:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@4086ea48 is not valid; is your activity running?
at android.view.ViewRoot.setView(ViewRoot.java:532)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:200)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:114)
at android.view.Window$LocalWindowManager.addView(Window.java:424)
at android.app.Dialog.show(Dialog.java:241)
at PortalExecutivoSales.Android.ActSincronizacao$3$1.run(ActSincronizacao.java:138)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3687)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
at dalvik.system.NativeStart.main(Native Method)
Upvotes: 1
Views: 6547
Reputation: 986
For those that are still looking for a solution to this issue:
The following link explains what and why this problem is happening. http://vinnysoft.blogspot.com.br/2010/11/androidviewwindowmanagerbadtokenexcepti.html
What I did to solve this problem:
1). Created, on the ActSincronizacao
class, a boolean variable, called isActivityRunning
, that will be used to inform if the Activity is running or not. This variable is set at the onPause and onResume events, as bellow:
@Override
protected void onPause() {
isActivityActive = false;
super.onPause();
}
@Override
protected void onResume() {
isActivityActive = true;
super.onResume();
}
2). When the process ends, before calling the Dialog, I check the status of isActivityActive
variable. If true
, I call the dialogue as usual. If not, I create a Notification as exemplified bellow:
http://developer.android.com/guide/topics/ui/notifiers/notifications.html#NotificationResponse
3). When the user clicks on the notification, I still like to show the user an Alert with the error message, if any. So, the Notification should pass the ErrorMessage to the activity that is being called. This could be done adding a extra
to the Notification Intent
. Look the example below to see How to Pass (@Vidar Vestnes' question) and How to Consume (@pinaise´s answer) an Extra created by a Notification.
How to send parameters from a notification-click to an activity?
4). Finally, when the user clicks on the notification, I want to start the Application if it´s not running. If it´s running, I´d like to bring it back to foreground. The default behavior is, when we click on the Notification, a new instance of the activity passed to the Notification Intent is created. To avoid creating a new activity and bring the app to foreground, see @santhosh response below:
re-open background application via notification item
Hope this helps others in the future.
Regards.
Upvotes: 9