Best Practice For Android
阿里巴巴Android开发手册个人理解记录
Android基本组件
Activity间的数据通信:
- putExtra: 数据量小于 < 1024kb (IPC缓冲区大小限制)
- Huge Data:
- EventBus
- ActivityResults.data(static)
- 数据库 & SP
Activity数据持久化
- onSaveInstanceState() : 用于异常情况Activity的重建恢复,默认实现方案恢复了设置ID的控件状态,如textview里的文字,checkbox的勾选状态
- onPause & onStop:用于数据的持久化
Activity 间通过隐式 Intent 的跳转,在发出 Intent 之前必须通过resolveActivity检查,避免找不到合适的调用组件,造成 ActivityNotFoundException 的异常
Service
避免在 BroadcastReceiver#onReceive()中执行耗时操作,如果有耗时工作, 应该创建 IntentService 完成,而不应该在 BroadcastReceiver 内创建子线程去做(10s)
避免使用隐式 Intent 广播敏感信息,信息可能被其他注册了对应 BroadcastReceiver 的 App 接收。
通过 Context#sendBroadcast()发送的隐式广播会被所有感兴趣的 receiver 接收,恶意应用注册监听该广播的 receiver 可能会获取到 Intent 中传递的敏感信息,并进行其他危险操作。如果发送的广播为使用 Context#sendOrderedBroadcast()方法发送的有序广播,优先级较高的恶意 receiver 可能直接丢弃该广播,造成服务不可用,或者向广播结果塞入恶意数据。如果广播仅限于应用内,则可以使用 LocalBroadcastManager#sendBroadcast()实现,避免敏感信息外泄和 Intent 拦截的风险。
7
不要在 Activity#onDestroy()内执行释放资源的工作,例如一些工作线程的 销毁和停止,因为 onDestroy()执行的时机可能较晚。可根据实际需要,在 Activity#onPause()/onStop()中结合 isFinishing()的判断来执行。
private boolean isDestroyed = false;
private void destroy() {
if (isDestroyed) {
return;
}
// 回收资源
isDestroyed = true;
}
@Override
protected void onPause() {
super.onPause();
if (isFinishing()) {
destroy();
}
}
@Override
public void onDestroy() {
destroy();
}
9
10
Service 需要以多线程来并发处理多个启动请求,建议使用 IntentService, 可避免各种复杂的设置。
- 对于只用于应用内的广播,优先使用 LocalBroadcastManager 来进行注册 和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率
- 当前 Activity 的 onPause 方法执行结束后才会执行下一个 Activity 的 onCreate 方法,所以在 onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳 转效率。
- 不要在 Android 的 Application 对象中缓存数据。基础组件之间的数据共享 请使用 Intent 等机制,也可使用 SharedPreferences 等数据持久化机制
- Application在应用被杀死再次重启时会被重新的创建
- 直接将数据通过intent传递给 Activity 。
- 使用官方推荐的几种方式将数据持久化到磁盘上。
- Application在应用被杀死再次重启时会被重新的创建
- 使用 Toast 时,建议定义一个全局的 Toast 对象,这样可以避免连续显示 Toast 时不能取消上一次 Toast 消息的情况(如果你有连续弹出 Toast 的情况,避免 使用 Toast.makeText)。
- 使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView()的 方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView 设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需 要为其显式设置属性(Textview 的文本为空也需要设置 setText(“”),背景透明也需要 设置),否则在滑动的过程中,因为 adapter item 复用的原因,会出现内容的显示错 乱。
- Activity 或者 Fragment 中动态注册 BroadCastReceiver 时,registerReceiver() 和 unregisterReceiver()要成对出现
UI与布局
- 布局中不得不使用 ViewGroup 多重嵌套时,不要使用 LinearLayout 嵌套, 改用 RelativeLayout,可以有效降低嵌套数
Android 应用页面上任何一个 View 都需要经过 measure、layout、draw 三个步骤才能被正确的渲染。从 xml layout 的顶部节点开始进行 measure,每个子节点都需 要向自己的父节点提供自己的尺寸来决定展示的位置,在此过程中可能还会重新
measure(由此可能导致 measure 的时间消耗为原来的 2-3 倍)。节点所处位置越深,套嵌带来的 measure 越多,计算就会越费时。这就是为什么扁平的 View 结构会性能更好。
同时,页面拥上的 View 越多,measure、layout、draw 所花费的时间就越久。要缩 短这个时间,关键是保持 View 的树形结构尽量扁平,而且要移除所有不需要渲染的View。理想情况下,总共的 measure,layout,draw 时间应该被很好的控制在 16ms以内,以保证滑动屏幕时 UI 的流畅。
要找到那些多余的 View(增加渲染延迟的 view),可以用 Android Studio Monitor
里的 Hierarachy Viewer 工具,可视化的查看所有的 view
- 【推荐】在 Activity 中显示对话框或弹出浮层时,尽量使用 DialogFragment,而非 Dialog/AlertDialog,这样便于随 Activity 生命周期管理对话框/弹出浮层的生命周期
- 源文件统一采用 UTF-8 的形式进行编码。
- 【强制】禁止在非 ui 线程进行 view 相关操作
【推荐】文本大小使用单位 dp,view 大小使用单位 dp。对于 Textview,如果在文 字大小确定的情况下推荐使用 wrap_content 布局避免出现文字显示不全的适配问题。
文字大小???
【强制】禁止在设计布局时多次设置子 view 和父 view 中为同样的背景造成页面过 度绘制,推荐将不需要显示的布局进行及时隐藏
【推荐】灵活使用布局,推荐 Merge、ViewStub 来优化布局,尽可能多的减少 UI 布局层级,推荐使用 FrameLayout,LinearLayout、RelativeLayout 次之。
布局顺序???
8 自定义View
- 9
- 【推荐】尽量不要使用 AnimationDrawable,它在初始化的时候就将所有图片加载
到内存中,特别占内存,并且还不能释放,释放之后下次进入再次加载时会报错 - 【强制】不能使用 ScrollView 包裹 ListView/GridView/ExpandableListVIew;因为这 样会把 ListView 的所有 Item 都加载到内存中,要消耗巨大的内存和 cpu 去绘制。推荐使用NestedScrollView
进程,线程与消息通信
- 不要通过 Intent 在 Android 基础组件之间传递大数据(binder transaction 缓存为 1MB),可能导致 OOM
- 【强制】在 Application 的业务初始化代码加入进程判断,确保只在自己需要的进程 初始化。特别是后台进程减少不必要的业务初始化。
- 【强制】新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor 或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。
- 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方 式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
- 【强制】子线程中不能更新界面,更新界面必须在主线程中进行,网络操作不能在 主线程中调用
- 【强制】不要在非 UI 线程中初始化 ViewStub,否则会返回 null
- 尽量减少不同 APP 之间的进程间通信及拉起行为。拉起导致占用系统资源,
影响用户体验。 - 【推荐】新建线程时,定义能识别自己业务的线程名称,便于性能优化和问题排查
- 【推荐】ThreadPoolExecutor 设置线程存活时间(setKeepAliveTime),确保空闲时 线程能被释放
- 【推荐】禁止在多进程之间用 SharedPreferences 共享数据,虽然可以 (MODE_MULTI_PROCESS),但官方已不推荐。6.0之上已经废弃,并未实现并发的机制。
- 11
文件与数据库
- 【强制】任何时候不要硬编码文件路径,请使用 Android 文件系统 API 访问。
- 【强制】当使用外部存储时,必须检查外部存储的可用性。
- 【强制】应用间共享文件时,不要通过放宽文件系统权限的方式去实现,而应使用 FileProvider。
- SP:http://blog.csdn.net/offbye/article/details/60882824
【推荐】SharedPreference 提交数据时,尽量使用 Editor#apply(),而非 Editor#commit()。一般来讲,仅当需要确定提交结果,并据此有后续操作时,才使 用 Editor#commit()。
SharedPreference 相关修改使用 apply 方法进行提交会先写入内存,然后异步写入 磁盘,commit 方法是直接写入磁盘。如果频繁操作的话 apply 的性能会优于 commit, apply 会将最后修改内容写入磁盘。但是如果希望立刻获取存储操作的结果,并据此做相应的其他操作,应当使用 commit。
数据库:6 ~ 10
Bitmap、Drawable 与动画
- 【强制】加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加 载,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡顿。
- 【强制】在 ListView,ViewPager,RecyclerView,GirdView 等组件中使用图片时, 应做好图片的缓存,避免始终持有图片导致内存泄露,也避免重复创建图片,引起 性 能 问 题 。 建 议 使 用 Fresco ( https://github.com/facebook/fresco )、 Glide(https://github.com/bumptech/glide)等图片库。
- 【强制】png 图片使用 tinypng 或者类似工具压缩处理,减少包体积。
【推荐】应根据实际展示需要,压缩图片,而不是直接显示原图。手机屏幕比较小,直接显示原图,并不会增加视觉上的收益,但是却会耗费大量宝贵的内存。
【强制】使用完毕的图片,应该及时回收,释放宝贵的内存。
- 【推荐】针对不同的屏幕密度,提供对应的图片资源,使内存占用和显示效果达到 合理的平衡。如果为了节省包体积,可以在不影响 UI 效果的前提下,省略低密度图片。
- 【强制】在 Activity.onPause()或 Activity.onStop()回调中,关闭当前 activity 正在执 行的的动画。