版权声明:本文出自汪磊的博客,转载请务必注明出处。
本篇继续上一篇《阿里巴巴Android编码规范》阅读纪要(一) ,还是建议各位同学有时间完整阅读一下《阿里巴巴Android编码规范》,如果实在没时间,就看我的本系列博客吧,主要摘录一些个人认为比较重要的地方。
UI 与布局部分
1,不能使用 ScrollView 包裹 ListView/GridView/ExpandableListVIew;因为这样会把 ListView 的所有 Item 都加载到内存中,要消耗巨大的内存和 cpu 去绘制图面。
说明:
ScrollView 中嵌套 List 或 RecyclerView 的做法官方明确禁止。除了开发过程中遇到的各种视觉和交互问题,这种做法对性能也有较大损耗。ListView 等 UI 组件自身有垂直滚动功能,也没有必要在嵌套一层 ScrollView。目前为了较好的 UI 体验,更贴近 Material Design 的设计,推荐使用 NestedScrollView。
正例:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout>
<android.support.v4.widget.NestedScrollView>
<LinearLayout>
<ImageView/>
...
<android.support.v7.widget.RecyclerView/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
反例:
<ScrollView> <LinearLayout> <TextView/> ... <ListView/> <TextView />
</LinearLayout> </ScrollView>
进程、线程与消息通信部分
1,新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。
说明
使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。另外创建匿名线程不便于后续的资源使用分析,对性能分析等会造成困扰。
2,线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:
Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool : 允 许 的 请 求 队 列 长 度 为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;
2) CachedThreadPool 和 ScheduledThreadPool :允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
关于线程池学习可以参考我之前写的两篇博客java线程池技术(一):ThreadFactory与BlockingQueue 与java线程池技术(二): 核心ThreadPoolExecutor介绍。
3,不要在非 UI 线程中初始化 ViewStub,否则会返回 null。
4,禁止在多进程之间用SharedPreferences共享数据,虽然可以(MODE_MULTI_PROCESS),但官方已不推荐。
文件与数据库部分
1,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();
2,大数据写入数据库时,请使用事务或其他能够提高 I/O 效率的机制,保证执行速度。
正例:
public void insertBulk(SQLiteDatabase db, ArrayList<UserInfo> 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(); } }
3,执行 SQL 语句时,应使用 SQLiteDatabase#insert()、update()、delete(),不要使用 SQLiteDatabase#execSQL(),以免 SQL 注入风险。
Bitmap、Drawable 与动画
1,加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡顿。
2,png 图片使用 tinypng 或者类似工具压缩处理,减少包体积。
个人简要说明:TinyPng官网地址:https://tinypng.com/。TinyPng能够对PNG和JPG/JPEG格式图片进行有效压缩,图片体积大幅减小,可达六七成,并且失真较小。
3,使用完毕的图片,应该及时回收,释放宝贵的内存。
正例:
Bitmap bitmap = null; loadBitmapAsync(new OnResult(result){ bitmap = result; }); ...使用该 bitmap... //使用结束,在 2.3.3 及以下需要调用 recycle()函数,在 2.3.3 以上 GC 会自动管理,除非你明确不需要再用。 if (Build.VERSION.SDK_INT <= 10) { bitmap.recycle(); } bitmap = null;
4,在 Activity.onPause()或 Activity.onStop()回调中,关闭当前 activity 正在执行的的动画。
正例:
public class MyActivity extends Activity { ImageView mImageView; Animation mAnimation; Button mBtn; /** 首次创建 activity 时调用 */ @Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); mImageView = (ImageView)findViewById(R.id.ImageView01); mAnimation = AnimationUtils.loadAnimation(this, R.anim.anim); mBtn= (Button)findViewById(R.id.Button01); mBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mImageView.startAnimation(mAnimation); } }); } public void onPause() { //页面退出,及时清理动画资源 mImageView.clearAnimation() } }
5,使用 ARGB_565 代替 ARGB_888,在不怎么降低视觉效果的前提下,减少内存占用。
说明:
android.graphics.Bitmap.Config 类中关于图片颜色的存储方式定义:
1)ALPHA_8 代表 8 位 Alpha 位图;
2)ARGB_4444 代表 16 位 ARGB 位图;
3)ARGB_8888 代表 32 位 ARGB 位图;
4)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);
安全部分
1,将 android:allowbackup 属性设置为 false,防止 adb backup 导出数据。
说明:
在 AndroidManifest.xml 文件中为了方便对程序数据的备份和恢复在 Android API level 8 以后增加了 android:allowBackup 属性值。默认情况下这个属性值为 true,故当 allowBackup 标志值为 true 时,即可通过 adb backup 和 adb restore 来备份和恢复应用程序数据。
正例:
<application android:allowBackup="false" android:largeHeap="true" android:icon="@drawable/test_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >
2,数据存储在 Sqlite 或者轻量级存储需要对数据进行加密,取出来的时候进行解密。
3,使用 Android 的 AES/DES/DESede 加密算法时,不要使用默认的加密模式ECB,应显示指定使用 CBC 或 CFB 加密模式。
说明:
加密模式 ECB、CBC、CFB、OFB 等,其中 ECB 的安全性较弱,会使相同的铭文在不同的时候产生相同的密文,容易遇到字典攻击,建议使用 CBC 或 CFB 模式。
1) ECB:Electronic codebook,电子密码本模式
2) CBC:Cipher-block chaining,密码分组链接模式
3) CFB:Cipher feedback,密文反馈模式
4) OFB:Output feedback,输出反馈模式
4,对于不需要使用 File 协议的应用,禁用 File 协议,显式设置 webView.getSettings().setAllowFileAccess(false),对于需要使用 File 协议的应用,禁止 File 协议调用 JavaScript,显式设置 webView.getSettings().setJavaScriptEnabled(false)。
5,Android5.0 以后安全性要求较高的应用应该使用 window.setFlag (LayoutParam.FLAG_SECURE) 禁止录屏。
6,Android WebView 组件加载网页发生证书认证错误时,采用默认的处理方法handler.cancel(),停止加载问题页面。
说明:
Android WebView 组件加载网页发生证书认证错误时,会调用 WebViewClient 类的 onReceivedSslError 方法,如果该方法实现调用了 handler.proceed()来忽略该证书错误,则会受到中间人攻击的威胁,可能导致隐私泄露.
反例:
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JsBridge(mContext), JS_OBJECT);
mWebView.loadUrl("http://www.example.org/tests/addjsif/");
mWebView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); // 忽略 SSL 证书错误 } });
好了,以上就是阅读《阿里巴巴Android编码规范》中个人摘录的一些觉得需要注意的地方,如果感兴趣可以自行查找完整版全部阅读一下。
本篇到此结束,希望对你有用。