45

I'm having a problem with a listener in a certain activity.

The problem is that this listener contains an alert.show(); which can be called after we try to push a new activity (which then gives an exception).

e.g.: I'm listening in activity A for a signal from an other phone. I press back and try to run a new activity B but the program crashes because of the alert.show() A's listener.

ERROR/AndroidRuntime(3573): android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@476c21c0 is not valid; is your activity running?

Can I check in A's listener whether this activity is active and then show the alert or not depending on this value?

gopi1410
  • 6,567
  • 9
  • 41
  • 75
Vincent
  • 6,058
  • 15
  • 52
  • 94

9 Answers9

75

There might be an easier way I can't think of but one way is to implement it yourself. On onResume() you set a member variable mIsRunning to true and on onPause() back to false. Using this boolean you should know not to call alert.show() on your callback.

harism
  • 6,011
  • 1
  • 36
  • 31
  • 1
    If you have this problem with AsynckTask, you can check in onPause() method if is running, and if is runing, cancel the task using .method cancel(true). – Joan Casadellà Sep 24 '15 at 11:12
  • 30
    Why is there no built-in method for this check? – JohnyTex Feb 17 '16 at 14:15
  • My problem was different but your solution helped me.Thanks – amin Jun 18 '17 at 08:11
  • 2
    I you just want to check if your activity is visible or not, you can use `hasWindowFocus()`. I used this when I was checking if it's safe to display ads. – rose000 Sep 26 '18 at 07:09
17
ArrayList<String> runningactivities = new ArrayList<String>();

ActivityManager activityManager = (ActivityManager)getBaseContext().getSystemService (Context.ACTIVITY_SERVICE); 

List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE); 

    for (int i1 = 0; i1 < services.size(); i1++) { 
        runningactivities.add(0,services.get(i1).topActivity.toString());  
    } 

    if(runningactivities.contains("ComponentInfo{com.app/com.app.main.MyActivity}")==true){
        Toast.makeText(getBaseContext(),"Activity is in foreground, active",1000).show(); 

        alert.show()
    }

This way you will know if the pointed activity is the current visible activity, else your alert will not display.

This will only work when we navigate between two activities without finish.

Chintan Soni
  • 24,761
  • 25
  • 106
  • 174
Samet
  • 917
  • 12
  • 26
  • 5
    For above code to work you will require to add `android.permission.GET_TASKS` in your Manifest file, otherwise application crashes..! – YuDroid Jul 20 '12 at 09:55
  • Maybe when using new apis. I personaly don't remember adding this to work. – Samet Jul 20 '12 at 12:10
  • Are you sure everything is right in your code? Because I do it for 2 of my tabbed activities and it works with both of them. – Samet Sep 14 '12 at 12:14
  • 4
    "Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future. For example, if multiple applications can be actively running at the same time, assumptions made about the meaning of the data here for purposes of control flow will be incorrect." From:http://developer.android.com/reference/android/app/ActivityManager.html#getRecentTasks(int, int) – Lucas Jota Nov 29 '13 at 19:40
  • 1
    `getRecentTasks()` method was deprecated in API level 21. – David Miguel Dec 23 '17 at 20:15
13

This happens when the Activity will be going through its destruction when the background thread finishes its work and tries to show a Dialog.

This exeption is rare to reproduce but happens when we do some async task / background operation and want to display a dialog with Activity context and in the mean while our activity is destroying itself due to some reason.

Android OS should handle this situation, but as of now it does not.

so before calling your dialog just check if activity is running and not in its destruction phase.

if(!isFinishing()){
 callDialog();
}
Ali Ashraf
  • 1,841
  • 2
  • 24
  • 27
  • first of all, `isFinishing` is a function, secondly this is not what it is used for. Its used for checking "whether the activity is simply pausing or completely finishing". – vikki Nov 03 '13 at 17:12
  • @vikki, you are perfectly right Its used for checking "whether the activity is simply pausing or completely finishing" We are also checking the same before calling our dialog. We need to check this because As specified by me in my answer "Android OS should handle this situation, but as of now it does not." Kindly check. !!! Cheers ;) – Ali Ashraf Nov 04 '13 at 09:49
  • What I meant was that **in this question**, checking whether activity is pausing or finishing is not important, whats important is that the activity is currently running, `isFinishing()` will not give you any info on that. – vikki Nov 04 '13 at 10:28
  • @vikki Please read questing description that Vincent has described as follows: The problem is that this listener contains an alert.show(); which can be called after we try to push a new activity (which then gives an exception). ALSO:::::::::::::Can I check in A's listener whether this activity is active and then show the alert or not depending on this value? – Ali Ashraf Nov 04 '13 at 10:54
  • 4
    I understand exactly what the question is asking, what I'm trying to tell you is that `isFinishing()` can return false even after an activity goes to the background, this means that the activity hasn't finished, you will still get an error when this happens. – vikki Nov 04 '13 at 11:11
6

