在重新获得焦点之前无法完成暂停的活动

时间:2016-10-10 18:46:51

标签: android multithreading

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

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

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

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

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

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的机制

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

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

以下是我的日志(某些软件包名称可能会被编辑)

    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秒,它似乎在这里关闭,但我已经看到它也完成了还要注意&#34;重复完成请求&#34; - 对我来说,它看起来像是完成排队等等。

如果在透明活动B下方暂停活动A,我怎样才能完成活动?

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

编辑查看我的回答

4 个答案:

答案 0 :(得分:8)

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

  

“如果活动失去焦点但仍然可见(即新的活动   非全尺寸或透明的活动专注于您的   活动),它被暂停了。暂停的活动完全存在(它   维护所有州和会员信息并保持附加   窗口管理器),但可以在极低内存中被系统杀死   情况。“see android doc.

你说:

  

//如果在活动A暂停时执行此回调,它将会执行   在它上面的活动完成之前不要进入onStop

当活动A可见时,您无法进入停止状态。Android destroying activities, killing processes
这可能会有所帮助:

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

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

这里有一些示例代码可以提供给您这个想法:

    /** 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

答案 1 :(得分:2)

  

您可以通过调用finish()方法来关闭活动。您还可以通过调用finishActivity()来关闭先前启动的单独活动。

您可以访问开发者页面here

现在,如果您正在使用许多活动,那么完成的方式是......

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

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

    finishActivityFromChild(MainActivity.this,0);
    
  3. 在你的情况下,应该使用 startActivityFromChild 启动其他活动,并在调用finish()之前使用 finishActivityFromChild ,当loadercallback为onLoaderFinished时。

    我希望这可以帮到你。

答案 2 :(得分:1)

我不知道你为什么要为自己这么简单的事情而努力而复杂。完成活动B活动B加载完毕后。

我不理解ContentLoaderLoaderCallbacksFragmentListFragment等内容的使用情况。 我甚至想知道为什么片段仍然不被弃用?!!!一个沉重的2209线程类只是为了在另一个视图中显示ViewGroup?!我可以自己充气并将ViewGroup添加到视图中,为什么我必须使用Fragments?!!反正。

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

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

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

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

这是如何:

你有一个像这样的Application类:

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:

        Intent intent = new Intent(A.this, B.class);
        App.addActivityListener(new ActivityLoadingFinishedListener() {

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

然后在活动B内:

@Override
protected void onResume() {
    super.onResume();
    App.notifyAllActivityListeners();
}

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

您可以更改我的示例以满足您的需求。

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

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

答案 3 :(得分:1)

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

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

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来源,所以我已经就如何完成暂停活动的任何明确解释提供了奖励。

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