Android面试题-onSaveInstanceState源码内核分析
来源:互联网 发布:名匠装饰怎么样知乎 编辑:程序博客网 时间:2024/05/17 20:15
源码分析相关面试题
- Volley源码分析
- 注解框架实现原理
- okhttp3.0源码分析
- onSaveInstanceState源码分析
Activity相关面试题
- 保存Activity的状态
与XMPP相关面试题
- 与XMPP相关试题一
- 与XMPP相关试题二
与性能优化相关面试题
- 与性能优化相关面试题一
- 与性能优化相关面试题二
- 与性能优化相关面试题三
- 与性能优化相关面试题四
- 与性能优化相关面试题五
- 与性能优化相关面试题六
- 与IPC机制相关面试题
与登录相关面试题
- oauth认证协议原理
- token产生的意义
- 微信扫一扫实现原理
与开发相关面试题
- 迭代开发的时候如何向前兼容新旧接口
- 手把手教你如何解决as jar包冲突
- context的原理分析
- 解决ViewPager.setCurrentItem中间很多页面切换方案
与人事相关面试题
- 人事面试宝典
经常有人问,后台的activity被系统自动回收的话,怎么回到界面的时候恢复数据,通过一个真实案例给大家讲讲如何保存状态,然后带着大家分析onSaveInstanceState的源码。
当前页面侧滑菜单指向专题,用户做了如下操作:
1)当用户按下HOME键时。
2)长按HOME键,选择运行其他的程序时。
3)按下电源按键(关闭屏幕显示)时。
4)从activity A中启动一个新的activity时。
5)屏幕方向切换时,例如从竖屏切换到横屏时。
失去焦点,activity很可能被进程终止!被KILL掉了,这时候就需要能保存当前的状态,不然下次用户再次进来看到的还是新闻,这样用户体验就不够好,代码有删减,我自己项目就这样使用的,解决方案如下:
@Overridepublic void onSaveInstanceState(Bundle outState) { outState.putInt("newsCenter_position", newsCenterPosition); outState.putInt("smartService_position", smartServicePosition); outState.putInt("govAffairs_position", govAffairsPosition); super.onSaveInstanceState(outState);}
如上代码可知:
1)界面被回收之后调用onSaveInstanceState方法保存当前的状态,每个侧滑菜单选项都有一个位置。
@Overridepublic void onCreate(Bundle savedInstanceState) {if (savedInstanceState != null && savedInstanceState.containsKey("newsCenter_position")) { newsCenterPosition = savedInstanceState .getInt("newsCenter_position"); smartServicePosition = savedInstanceState .getInt("smartService_position"); govAffairsPosition = savedInstanceState .getInt("govAffairs_position");} super.onCreate(savedInstanceState);}
由以上代码可知:
1)判断当前Bundle 是否有刚刚我们保存的位置,如果不为空,从当前的Bundle取出来,给每一个位置赋值。
public void switchMenu(int type) {switch (type) { case NEWS_CENTER: ...... if (newsCenterAdapter == null) { newsCenterAdapter = new MenuAdapter(ct, newsCenterMenu); newsCenterclassifyLv.setAdapter(newsCenterAdapter); } else { newsCenterAdapter.notifyDataSetChanged(); } newsCenterAdapter.setSelectedPosition(newsCenterPosition); break; case SMART_SERVICE: ...... if (smartServiceAdapter == null) { smartServiceAdapter = new MenuAdapter(ct, smartServiceMenu); smartServiceclassifyLv.setAdapter(smartServiceAdapter); } else { smartServiceAdapter.notifyDataSetChanged(); } smartServiceAdapter.setSelectedPosition(smartServicePosition); break; case GOV_AFFAIRS: ...... if (govAffairsAdapter == null) { govAffairsAdapter = new MenuAdapter(ct, govAffairsMenu); govAffairsclassifyLv.setAdapter(govAffairsAdapter); } else { govAffairsAdapter.notifyDataSetChanged(); } govAffairsAdapter.setSelectedPosition(govAffairsPosition); break;}
以上代码可知:
1)根据当前的位置设置到adapter当中,这样下次用户进来就还是专题了。
总结下savedInstanceState的使用,代码如下:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(savedInstanceState != null) System.out.println("onCreate() : " + savedInstanceState.getString("octopus")); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); System.out.println("onRestoreInstanceState() : " + savedInstanceState.getString("octopus")); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("octopus", "www.baidu.com"); System.out.println("onSaveInstanceState() : save date www.baidu.com"); } }
横竖屏切换,打印结果如下:
I/System.out( 8167): onSaveInstanceState() : save date www.baidu.com I/System.out( 8167): onCreate() : www.baidu.com I/System.out( 8167): onRestoreInstanceState() : www.baidu.com
从打印结果可以看出来,当前Activity被系统回收之后,会调用onSaveInstanceState()保存状态,然后在activity判断bundler是否有当前状态,如果只是到这,估计你们就会吐槽没啥含金量,没办法硬着头皮上,接着咱们来分onSaveInstanceState()源码,请看如下代码:
@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); }
以上代码可知
1)调用父类Activity源码里面的onSaveInstanceState方法,代码如下:
protected void onSaveInstanceState(Bundle outState) { outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState()); Parcelable p = mFragments.saveAllState(); if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); } ......}
以上代码可知
1)outState.put一个tag调用了mWindow里面的saveHierarchyState方法,继续分析Window源代码。
2)window是抽象类调用子类PhoneWindow里面的saveHierarchyState方法代码如下:
@Overridepublic Bundle saveHierarchyState() { Bundle outState = new Bundle(); if (mContentParent == null) { return outState; } SparseArray<Parcelable> states = new SparseArray<Parcelable>(); mContentParent.saveHierarchyState(states); outState.putSparseParcelableArray(VIEWS_TAG, states); ...... return outState;}
以上代码可知
1 ) Bundle outState = new Bundle()初始化Bundle对象,Bundle实现了Parcelable接口。
2)states = new SparseArray()并且把自己放到outState当中。
3)mContentParent.saveHierarchyState(states),整个View树的顶层视图保存了层级状态代码如下:
public void saveHierarchyState(SparseArray<Parcelable> container) { dispatchSaveInstanceState(container);}
以上代码可知:
1)调相应的dispatchSaveInstanceState方法,代码如下:
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) { mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED; Parcelable state = onSaveInstanceState(); if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) { throw new IllegalStateException( "Derived class did not call super.onSaveInstanceState()"); } if (state != null) { // Log.i("View", "Freezing #" + Integer.toHexString(mID) // + ": " + state); container.put(mID, state); } } }
以上代码可知:
1) mID != NO_ID 判断一个View必须有一个id,也就是说你要么在xml里通过android:id指定要么在代码里通过setId,但是你从如上代码压根是看不出来谷歌想干啥,必须全局搜索NO_ID 和 mID ,一般在源码里面都会有谷歌工程师的注释方便我们理解,搜索NO_ID 可知代码如下:
/** * Used to mark a View that has no ID. */ public static final int NO_ID = -1;
原来NO_ID用来标记没有id的View,搜索mID可知原来在如下代码赋值
public void setId(@IdRes int id) { mID = id; if (mID == View.NO_ID && mLabelForId != View.NO_ID) { mID = generateViewId(); } }
经常当我们看不懂谷歌源码的时候,可以通过曲线救国的方式,看看英文注释,看看源码哪个地方用到当前的类或者方法或者变量,这样就好理解了,好了扯远了,继续分析代码;
2)通过if判断,检测子类是否调用父类的onSaveInstanceState()方法,否则会抛异常,突然看到这才明白,还记得刚刚开始学Android的时候,经常一不小心就把代码里面的super.onCreate(savedInstanceState);这行代码删掉,报了错误还看不懂,原来系统在这里检测了,都怪自己曾经太年轻。
3)container.put(mID, state)这行代码,将state放进SparseArray中,以view自身的id为key,并且从注释来看打印mID的Hex值用来保证每页的id必须是唯一的,难怪每当我给view取id的时候,一个页面有重复的id就会报错,谷歌大婶在这里做判断了,腻害了word哥,总是百思不得其姐,凭啥不让我共用id(因为取名字太难了),原来是想把id做为key来使用。
4)走到这onSaveInstanceState(),调用如下代码:
@CallSuperprotected Parcelable onSaveInstanceState() { mPrivateFlags |= PFLAG_SAVE_STATE_CALLED; ...... return BaseSavedState.EMPTY_STATE;}
以上代码可知:
1)设置位标志, 默认不save任何东西,状态为空,这就是为啥我们每次随便写个类继承activity实现onCreate方法的时候可以使用参数savedInstanceState保存状态,因为默认为null,代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); savedInstanceState.putString("key","value");}
至此整个savedInstanceState保存状态源码分析完毕。
欢迎关注微信公众号,长期推荐技术文章和技术视频
微信公众号名称:Android干货程序员
- Android面试题-onSaveInstanceState源码内核分析
- Android面试题-OkHttp3源码分析
- Android面试题-Volley源码分析
- Android面试题-onCreate源码分析
- Android面试题-IntentService源码分析
- Android面试题-LayoutInflater源码分析
- Android面试题-SharedPreferences源码分析
- Android 经典面试题分析
- android面试题分析总结
- Android面试题总结分析
- HashMap源码分析及面试题解答
- String源码分析,解读面试题
- linux内核面试题
- LINUX内核面试题
- linux内核面试题
- java android面试题分析总结
- android 腾讯面试题分析与讲解
- java android面试题分析总结
- 对拍
- 高瓴资本张磊:太循规蹈矩的创业者,不适合创业
- Java中常用的锁分析总结
- Android学习之剪贴板操作
- 矩阵(matrix)应用大总结(一)WOJ 642 Lost In WHU + POJ 3233
- Android面试题-onSaveInstanceState源码内核分析
- mysql学习笔记
- Objective-C 编程基本概念
- 使用GCD进行倒计时操作
- php 远程上传文件 远程上传图片
- 谷歌深度学习公开课学习笔记(0)
- 生产者和消费者问题(java简单实现)
- POJ 3368 ST处理RMQ
- 学习dubbo第一步,了解spring框架的XML扩展特性:让spring加载和解析你自定义的XML文件