我有一个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
。我正在SubActivity
来startActivityForResult
来FragmentA
。{ {1}},应用程序退出。我已在设备中启用SubActivity
来测试此特定方案。当我移动到Dont keep activities
时,我可以看到MainActivity
被破坏。但是从SubActivity
返回时,android没有重新创建SubActivity
(偶数MainActivity
没有被调用。应用程序刚刚退出。)
答案 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()
也会在活动和片段上被调用。
以下是视觉结果:
然后选择一个地方:
然后,我在“设置”中的“开发者选项”下启用了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。
我创建了一个非常简单的应用来测试您描述的场景,其中包含以下内容:
startActivityForResult()
ActivityLifecycleCallbacks
记录活动生命周期事件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
}
}