开发手册 - 小知识补充
目录:
1、Android组件
2、UI与布局
3、进程、线程与消息通信
4、文件与数据库
5、Bitmap、Drawable 与动画
6、安全
7、其他
一、Android组件
1.1、Activity
之间需要传递大数据量的时候避免使用Intent+Parcelable
的方式。
用EventBus
替代,以避免造成TransactionTooLargeException
Lisa's EventBus Demo
1.2、耗时操作
- 避免在
Service#onStartCommand()/onBind()
方法中执行耗时操作。- 如果有耗时操作,用
IntentService
来替代Service
或者采用其他的异步机制
- 如果有耗时操作,用
- 避免在
BroadcastReceiver#onReceive()
中执行耗时操作- 有耗时工作,创建IntentService完成。
BroadcastReceiver#onReceive()
方法耗时超过10秒可能被系统杀死
- 有耗时工作,创建IntentService完成。
- 不要在
onPause()
中执行耗时操作。- 如果在
onPause
方法中做耗时操作,可能会影响页面跳转,造成卡顿
- 如果在
1.3、避免使用隐式Intent广播敏感信息。避免被其他APP接收
仅限于应用内的广播,用LocalBroadcastManager#sendBroadcast()
LocalBroadcastManager.getInstance(context)
.registerReceiver(receiver, filter);
LocalBroadcastManager.getInstance(context)
.unregisterReceiver(receiver);
1.4、不要在Activity.onDestroy()
执行资源释放工作
因为onDestroy()执行的时间可能比较晚。
在Activity#onPause()/onStop()
中用Activity.isFinishing()
方法来判断是否APP处于预备销毁的阶段,然后做对应的资源销毁工作。
而且,假设Activity的引用还被其他部分所持有,那么很可能会一直不执行onDestroy()
方法,在这种情况下如果资源释放工作都放在onDestroy()
里面,很可能导致内存溢出。
@Override
protected void onPause() {
super.onPause();
if(isFinishing()){
// 资源可以释放了。。。
}
}
-
Activity.isFinishing()
- true : Activity处于待回收状态
- false:Activity处于活跃状态
-
Activity.isDestroyed()
- true:Activity已经执行过
onDestroy()
方法 - false:Activity还没执行
onDestroy()
方法
- true:Activity已经执行过
1.5、如果要创建Service
,尽量用IntentService
1.6、全局定义一个Toast对象,避免出现连续显示吐司时无法取消上一次吐司。
- 原来的
ToastUtils
:
public class ToastUtils {
private ToastUtils() {
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
public static boolean isShow = true;
public static void showShort(Context context, CharSequence message) {
if (isShow){
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
}
- 改良后的
ToastUtils
:
**
* Toast要全局定义统一的一个对象,避免出现Toast无法销毁的情况
* @author Lisa
* @date 2018/9/18
*/
public class ToastUtils {
private Toast mToast;
private static ToastUtils mToastUtils;
private ToastUtils(Context context) {
mToast = Toast.makeText(context.getApplicationContext(), null, Toast.LENGTH_SHORT);
}
public static synchronized ToastUtils getInstanc(Context context) {
if (null == mToastUtils) {
mToastUtils = new ToastUtils(context);
}
return mToastUtils;
}
/**
* 显示toast
*
* @param toastMsg
*/
public void showToast(String toastMsg) {
mToast.setDuration(Toast.LENGTH_LONG);
mToast.setText(toastMsg);
mToast.show();
}
/**
* 显示toast
*
* @param toastMsg
*/
public void showShortToast(String toastMsg) {
mToast.setDuration(Toast.LENGTH_SHORT);
mToast.setText(toastMsg);
mToast.show();
}
/**
* 显示toast
*
* @param toastMsg
*/
public void showLongToast(String toastMsg) {
mToast.setDuration(Toast.LENGTH_LONG);
mToast.setText(toastMsg);
mToast.show();
}
/**
* 取消toast,在activity的destory方法中调用
*/
public void destory() {
if (null != mToast) {
mToast.cancel();
mToast = null;
}
mToastUtils = null;
}
}
最后在Activity#onDestroy()
调用销毁Toast
ToastUtils.getInstanc(getActivity()).destory();
1.7、使用 Adapter 注意事项
如果使用了 ViewHolder
做缓存,在 getView()
的方法中无论这项convertView
的每个子控件是否需要设置属性(比如某个 TextView
设置的文本可能为 null
,某个按钮的背景色为透明,某控件的颜色为透明等),都需 要为其显式设置属性(Textview
的文本为空也需要设置 setText("")
,背景透明也需要 设置),否则在滑动的过程中,因为 adapter item
复用的原因,会出现内容的显示错乱。
二、UI与布局
2.1、布局
-
ViewGroup
不得不多重嵌套时,RelativeLayout
可有效降低嵌套数-
measure、layout、draw
耗费的总时间尽量控制在16ms以内。 -
Hierarchy Viewer
工具查看多余的View
,Stetho
似乎也可以看。- AndroidStudio3.1开始
Layout Inspector
替代了Hierarchy Viewer
。不管怎么说我用Stetho看好了
- AndroidStudio3.1开始
-
文本大小用sp,View大小用dp,文字大小不要小于12sp
不要多次在子View和父View中一起设置背景造成过度绘制。不需要用的布局及时隐藏掉。
-
学习用
Merge\ViewStub
来优化布局,尽可能减少UI层级- 优先用
FrameLayout
,然后再考虑LineayLayout\RelativeLayout.
- 优先用
-
在需要时刻刷新某一区域的组件时,避免layout全局刷新
- 具体看开发手册
2.2、用DialogFragment
替代Dialog/AlertDialog
使对话框或浮层的生命周期能够随着Activity
的生命周期走。
2.3、源文件统一用UTF-8
进行编码
2.4、尽量不要使用 AnimationDrawable
三、进程、线程与消息通信
3.1、线程与线程池
- 线程要由线程池提供
- 线程池用
ThreadPoolExecutor
创建 - 要给每条线程命名,方便回溯
- 要设置线程存活时间
setKeepAliveTime
,尽早释放资源
四、文件与数据库
4.1、不要硬编码文件路径
/sdcard/storage....
在项目中都不要这样直接写这样的路径。
android.os.Environment#getExternalStorageDirectory()
android.os.Environment#getExternalStoragePublicDirectory()
android.content.Context#getFilesDir()
android.content.Context#getCacheDir
4.2、SharedPreserences
只存放简单的数据类型
其他的考虑用文件或数据库
4.3、大数据写入数据库时,请使用事务或其他能够提高 I/O 效率的机制,保证执行速度。
4.4、多线程操作写入数据库时,需要使用事务,避免出现同步问题
五、Bitmap
、Drawable
与动画
5.1、图片加载
- 异步加载
- 列表展示时做好图片缓存
- png图片要用压缩工具压缩以减小包体积,tinyPng 、webp
- 在AndroidStudio中选中要压缩的图片,右击选择convertToWebp
5.2、Bitmap
使用
- 用完及时recycle释放资源
- 使用
ARGB_565
代替ARGB_888
可解决一定的OOM问题
5.3、如果为了节省包体积,可以在不影响UI效果的前提下,省略低密度图片。
5.4、动画
- 页面退出时要关闭停止动画
- 在
onPause()
\onStop()
中停止动画
- 在
- 动画或其他异步任务结束时,在回调中要注意判断资源是否已被释放掉了,避免空指针
- 动画执行结束要释放相关资源:
View.clearAnimation()
anim.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
//判断一下资源是否被释放了
if (v != null) {
v.clearAnimation();
}
}
});
六、安全
6.1、Intent
、PendingIntent
- 用于构造
PendingIntent
的Intent
要指定ComponentName
或者Action
- 用于构造
PendingIntent
的Intent
不可以是隐式的Intent
6.2、AndroidManifest
- 将
android:allowbackup
属性设置为 false,防止 adb backup 导出数据。 -
Receiver/Provider
在毫无权限控制的情况下,不要将android:export
设置为 true。 - 内部使用的组件,将
android:export
设置为 false。
6.3、应用上线注意事项:
- 不要把敏感信息打印到日志中,线上版本要关闭调试接口
- 确保把
android:debuggable
属性设置为false
6.4、加密
- 秘钥不要硬编码到程序中
- 参考资料: 在 Android 中如何优雅地配置私密信息
- 使用 AES/DES/DESede 加密算法时,不要使用默认的加密模式 ECB,应显示指定使用 CBC 或 CFB 加密模式。
- 不要用不安全的Hash算法(MD5、SHA-1)做加密,用SHA-256安全系数更高