开发手册目前最新的为1.01版.本人阅读的为正式版第一版1.0.其中许多技巧值得反复研读,此文记录本人认为有价值的知识点,部分知识点为延伸知识点
1.Activity & Fragment生命周期再回顾
上图相比于平时接触到的多了许多,简单说几个:
1)onPostCreate、onPostResume
onPostCreate是指onPostCreate方法是指onCreate方法彻底执行完毕的回调,onPostResume类似,那我们什么时候在可以使用这个呢。大家肯定遇到这种情况,在onCreate中获取某个View的高度和宽度,发现获取到的值是0,因为这个View可能还没初始化好,这时候比如我们在onPostResume中取获取这个View的高和宽,因为onPostResume是指onResume彻底执行完毕的回调,所以这时候去获取就可以了。
2)onUserInteraction ,onUserLeaveHint
onUserInteraction我们可以用这个方法来监控用户有没有与当前的Activity进行交互,那我们就可以针对这个来假设场景,有个APP要求N分钟后用户没有进行操作,那就自动出来动态壁纸,或者进行锁屏界面,或者跳到登录界面重新登录等!那我们只需要写个倒计时,然后每次调用了onUserInteraction方法,就把时间重置即可。。多方便!!
我们再看onUserLeaveHint:
用户手动离开当前activity,会调用该方法,比如用户主动切换任务,短按home进入桌面等。系统自动切换activity不会调用此方法,如来电,灭屏等。
我们一般监听返回键,肯定是重写onKeyDown方法,但是Home键和Menu键就不好监听了。但是有了这个方法。我们可以做统一的监听了。比如要监听用户点了Home键跳回到桌面后。我们要求这个APP自动跳转到解锁界面。我们只要在这里做监听出来即可。
2.添 加 Fragment 时 , 确 保 FragmentTransaction#commit() 在Activity#onPostResume()或者 FragmentActivity#onResumeFragments()内调用。不 要 随 意 使 FragmentTransaction#commitAllowingStateLoss() 来 代 替 , 任 何commitAllowingStateLoss()的使用必须经过 code review,确保无负面影响。
Activity 可 能 因 为 各 种 原 因 被 销 毁 , Android 支 持 页 面 被 销 毁 前 通 过Activity#onSaveInstanceState()保存自己的状态。但如果FragmentTransaction.commit()发生在 Activity 状态保存之后,就会导致 Activity 重建、恢复状态时无法还原页面状态,从而可能出错。为了避免给用户造成不好的体验,系统会抛出 IllegalStateExceptionStateLoss 异常。推荐的做法是在 Activity 的onPostResume() 或 onResumeFragments() ( 对 FragmentActivity ) 里 执 行FragmentTransaction.commit(),如有必要也可在 onCreate()里执行。不要随意改用FragmentTransaction.commitAllowingStateLoss() 或 者 直 接 使 用 try-catch 避 免crash,这不是问题的根本解决之道,当且仅当你确认 Activity 重建、恢复状态时,本次 commit 丢失不会造成影响时才可这么做。
3.不要在 Activity#onDestroy()内执行释放资源的工作,例如一些工作线程的销 毁 和 停 止 , 因 为onDestroy() 执 行 的 时 机 可 能 较 晚 。 可 根 据 实 际 需 要 , 在Activity#onPause()/onStop()中结合 isFinishing()的判断来执行。
4.总是使用显式 Intent 启动或者绑定 Service,且不要为服务声明 Intent Filter,保证应用的安全性。如果确实需要使用隐式调用,则可为 Service 提供 Intent Filter并从 Intent 中排除相应的组件名称,但必须搭配使用 Intent#setPackage()方法设置Intent 的指定包名,这样可以充分消除目标服务的不确定性。
5.当前 Activity 的 onPause 方法执行结束后才会执行下一个 Activity 的 onCreate方法,所以在 onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率。
6.【强制】不要在 Android 的 Application 对象中缓存数据。基础组件之间的数据共享请使用 Intent 等机制,也可使用 SharedPreferences 等数据持久化机制.
7.Activity 或者 Fragment 中动态注册 BroadCastReceiver 时, registerReceiver()和 unregisterReceiver()要成对出现。
说明:
如果 registerReceiver()和 unregisterReceiver()不成对出现,则可能导致已经注册的
receiver 没有在合适的时机注销,导致内存泄漏,占用内存空间,加重 SystemService
负担。
部分华为的机型会对 receiver 进行资源管控,单个应用注册过多 receiver 会触发管
控模块抛出异常,应用直接崩溃。
正例:
public class MainActivity extends AppCompatActivity {
private static MyReceiver myReceiver = new MyReceiver();
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter("com.example.myservice");
registerReceiver(myReceiver, filter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(myReceiver);
}
...
}
反例:
public class MainActivity extends AppCompatActivity {
private static MyReceiver myReceiver;
@Override
protected void onResume() {
super.onResume();
myReceiver = new MyReceiver();
IntentFilter filter = new IntentFilter("com.example.myservice");
registerReceiver(myReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
}
8.在 Activity 中显示对话框或弹出浮层时,尽量使用 DialogFragment,而非Dialog/AlertDialog,这样便于随 Activity 生命周期管理对话框/弹出浮层的生命周期。
9.尽量不要使用 AnimationDrawable,它在初始化的时候就将所有图片加载到内存中,特别占内存,并且还不能释放,释放之后下次进入再次加载时会报错。
说明:
Android 的帧动画可以使用 AnimationDrawable 实现,但是如果你的帧动画中如果
包含过多帧图片,一次性加载所有帧图片所导致的内存消耗会使低端机发生 OOM
异常。帧动画所使用的图片要注意降低内存消耗,当图片比较大时,容易出现 OOM。
图片数量较少的 AnimationDrawable 还是可以接受的。
10.【推荐】新建线程时,定义能识别自己业务的线程名称,便于性能优化和问题排查。
public class MyThread extends Thread {
public MyThread(){
super.setName("ThreadName");
...
}
}
11.【推荐】谨慎使用 Android 的多进程,多进程虽然能够降低主进程的内存压力,但会遇到如下问题:
- 不能实现完全退出所有 Activity 的功能;
- 首次进入新启动进程的页面时会有延时的现象(有可能黑屏、白屏几秒,是白屏还是黑屏和新 Activity 的主题有关);
- 应用内多进程时,Application 实例化多次,需要考虑各个模块是否都需要在所有进程中初始化;
- 多进程间通过 SharedPreferences 共享数据时不稳定。
12.【 推 荐 】 SharedPreference 提 交 数 据 时 , 尽 量 使 用 Editor#apply() , 而 非Editor#commit()。一般来讲,仅当需要确定提交结果,并据此有后续操作时,才使用 Editor#commit()。说明:SharedPreference 相关修改使用 apply 方法进行提交会先写入内存,然后异步写入磁盘,commit 方法是直接写入磁盘。如果频繁操作的话 apply 的性能会优于 commit,apply 会将最后修改内容写入磁盘。但是如果希望立刻获取存储操作的结果,并据此做相应的其他操作,应当使用 commit。
正例:
public void updateSettingsAsync() {
SharedPreferences mySharedPreferences = getSharedPreferences("settings",
Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
editor.putString("id", "foo");
editor.apply();
}
public void updateSettings() {
SharedPreferences mySharedPreferences = getSharedPreferences("settings",
Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
editor.putString("id", "foo");
if (!editor.commit()) {
Log.e(LOG_TAG, "Failed to commit setting changes");
}
}
反例:
editor.putLong("key_name", "long value");
editor.commit();
13.【强制】在 Activity.onPause()或 Activity.onStop()回调中,关闭当前 activity 正在执行的的动画。
14.【推荐】使用 ARGB_565 代替 ARGB_888,在不怎么降低视觉效果的前提下,减少内存占用。
说明:
android.graphics.Bitmap.Config 类中关于图片颜色的存储方式定义:
- ALPHA_8 代表 8 位 Alpha 位图;
- ARGB_4444 代表 16 位 ARGB 位图;
- ARGB_8888 代表 32 位 ARGB 位图;
- RGB_565 代表 8 位 RGB 位图。
位图位数越高,存储的颜色信息越多,图像也就越逼真。大多数场景使用的是
ARGB_8888 和 RGB_565,RGB_565 能够在保证图片质量的情况下大大减少内存
的开销,是解决 oom 的一种方法。
但是一定要注意 RGB_565 是没有透明度的,如果图片本身需要保留透明度,那么
就不能使用 RGB_565。
正例:
Config config = drawableSave.getOpacity() != PixelFormat.OPAQUE ? Config.ARGB_8888 :
Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
反例:
Bitmap newb = Bitmap.createBitmap(width, height, Config.ARGB_8888);
15.【推荐】在有强依赖 onAnimationEnd 回调的交互时,如动画播放完毕才能操作页面 , onAnimationEnd 可 能 会 因 各 种 异 常 没 被 回 调 ( 参 考 :https://stackoverflow.com/questions/5474923/onanimationend-is-not-getting-called-onanimationstart-works-fine ), 建 议 加 上 超 时 保 护 或 通 过 postDelay 替 代onAnimationEnd。
正例:
View v = findViewById(R.id.xxxViewID);
final FadeUpAnimation anim = new FadeUpAnimation(v);
anim.setInterpolator(new AccelerateInterpolator());
anim.setDuration(1000);
anim.setFillAfter(true);
new Handler().postDelayed(new Runnable() {
public void run() {
if (v != null) {
v.clearAnimation();
}
}
}, anim.getDuration());
v.startAnimation(anim);
参考:
超详细的生命周期图-你能回答全吗