Android性能调优--StrictMode
转载自:https://blog.csdn.net/weixin_40763897/article/details/89018306
性能无外乎就是CPU密集型或I/O密集型两种。
StrictMode是一个开发者工具,常用于捕获在应用主线程中发生的磁盘I/O、网络访问违例等问题。
StrictMode具体能检测什么呢
StrictMode主要检测两大问题:线程策略(TreadPolicy)和VM策略(VmPolicy)。
ThreadPolicy线程策略:
1.自定义的耗时调用,使用detectCustomSlowCalls()开启;
2.磁盘读取操作,使用detectDiskReads()开启;
3.磁盘写入操作,使用detectDiskWrites()开启;
4.网络操作,使用detectNetwork()开启。
VmPolicy虚拟机策略:
1.Activity泄漏,使用detectActivityLeaks()开启;
2.未关闭的Closable对象泄漏,使用detectLeakedClosableObjects()开启;
3.泄漏的Sqlite对象,使用detectLeakedSqlLiteObjects()开启;
4.检测实例数量,使用setClassInstanceLimit()开启。
如何使用呢?
可以在应用的Application、Activity或者其他应用组件的onCreate方法中加入检测代码,如:
public void onCreate() {if (DEVELOPER_MODE) {StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork() // or .detectAll() for all detectable problems.penaltyLog().build());StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());}super.onCreate();}
如果观测结果呢?
StrictMode有多种报告违例的形式,但要分析具体违例情况,还是需要查看日志。我们在此介绍两种方式,一种是在android studio IDE的logcat里查看:
另一种是在终端下,过滤StrictMode得到违例的具体stacktrace信息(手机要打开调试用的app),然后打开命令终端,使用adb命令来查看:
~$ adb logcat | grep StrictMode
如果发现有违例的行为,可以通过使用线程(threads)、Handler、AsyncTask、IntentService等帮助解决。提供一下些常用的措施:
- 假如在主线程中进行文件读写出现了违例,可用工作线程(另外开辟子线程)来解决,必要时还可以结合Handler一起来解决。
- SharedPreferences的写入操作,在API 9以上应该优先使用apply而非commit。
- 如果是存在未关闭的Closable对象(如有些流OutputStream,在出现异常时,未来得及关闭),根据对应的stacktrace进行关闭。
- 如果是SQLite对象泄漏,根据对应的stacktrace进行释放。
例子
接下来我们来举个在主线程中的文件写入,引起违例警告的例子:
1.首先Activity的onCreate方法中加上检测代码:
注:以下的代码启用全部的ThreadPolicy和VmPolicy违例检测
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
2.这是引起违例的代码:
public void writeToExternalStorageInMainThread() {File externalStorage = Environment.getExternalStorageDirectory();File destFile = new File(externalStorage, "hello.txt");try {OutputStream output = new FileOutputStream(destFile, true);output.write("I am testing io".getBytes());output.flush();output.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
3.运行app,观察logcat的输出,下面是部分截图:
logcat已告诉我们出现了违例和出现的位置。
4.解决这个违例
修改一下writeToExternalStorageInMainThread方法,将引起违例的代码都放在一个工作线程中去执行,如下所示:
public void writeToExternalStorageInMainThread() {new Thread(new Runnable() {@Overridepublic void run() {File externalStorage = Environment.getExternalStorageDirectory();File destFile = new File(externalStorage, "hello.txt");OutputStream output = null;try {output = new FileOutputStream(destFile, true);output.write("I am testing io".getBytes());output.flush();output.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally {if(output != null){try {output.close();} catch (IOException e) {e.printStackTrace();}}}}}).start();}
demo示例
检测内存泄漏
通常情况下,检测内存泄漏,我们会使用MAT(Eclipse Memory Analyzer)工具对heap dump 文件进行分析。但是使用StrictMode,只需要过滤日志就能发现内存泄漏,更快捷方便。
1.首先,需要开启对检测Activity泄漏的违例检测,可以使用detectAll或者detectActivityLeaks():
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectActivityLeaks().penaltyLog().build());
2.写一段能够产生Activity泄漏的代码
public class LeakActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_leak);if(MyApplication.IS_DEBUG){MyApplication.sLeakyActivities.add(this);}}
}
3.MyApplication中关于sLeakyActivities的部分实现
public class MyApplication extends Application {public static final boolean IS_DEBUG = true;public static ArrayList<Activity> sLeakyActivities = new ArrayList<Activity>();@Overridepublic void onCreate() {StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectActivityLeaks().penaltyLog().build());super.onCreate();}
}
4.引发内存泄漏的操作:
通过不断从MainActivity打开LeakActivity,再返回,再打开,如此反复操作,引发内存泄漏,下面是MainActivity的代码:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView textView = (TextView)findViewById(R.id.tv);textView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(MainActivity.this,LeakActivity.class));}});}
}
5.当我们反复进入LeakyActivity再退出,在Logcat中过滤StrictMode就会得到这样的日志:
2019-04-04 19:49:43.502 32708-32708/com.wong.appmemoryleakydemo E/StrictMode: class com.wong.appmemoryleakydemo.LeakActivity; instances=7; limit=1android.os.StrictMode$InstanceCountViolation: class com.wong.appmemoryleakydemo.LeakActivity; instances=7; limit=1at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
分析日志:LeakyActivity本应该只存在一个实例的,但现在存在了7个,说明LeakyActivity发生了内存泄漏。
检测内存泄漏demo
自定义检测类的实例泄漏
我们还可以通过StrictMode自定义检测类的实例泄漏。从API 11 开始,系统提供的这个方法setClassInstanceLimit可以实现我们的需求。比如说有个类叫SingleAction.class,我们认为在运行时,它应该只有一个实例,如果多一个,我们就可以认为发生了内存泄漏:
1.开启违例检测,如下:
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().setClassInstanceLimit(SingleAction.class,1).penaltyLog().build());
上面代码就是说,当SingleAction类出现多于一个实例时,就报告内存泄漏。
耗时调用违例:noteSlowCall
StrictMode从API 11开始允许开发者自定义一些耗时调用违例,这种自定义适用于自定义的任务执行类中,比如自定义任务处理类MyTaskExecutor:
public class MyTaskExecutor {public void execute(Runnable task){task.run();}
}
但是如果我们想跟踪每个任务执行的耗时情况,如果耗时大于500毫秒就通知我们,我们该怎么办呢?StrictMode的noteSlowCall方法可以实现这个功能,修改MyTaskExecutor,如下所示:
public class MyTaskExecutor {public static long CAN_BEAR_TIME = 500;public void execute(Runnable task){long sTime = SystemClock.uptimeMillis();task.run();long cTime = SystemClock.uptimeMillis() - sTime;if(cTime > CAN_BEAR_TIME){StrictMode.noteSlowCall("slow call cost:"+cTime);}}
执行一个耗时1000毫秒的任务,测试一下:
package com.wong.timeconsumingviolation;import android.os.StrictMode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyTaskExecutor taskExecutor = new MyTaskExecutor();taskExecutor.execute(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});}
}
在Logcat中过滤StrictMode就会得到这样的日志:
2019-04-04 20:27:27.020 6147-6147/com.wong.timeconsumingviolation D/StrictMode: StrictMode policy violation; ~duration=29 ms: android.os.StrictMode$StrictModeCustomViolation: policy=65599 violation=8 msg=slow call cost:1001at android.os.StrictMode$AndroidBlockGuardPolicy.onCustomSlowCall(StrictMode.java:1397)at android.os.StrictMode.noteSlowCall(StrictMode.java:2340)at com.wong.timeconsumingviolation.MyTaskExecutor.execute(MyTaskExecutor.java:19)at com.wong.timeconsumingviolation.MainActivity.onCreate(MainActivity.java:17)at android.app.Activity.performCreate(Activity.java:7040)at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2809)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2931)at android.app.ActivityThread.-wrap11(Unknown Source:0)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1620)at android.os.Handler.dispatchMessage(Handler.java:105)at android.os.Looper.loop(Looper.java:173)at android.app.ActivityThread.main(ActivityThread.java:6698)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:782)
从日志分析来看,我们已成功跟踪到耗时超过500ms的任务了。
注意上面的日志结果中的:duration=29 ms,并非耗时任务的执行时间,而我们自定义信息msg=slow call cost:1001包包含的时间才是真正的耗时。
自定义耗时违例demo
注意
- StrictMode无法监控JNI中的磁盘IO和网络请求。
- 应用中并非需要解决全部的违例情况,比如有些IO操作必须在主线程中进行。
- 通常情况下StrictMode给出的耗时相对实际情况偏高,并不是真正的耗时数据。
Android性能调优--StrictMode相关推荐
- Android性能调优篇之探索垃圾回收机制
开篇废话 如果我们想要进行内存优化的工作,还是需要了解一下,但这一块的知识属于纯理论的,有可能看起来会有点枯燥,我尽量把这一篇的内容按照一定的逻辑来走一遍.首先,我们为什么要学习垃圾回收的机制,我大概 ...
- android性能调优的工具,神兵利器-Android 性能调优工具 Hugo
在进行Android性能调优.减少应用卡顿时,寻找可优化的code是一个必要的过程.如何发现应用中的耗时任务甚至是耗时函数呢,如果可以在log中打印每个方法的执行时间,甚至把执行方法时的输入输出同时打 ...
- Android性能调优实例
本文主要分享自己在appstore项目中的性能调优点,包括 同步改异步.缓存.Layout优化.数据库优化.算法优化.延迟执行等. 一.性能瓶颈点 整个页面主要由6个Page的ViewPager,每个 ...
- StrictMode ——Android性能调优的利器
性能无外乎就是CPU密集型或I/O密集型两种. StrictMode是一个开发者工具,常用于捕获在应用主线程中发生的磁盘I/O.网络访问违例等问题. StrictMode具体能检测什么呢 Strict ...
- Android性能调优利器StrictMode
2019独角兽企业重金招聘Python工程师标准>>> 作为Android开发,日常的开发工作中或多或少要接触到性能问题,比如我的Android程序运行缓慢卡顿,并且常常出现ANR对 ...
- Android性能调优篇之探索JVM内存分配
开篇废话 今天我们一起来学习JVM的内存分配,主要目的是为我们Android内存优化打下基础. 一直在想以什么样的方式来呈现这个知识点才能让我们易于理解,最终决定使用方法为:图解+源代码分析. 欢迎访 ...
- Android性能调优:App启动速度优化
一.App启动分类 1.冷启动 Cold start 在启动应用前,系统还没有App的任何进程.比如设备开机后应用的第一次启动,系统杀掉应用进程 (如:系统内存吃紧引发的 kill 和 用户主动产生的 ...
- android性能调优!2021年Android春招面试经历,再不刷题就晚了!
前言 对于字节跳动的二面三面而言,Framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等问题都成高频问点!然而很多的朋友在面试时却答不上或者答不全!今天在这分享下这些 ...
- 深入浅出Android性能调优,震撼来袭免费下载!
前言 成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样. 本文思维导图 1. 简历篇 如果把自己看成一个产品,那么简历就是说明书. 写简历看似简单,想写 ...
最新文章
- python numpy矩阵索引_python-为什么scipy csr矩阵的行索引比numpy数组...
- 如何去除Windows8测试版界面“水印”
- pytorch实现LeNet5
- linux登录用户目录,linux命令
- LintCode 4.丑数
- 软件测试之“支付功能”测试
- VB text控件如何实现换行
- FlexBuilder3安装集成eclipse
- 【点宽专栏】Dual Thrust 交易策略
- 用Java实现24点游戏
- 制作Windows severs 2019启动盘
- python中的Pickle文件和npy文件
- nmn产品品牌,nmn产品哪家更靠谱,看完不花冤枉钱
- MTK-EIS电子防抖-gyro校准
- linux硬盘对拷 再生龙,使用Clonezilla(再生龙)克隆Linux系统
- 攻防世界_moile
- django--安装
- 项目构建工具--webpack
- 学PS平面设计前,你需要先了解这些
- 内网渗透:八、CVE-2020-1472 NetLogon 域内提权漏洞(域控密码置空)