Background thread after finishing their networking tasks invokes callback onSuccess()/onFailure() on the main thread. And if at that time, the activity which initiated this background thread task is not in the foreground and you try to use getActivity() in either onSuccess()/onFailure(), it will give you the exception. So try to add this check before doing any UI operation.

if(!((Activity) context).isFinishing())
{
    //show alert
}
Richa
  • 700
  • 8
  • 12
  • This is actually incorrect. [isFinishing will only be set if the activity is not going to be recovered](http://stackoverflow.com/a/5227142/1758149) - the question asked how to detect if the activity isPaused. Right now I believe the only way would be the accepted answer. – RyPope Jan 12 '17 at 20:45
4

For API level >= 23 you can use 'lifecycle' property of activity to detect the state of activity. This gives a very compact code. Here is an example code in Kotlin, as class extension:

fun FragmentActivity.isRunning() = lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)

// ...
// Here 'myActivity' can be inherited from different activity-like classes, 
// for example, from class AppCompatActivity:
if (myActivity.isRunning()) {
    Log.d("log", "Activity is running!")
}
Gregory
  • 802
  • 10
  • 16
4

Yes you can check if activity is active: Refreshing an Activity from service when Active

Also, if your are not doing anything when your Activity in not active, then you should probably unregister the listener when your Activity deactivates.

Community
  • 1
  • 1
Peter Knego
  • 79,991
  • 11
  • 123
  • 154
1

I was having 2 activities A & B, I just wanted to know Activity B is running or not from A.

Initially i followed "RunningTaskInfo" for resolving the problem, It was not working 100%.

So i created own solution, I will post my solution. Using HashMap and AtomicBoolean class.

public class ActivityStateTracker {
final private Map<String, AtomicBoolean> mMap = new HashMap<String, AtomicBoolean>();

private static ActivityStateTracker instance = null;
/**
 * SingletonClass
 * */
private ActivityStateTracker() {

}

public static ActivityStateTracker getInstance(String activityName, boolean defaultVal) {

    if(instance == null) {
        instance = new ActivityStateTracker();
    }
    instance.setDefaultValue(activityName, defaultVal);
    return instance;
}

private void setDefaultValue(String activityName, boolean defaultVal) {
    mMap.put(activityName, new AtomicBoolean(defaultVal));
}

public boolean isRunning(String activityName) {
    final AtomicBoolean atomicBool = mMap.get(activityName);
    return (mMap.get(activityName) == null) ? false : atomicBool.get();
}

public void setChangeState(String activityName, boolean value) {
    final AtomicBoolean atomicBool = mMap.get(activityName);

    if(atomicBool == null) {
        setDefaultValue(activityName, value);
    } else {
        atomicBool.set(value);
        mMap.put(activityName, atomicBool);
    }
}

}

Now in Activity B.

 public static final String TAG = "EditScreenPopupActivity";

static ActivityStateTracker mActivityState = ActivityStateTracker.getInstance(TAG, false);

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mActivityState.setChangeState(TAG, true);

    requestWindowFeature(Window.FEATURE_NO_TITLE);

    setContentView(R.layout.activity_traslucent);

}

@Override
protected void onDestroy() {
    mActivityState.setChangeState(TAG, false);
    super.onDestroy();
}

Now in Activity A.

public static final String TAG = "ToolTipPopupActivity";

static ActivityStateTracker mActivityState = ActivityStateTracker.getInstance(TAG, false);

   /** Check Edit screen activity is running or not? */
   if(mActivityState.isRunning("EditScreenPopupActivity")) {
        finish();
    }

......................................

This solution is working properly in my case.. I hope it will help you as well..

Ruchit Mittal
  • 312
  • 1
  • 9
0

If you do not want to show alert dialog or other operation after you close your activity then use this code. it works 100% accurately.

if(!(DownloadVideoActivity.this.isDestroyed()))
{
   //show alert dialog or other things
}
Pir Fahim Shah
  • 10,505
  • 1
  • 82
  • 81
0

This kind of error is happened when activity is destroyed but unfinished business logic was still running and when it is done its logic ready to show the outcome in UI. For example showing a dialog window or showing an outcome from background tasks The best way to support old and new android versions, and avoid compatibility issues to find out if your activity is still running or not is the following:

        //check if activity is running. if so, display a dialog or what ever 
        final Boolean isAlive = isActivityRunning("com.example.mypackage.MyActivity.this");
        if (isAlive) {
            //Activity is running, you can show a dialog here or do what ever you like here
           
        }

public static Boolean isActivityRunning(Context context) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
        ActivityManager.RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
        String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName();

        return foregroundTaskPackageName.toLowerCase().equals(context.getPackageName().toLowerCase());
    } else {
        ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
        ActivityManager.getMyMemoryState(appProcessInfo);
        if (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE) {
            return true;
        }

        KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
        // App is foreground, but screen is locked, so show notification
        return km.inKeyguardRestrictedInputMode();
    }


}
Yosidroid
  • 2,053
  • 16
  • 15