发生这种情况的原因有很多,但希望这种情况很少发生。
如果需要回收资源,操作系统会在后台销毁 Activity,这更可能发生在内存和处理能力较低的设备上。
使用Do not keep Activities 设置是测试这种情况的好方法,在这种情况下,即使重新创建了 Activity/Fragment 也会出现其他问题。启用此设置后,显示 PlacePicker 时 Activity 和 Fragment 确实会被销毁,然后当 onActivityResult() 进入时,没有有效的 Context 因为 Activity 和 Fragment 仍在重新创建过程中。
我在禁用设置的情况下执行受控测试后发现了这一点,然后启用了设置,然后查看结果
我将日志记录在 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 上都被调用。
这是视觉上的结果:
然后选好地方后:
然后,我在“设置”的“开发人员选项”下启用了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 的同时进行的,您需要一个有效的上下文来调用 PlacePicker.getPlace(data, getActivity());。
好消息是大多数最终用户不会启用Do not keep Activities 设置,并且大多数情况下您的活动不会被操作系统破坏。