Не удалось завершить приостановленную деятельность, пока она не восстановит фокус

Я хотел бы закончить() приостановленную деятельность, которая находится под прозрачной деятельностью.

У меня есть активность, называемая активностью 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);
        }
    }
}

Внутри фрагмента списка, отображаемого этой активностью, есть механизм запуска 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)
    }
}

Моя проблема в том, что callback для завершения действия A называется AFTER, запускается активность 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 секунд, и он не заканчивается. finishAgain() вызывает завершение снова: 12 секунд, и, похоже, он закрывается здесь, но я видел, что он заканчивается позже. обратите внимание на "дублированный запрос на завершение" - мне кажется, что финиш был поставлен в очередь или что-то в этом роде).

Как я могу заставить Activity A закончить, когда он приостановлен под прозрачной Activity B?

Честно говоря, я удивлен, что это проблема; Я думал, что действия на заднем ходу должны быть легко уничтожены, но, возможно, не в состоянии onPause? Я не смог найти документацию по этому поводу, возможно, кто-то знает соответствующий документ/код?

EDIT см. мой ответ

Ответы

Ответ 1

Хммм, этот вопрос сложнее, чем кажется, прозрачная активность вызывает проблемы. Вы объяснили это хорошо, но люди обычно не отвечают на вопрос, они разливают то, с чем они знакомы.

"Если активность потеряла фокус, но все еще видна (т.е. новая не полноразмерная или прозрачная деятельность фокусируется на активности), он приостановлен. Остановленная деятельность полностью жива (она хранит всю информацию о штатах и ​​членах и остается прикрепленной к оконный менеджер), но может быть убит системой в крайне низкой памяти ситуаций." см. документ android doc.

вы говорите:

//Если этот обратный вызов выполняется, когда действие A приостановлено, оно будет не входите в onStop, пока не закончите работу над ней.

Вы не можете перейти в состояние остановки, пока видна активность A. Android, разрушающая действия, процессы убийства
Это может помочь:

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

В моем ответе случай по умолчанию - асинхронный (это широковещательная передача). Зарегистрируйте каждую активность (которую вы хотите позже убить, ActivityA) для локальной широковещательной рассылки при ее создании. Когда он переходит на задний план (приостановленное состояние), (т.е. новое действие переходит к вершине стека), его вызов onPause() {onStop()} будет вызван, но он может получать трансляции. Вам просто нужно убедиться, что вы вызываете unregisterReceiver() в onDestroy(), а не в onPause() {onStop()}.

Вот пример кода, который даст вам идею:

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

Ответ 2

Вы можете отключить действие, вызвав метод finish(). Вы также можете отключить отдельное действие, которое вы ранее запускали, вызвав функцию finishActivity().

Вы можете посетить страницу разработчика здесь

так что теперь, если вы используете для многих видов деятельности, путь для финиша...

  • Запуск других действий требует только кода класса, намерения и запроса.

    public void startAcc(View v){
    Intent i = new Intent(this, SecondActivity.class);
    startActivityFromChild(this,i,0);
    }
    
  • Завершение работы с другими в режиме паузы. Только нужен класс child и код запроса, который вы должны использовать для инициализации.

    finishActivityFromChild(MainActivity.this,0);
    

В вашем случае следует использовать startActivityFromChild, чтобы запустить другое действие, и закончитьActivityFromChild до завершения вызова(), когда loadercallback is onLoaderFinished.

Надеюсь, это поможет вам.

Ответ 3

Я не знаю, почему вы делаете это так сложно и сложно для себя такой простой вещи. завершающая активность A после завершения операции B.

Я не понимаю пример использования таких вещей, как ContentLoader, LoaderCallbacks, Fragment, ListFragment и т.д. Я даже удивляюсь, почему Фрагменты все еще не устарели?!!! тяжелый класс, состоящий из 2209, только для показа ViewGroup внутри другого View?! хорошо, я могу раздуть и добавить, что ViewGroup для представления сам, зачем мне использовать Фрагменты?!! в любом случае.

Ну, они создали класс Application по какой-либо причине. почему бы вам не использовать это?!

Я только предполагаю, что ваш вопрос: как закончить действие А после завершения операции B:

"мы можем запустить (прозрачную) активность 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();
}

Это просто пример, показывающий, что вы можете легко создавать свои собственные события. они настолько полезны, просты, быстры и надежны.

вы можете изменить свой пример в соответствии с вашими потребностями.

и для вашего вопроса: "Как я могу заставить Activity A закончить, когда он приостановлен под прозрачной Activity B?"

вы можете использовать тот же подход. когда действие B возобновляется, оно должно уведомить всех слушателей (какая деятельность A является одной из них).

Ответ 4

Изменить. Посмотрел 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) конкретное условие находится где-то в 4k строках ActivityState.java.

оригинальное сообщение

В итоге я написал обходной путь для этой проблемы. Проблема, как вы помните, заключается в том, что вызов 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();
    }
}