Подтвердить что ты не робот

Android parentActivity не воссоздается после возврата startActivityForResult

У меня есть MainActivity и внутри него, я загружаю фрагмент A. Из FragmentA я вызываю активность google placepicker, используя startActivityforResult следующим образом.

PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
Intent intent = builder.build(getActivity());
getActivity().startActivityForResult(intent,PLACE_PICKER_REQUEST);

Но когда я выбираю место, onActivityResult (либо в FragmentA, либо MainActivity) не получает вызов. Фактически, мое приложение уничтожается после вызова startActivityForResult.

В соответствии с моим пониманием, андроид должен воссоздать вызывающую деятельность, если она недоступна в памяти. Но этого не происходит. Даже onCreate не получает вызов внутри MainActivity.

Может ли кто-нибудь сказать мне причину такого поведения или я что-то пропустил?

Теперь вместо ActivityPicker Activity я попытался использовать другое действие в том же приложении.

Скажем, у меня есть MainActivity с FragmentA. Я вызываю SubActivity с startActivityForResult из FragmentA. Теперь, возвращаясь из SubActivity, приложение завершает работу. Я включил Dont keep activities на своем устройстве, чтобы протестировать этот конкретный сценарий. Я могу увидеть, что MainActivity будет уничтожен, когда я перейду к SubActivity. Но при возврате из SubActivity, андроид не воссоздает MainActivity (даже onCreate не получает вызов. Приложение просто выходит).

4b9b3361

Ответ 1

Кажется довольно необычным для Android, чтобы очистить активность так, как вы описали, но если это так, то ваша деятельность должна быть восстановлена. Android не должен уничтожать активность, если вы специально не назовете finish(), или что-то заставляет действие заканчиваться досрочно.

Если вы ссылаетесь на диаграмму жизненного цикла активности:

В описанном вами сценарии первое действие должно вызывать onStop, но не onDestroy, а затем, когда вы возвращаетесь со второго действия, он должен снова вызвать onStart.

Я создал очень простое приложение для тестирования описанного вами сценария, который содержал следующее:

  • Есть 2 действия, FirstActivity и SecondActivity
  • FirstActivity имеет кнопку, когда нажата кнопка, она запускает SecondActivity с startActivityForResult()
  • События жизненного цикла активности регистрируются с помощью ActivityLifecycleCallbacks в пользовательском классе приложения
  • В FirstActivity onActivityResult дополнительно выводится в журнал при его вызове

Вот что выводится:

Запускается приложение (FirstActivity создается и запускается и отображается):

FirstActivity onCreate
FirstActivity onStart
FirstActivity onResume

Я нажимаю кнопку, чтобы запустить SecondActivity:

FirstActivity onPause
SecondActivity onCreate
SecondActivity onStart
SecondActivity onResume
FirstActivity onSaveInstanceState
FirstActivity onStop

Примечание. onDestroy не вызывается.

Теперь я нажимаю кнопку "Назад" и возвращаюсь к первому действию:

SecondActivity onPause
FirstActivity onStart
FirstActivity onActivityResult
FirstActivity onResume
SecondActivity onStop
SecondActivity onDestroy

Задняя кнопка вызывает finish в SecondActivity, чтобы она уничтожила

Теперь, если я снова вернусь, FirstActivity также будет закончен, вызывая onDestroy для вызова.

FirstActivity onPause
FirstActivity onStop
FirstActivity onDestroy

Вы можете видеть, что этот пример точно соответствует диаграмме жизненного цикла. Действия уничтожаются только после нажатия кнопки "Назад", что заставляет операцию вызывать finish().

Вы упомянули, что вы пытались включить "Не продолжать действия" в настройках разработчика, мы можем повторить вышеупомянутый эксперимент, который включил эту опцию, и посмотреть, что произойдет. Я только что добавил соответствующие события жизненного цикла, чтобы сохранить повторяющееся все, что находится выше:

После нажатия кнопки в первом действии для запуска второго действия:

...
SecondActivity onResume
FirstActivity onSaveInstanceState
FirstActivity onStop
FirstActivity onDestroy

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

