Android开发注意事项
以下总结全部来源于开发经验积累,干货满满的,有问题请邮箱1057915589@qq.com,感谢!敬礼~
1、安卓xml命名:比如以音乐模块为例
music_activity.xml、music_fragment.xml、music_dialog.xml、music_list_item.xml、
music_recycle_item.xml、music_grid_item.xml等
2、资源文件命名:登录按钮背景资源图片(模块名_功能_控件描述_控件限定词)
base_login_btn_pressed.png、base_login_btn_normal.png
3、颜色由于是整个app共用的,所以我不太同意阿里规约的命名方式,我感觉可以写一套直接根据色值查找更合理
比如:白色<color name="color_ffffff">#ffffff</color> 只需要命名一套即可
4、控件命名可以使用控件名称缩写作为前缀,如下:
LinearLayout对应ll、RelativeLayout对应rl、ConstraintLayout对应cl、ListView对应lv
ScollView对应sv、TextView对应tv、Button对应btn、ImageView对应iv、CheckBox对应cb
RadioButton对应rb、EditText对应et
5、Android的四大组件:Activity、Fragment、Service、BroadcastReceiver
Activity间通信可以考虑EventBus,避免数据太大报TransactionTooLargeException
Activity的onSaveInstanceState()方法不是生命周期方法,不保证一定会被调用,它是Activity意外被销毁保存Ui状态的,只能用于临时保存数据,持久化存储应该放在onPause、onStop中
6、Activity间隐式跳转,在发出Intent之前要通过resolveActivity检查目标组件能否找到,避免报ActivityNotFoundException的异常,比如:
if(getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ ONLY) !=null) {
..................
}
7、Service的onStartCommand()、onBind()不能执行耗时操作,如果需要应改用IntentService或者采用异步机制完成
8、避免在BroadcastReceiver的onReceiver()中执行耗时操作,可以通过创建IntentService完成
9、Context的sendBroadcast()发送隐式广播会被所有感兴趣的receiver接收,避免敏感信息泄露,广播可以仅限于应用内,可以使用LocalBroadcastManager的sendBroadcast()方法,避免敏感信息外泄和Intent拦截风险,比如:
Intent intent = new Intent("my-sensitive-event");
intent.putExtra("event", "this is a test event");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
10、不要在Activity的onDestory()内执行释放工作,比如线程的销毁、停止,由于onDestory()执行的时机比较晚,可以按实际需要结合ifFinishing()判断在Activity的onPause()、onStop()中
11、Service组件运行在主线程中,避免耗时操作,如果有耗时操作可以使用IntentService执行后台任务
12、当前Activity的onPause方法执行结束后会执行下一个Activity的onCreate方法,所以onPause方法中不适合执行耗时工作,会影响页面间跳转效率。
13、不要在Application对象缓存全局数据,尽量用Intent传递或者SharedPreferences数据持久化机制
14、Activity或者Fragment中动态注册的BroadCastReceiver时,registerReceiver()和unregisterReceiver()要成对出现
15、布局尽量扁平化,防止View渲染经过measure、layout、draw消耗过多时间,尽量控制每一帧在16毫秒内绘制完成。
16、禁止在布局中多次设置子View和父View中为同样的背景造成过度绘制
17、尽量不要使用AnimationDrawable,它在初始化的时候将所有图片加载到内存中,特别占内存,并且还不能释放,容易内存溢出
18、不要使用ScrollView包裹LIstView/GridView/ExpandableListView,因为它会把ListView的所有Item都加载到内存中,要消耗巨大的内存与CPU去绘制图面,如果非要用,推荐NestedScrollView。
19、不要通过Intent在Android基础组件之间传递大数据(binder transaction 缓存为1MB),可能导致OOM。
20、Application的业务初始化加入进程判断,确保只在自己需要的进程初始化,特别是后台进程减少不必要的业务初始化。
21、新建线程时,必须通过线程池提供,不允许在应用中自行闲时创建线程。
比如:
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue taskQueue = new LinkedBlockingQueue();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue,new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());
//
执行任务
executorService.execute(new Runnnable() {
...
});
22、子线程中不能更新界面,更新界面必须在主线程中进行,网络操作不能在主线程中调用。
23、ThreadPoolExecutor设置线程存活时间,确保空闲时线程能被释放。
24、谨慎使用Android多进程,多进程虽然能够降低主进程的内存压力,但会有如下问题:
第一、不能实现完全退出所有Activity的功能
第二、首次进入新启动进程页面会有延时的现象(黑屏、白屏几秒)
第三、应用内多进程是,Application实例化多次,需要考虑各个模块是否都需要在所有进程初始化;
第四、多进程间通过SharedPreferencs共享数据是不稳定
25、SharedPreference提交数据时,尽量使用Editor的apply(),而非Editor的commit(),仅当需要确定提交结果,并据此有后续操作是,才用commit()。(apply方法提交先写入内存,然后异步写入磁盘,commit方法直接写入磁盘,频繁操作的话,apply的性能会优于commit);
26、数据库Cursor必须确保使用完后关闭,以免内存泄漏
public void handlePhotos(SQLiteDatabase db, String userId) {
Cursor cursor;
try {
cursor = db.query(TUserPhoto, new String[] { "userId", "content" }, "userId=?", new
String[] { userId }, null, null, null);
while (cursor.moveToNext()) {
// TODO
}
} catch (Exception e) {// TODO
} finally {
if (cursor != null) {
cursor.close();
}
}
}
27、多线程操作写入数据库时,需要使用实物,避免出现同步问题。
比如:
public void insertUserPhoto(SQLiteDatabase db, String userId, String content) {
ContentValues cv = new ContentValues();
cv.put("userId", userId);
cv.put("content", content);
db.beginTransaction();
try {
db.insert(TUserPhoto, null, cv);
// 其他操作
db.setTransactionSuccessful();} catch (Exception e) {
// TODO
} finally {
db.endTransaction();
}
}
28、大数据写入数据库时,请使用事务或其他能够提高I/O效率的机制,保证执行速度。
比如:
public void insertBulk(SQLiteDatabase db, ArrayList users) {db.beginTransaction();
try {
for (int i = 0; i < users.size; i++) {
ContentValues cv = new ContentValues();
cv.put("userId", users[i].userId);
cv.put("content", users[i].content);
db.insert(TUserPhoto, null, cv);
}
//
其他操作db.setTransactionSuccessful();
} catch (Exception e) {
// TODO
} finally {
db.endTransaction();
}
}
29、加载大图片或者一次性加载多张图片,应在异步线程中进行,图片加载设计到IO操作,以及CPU密集操作,容易引起卡顿。
正例:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
//
在后台进行图片解码
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = BitmapFactory.decodeFile("some path");
return bitmap;
}
...
}
30、在 ListView,ViewPager,RecyclerView,GirdView 等组件中使用图片时,
应做好图片的缓存,避免始终持有图片导致内存泄露,也避免重复创建图片,引起
性 能 问 题 。建议使用Fresco、Glide等图片库,可以使用系统LruCache缓存:
正例:
private LruCache mMemoryCache;@Override
protected void onCreate(Bundle savedInstanceState) {
...
//
获取可用内存的最大值,使用内存超出这个值将抛出 OutOfMemory 异常。LruCache 通
过构造函数传入缓存值,以 KB 为单位。
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 把最大可用内存的 1/8 作为缓存空间
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
...
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
- 45 -
七、Bitmap、Drawable 与动画
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
} else {
mImageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
task.execute(resId);
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// 在后台进行图片解码
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(getResources(),
params[0], 100, 100));
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);return bitmap;
}
...
}
31、png图片使用tinypng工具压缩处理,减少apk包体积大小
32、在Activity.onPause()或者Activity.onStop()回调中,关闭当前activity正在执行的动画。
33、使用ARGB_565代替ARGB_888,在不怎么降低视觉效果的前提下,减少内存占用
34、在有强依赖 onAnimationEnd 回调的交互时,如动画播放完毕才能操作页
面,onAnimationEnd 可能会因各种异常没被回调,建 议 加 上 超 时 保 护 或 通 过 postDelay 替 代
onAnimationEnd。
正例:View v = findViewById(R.id.xxxViewID);
final FadeUpAnimation anim = new FadeUpAnimation(v);anim.setInterpolator(new AccelerateInterpolator());anim.setDuration(1000);
anim.setFillAfter(true);
anim.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
//判断一下资源是否被释放了if (v != null) {
v.clearAnimation();
}
}
});
v.startAnimation(anim);
35、未完,待更新~