美文网首页
Android严格模式

Android严格模式

作者: 文泰ChrisTwain | 来源:发表于2022-10-22 14:29 被阅读0次

    1.背景

    为防止出现资源泄漏或者主线程发生的意外耗时网络操作或IO操作导致卡顿,Android2.3(API9)开始提供了监测接口StrictMode类,针对单个线程和虚拟机的所有对象定义了检查策略。监测到异常后日志能打印到具体的代码堆栈,便于立刻排查解决。

    2.使用方式

    应用启动处设置严苛模式,可在Application.onCreate()或Activity中启用监测,包含线程级别和进程级别两类监测接口

    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            if (BuildConfig.DEBUG) {
                enableStrictMode();
            }
        }
    
        private void enableStrictMode() {
            // 监测当前线程(UI线程)上的网络、磁盘读写等耗时操作
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectDiskReads()  // 监测读磁盘
                 .detectDiskWrites()  // 监测写磁盘
                 .detectNetwork()      // 监测网络操作
                 .detectCustomSlowCalls()  // 监测哪些方法执行慢
                 .detectResourceMismatches()  // 监测资源不匹配
                 .penaltyLog()   // 打印日志,也可设置为弹窗提示penaltyDialog()或者直接使进程死亡penaltyDeath()
                 .penaltyDropBox()  //监测到将信息存到Dropbox文件夹 data/system/dropbox
                 .build());
    
          // 监测VM虚拟机进程级别的Activity泄漏或者其它资源泄漏
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectActivityLeaks()  // 监测内存泄露情况
                 .detectLeakedSqlLiteObjects()  // SqlLite资源未关闭,如cursor
                 .detectLeakedClosableObjects()  // Closable资源未关闭,如文件流
                 .detectCleartextNetwork()  // 监测明文网络
                 .setClassInstanceLimit(MyClass.class, 1)  // 设置某个类的实例上限,可用于内存泄露提示
                 .detectLeakedRegistrationObjects()  // 监测广播或者ServiceConnection是否有解注册
                 .penaltyLog()
                 .build());
        }
    }
    

    可按需建造需要监测的场景,也可使用Builder().detectAll()监测所有情况。
    比如文件流未关闭,打印的日志提示如下,可根据堆栈找到导致问题的地方

    2022-01-11 11:11:53.096 20010-20031/com.hello.myApp D/StrictMode: StrictMode policy violation: android.os.strictmode.LeakedClosableViolation: 
    A resource was acquired at attached stack trace but never released. 
    See java.io.Closeable for information on avoiding resource leaks.
            at android.os.StrictMode$AndroidCloseGuardReporter.report(StrictMode.java:1987)
            at dalvik.system.CloseGuard.warnIfOpen(CloseGuard.java:345)
            at java.io.FileInputStream.finalize(FileInputStream.java:503)
            at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:291)
            at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:278)
            at java.lang.Daemons$Daemon.run(Daemons.java:139)
            at java.lang.Thread.run(Thread.java:930)
         Caused by: java.lang.Throwable: Explicit termination method 'close' not called
            at dalvik.system.CloseGuard.openWithCallSite(CloseGuard.java:295)
            at dalvik.system.CloseGuard.open(CloseGuard.java:263)
            at java.io.FileInputStream.<init>(FileInputStream.java:176)
            at java.io.FileInputStream.<init>(FileInputStream.java:115)
            ... 自己代码调用栈
    

    ps: 开发人员选项里的严格模式跟这个有差别
    开发者选项中开启严格模式,已提示应用在主线程上执行长时间操作时闪烁屏幕


    开发人员选项-严格模式

    3.try-with-resources语句关闭资源

    一种自动关闭资源的方式能更优雅处理资源关闭,JDK7中一个新的异常处理机制,在处理必须关闭的资源时,使用try-with-resources语句替代try-finally语句。
    try-finally语句使用

    InputStream in = null;
    try {
        in = new FileInputStream(file);
        int temp;
        while ((temp = in.read()) != -1) {
            System.out.write(temp);
        }
    } catch (IOException e) {
        // todo
    } finally {
        in.close();
    }
    

    替换为try-with-resources语法,即try后面加括号引入资源

    try (InputStream in = new FileInputStream(file)) {
        in = new FileInputStream(file);
        int temp;
        while ((temp = in.read()) != -1) {
            System.out.write(temp);
        }
    } catch (IOException e) {
        // todo
    }
    

    实现AutoCloseable或Closeable接口均可使用此操作,可将实现此接口的类定义为资源,自己定义的资源也可以实现此接口。
    ps: try-with-resources只能在JDK7及以上使用,必选在括号内有变量声明,JDK9之后括号内直接使用外部定义的变量即可

    4.原理

    (1)通过在需要监控的代码中植入代码实现此功能,比如IO中通过在open,read,write,close时进行监控,从上面监听到异常打印的日志堆栈即可看出植入的代码

    android.os.StrictMode$AndroidCloseGuardReporter.report(StrictMode.java:1987)
            at dalvik.system.CloseGuard.warnIfOpen(CloseGuard.java:345)
            at java.io.FileInputStream.finalize(FileInputStream.java:503)
    
    image.png

    Object finalize()方法当GC确定不存在对该对象的有更多引用时,对象的垃圾回收器就会调用这个方法,FileInputStream将被回收时判断资源还未关闭随即提示。
    (2)再如监测Acitivity泄露时,performLaunchActivity、performDestroyActivity方法中启动和回收Activity时用HashMap<Class, Integer>计数。
    (3)StrictMode是建立在BlockGuard和CloseGuard之上的机制,Guard表示守卫,Block表示阻塞,在进行一些耗时操作时,譬如磁盘读写、网络操作,有一个守卫在监测着,它就是BlockGuard,如果这些耗时的操作导致主线程阻塞,BlockGuard就会发出通知,Close对应可打开的文件,在文件被打开后,也有一个守卫在监测着,它就是CloseGuard,如果没有关闭文件,则CloseGuard就会发出通知。

    参考

    严苛模式StrictMode使用指南
    未关闭的文件流会引起内存泄露么
    Java9改进的try-with-resources
    try-with-resource语句使用

    相关文章

      网友评论

          本文标题:Android严格模式

          本文链接:https://www.haomeiwen.com/subject/juvwortx.html