返回startActivityForResult后,不会重新创建Android parentActivity

Ren*_*ith 22 android android-intent android-fragments android-activity

我有一个MainActivity,在里面,我正在加载一个片段A.从FragmentA,我使用startActivityforResult调用google placepicker活动,如下所示.

PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
Intent intent = builder.build(getActivity());
getActivity().startActivityForResult(intent,PLACE_PICKER_REQUEST);
Run Code Online (Sandbox Code Playgroud)

但是当我选择地点时,onActivityResult(在FragmentA或MainActivity中)不会被调用.事实上,我的应用程序在startActivityForResult调用后被破坏了.

根据我的理解,android应该重新创建调用活动,如果它在内存中不可用.但它没有发生.甚至没有在MainActivity中调用onCreate.

谁能告诉我这种行为背后的原因还是我错过了什么?

现在我尝试在同一个应用程序中使用另一个活动,而不是PlacePicker活动.

比方说,我有MainActivityFragmentAloaded.I我打电话SubActivitystartActivityForResultFragmentA.现在虽然无法返回SubActivity,退出应用程序.我已Dont keep activities在设备中启用此特定方案的测试.MainActivity当我搬到时,我可以看到被摧毁SubActivity.但是从返回时SubActivity,android没有重新创建MainActivity(甚至onCreate没有被调用.应用程序刚刚退出).

Dan*_*ent 18

这可能由于许多原因而发生,但希望这种情况很少发生.如果需要回收资源,操作系统将在后台处理时销毁活动,这更有可能发生在内存和处理能力较低的设备上.

使用该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)));

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是生成的日志,可以深入了解在正常情况下进程中调用的生命周期回调:

 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
Run Code Online (Sandbox Code Playgroud)

所以,你可以看到onDestroy()永远不会被调用,并onPause()onResume()被称为双方的活动和片段.

以下是视觉结果:

PlacePicker

然后挑选一个地方:

地图上显示的地方

然后,我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
Run Code Online (Sandbox Code Playgroud)

因此,当PlacePicker被显示时,您可以看到Activity和Fragment都被销毁,并且在PlacePicker中选择了地点之后,代码执行从未进入Fragment processActivityResult日志条目,并且应用程序从未在地图上显示选定的位置.

那是因为空的上下文检查:

 if (getActivity() == null) return;

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

 //process Intent......
 Place place = PlacePicker.getPlace(data, getActivity());
Run Code Online (Sandbox Code Playgroud)

因此,调用onActivityResult()确实会进入,但它会在重新创建Activity和Fragment的同时执行,并且您需要一个有效的Context来进行调用PlacePicker.getPlace(data, getActivity());.

好消息是大多数最终用户都没有Do not keep Activities启用该设置,并且大多数时候您的活动不会被操作系统销毁.


Phi*_*lio 7

Android以您描述的方式清理活动似乎很不寻常,但如果是这种情况,那么您的活动仍应恢复.Android不应该破坏活动,除非你专门打电话finish()或某些事情导致活动提前结束.

如果您参考活动生命周期图:

在您描述的场景中,第一个活动应该调用onStop,而不是onDestroy,然后当您从第二个活动返回时,它应该再次调用onStart.

我创建了一个非常简单的应用程序来测试您描述的场景,其中包含以下内容:

  • 有两个活动,FirstActivity和SecondActivity
  • FirstActivity有一个按钮,当单击该按钮时,它启动SecondActivity startActivityForResult()
  • 使用ActivityLifecycleCallbacks自定义应用程序类记录活动生命周期事件
  • 在FirstActivity onActivityResult中,当它被调用时另外输出到日志

这是输出:

应用程序启动(创建并启动并显示FirstActivity):

FirstActivity onCreate
FirstActivity onStart
FirstActivity onResume
Run Code Online (Sandbox Code Playgroud)

我按下按钮启动SecondActivity:

FirstActivity onPause
SecondActivity onCreate
SecondActivity onStart
SecondActivity onResume
FirstActivity onSaveInstanceState
FirstActivity onStop
Run Code Online (Sandbox Code Playgroud)

注意,onDestroy不会被调用.

现在我按下后退按钮返回第一个活动:

SecondActivity onPause
FirstActivity onStart
FirstActivity onActivityResult
FirstActivity onResume
SecondActivity onStop
SecondActivity onDestroy
Run Code Online (Sandbox Code Playgroud)

后退按钮调用finishSecondActivity,因此它被销毁

现在,如果我再次按回来,FirstActivity也将完成,导致onDestroy被调用.

FirstActivity onPause
FirstActivity onStop
FirstActivity onDestroy
Run Code Online (Sandbox Code Playgroud)

您可以看到此示例已完全遵循生命周期图.只有按下后退按钮才会销毁活动,这会导致活动调用finish().

您提到您尝试在开发人员选项中启用"不要保留活动",我们可以重复上面启用此选项的实验,看看会发生什么.我刚刚添加了相关的生命周期事件来保存重复上面的所有内容:

在第一个活动中按下按钮后开始第二个活动:

...
SecondActivity onResume
FirstActivity onSaveInstanceState
FirstActivity onStop
FirstActivity onDestroy
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,此次活动已被破坏.当您再次导航回第一个活动时会发生这种情况:

SecondActivity onPause
FirstActivity onCreate
FirstActivity onStart
FirstActivity onActivityResult
FirstActivity onResume
...
Run Code Online (Sandbox Code Playgroud)

此次onCreate再次被调用,因为系统没有要重新启动的第一个活动的停止版本.也onActivityResult()仍称为,不管事实,活动不得不重新创建.

这进一步支持您的第一个活动中的某些内容必须调用finish()或导致其崩溃.但是,如果没有看到您的实际代码,这就是猜想.

最后,要维护状态,如果您的活动由于某种原因需要重新创建,您可以覆盖onSaveInstanceState()并向捆绑添加任何状态信息:

protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString(MY_STRING_KEY, "my string value");
}
Run Code Online (Sandbox Code Playgroud)

重新创建活动后,您将在onCreate中获得一个包,其中应包含您保存的所有内容:

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

  • 为什么这是公认的答案?它没有回答imo这个问题. (2认同)