• 使用StrictMode-StrictMode原理(1)


    1. strictmode入门使用:

    strictmode是android提供的一种调试环境的动态检测机制,主要的作用有两类:

    1.1 线程策略

    • 检测主线程读写卡顿

      detectDiskReads();
      detectDiskWrites();
      
      • 1
      • 2
    • 检测是否主线程使用网络

      detectNetwork();
      
      • 1
    • 检测自定义的慢检测

      detectCustomSlowCalls()
      
      • 1
    • 资源不匹配检测

      detectResourceMismatches()
      
      • 1
    • 没有buffer的IO操作

      detectUnbufferedIo()
      
      • 1

    具体的代码位置:

    StrictMode#ThreadPolicy#Builder.detectAll

    public @NonNull Builder detectAll() {
      detectDiskReads();
      detectDiskWrites();
      detectNetwork();
    
      final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion();
      if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) {
        detectCustomSlowCalls();
      }
      if (targetSdk >= Build.VERSION_CODES.M) {
        detectResourceMismatches();
      }
      if (targetSdk >= Build.VERSION_CODES.O) {
        detectUnbufferedIo();
      }
      return this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.2 虚拟机VM策略

    • 检测sqlite泄漏

      detectLeakedSqlLiteObjects();
      
      • 1
    • 检测activity泄漏:注意这里是引用计数,实际无法检测出真的泄漏,后续会进行详细讲解

      detectActivityLeaks()
      
      • 1
    • 检测是否需要关闭的没有关闭,比如IO流

      detectLeakedClosableObjects
      
      • 1
    • 检测是否有组件忘记反注册(BroadCastReceiver,Service)

      detectLeakedRegistrationObjects()
      
      • 1
    • 检测File Uri是否暴露

      detectFileUriExposure()
      
      • 1
    • 检测是否明文网络请求

      detectCleartextNetwork()
      
      • 1
    • 检测不正确的conext使用

      detectIncorrectContextUse()
      
      • 1
    • 检测不安全的intent launch

      detectUnsafeIntentLaunch()
      
      • 1

      源代码:

    public @NonNull Builder detectAll() {
        detectLeakedSqlLiteObjects();
    
        final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion();
        if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) {
          detectActivityLeaks();
          detectLeakedClosableObjects();
        }
        if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN) {
          detectLeakedRegistrationObjects();
        }
        if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
          detectFileUriExposure();
        }
        if (targetSdk >= Build.VERSION_CODES.M) {
          // TODO: always add DETECT_VM_CLEARTEXT_NETWORK once we have
          // facility for apps to mark sockets that should be ignored
          if (SystemProperties.getBoolean(CLEARTEXT_PROPERTY, false)) {
            detectCleartextNetwork();
          }
        }
        if (targetSdk >= Build.VERSION_CODES.O) {
          detectContentUriWithoutPermission();
          detectUntaggedSockets();
        }
        if (targetSdk >= Build.VERSION_CODES.Q) {
          detectCredentialProtectedWhileLocked();
        }
        if (targetSdk >= Build.VERSION_CODES.R) {
          detectIncorrectContextUse();
        }
        if (targetSdk >= Build.VERSION_CODES.S) {
          detectUnsafeIntentLaunch();
        }
    
        // TODO: Decide whether to detect non SDK API usage beyond a certain API level.
        // TODO: enable detectImplicitDirectBoot() once system is less noisy
    
        return this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    1.3 入门使用

    
        private fun strictModeOnDebug() {
            if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= 28) {
                StrictMode.setThreadPolicy(
                    ThreadPolicy.Builder()
                        .detectAll() //检测Thread所有选项
                        .penaltyLog() //打印日志
                        .build()
                )
                StrictMode.setVmPolicy(
                    VmPolicy.Builder() 
                        .detectAll() //检测VM所有选项
                        .penaltyLog() //打印日志
                        .build()
                )
                TrafficStats.setThreadStatsTag(0xF00D)
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    1.3.1 Thread检测操作

    • 监控写操作
            findViewById<Button>(R.id.io_read_btn).setOnClickListener {
                val outputStream = FileOutputStream(File(getExternalFilesDir("")?.path + "hello.json"))
                outputStream.write("hello world".toByteArray())
                outputStream.flush()
                outputStream.close()
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    logcat日志:

    2022-06-20 07:57:35.043 13012-13012/com.ifreedomer.strictmode D/StrictMode: StrictMode policy violation; ~duration=1 ms: android.os.strictmode.DiskWriteViolation
            at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1460)
            at libcore.io.BlockGuardOs.write(BlockGuardOs.java:347)
            at libcore.io.IoBridge.write(IoBridge.java:526)
            at java.io.FileOutputStream.write(FileOutputStream.java:381)
            at java.io.FileOutputStream.write(FileOutputStream.java:359)
            at com.ifreedomer.strictmode.MainActivity.onCreate$lambda-0(MainActivity.kt:26)
            at com.ifreedomer.strictmode.MainActivity.$r8$lambda$J_2M7j2bq49wREm_StCdiMLvSuc(Unknown Source:0)
            at com.ifreedomer.strictmode.MainActivity$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
            at android.view.View.performClick(View.java:6597)
            at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
            at android.view.View.performClickInternal(View.java:6574)
            at android.view.View.access$3100(View.java:778)
            at android.view.View$PerformClick.run(View.java:25885)
            at android.os.Handler.handleCallback(Handler.java:873)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:193)
            at android.app.ActivityThread.main(ActivityThread.java:6669)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    读日志也同理。需要注意的是,IO我们确实需要经常性的在主线程进行,为避免过多的日志对开发造成干扰,推荐使用时可以屏蔽此选项

    1.3.2 VM检测操作

    • IO未关闭检测

      示例代码:

              findViewById<Button>(R.id.io_not_close_btn).setOnClickListener {
                  var outputStream = FileOutputStream(File(getExternalFilesDir("")?.path + "hello.json"))
                  outputStream.write("hello world".toByteArray())
                  outputStream = FileOutputStream(File(getExternalFilesDir("")?.path + "hello.json"))
                  Runtime.getRuntime().gc()
                  Runtime.getRuntime().gc()
              }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      logcat:

      2022-06-20 08:11:36.071 13677-13687/com.ifreedomer.strictmode 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:1786)
              at dalvik.system.CloseGuard.warnIfOpen(CloseGuard.java:264)
              at java.io.FileOutputStream.finalize(FileOutputStream.java:475)
              at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)
              at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:237)
              at java.lang.Daemons$Daemon.run(Daemons.java:103)
              at java.lang.Thread.run(Thread.java:764)
           Caused by: java.lang.Throwable: Explicit termination method 'close' not called
              at dalvik.system.CloseGuard.open(CloseGuard.java:221)
              at java.io.FileOutputStream.<init>(FileOutputStream.java:241)
              at java.io.FileOutputStream.<init>(FileOutputStream.java:180)
              at com.ifreedomer.strictmode.MainActivity.onCreate$lambda-4(MainActivity.kt:57)
              at com.ifreedomer.strictmode.MainActivity.$r8$lambda$sFdQJzxyBqsXzAQCFxp0PCpUtgg(Unknown Source:0)
              at com.ifreedomer.strictmode.MainActivity$$ExternalSyntheticLambda3.onClick(Unknown Source:2)
              at android.view.View.performClick(View.java:6597)
              at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
              at android.view.View.performClickInternal(View.java:6574)
              at android.view.View.access$3100(View.java:778)
              at android.view.View$PerformClick.run(View.java:25885)
              at android.os.Handler.handleCallback(Handler.java:873)
              at android.os.Handler.dispatchMessage(Handler.java:99)
              at android.os.Looper.loop(Looper.java:193)
              at android.app.ActivityThread.main(ActivityThread.java:6669)
              at java.lang.reflect.Method.invoke(Native Method)
              at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
    • 数据库泄漏检测

    cursor未关闭

            findViewById<Button>(R.id.database_open_btn).setOnClickListener {
                val readableDatabase =
                    dbHelper.readableDatabase
                var rawQuery = readableDatabase.rawQuery("select * from user", null)
                val sqLiteCursor = rawQuery as SQLiteCursor
                sqLiteCursor.moveToFirst()
                Log.d(TAG,"sql window = ${sqLiteCursor.window}")
                rawQuery = null
                readableDatabase.close()
                Runtime.getRuntime().gc()
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    logcat:

    2022-06-20 08:00:19.687 13012-13022/com.ifreedomer.strictmode D/StrictMode: StrictMode policy violation: android.os.strictmode.SqliteObjectLeakedViolation: Finalizing a Cursor that has not been deactivated or closed. database = /data/user/0/com.ifreedomer.strictmode/databases/haha.db, table = null, query = select * from user
            at android.os.StrictMode.onSqliteObjectLeaked(StrictMode.java:1956)
            at android.database.sqlite.SQLiteCursor.finalize(SQLiteCursor.java:285)
            at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)
            at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:237)
            at java.lang.Daemons$Daemon.run(Daemons.java:103)
            at java.lang.Thread.run(Thread.java:764)
         Caused by: android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
            at android.database.sqlite.SQLiteCursor.<init>(SQLiteCursor.java:103)
            at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:52)
            at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1408)
            at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1347)
            at com.ifreedomer.strictmode.MainActivity.onCreate$lambda-2(MainActivity.kt:43)
            at com.ifreedomer.strictmode.MainActivity.$r8$lambda$KlEPG7__y4DedDU3EV-gqJ4mzpo(Unknown Source:0)
            at com.ifreedomer.strictmode.MainActivity$$ExternalSyntheticLambda3.onClick(Unknown Source:2)
            at android.view.View.performClick(View.java:6597)
            at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
            at android.view.View.performClickInternal(View.java:6574)
            at android.view.View.access$3100(View.java:778)
            at android.view.View$PerformClick.run(View.java:25885)
            at android.os.Handler.handleCallback(Handler.java:873)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:193)
            at android.app.ActivityThread.main(ActivityThread.java:6669)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    1.4 设置监听

    如果你想获得这些泄漏的信息,你可以自己实现StrictMode的Listener

    
            if (BuildConfig.DEBUG && Build.VERSION.SDK_INT >= 28) {
                StrictMode.setThreadPolicy(
                    ThreadPolicy.Builder()
                        .detectAll()
                        .penaltyLog()
                        .penaltyListener(Executors.newSingleThreadExecutor(),{
    
                        })
                        .build()
                )
                StrictMode.setVmPolicy(
                    VmPolicy.Builder()
                        .detectAll()
                        .penaltyLog()
                        .penaltyListener(Executors.newSingleThreadExecutor(),{
                        })
                        .build()
                )
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    客户案例 | 思腾合力助力深度图灵生成式AI应用平台建设
    卷积神经网络 作业
    <C++>多态的实战,详解三个具体案例
    【ODOO】Docker Compose 编排ODOO应用
    [祥云杯 2022] pwn2 leak
    《通用源码阅读指导书-MyBatis源码详解》笔记一(MyBatis概述、使用、源码大体执行流程、源码大体结构)
    Mac电脑风扇控制推荐 Macs Fan Control Pro 中文 for mac
    【JS】-内置对象API-Array(数组)-(一)改变原数组-的API-篇
    【网安神器篇】——LaZagne凭证收集工具
    MM41/MM42/MM43零售物料主数据BAPI创建示例(WRF_MATERIAL_MAINTAINDATA_RT)
  • 原文地址:https://blog.csdn.net/aa375809600/article/details/125420033