Android 性能优化:StrictMode
2014.09.07
xiazdong
 热度
℃
StrictMode
Android 2.3 引入了 android.os.StrictMode
(Android 3.0 中又在 StrictMode 中新加入了几个方法),为了能够帮助开发者检测 “主线程” 或 “虚拟机” 的一些影响性能或者不良的代码,为了能够让应用更平滑、响应更快。当然如果你已经足够了解 android 最佳实践,完全可以忽略这个类。注意:这个类只在开发时使用,发布时请去除。
目前 Android 4.0 以上的占有率为 84.3%,因此有很多应用直接 minSdk 设置为 14。因此一般不必考虑 StrictMode 中 API Level 的问题。
StrictMode 共有两种策略(policy):
- ThreadPolicy: 线程相关策略,包括主线程访问网络、磁盘(现在手机中使用闪存)读写、慢代码的检测。我们能够分别检测(detect)这些操作或允许(permit)这些操作。一旦出现了违规(violation),就会有相应的提示(比如 Log 显示)。
- VMPolicy: 虚拟机相关的策略,包括 SQLite 或 SQLiteCursor 没关闭、实现 Closable 接口的类使用后没关闭 等。
当然也可以忽略某些违规,比如检测磁盘读写,因为一般来说在主线程中进行文件系统的读写是可以的。
一旦我们在入口 Activity 的 onCreate()
的最前面添加 StrictMode 相关代码,则在整个程序中 StrictMode 都会有效。
模版代码:
protected void onCreate(Bundle savedInstanceState) { if (DEVELOPER_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } super.onCreate(savedInstanceState); }
|
当然,为了方便,可以直接使用 StrictMode.enableDefaults()
启用 StrictMode。
其实 ThreadPolicy 和 VMPolicy 差不多,detectAll()
表示检测全部的违规,penaltyLog()
表示当违规时打印 Log。
- 对于 ThreadPolicy 来说,我们可以使用
detectNetwork()
检测主线程网络访问,detectDiskReads()
和 detectDiskWrites()
检测主线程磁盘读写,detectCustomSlowCalls()
检测主线程自定义的慢代码。当然也可以使用 permitXXX()
允许这些操作。
- 对于 VMPolicy 来说,
detectLeakedSqliteObjects()
检测 SQLite 和 SQLiteCursor 内存泄漏(没关闭),detectLeakedClosableObjects()
检测实现 Closable 接口的对象内存泄漏。
- 我们能通过
StrictMode.getThreadPolicy()
和 StrictMode.getVMPolicy()
获得当前采取的 ThreadPolicy 和 VMPolicy。
案例
场景:从网络上下载一张图片并且在 ImageView 中显示。
核心代码如下:
ImageView imageView = (ImageView)findViewById(R.id.view); URL url = null; Bitmap bmp = null; try { url = new URL("http://www.tencent.com/1.jpg"); bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream()); }catch(Exception e){ e.printStackTrace(); }finally { if(bmp!=null){ imageView.setImageBitmap(bmp); } }
|
这段代码一运行,StrictMode 就出现了一堆建议:
1、 根据下面的 log 可以看出查找域名花费了 446 ms。
D/StrictMode﹕ StrictMode policy violation; ~duration=446 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4 at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184) at java.net.InetAddress.lookupHostByName(InetAddress.java:394) ... at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:168) at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:33) ...
|
2、连接服务器花费 377 ms。
D/StrictMode﹕ StrictMode policy violation; ~duration=377 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4 at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184) at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84) at libcore.io.IoBridge.connectErrno(IoBridge.java:127) at libcore.io.IoBridge.connect(IoBridge.java:112) ... at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:168) at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:36) ...
|
3、 解码图片花费了 205 ms。
D/StrictMode﹕ StrictMode policy violation; ~duration=205 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=63 violation=4 at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1184) at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:163) at libcore.io.IoBridge.recvfrom(IoBridge.java:503) at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488) ... at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:791) at info.xiazdong.performanceoptimization.MainActivity.onCreate(MainActivity.java:36) ...
|
因此访问网络的操作都建议在子线程中进行,绝对不能在主线程进行。
现在手机的像素越来越高,魅族4的相机像素达到 2070 万,拍出照片的尺寸是 5248 3936,如果这张照片是 ARGB_8888 类型的位图,则放在内存中将占据:2070 4 字节,约 78M,即使手机有足够内存放这张图片,在主线程解码(decode)也需要花费不少时间,因此一般这种耗时的操作都在子线程中进行,比如使用 AsyncTask(因为屏幕像素和相机像素差距比较大,因此一般都不会原图显示,因为手机上显示一张高清图片没有什么价值)
参考文献