在startActivityForResult返回

时间:2015-06-15 18:29:17

标签: 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);

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

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

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

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

我们说我MainActivity加载了FragmentA。我正在SubActivitystartActivityForResultFragmentA。{ {1}},应用程序退出。我已在设备中启用SubActivity来测试此特定方案。当我移动到Dont keep activities时,我可以看到MainActivity被破坏。但是从SubActivity返回时,android没有重新创建SubActivity(偶数MainActivity没有被调用。应用程序刚刚退出。)

2 个答案:

答案 0 :(得分:18)

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

使用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()也会在活动和片段上被调用。

以下是视觉结果:

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时,您可以看到Activity和Fragment都被销毁,并且在PlacePicker中选择了该位置之后,代码执行从未进入Fragment processActivityResult日志条目,并且应用程序从不在地图上显示了选中的位置。

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

 if (getActivity() == null) return;

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

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

因此,对onActivityResult()的调用确实进入了,但它在重新创建Activity和Fragment的同时这样做,并且你需要一个有效的Context来调用{{1 }}

好消息是大多数最终用户都没有启用PlacePicker.getPlace(data, getActivity());设置,并且大部分时间您的活动都不会被操作系统销毁。

答案 1 :(得分:7)

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

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

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

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

  • 有2项活动,FirstActivity和SecondActivity
  • FirstActivity有一个按钮,当点击该按钮时,它会以startActivityForResult()
  • 启动SecondActivity
  • 使用自定义应用程序类中的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

后退按钮在SecondActivity上调用finish以便它被销毁

现在,如果我再次按回来,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
    }
}