SecondActivity onPause
FirstActivity onCreate
FirstActivity onStart
FirstActivity onActivityResult
FirstActivity onResume
...

В этот раз onCreate был вызван снова, поскольку в системе не было остановленной версии первого действия для перезапуска. Кроме того, еще onActivityResult() вызывался, независимо от того, что активность должна была быть воссоздана.

Это также подтверждает, что что-то в вашем первом действии должно вызывать finish() или вызвать его сбой. Однако, не видя своего фактического кода, это гипотеза.

Наконец, чтобы сохранить состояние, если ваша деятельность по какой-то причине нуждается в воссоздании, вы можете переопределить onSaveInstanceState() и добавить любую информацию о состоянии в пакет:

protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString(MY_STRING_KEY, "my string value");
}

Когда активность будет воссоздана, вы получите пакет в onCreate, который должен содержать все, что вы сохранили:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    if (savedInstanceState != null) {
        // Restore previous state
    }
}

Ответ 2

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

Использование параметра Do not keep Activities - хороший способ протестировать этот сценарий, и в этом случае возникают другие проблемы, даже если активность/фрагмент действительно воссоздаются. Когда этот параметр включен, активность и фрагмент будут уничтожены, когда будет показан PlacePicker, а затем, когда появится onActivityResult(), нет действительного контекста, потому что активность и фрагмент все еще находятся в процессе воссоздания.

Я нашел это, выполнив контролируемый тест с отключенным параметром, а затем включив параметр, а затем просмотрев результаты

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

Вот полный класс, который я использовал, который включает как Activity, так и Fragment:

public class MainActivity extends AppCompatActivity {

    MyFragment myFrag;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d("PlacePickerTest", "Activity onCreate");

        myFrag = new MyFragment();

        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, myFrag)
                    .commit();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        Log.d("PlacePickerTest", "Activity onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();

        Log.d("PlacePickerTest", "Activity onPause");
    }

    @Override
    protected void onDestroy() {
        Log.d("PlacePickerTest", "Activity onDestroy");
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public void onActivityResult (int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        Log.d("PlacePickerTest", "Activity onActivityResult requestCode:" + requestCode);

        if (requestCode == 199){
            //process result of PlacePicker in the Fragment
            myFrag.processActivityResult(data);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            //open PlacePicker from menu item
            myFrag.startPlacePicker();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * Fragment containing a map and PlacePicker functionality
     */
    public static class MyFragment extends Fragment {

        private GoogleMap mMap;
        Marker marker;

        public MyFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);

            Log.d("PlacePickerTest", "Fragment onCreateView");

            return rootView;
        }


        @Override
        public void onResume() {
            super.onResume();

            Log.d("PlacePickerTest", "Fragment onResume");

            setUpMapIfNeeded();
        }

        @Override
          public void onPause() {
            super.onPause();

            Log.d("PlacePickerTest", "Fragment onPause");
        }

        @Override
        public void onDestroy() {
            Log.d("PlacePickerTest", "Fragment onDestroy");
            super.onDestroy();
        }

        private void setUpMapIfNeeded() {
            // Do a null check to confirm that we have not already instantiated the map.
            if (mMap == null) {
                // Try to obtain the map from the SupportMapFragment.
                mMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map))
                        .getMap();
                // Check if we were successful in obtaining the map.
                if (mMap != null) {
                    setUpMap();
                }
            }
        }

        private void setUpMap() {

            // Enable MyLocation Layer of Google Map
            mMap.setMyLocationEnabled(true);
            mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
            mMap.getUiSettings().setZoomControlsEnabled(true);
            mMap.getUiSettings().setMyLocationButtonEnabled(true);
            mMap.getUiSettings().setCompassEnabled(true);
            mMap.getUiSettings().setRotateGesturesEnabled(true);
            mMap.getUiSettings().setZoomGesturesEnabled(true);

        }

        public void startPlacePicker(){
            int PLACE_PICKER_REQUEST = 199;
            PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
            //Context context = getActivity();
            try {
                Log.d("PlacePickerTest", "Fragment startActivityForResult");
                getActivity().startActivityForResult(builder.build(getActivity()), PLACE_PICKER_REQUEST);
            } catch (GooglePlayServicesRepairableException e) {
                e.printStackTrace();
            } catch (GooglePlayServicesNotAvailableException e) {
                e.printStackTrace();
            }
        }

        public void processActivityResult ( Intent data) {

            if (getActivity() == null) return;

            Log.d("PlacePickerTest", "Fragment processActivityResult");


            //process Intent......
            Place place = PlacePicker.getPlace(data, getActivity());
            String placeName = String.format("Place: %s", place.getName());
            String placeAddress =  String.format("Address: %s", place.getAddress());

            LatLng toLatLng = place.getLatLng();

            // Show the place location in Google Map
            mMap.moveCamera(CameraUpdateFactory.newLatLng(toLatLng));
            mMap.animateCamera(CameraUpdateFactory.zoomTo(15));

            if (marker != null) {
                marker.remove();
            }
            marker = mMap.addMarker(new MarkerOptions().position(toLatLng)
                    .title(placeName).snippet(placeAddress)
                    .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA)));

        }
    }
}

