关于android:无法完成暂停的活动,直到它重新获得焦点

Unable to finish a paused activity until it regains focus

我想完成()一个透明活动下面的暂停活动。

我有一项活动,称为活动A.当活动A处于活动状态时,可能会发生两件事;

  • 我们可以启动(透明)活动B.

  • 我们可以接收异步回调来完成活动A.

这两个动作彼此非常接近。代码看起来像这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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);
        }
    }
}

在此活动显示的listfragment内部,有一个启动Activity B的机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
    }
}

我的问题是当完成活动A的回调被称为AFTER活动B被启动,然后活动A没有立即完成。它一直处于暂停状态,直到活动B结束,然后两者都完成。这是一个竞争条件,我已经通过尝试再次完成来确认这一点,而在暂停状态下,使用一个简单的等待线程。正如预期的那样,所有结束调用都在主线程上执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}

这是我的日志(一些软件包名称可能会被编辑)

1
2
3
4
5
6
7
8
9
    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}

(注意时间戳 - 我称之为:07秒完成,并且它没有完成.finoneAgain()再次调用:12秒,它似乎在这里关闭,但我已经看到它也完成了。还要注意"复制完成请求" - 对我来说,它看起来像是排队等待的东西)。

如何在透明活动B下面暂停活动A以完成活动A?

说实话,我很惊讶这是一个问题;我认为在靠背上的活动应该是容易杀人的,但也许不是那些处于onPause状态的人?我还没有找到关于此的文档,也许有人知道相关的文档/代码?

编辑看到我的答案


嗯,这个问题比它看起来更棘手,透明的Activity会导致问题。你解释得很好,但人们通常不会回答这个问题,他们会把他们熟悉的东西说出来。

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

你说:

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

当活动A可见时,你不能进入停止状态.Android破坏活动,杀死进程
这可能有所帮助:

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

