性能优化工具(一)StrictMode使用
一、简介:
StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题,以达到提升应用响应能力的目的。举个例子来说,如果开发者在UI线程中进行了网络操作或者文件系统的操作,而这些缓慢的操作会严重影响应用的响应能力,甚至出现ANR对话框。为了在开发中发现这些容易忽略的问题,我们使用StrictMode,系统检测出主线程违例的情况并做出相应的反应,最终帮助开发者优化和改善代码逻辑。
二、用途:
使用严格模式,系统检测出主线程违例的情况会做出相应的反应,如日志打印,弹出对话框亦或如日志打印,弹出对话框亦或通过一定的方式暴露给开发者方便优化与改善。
三、使用:
private boolean DEV_MODE = true;
public void onCreate() {
if (DEV_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectCustomSlowCalls() //API等级11,使用StrictMode.noteSlowCode
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyDialog() //弹出违规提示对话框
.penaltyLog() //在Logcat 中打印违规异常信息
.penaltyFlashScreen() //API等级11
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects() //API等级11
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
ThreadPolicy线程策略检测
- 线程策略检测的内容有
- 自定义的耗时调用 使用detectCustomSlowCalls()开启
- 磁盘读取操作 使用detectDiskReads()开启
- 磁盘写入操作 使用detectDiskWrites()开启
- 网络操作 使用detectNetwork()开启
VmPolicy虚拟机策略检测
- Activity泄露 使用detectActivityLeaks()开启
- 未关闭的Closable对象泄露 使用detectLeakedClosableObjects()开启
- 泄露的Sqlite对象 使用detectLeakedSqlLiteObjects()开启
- 检测实例数量 使用setClassInstanceLimit()开启
ThreadPolicy 详解
- detectLeakedSqlLiteObjects() 和detectLeakedClosableObjects()的用法类似,只不过是用来检查 SQLiteCursor 或者 其他 SQLite对象是否被正确关闭
- detectLeakedRegistrationObjects() 用来检查 BroadcastReceiver 或者ServiceConnection 注册类对象是否被正确释放
- setClassInstanceLimit(),设置某个类的同时处于内存中的实例上限,可以协助检查内存泄露
- detectLeakedClosableObjects()用于资源没有正确关闭时提醒
- detectActivityLeaks() 用户检查 Activity 的内存泄露情况
- detectDiskReads() 和 detectDiskWrites() 是磁盘读写检查
- noteSlowCall针对执行比较耗时的检查
- penaltyDeath(),当触发违规条件时,直接Crash掉当前应用程序。
- penaltyDeathOnNetwork(),当触发网络违规时,Crash掉当前应用程序。
- penaltyDialog(),触发违规时,显示对违规信息对话框。
- penaltyFlashScreen(),会造成屏幕闪烁,不过一般的设备可能没有这个功能。
- penaltyDropBox(),将违规信息记录到 dropbox 系统日志目录中(/data/system/dropbox),你可以通过如下命令进行插件:
StrictMode.ThreadPolicy.Builder 主要方法如下
1、detectNetwork() 用于检查UI线程中是否有网络请求操作
public class MainActivity extends Activity implements View.OnClickListener{
private Button btnTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectNetwork()
.penaltyLog()
.build());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnTest = (Button) findViewById(R.id.button);
btnTest.setOnClickListener(MainActivity.this);
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.button:
postNetwork();
break;
}
}
/**
* 网络连接的操作
*/
private void postNetwork() {
try {
URL url = new URL("http://www.wooyun.org");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String lines = null;
StringBuffer sb = new StringBuffer();
while ((lines = reader.readLine()) != null) {
sb.append(lines);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
运行后,触发的警告如下
2020-01-03 15:00:33.422 8120-8120/com.zqc.testdemo D/StrictMode: StrictMode policy violation; ~duration=13 ms: android.os.StrictMode$StrictModeNetworkViolation: policy=65540 violation=4
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1456)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:102)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:90)
at java.net.InetAddress.getAllByName(InetAddress.java:787)
at com.android.okhttp.Dns$1.lookup(Dns.java:39)
at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:175)
at com.android.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:141)
at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:83)
at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:174)
at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:126)
at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:95)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:281)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:224)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:461)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
at com.zqc.testdemo.MainActivity.postNetwork(MainActivity.java:52)
at com.zqc.testdemo.MainActivity.onClick(MainActivity.java:41)
at android.view.View.performClick(View.java:6311)
at android.view.View$PerformClick.run(View.java:24829)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6752)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:449)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
2、setClassInstanceLimit(),设置某个类的同时处于内存中的实例上限,可以协助检查内存泄露
public class MainActivity extends Activity{
private static class CastielClass{}
private static List<CastielClass> classList;
@Override
protected void onCreate(Bundle savedInstanceState) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectNetwork()
.penaltyLog()
.build());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
classList = new ArrayList<CastielClass>();
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.setClassInstanceLimit(CastielClass.class, 2)
.penaltyLog()
.build());
classList.add(new CastielClass());
classList.add(new CastielClass());
classList.add(new CastielClass());
classList.add(new CastielClass());
classList.add(new CastielClass());
classList.add(new CastielClass());
classList.add(new CastielClass());
classList.add(new CastielClass());
}
}
运行之后会有如下警告:
2020-01-03 15:14:56.556 11331-11331/com.zqc.testdemo E/StrictMode: class com.zqc.testdemo.MainActivity$CastielClass; instances=8; limit=2
android.os.StrictMode$InstanceCountViolation: class com.zqc.testdemo.MainActivity$CastielClass; instances=8; limit=2
at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
3、detectActivityLeaks() 用户检查 Activity 的内存泄露情况
public class MainActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.penaltyLog()
.build()
);
new Thread() {
@Override
public void run() {
while (true) {
SystemClock.sleep(1000);
}
}
}.start();
}
}
运行如下所示:
2020-01-03 15:35:33.045 3459-3459/? D/StrictMode: StrictMode policy violation; ~duration=91 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=65599 violation=2
at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1440)
at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:251)
at java.io.File.exists(File.java:807)
at android.app.ContextImpl.getDataDir(ContextImpl.java:2197)
at android.app.ContextImpl.getPreferencesDir(ContextImpl.java:517)
at android.app.ContextImpl.getSharedPreferencesPath(ContextImpl.java:714)
at android.app.ContextImpl.getSharedPreferences(ContextImpl.java:368)
at android.content.ContextWrapper.getSharedPreferences(ContextWrapper.java:167)
at android.preference.PreferenceManager.getDefaultSharedPreferences(PreferenceManager.java:526)
at com.sprd.providers.contacts.FSA.<init>(SyncSimAccountsReceiver.java:202)
at com.sprd.providers.contacts.FSA.getInstance(SyncSimAccountsReceiver.java:146)
at com.sprd.providers.contacts.SyncSimAccountsReceiver.onReceive(SyncSimAccountsReceiver.java:865)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:3296)
at android.app.ActivityThread.-wrap18(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1733)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6752)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:449)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)