在 startActivityForResult 返回后,Android parentActivity 没有重新创建
Posted
技术标签:
【中文标题】在 startActivityForResult 返回后,Android parentActivity 没有重新创建【英文标题】:Android parentActivity not getting recreated after startActivityForResult returns 【发布时间】:2015-08-31 09:03:16 【问题描述】:我有一个 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 应该重新创建它。但它没有发生。即使 onCreate 也没有在 MainActivity 中被调用。
谁能告诉我这种行为背后的原因还是我错过了什么?
现在我尝试在同一个应用程序中使用另一个 Activity,而不是 PlacePicker Activity。
假设我加载了MainActivity
和FragmentA
。我正在从FragmentA
调用SubActivity
和startActivityForResult
。现在从SubActivity
返回时,应用程序退出。我在我的设备中启用了Dont keep activities
来测试这个特定的场景。当我移动到SubActivity
时,我可以看到MainActivity
被破坏。但是从SubActivity
返回时,android 不会重新创建MainActivity
(甚至onCreate
也没有被调用。应用程序只是退出)。
【问题讨论】:
“我的应用程序在 startActivityForResult 调用后被破坏了。” 你的意思是它正在崩溃吗? 您是否在清单文件中提供了意图过滤器?这是重新创建您的活动所必需的 @codeMagic 它没有崩溃。调用 startActivityForResult 后,我可以看到 MainActivity 中的 onDestory 方法被调用。当我从启动的活动中选择一个位置时,它会返回。但我的应用程序没有重新创建。如果我选择的地方足够快(在我的 MainActivity 被破坏之前),onActivityResult 会被正确调用。 您是否在某处为该活动致电finish()
?也许在onActivityResult()
或其他地方?
我在我的一个应用程序中遇到了相同的情况,并且启用了“不保留活动”后,一旦启动的 Activity 返回,我的 Activity 将被销毁并重新创建。这是预期的行为,因此您的应用程序或设备有一些不寻常的地方。如果没有更多代码(包括清单)和有关您的设备/操作系统的详细信息,我无法回答这个问题。
【参考方案1】:
发生这种情况的原因有很多,但希望这种情况很少发生。 如果需要回收资源,操作系统会在后台销毁 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
设置,并且大多数情况下您的活动不会被操作系统破坏。
【讨论】:
如果操作系统删除了调用 startActivityForResult 的活动,就会发生这种情况。 @JawadLeWywadi 你说得对,再读一遍我意识到这个答案有一些问题。刚刚编辑,我觉得现在好多了。谢谢! 这应该是正确的答案,尤其是对于设置“不要让活动保持活动状态”的提示......【参考方案2】:Android 以您描述的方式清理活动似乎很不寻常,但如果是这种情况,那么您的活动仍应恢复。 Android 不应销毁 Activity,除非您专门调用 finish()
或某些原因导致 Activity 过早结束。
如果参考活动生命周期图:
在您描述的场景中,第一个活动应该调用 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()
或导致它崩溃。但是,如果没有看到您的实际代码,这是推测。
最后,如果您的 Activity 出于某种原因需要重新创建,为了保持状态,您可以覆盖 onSaveInstanceState()
并将任何状态信息添加到包中:
protected void onSaveInstanceState(Bundle outState)
super.onSaveInstanceState(outState);
outState.putString(MY_STRING_KEY, "my string value");
重新创建 Activity 后,您会在 onCreate 中重新获得一个包,其中应该包含您保存的所有内容:
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
...
if (savedInstanceState != null)
// Restore previous state
【讨论】:
我正在测试重新创建活动的相同情况(请参阅我的编辑)。问题是即使返回时也没有调用 onCreate 方法。 为什么这是公认的答案?它没有回答 imo 的问题。以上是关于在 startActivityForResult 返回后,Android parentActivity 没有重新创建的主要内容,如果未能解决你的问题,请参考以下文章
Android Fragment 中的 startActivityForResult() 与 getActivity().startActivityForResult()
如何在 RecyclerView 中实现 StartActivityForResult
在视图中实现方法 startActivityForResult
Fragment的startActivityForResult和Activity的startActivityForResult的区别