一、
先看Application Fundamentals上的一段话:
Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key)
从这句话可以知道,当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。
注意上面的双引号,何为“容易”?言下之意就是该activity还没有被销毁,而仅仅是一种可能性。这种可能性有哪些?通过重写一个activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我们可以清楚地知道当某个activity(假定为activity A)显示在当前task的最上层时,其onSaveInstanceState方法会在什么时候被执行,有这么几种情况:
1、当用户按下HOME键时。
这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则
2、长按HOME键,选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从activity A中启动一个新的activity时。
5、屏幕方向切换时,例如从竖屏切换到横屏时。
在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState一定会被执行
总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。
至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity
A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行
另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原
二、
1 The system calls this method before making the activity vulnerable to being destroyed and passes it a Bundle object. The Bundle is where you can store state information about the activity as name-value pairs, using methods such as putString(). Then, if the system kills your activity's process and the user navigates back to your activity, the system passes the Bundle to onCreate() so you can restore the activity state you saved during onSaveInstanceState(). If there is no state information to restore, then the Bundle passed to onCreate()is null.
一般onSaveInstanceState(Bundle ) 和 onCreate(Bundle ) 结合使用来保存activity的状态,但是文档里面指出了几个问题:系 统不会保证onSaveInstanceState()在activity被销毁前总被调用(因为有一些不需要保存状态的情况,比如,用户用BACK键关 闭此activity);如果onSaveInstanceState()被调用,那么一定是在onStop()之前,或者可能在onPause()之 前。
2 如果你在onSaveInstanceState()不做啥事(不重载它),有些activity的状态也会被activity默认的 onSaveInstanceState()保存。比如,通常EditText会保存自己的状态,原因就是activity默认的 onSaveInstanceState()会调用其layout里面每一个View的onSaveInstanceState(),当然也包括此 EditView。你只需要为这些view提供唯一的ID(android:id属性),他们就会保存自己的状态,不用你操心了。这个自动保存可以通过 android:saveEnabled属性或者 setSaveEnabled()方法 来禁用。
3 “onSaveInstanceState() 只应该被用来保存activity暂时的状态(例如,类成员变量的值关联着UI),而不应该用来保存持久化数据。持久化数据应该当用户离开当前的 activity时,在 onPause() 中保存(比如,保存到数据库)。” 这里用onPause()令我不理解,因为文档前面提过,onPause()时,activity并没有完全看不到,而只是可能有个dialog盖住 它,所以不建议在onPause()中做耗时的工作。(这里我只能将写入数据库理解为轻量级操作了。。。。暂时我觉得持久化数据应该在 onDestroy()中做)。
4 可以用旋转屏幕来测试你的程序保存状态的能力。因为一般旋转时,activity会被重新 onCreate() onStart()等等,程序此时能够保存状态是很重要的。
三、
1 利用onSaveInstanceState()保存数据:
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt("flag", flag);
outState.putString("menu", this.menu);
outState.putString("shop", shop);
outState.putInt("editPosition", editPosition);
super.onSaveInstanceState(outState);
}
2 在onCreate方法中做数据还原
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (savedInstanceState != null) {
this.flag = savedInstanceState.getInt("flag");
this.editPosition = savedInstanceState.getInt("editPosition");
String menu = savedInstanceState.getString("menu");
if (!TextUtils.isEmpty(menu)) {
this.menu = menu;
}
String shop = savedInstanceState.getString("shop");
if (!TextUtils.isEmpty(shop)) {
this.shop = shop;
}
}
这篇文章是我在eoewiki上的翻译,如有不妥之处,请求提出您的宝贵建议或者建议。大家可以相互交流。
原文地址:http://docs.eoeandroid.com/training/multiple-apks/api.html 翻译:苏长利
- 1 为不同的API版本创建不同的APK
- 1.1 确认您是否需要多apk支持
- 1.2 画出你的需求
- 1.3 把所有的共用代码和共用资源放在同一个库工程里( Put All Common Code and Resources in a Library Project)
- 1.4 创建新的APK工程(Create New APK Projects)
- 1.5 调整Manifests文件(Adjust the Manifests)
- 1.6 发布前的检查(Go Over Pre-launch Checklist)
为了在开发android应用程序的时候加以利用google安卓市场的多apk支持特性,刚开始就采取一些良好的措施去增加对多apk的支持,是非常重要的,这样可以在将来开发的过程中减少不必要的麻烦。这一节将向您展示如何为你的app创建多apk支持--不同的apk支持不同的api版本。还将获得一些维护多apk代码库尽可能的简单的工具。
当你试图创建一个支持跨多代android系统的应用程序时,很自然的你希望你的应用程序可以在新设备上使用新特性,并且不会牺牲向后兼容。刚开始的时候认为通过创建多个apk去支持多设备是最好的,但是往往不是这样。而是使用单个的apk去替代多个apk,开发指南中有去完成这个目标有用的信息,包括如何使用支持库的信息。还可以学习到如何写只能运行在特定api版本的代码的方法,而不去使用像反射这样的非常消耗资源的技术。
如果你能让你的应用程序只使用一个apk,将有如下几点好处:
*发布和测试简单 *只需维护一个代码库 *应用程序可以适应不同配置的设备 *App可以跨设备运行 *你不必考虑market的要求,apk的升级或者apk属于哪类设备
假设您已经研究了这个文档,已经学习了链接页面的内容,并且确定多apk支持的程序是你需要的,那么请继续看下面的小节。
首先创建一个简单的图表来快速确定你需要多少个APK, 每个Apk覆盖的API范围。为方便参考,Android开发者网站[平台版本页面]提供各个系统平台相对活跃的设备数量的分布数据。虽然刚开始听起来非常容易,但是每个pai版本的apk去实现目标有是比较困难的,特别是经常有重叠的部分。幸运的是,通过本方法,你会很容易绘制出你的需求,供以后开发参考。
为了创建你的多apk的需求,首先画一行表格代表各个android系统的api版本,在最后多画出一个格,代表将来的android版本。
3 4 5 6 7 8 9 10 11 12 13 + 3 4 5 6 7 8 9 10 11 12 13 + 接着是调整图表的颜色,一种颜色代表一个apk。下面的这个例子是每种颜色对应一个api版本范围。
一旦创建完这个图表,就分发给你的团队。这样项目团队成员之间的沟通就会变的非常简单了,为什么这么说呢,之前大家沟通都是问“为api3-api6创建的apk怎么样?除非大家知道这是android1.x中的一个,但是当api版本多了的时候怎么办?”,很明显,那样问可能大家都不知道说的是哪一个系统版本,有了这个图表之后,就可以简单的改问:“那个蓝色的apk怎么样?”,这样大家心里就很明白了;
[编辑]把所有的共用代码和共用资源放在同一个库工程里( Put All Common Code and Resources in a Library Project)无论你是修改一个已经存在的Android应用程序还是开始创建一个新的程序,首先最重要的任务就是创建一个共用代码库(如标题所说的库工程)。把那些只需更新一次就可以减少项目的开发时间,减少项目错误的代码或者资源放进这个库工程里(比如可以放在代码库里的像本地化语言字符串,颜色主题,共用bug的修复等)。
注意: 如何创建库项目的细节,不是本节要讲解的范围,您可以通过下面的链接快速的了解如何创建库工程:
- 创建库工程(在eclipse中)
- 创建库工程(命令行)
如果你想把已有的应程序转成多apk的支持,需要重新组织你的代码中的所有的本地化字符串文件,值列表,颜色主题,菜单图标,布局文件,这些跨apk的不会改变的资源文件,并把他们放到库工程里。还有把那些不会改变太多的代码放到库工程里。这样你将会发现,你可以从一个apk继承另一个apk那些类的,扩展一到两个方法(函数)。
如果你刚开始创建一个新应用程序,首先要尽量在一个库工程中写代码,如果必要的情况下做成一个独立的apk。这样在以后长时间的开发过程中,长远看来是非常容易管理的,可以一点一点的添加共用代码和资源,并在数月之后指出这些代码或者资源在不经修改的情况下是否该移动到库里。
[编辑]创建新的APK工程(Create New APK Projects)对于需要发布的每个APK,要分别为每个APK创建Android工程。为了便于管理,把库工程和所有相关的APK工程放在相同的父目录下,同时需要记住每个APK需要相同的包名,虽然他们在这个库里不必须去共享他们的包名。比如,如果你有3个APK,正如前面的规则所描述的,你的根目录会像这样:
alexlucas:~/code/multi-apks-root$ ls
foo-blue
foo-green
foo-lib
foo-red
一旦这个工程创建之后,把这个库工程引用到每个APK工程,如果可以的话,在库工程里定义启动Activity,并在APK工程里继承这个Activity。有了这个在库工程中定义的启动Activity,可以把你的所有应用程序的初始化工作放在一个地方,这样一来每个一个单独的APK就不需要重新实现这些像初始化分析,运行许可检查等其他的一些初始化工作,不用一个个APK的去写,维护起来也会简单好多。
当用户从google安卓市场下载对多APK支持的程序时,正确的APK使用两个简单规则: 1.maifest已经展示这个APK是合格的 2.合格的APK有最高支持的Android API版本号
下面我们将以举例的方式讲解,首先,假设我们已经了解了前面所述的多APK的描述,并且没有为任何一个APK设定最高的API版本支持。 就每个apk单独来看,各个apk对应的版本号可能是这样:
3 4 5 6 7 8 9 10 11 12 13 + 3 4 5 6 7 8 9 10 11 12 13 + 3 4 5 6 7 8 9 10 11 12 13 + 幸运的是,如果用户在google安卓市场使用这样的设备浏览软件的时候,安卓市场的引擎首先会到manifest文件里面去查看是否有要求的前置摄像头,如果没有将会被忽略掉,然后再查看red apk要求的api版本号和设备的api版本号是否匹配,只有这两个全匹配了才可以下载。绿色的pak不仅向前兼容到API11(只有在没有定义最大版本号的情况下才可以),并且不去检查设备是否有前置摄像头。这个app仍然可以从谷歌安卓市场上下载下来,因为仍有一个apk支持这个设备的API版本,并且这个apk也不要求设备必须有前置摄像头。因为它要求高于minSdkVersion,还需要高于minSdkVersion的代码,所以我们知道版本号的的大小排列是:red ≥ green ≥ blue.因此我们可以重叠起来看这三个图表,会变成如下图表: 3 4 5 6 7 8 9 10 11 12 13 +
现在我们进一步假设,红色的apk需要一些必须的东西,而其他的两种apk不需要有。goole安卓市场的开发者指南页面上列出了所有的可能过滤掉你的程序的罪魁祸首。为了更好的讲解例子,我们继续假设,红色的apk需要前置摄像头,但是实际上呢,红色apk有前置摄像头了,如果需要使用新功能,关键还需要的附加条件是必须在API11或者以上,但是并不是所有的设备都支持API11或以上系统版本,即使这个设备有前置摄像头也于事无补,是不是很恐怖。
为了把所有的apk分别处理,设定一个好的版本号使用规则特别重要,在开发者指南上有推荐的版本号规则Version Codes。咱们现在说的这个例子只涉及三个维度,用数字1000就足以把他们分别开,前面的两个数字代码区别特定apk的最低支持的版本号,支持不同的版本号只需要更改这个两个数字即可,如下所示:
Blue: 03001, 03002, 03003, 03004...
Green: 07001, 07002, 07003, 07004...
Red:11001, 11002, 11003, 11004...
这三种在程序的manifests文件中定义分别如下: Blue:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="03001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="3" /> ...
Green:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="07001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="7" /> ...
Red:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="11001" android:versionName="1.0" package="com.example.foo"> <uses-sdk android:minSdkVersion="11" /> ...[编辑]发布前的检查(Go Over Pre-launch Checklist)
往google play上上传程序之前,一定要根据下面的条目仔细的检查下程序。记住,这些条目与多apk支持关系非常密,但是这里并没有列出所有的需要检查清单。
- 所有的apk必须有相同的包名
- 所有的apk必须用相同的数字证书签名
- 如果apk有系统版本重合的地方,必须定义最低与最低版本号
- 仔细检视manifest的过滤条件是否有冲突的地方(比如,一个apk只支持xlager的屏幕的设备,就不要被所有的设备看到)
- 每一个apk的menifest必须至少支持一种屏幕,openGL texture或者系统版本。
- 每一个apk至少在一个种设备上测试过。除非你是为定制的设备开发程序。
把程序提交到google市场之前,还要对编译过的程序进行检查,确保没有一些其他明显的问题而影响你的程序在google市场上被发现。使用aapt(Android Asset Packaging Tool,是生成和打包android应用程序的重要构建工具)这个工具进行编译检查特别的简单便利。
>aapt dump badging package: name='com.example.hello' versionCode='1' versionName='1.0' sdkVersion:'11' uses-permission:'android.permission.SEND_SMS' application-label:'Hello' application-icon-120:'res/drawable-ldpi/icon.png' application-icon-160:'res/drawable-mdpi/icon.png' application-icon-240:'res/drawable-hdpi/icon.png' application: label='Hello' icon='res/drawable-mdpi/icon.png' launchable-activity: name='com.example.hello.HelloActivity' label='Hello' icon='' uses-feature:'android.hardware.telephony' uses-feature:'android.hardware.touchscreen' main supports-screens: 'small' 'normal' 'large' 'xlarge' supports-any-density: 'true' locales: '--_--'
当你在检查aapt的输出时,确保支持的屏幕和兼容的屏幕的值不冲突,不要在manifest中的"uses-feature"添加一些你并不需要的系统权限.如上面的例子,这个apk不会被很多的设备看到。
为什么呢?例子中显示,增加了SEND_SMS的权限要求,这个特征需要增加android.hardware.telephony硬件支持。从api11 Honeycomb开始是针对平板电脑的系统,没有打电话的硬件支持,Google play将会过滤掉不支持打电话的设备,只有API版本匹配,同时支持打电话的硬件支持才不会被过滤掉。
可以非常容易的在manifest中修复这个问题:
<uses-feature android:name="android.hardware.telephony" android:required="false" />
android.hardvare.touchscreen 权限要求也本不正确的加进去了,如果你想你的apk能被没有触碰功能的android系统的智能电视也能在市场上搜索到,进行如下设置:
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
当你完成了发布前的检查,你就可以向google play上提交你的apk了。发布完之后再去google play做最后一点检查,去上面搜索你的程序,下载下来,以确保你的目标设备可以搜到,并能使用。恭喜您,现在您已经完成了本节课程!!!
初步总结:
1、团队精神
2、乐意分享精神
3、个人能力
1楼www_g258w_com前天 22:54沟通沟通很重要。。。