在我的回答中,默认情况是异步的(它是广播)。
在创建本地广播时注册每个活动(您想要稍后杀死,ActivityA)。
当它进入后台(暂停状态)时,
(即,新活动到达堆栈的顶部),
它的onPause() {onStop()}将被调用,但它仍然可以接收广播。
您只需要确保在onDestroy()中而不是在onPause() {onStop()}中调用unregisterReceiver()。这里有一些示例代码可以提供您的想法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    /** 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


You can shut down an activity by calling its finish() method. You can also shut down a separate activity that you previously started by calling finishActivity().

您可以在此处访问开发者页面

所以现在,如果你正在使用很多活动,那么完成的方式是......

  • 启动其他活动,只需要类,意图和请求代码。

    1
    2
    3
    4
    public void startAcc(View v){
    Intent i = new Intent(this, SecondActivity.class);
    startActivityFromChild(this,i,0);
    }
  • 暂停完成其他活动。只需要用于初始化的子类和请求代码。

    1
    finishActivityFromChild(MainActivity.this,0);
  • 在你的情况下,应该使用startActivityFromChild来启动另一个活动,并在调用finish()之前使用finishActivityFromChild,当loadercallback是onLoaderFinished时。

    我希望这可以帮到你。


    编辑查看services / java / com / android / server / am / ActivityStack.java以查找返回"ActivityRecord的重复完成请求..."日志的行(链接版本的第3424行)。它位于一个名为finishActivityLocked(...)的函数中,用于检查finished标志。

    然后我在相同功能的第3505行遇到了这个

    1
    2
    3
    4
    5
    6
    7
    else if (r.state != ActivityState.PAUSING) {
                // If the activity is PAUSING, we will complete the finish once
                // it is done pausing; else we can just directly finish it here.
                if (DEBUG_PAUSE) Slog.v(TAG,"Finish not pausing:" + r);
                return finishCurrentActivityLocked(r, index,
                        FINISH_AFTER_PAUSE) == null;
            }

    我没有时间看得太深,但我有可能遇到这种情况,或类似的条件。我不确定是什么构成PAUSING与PAUSED(两个州都列出)。也许这不是我陷入的确切逻辑,但显然活动堆栈的状态机非常细微,它们确实有一个排队完成请求的机制。

    总而言之,我无法提供明确的行号,但我对此非常有信心

    1)活动状态机将完成请求排队

    2)特定条件位于ActivityState.java的4k行中。

    原帖

    我最后为这个问题编写了一个解决方法。您记得,问题是对透明活动暂停的活动的finish()调用会被延迟。我的解决方法是在没有动画的情况下启动暂停的活动并立即将其终止。这在性能方面相对不明显。

    仍然不确定核心问题,而且我没有花太多时间深入研究AOSP来源,因此我已经就如何完成暂停活动的任何明确解释提供了奖励。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    private boolean notfinished;

    /**
     * This function is used to finish an IR if it is stuck in a paused state.
     *
     * An activity will not finish() while paused underneath another activity. This function will
     * force a finish immediately by relaunching our activity and killing it immediately.
     *
     * This function assumes the launchmode is singletask or singleinstance, and does
     * not contain flags that clear or change stack (like FLAG_ACTIVITY_CLEAR_TOP)
     */
    private void finishagain()
    {
        Handler handler = new Handler();
        handler.postDelayed(new Runnable()
        {
            public void run()
            {
                if (notfinished)
                {
                    Log.d(TAG,"ContentActivity couldn't finish immediately." +
                           "This may happen if the Activity is paused when finish is called. Trying again");
                    Intent i = getIntent();
                    i.putExtra(MyConstants.FINISH_STATUS_FLAG, true);
                    i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                    startActivity(i) ;
                }
            }
        }, DELAY_CHECK_FINISHED);
    }    

    @Override
    public void onStart()
    {
        super.onStart();
        notfinished = true;
    }

    @Override
    public void onResume()
    {
        super.onResume();
        //There is a case where we need to take this activity out of paused state to kill it
        if (getIntent().getBooleanExtra(MyConstants.FINISH_STATUS_FLAG, false))
        {
            Log.d(TAG,"Finishing ContentActivity");
            finish();
            overridePendingTransition(0, 0); //Blocks the default finishing transition animation
        }
    }

    @Override
    public void onStop()
    {
        super.onStop();
        notfinished = false;
    }

    //sample useage
      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)
            {
                finish();
                finishagain();
                return;
            }

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

    我不知道你为什么这么简单的事情让你自己变得困难和复杂。活动B完成加载后的完成活动A.

    我不明白ContentLoaderLoaderCallbacksFragmentListFragment等用例。
    我甚至想知道为什么片段仍然不被弃用?!!!一个沉重的2209线程类只是为了在另一个视图中显示ViewGroup?!我可以自己充气并将ViewGroup添加到视图中,为什么我必须使用Fragments?!!无论如何。

    他们出于某种原因创建了Application类。你为什么不用那个?!

    我只是假设您的问题是:如何在活动B加载完成后完成活动A:

    "我们可以启动(透明)活动B.
    我们可以收到一个异步回调来完成活动A."

    如果您希望通过事件得到通知,请为此创建一个事件侦听器,而不是使用那些复杂的类和方法。

    这是如何:

    你有一个像这样的Application类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class App extends Application{

        private static ArrayList<ActivityLoadingFinishedListener> activityListeners;

    public static interface ActivityLoadingFinishedListener{
        void OnActivityFinishedLoading();
    }

    public static void addActivityListener(ActivityLoadingFinishedListener listener){
        if(activityListeners == null){
            activityListeners = new ArrayList<ActivityLoadingFinishedListener>();
        }
        activityListeners.add(listener);
    }

    public static void removeAllActivityListeners(){
        if(activityListeners != null){
            activityListeners.clear();
        }
    }

    public static void notifyAllActivityListeners(){
        if(activityListeners != null){
            for(ActivityLoadingFinishedListener alfl:activityListeners){
                alfl.OnActivityFinishedLoading();
            }
        }
    }
    }

    内部活动A:

    1
    2
    3
    4
    5
    6
    7
    8
    9
            Intent intent = new Intent(A.this, B.class);
            App.addActivityListener(new ActivityLoadingFinishedListener() {

                @Override
                public void OnActivityFinishedLoading() {
                    finish();
                }
            });
            startActivity(intent);

    然后在活动B内:

    1
    2
    3
    4
    5
    @Override
    protected void onResume() {
        super.onResume();
        App.notifyAllActivityListeners();
    }

    这只是一个示例,向您展示您可以轻松创建自己的事件。它们非常有用,简单,快速,可靠。

    你可以改变我的榜样以满足你的需求。

    并且对于您的问题:"如果在透明活动B下面暂停活动A,我如何才能完成活动A?"

    你可以使用相同的方法。当活动B恢复时,它应该通知所有听众(活动A是其中之一)。