关于此问题的官方文档:https://developer.android.com/guide/topics/resources/runtime-changes.html
问题:系统配置发生改变后,会导致back stack中的activity重新走一遍生命周期,从而会引发一些逻辑问题以及状态显示相关的问题。
解决方法:1.按官方文档来。
缺点:很明显,需要制定规范,每个activity都需要注意,沟通+维护都会有潜在问题。
2.由于项目中遇到此问题已经处于维护阶段,针对每个activity按官方文档修改已不太现实,只能简单粗暴的修改:
(1) 当前处于非主界面,系统配置发生变化后跳转至主界面(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
(2)所有activity中带有fragment的,对fragments进行缓存,activity创建时优先读取缓存对fragment
代码:
AndroidManifest登录成功的主界面会监听配置改变的事件:
1 <activity 2 android:name=".activity.MainActivity" 3 android:configChanges="locale|layoutDirection|navigation|fontScale" 4 android:screenOrientation="portrait"> 5 </activity>
MainActivity监听:
@Override public void onConfigurationChanged(Configuration newConfig) { Logger.i("@@@@@@","ON Main Activity Cfg change "); super.onConfigurationChanged(newConfig); Intent goToMainActivity = new Intent(getApplicationContext(), MainActivity.class); goToMainActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);; // Will clear out your activity history stack till now startActivity(goToMainActivity); }
带有fragment的activity(以下为使用ViewPager管理fragment的修改方法):
重写onResumeFragments方法,把fragment放入fragmentManager中进行缓存
1 @Override 2 protected void onSaveInstanceState(Bundle outState) { 3 super.onSaveInstanceState(outState); 4 android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager(); 5 fragmentManager.putFragment(outState, ChannelsFragment.class.getName(), fmChannels); 6 Logger.d(LOG_TAG,"mainActivity onSaveInstanceState"); 7 }
onCreate时尝试从缓存中读取:
1 if(savedInstanceState!=null){ 2 Logger.d(LOG_TAG,"mainActivity onCreate"); 3 fmChannels = (ChannelsFragment) getSupportFragmentManager().getFragment(savedInstanceState,ChannelsFragment.class.getName());
8 }
if(fmChannels==null){
Logger.d(LOG_TAG,"fmChannels is null");
fmChannels = new ChannelsFragment();
}
关于VIewPager使用缓存fragment链接:https://stackoverflow.com/questions/7951730/viewpager-and-fragments-whats-the-right-way-to-store-fragments-state
里面提到了使用FragmentPageAdapter.instantiateItem(View, int)同样也可以实现缓存读取,也更方便一点。
附阿里Android开发手册中:
添 加 Fragment 时 , 确 保 FragmentTransaction#commit() 在 Activity#onPostResume()或者 FragmentActivity#onResumeFragments()内调用。 不要随意使用 FragmentTransaction#commitAllowingStateLoss()来代替,任何 commitAllowingStateLoss()的使用必须经过 code review,确保无负面影响。
推荐的做法是在 Activity 的onPostResume() 或 onResumeFragments() ( 对 FragmentActivity ) 里 执 行 FragmentTransaction.commit(),如有必要也可在 onCreate()里执行。不要随意改用FragmentTransaction.commitAllowingStateLoss()或者直接使用 try-catch 避免crash,这不是问题的根本解决之道,当且仅当你确认 Activity 重建、恢复状态时,本次 commit 丢失不会造成影响时才可这么做。
正例:
public class MainActivity extends FragmentActivity { FragmentManager fragmentManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); fragmentManager = getSupportFragmentManager(); FragmentTransaction ft = fragmentManager.beginTransaction(); MyFragment fragment = new MyFragment(); ft.replace(R.id.fragment_container, fragment); ft.commit(); } }
反例:
public class MainActivity extends FragmentActivity { FragmentManager fragmentManager; @Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { super.onSaveInstanceState(outState, outPersistentState); fragmentManager = getSupportFragmentManager(); FragmentTransaction ft = fragmentManager.beginTransaction(); MyFragment fragment = new MyFragment(); ft.replace(R.id.fragment_container, fragment); ft.commit(); } }