首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在重新获得焦点之前,无法完成暂停的活动

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

Stack Overflow用户
提问于 2016-10-11 02:46:51
回答 2查看 2.6K关注 0票数 16

我想完成()一个暂停的活动,它位于一个透明的活动之下。

我有一个活动,叫做活动A,当活动A处于活动状态时,可能会发生两件事;

  • 我们可以启动(透明的) Activity B
  • 我们可以接收异步回调来完成activity A。

这两个动作发生得非常接近。代码如下所示

代码语言:javascript
运行
复制
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);
        }
    }
}

在此活动显示的列表片段中,有一种启动活动B的机制

代码语言:javascript
运行
复制
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)
    }
}

我的问题是,当活动B启动后调用完成活动A的回调时,活动A不会立即完成。它将保持暂停状态,直到活动B完成,然后两个活动都完成。这是一个竞争条件,我已经通过在暂停状态下使用一个简单的等待线程再次尝试完成来确认这一点。正如预期的那样,所有的finish调用都是在主线程上执行的。

代码语言:javascript
运行
复制
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);
}

这是我的日志(一些包的名字可能会被修改)

代码语言:javascript
运行
复制
    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秒调用finish,但它没有结束。finishAgain()调用在12秒时再次结束,它似乎在这里关闭,但我也看到它稍后结束。还要注意“重复完成请求”--对我来说,它看起来像是完成被排队了或别的什么)。

当活动A在透明活动B下暂停时,如何让它完成?

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

编辑请参阅我的答案

EN

回答 2

Stack Overflow用户

发布于 2016-10-30 20:35:28

我不知道你为什么要把这件事搞得如此复杂和复杂。在活动B完成加载之后,完成活动A。

我不明白ContentLoaderLoaderCallbacksFragmentListFragment等的用例。我甚至想知道为什么片段还没有被弃用?!一个沉重的2209行的类,仅仅是为了在另一个视图中显示一个ViewGroup?!我可以自己膨胀并将ViewGroup添加到视图中,为什么我必须使用片段呢?!!不管怎么说。

他们创建Application类是有原因的。你为什么不用这句话?!

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

“我们可以启动(透明的)活动B,我们可以接收异步回调来完成活动A。”

如果你想得到一个事件的通知,我们将创建一个事件侦听器,而不是使用那些复杂的类和方法。

下面是如何实现的:

您有一个如下所示的Application类:

代码语言:javascript
运行
复制
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中:

代码语言:javascript
运行
复制
        Intent intent = new Intent(A.this, B.class);
        App.addActivityListener(new ActivityLoadingFinishedListener() {

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

然后在活动B中:

代码语言:javascript
运行
复制
@Override
protected void onResume() {
    super.onResume();
    App.notifyAllActivityListeners();
}

这只是一个例子,告诉你你可以很容易的创建你自己的事件。它们是如此有用、简单、快速和可靠。

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

对于您的问题:“当活动A在透明的活动B下暂停时,我如何才能让它完成?”

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

票数 1
EN

Stack Overflow用户

发布于 2016-11-01 04:54:55

编辑查看services/java/com/android/server/am/ActivityStack.java以查找返回"Duplicate finish request ActivityRecord...“的行。日志(链接版本的第3424行)。它位于一个名为finishActivityLocked(...)的函数中它检查一个finished标志。

然后我在同一函数的第3505行遇到了这个问题

代码语言:javascript
运行
复制
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;
        } 

我没有时间去深入研究,但我有可能满足了这个条件,或者类似的条件。我不确定是什么构成了暂停和暂停(两种状态都列出了)。也许这不是我遇到的确切逻辑,但很明显,活动堆栈的状态机是非常细微的,它们确实有一种将finish请求排队的机制。

所以总而言之,我不能提供一个明确的行号,但我相当有信心

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

2)具体条件在ActivityState.java的4k行中。

原文

我最终为这个问题写了一个变通方法。您还记得,问题在于对透明活动下暂停的活动的finish()调用被延迟了。我的解决方法是让暂停的活动没有动画,然后立即终止它。它在性能方面相对不明显。

我仍然不确定核心问题,我也没有花太多时间深入研究AOSP源代码,所以我悬赏任何关于finish如何在暂停的活动上工作的明确解释。

代码语言:javascript
运行
复制
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();
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39964817

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档