Вот результирующие журналы, которые дают представление о том, какие вызовы жизненного цикла вызываются в процессе при нормальных обстоятельствах:

 D/PlacePickerTest﹕ Activity onCreate
 D/PlacePickerTest﹕ Fragment onCreateView
 D/PlacePickerTest﹕ Activity onResume
 D/PlacePickerTest﹕ Fragment onResume
 D/PlacePickerTest﹕ Fragment startActivityForResult
 D/PlacePickerTest﹕ Fragment onPause
 D/PlacePickerTest﹕ Activity onPause
 D/PlacePickerTest﹕ Activity onActivityResult requestCode:199
 D/PlacePickerTest﹕ Fragment processActivityResult
 D/PlacePickerTest﹕ Activity onResume
 D/PlacePickerTest﹕ Fragment onResume

Итак, как вы можете видеть, onDestroy() никогда не вызывается, а onPause() и onResume() вызываются как для Activity, так и для Fragment.

Вот результат визуально:

PlacePicker

Затем после выбора места:

place shown on map

Затем я включил Do not keep Activities в разделе "Параметры разработчика" в "Настройки" и выполнил тот же тест.

Это результирующие журналы:

 D/PlacePickerTest﹕ Activity onCreate
 D/PlacePickerTest﹕ Fragment onCreateView
 D/PlacePickerTest﹕ Activity onResume
 D/PlacePickerTest﹕ Fragment onResume
 D/PlacePickerTest﹕ Fragment startActivityForResult
 D/PlacePickerTest﹕ Fragment onPause
 D/PlacePickerTest﹕ Activity onPause
 D/PlacePickerTest﹕ Activity onDestroy
 D/PlacePickerTest﹕ Fragment onDestroy
 D/PlacePickerTest﹕ Activity onCreate
 D/PlacePickerTest﹕ Fragment onCreateView
 D/PlacePickerTest﹕ Activity onActivityResult requestCode:199
 D/PlacePickerTest﹕ Activity onResume
 D/PlacePickerTest﹕ Fragment onResume

Итак, вы можете увидеть, как активность и фрагмент уничтожаются, когда отображается PlacePicker, и после того, как место выбрано в PlacePicker, выполнение кода никогда не попадало в запись журнала Fragment processActivityResult, и приложение никогда не показывало выбранное место на карте.

Это из-за нулевой проверки контекста:

 if (getActivity() == null) return;

 Log.d("PlacePickerTest", "Fragment processActivityResult");

 //process Intent......
 Place place = PlacePicker.getPlace(data, getActivity());

Таким образом, вызов onActivityResult() поступает, но он делает это одновременно с тем, что активность и фрагмент восстанавливаются заново, и для выполнения вызова PlacePicker.getPlace(data, getActivity()); необходим действительный контекст.

Хорошей новостью является то, что большинство конечных пользователей не будут включать параметр Do not keep Activities, и большую часть времени ваша активность не будет уничтожена ОС.