Vincent
Vincent

Reputation: 6188

Check whether activity is active

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?

Upvotes: 45

Views: 98518

Answers (9)

Yosidroid
Yosidroid

Reputation: 2233

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();
    }


}

Upvotes: 0

Pir Fahim Shah
Pir Fahim Shah

Reputation: 10633

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
}

Upvotes: 0

Gregory
Gregory

Reputation: 1007

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!")
}

Upvotes: 4

Richa
Richa

Reputation: 700

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
}

Upvotes: 6

Samet
Samet

Reputation: 917

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.

Upvotes: 18

Ali Ashraf
Ali Ashraf

Reputation: 1919

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();
}

Upvotes: 13

Ruchit Mittal
Ruchit Mittal

Reputation: 312

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..

Upvotes: 1

Peter Knego
Peter Knego

Reputation: 80350

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.

Upvotes: 4

harism
harism

Reputation: 6083

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.

Upvotes: 75

Related Questions