Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to finish a paused activity until it regains focus

I would like to finish() a paused activity that is underneath a transparent activity.

I have an activity, called activity A. Two things can happen while activity A is active;

  • we can launch (a transparent) Activity B

  • we can receive an asynchronous callback to finish activity A.

These two actions happen very close to each other. The code looks like this

public class ActivityA extends Activity
{
    public class DataHandler implements ContentLoader.OnDataListener
    {
        @Override
        public void onData(Cursor data)
        {
            _binder.bind(data);
        }
    }

    //If this callback is executed while Activity A is paused, it will not go into onStop until it the activity above it is finished
    private class LoaderCallbacks extends ContentLoader.LoaderCallbacks
    {
        public LoaderCallbacks(ContentLoader loader)
        {
            super(loader);
        }

        @Override
        public void onLoadFinished(
                Loader<Cursor> loader,
                Cursor cursor)
        {
            if (cursor == null || cursor.getCount() <= 0)
            {
                Log.d("Eric", "* ON FINISH *");
                finish();
                finishagain();
                return;
            }

            super.onLoadFinished(loader, cursor);
        }
    }
}

Inside of a listfragment shown by this activity there is a mechanism for launching Activity B

public class FragmentA extends ListFragment
{
    //Some fragment functions here...

        @Override
    public void onListItemClick(
            ListView list,
            View view,
            int position,
            long id)
    {
            Intent intent = new Intent();
            intent.setAction(Intent.LAUNCH_ACTIVITY_B);
            getActivity().sendBroadcast(intent)
    }
}

My problem is when the callback to finish activity A is called AFTER activity B is launched, then Activity A is not being finished immediately. It remains in the paused state until Activity B is finished, and then both finish. This is a race condition, and I've confirmed this by trying to finish again, while in the paused state, using a simple waiting thread. All the finish calls are performed on the main thread, as expected.

private void finishagain()
{
    Handler handler = new Handler();
    int LOCK_HOME_DELAY = 5000;
    handler.postDelayed(new Runnable()
    {
        public void run()
        {
            if (notfinished){
                Log.d("Eric", "*************** FINISH AGAIN ****************");
                finish(); //Does nothing while the activity is paused
            }
            else{
                Log.d("Eric", "* Times up do nothing *");
            }

        }
    }, LOCK_HOME_DELAY);
}

Here are my logs (some package names may be redacted)

    10-10 18:23:05.168 74-98/system_process I/ActivityManager: Displayed somepackage/com.eric.activity.A: +894ms
    10-10 18:23:07.135 74-98/system_process I/ActivityManager: Displayed somepackage/com.eric.activity.B: +343ms
    10-10 18:23:07.102 547-547/somepackage D/Eric: * Times up do nothign *
    10-10 18:23:07.231 547-547/somepackage D/Eric: * ON FINISH *
    10-10 18:23:08.220 547-547/com.eric.Status D/Eric: * Times up do nothign *
    10-10 18:23:08.305 547-547/com.eric.Status D/Eric: * Times up do nothign *
    10-10 18:23:12.305 547-547/com.eric.Status D/Eric: *************** FINISH AGAIN ****************
    10-10 18:23:12.305 74-668/system_process W/ActivityManager: Finishing task with all activities already finished
    10-10 18:23:12.305 74-668/system_process W/ActivityManager: Duplicate finish request for ActivityRecord{3627639c u0 somepackage/com.eric.activity.A t2292 f}

(Notice the timestamps - I call finish at :07 seconds, and it doesnt finish. finishAgain() calls finish again at :12 seconds, and it appears to close here, but I've seen it finish later too. Also note the "duplicate finish request" - to me it looks like the finish was queued or something).

How can I get Activity A to finish when it is paused underneath the transparent Activity B?

TO be honest, I'm surprised this is an issue; I thought activities on the backstack should be readily killeable, but perhaps not those in the onPause state? I haven't been able to find documentation on this, perhaps someone knows the relevant doc/code?

EDIT see my answer

like image 433
Eric S. Avatar asked Oct 10 '16 18:10

Eric S.


People also ask

When a migration activity is paused by the user what methods can be used to resume the activity?

Resume Your Activity When the user resumes your activity from the Paused state, the system calls the onResume() method. Be aware that the system calls this method every time your activity comes into the foreground, including when it's created for the first time.

When an activity is in the paused state the activity?

paused: In this state, the activity is partially obscured by another activity—the other activity that's in the foreground is semi-transparent or doesn't cover the entire screen. The paused activity does not receive user input and cannot execute any code.

What are the methods involved in the activity life cycle?

An Android activity goes through six major lifecycle stages or callbacks. These are: onCreate() , onStart() , onResume() , onPause() , onStop() , and onDestroy() . The system invokes each of these callbacks as an activity enters a new state.

Which activity lifecycle method would you use to clean up resources right before your activity is finished?

The onDestroy() method runs immediately before the activity is destroyed. The onDestroy() method enables you to perform any final clean up such as freeing up resources. After the onDestroy() method has run, the activity is destroyed.


1 Answers

Hmmm this question is trickier than it looks, the transparent Activity causes problems. You explained it well, but people dont normally answer the question, they spill-out something they are familiar with.

"If an activity has lost focus but is still visible (that is, a new non-full-sized or transparent activity has focus on top of your activity), it is paused. A paused activity is completely alive (it maintains all state and member information and remains attached to the window manager), but can be killed by the system in extreme low memory situations." see android doc.

you say:

//If this callback is executed while Activity A is paused, it will not go into onStop until it the activity above it is finished

You cannot go into stopped state while Activity A is visible.Android destroying activities, killing processes
This may help:

View topLevelLayout = findViewById(R.id.top_layout);
topLevelLayout.setVisibility(View.INVISIBLE);

In my answer the default case is asynchronous (it's a broadcast). Register each Activity (that you want to later kill, ActivityA) for local broadcast when it gets created. When it goes to the background (paused state), (i.e., a new activity comes to the top of the stack), its onPause() {onStop()} will get called, but it can still receive broadcasts. You just need to make sure you call unregisterReceiver() in onDestroy() rather than in onPause() {onStop()}.

Here'a some sample code to give you the idea:

    /** your "kill" method (ActivityA) **/
    Intent broadcastIntent = new Intent();
    broadcastIntent.setAction("com.package.ACTION_KILL");//some string
    //  sendBroadcast(broadcastIntent);//global broadcast
    LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);

     /**    The receiver (ActivityA) **/
        BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver(); 
        protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.package.ACTION_KILL");//some string
    LocalBroadcastManager.getInstance(this).registerReceiver(myBroadcastReceiver     {
     @Override
     public void onReceive(Context context, Intent intent) {
          Log.d("onReceive","KILL in progress");
          //At this point you should do your stuff ;O)
          finish();//kill it
         //System.exit(0)// to clear static variables
         //android.os.Process.killProcess(android.os.Process.myPid());//"cave man" kill                          }
      }, intentFilter);
   }

    protected void onDestroy(
    {
      LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadcastReceiver );
       super.onDestroy();
    }

https://developer.android.com/training/basics/activity-lifecycle/index.html https://developer.android.com/reference/android/app/Activity.html https://developer.android.com/reference/android/content/BroadcastReceiver.html

like image 177
Jon Goodwin Avatar answered Oct 07 '22 21:10

Jon Goodwin