Android StrictMode运行流程

什么是 StrictMode(严苛模式)

strictmode是android在 API9后引入的检测影响app运行流畅性的一种机制,例如我们都知道的主线程中不允许有网络操作这条规则就是严苛模式规则的一种.

strictmode.java 这个类中设定了许多detect标志位例如 DETECT_NETWORK ,还有许多 penalty标志位例如 PENALTY_NETWORK , DETECT标志位决定strictmode是否要对这项内容进行检测,PENALTY标志位决定了在这项内容发生时是否要抛出异常(相当于一种惩罚机制,PENALTY的意思就是惩罚).

StrictMode 类的作用之一就是对这些标志位进行管理,通过 setThreadPolicy() 方法可以设定 Policy 变量中的mask值.

之后会将 POLICY 变量传入 BlockGuard 中,BlockGuard 运行在 Dalvik虚拟机中,对所有的异常操作进行统一的管理.

Android官方文档中对于strict mode 给出的解释

strictmode 是一种开发工具,引入它可以使你发现在开发过程中产生的问题,并修复它们.
在 application main thread中常有UI相关的操作和动画发生,strictmode可以在主线程中检测硬盘和网络相关的操作.将硬盘读写操作和网络相关操作挪出主线程可以使你的app更加流畅和具有响应性.同时为了使app更加响应性,你可以屏蔽ANR发生时弹出的dialog.
需要注意的是,尽管android设备的硬盘类型大多为 flash memory,建立在这种存储介质上的文件系统的并发性仍然是非常有限的(速度上肯定是RAM比较快).
大部分情况下,硬盘的读写操作都是非常快的,但在某些情况下,后台进程中会运行耗费很大的I/O操作,在这种情况下,app的响应速度会下降很多.

一.setThreadPolicy()流程

StrictMode类的文档中给出的strictmode启动方式

 * public void onCreate() {
 *     if (DEVELOPER_MODE) {
 *         StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
 *                 .detectDiskReads()
 *                 .detectDiskWrites()
 *                 .detectNetwork()   // or .detectAll() for all detectable problems
 *                 .penaltyLog()
 *                 .build());
 *         StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
 *                 .detectLeakedSqlLiteObjects()
 *                 .detectLeakedClosableObjects()
 *                 .penaltyLog()
 *                 .penaltyDeath()
 *                 .build());
 *     }
 *     super.onCreate();

1.

在执行了 setThreadPolicy()函数后会调用 setThreadPolicyMask()方法.

    public static void setThreadPolicy(final ThreadPolicy policy) {
        setThreadPolicyMask(policy.mask);
    }

2.

setThreadPolicyMask()方法中,除了在java层的threadLocal中设置外,还需要在Native层也进行一个设置.

    private static void setThreadPolicyMask(final int policyMask) {
        // In addition to the Java-level thread-local in Dalvik's
        // BlockGuard, we also need to keep a native thread-local in
        // Binder in order to propagate the value across Binder calls,
        // even across native-only processes.  The two are kept in
        // sync via the callback to onStrictModePolicyChange, below.
        setBlockGuardPolicy(policyMask);

        // And set the Android native version...
        Binder.setThreadStrictModePolicy(policyMask);
    }

3.

首先分析java层的 setBlockGuardPolicy()方法.
如果policyMask==0,会返回一个默认policy,默认policy不进行任何设置和检测,policy对象存储在threadLocal变量中(每个线程保存一个policy的对象),首次运行该方法会生成一个默认policy(mMask=0)保存在threadLocal中,这里的policy对象是AndroidBlockGuardPolicy类型.

    // Sets the policy in Dalvik/libcore (BlockGuard)
    private static void setBlockGuardPolicy(final int policyMask) {
        if (policyMask == 0) {
            BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
            return;
        }
        final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
        final AndroidBlockGuardPolicy androidPolicy;
        if (policy instanceof AndroidBlockGuardPolicy) {
            androidPolicy = (AndroidBlockGuardPolicy) policy;
        } else {
            androidPolicy = threadAndroidPolicy.get();
            BlockGuard.setThreadPolicy(androidPolicy);
        }
        androidPolicy.setPolicyMask(policyMask);
    }

4.

再看Native层的代码:
设置了policy

386void IPCThreadState::setStrictModePolicy(int32_t policy)
387{
388    mStrictModePolicy = policy;
389}

二.StrictMode如何检测问题.

1.

CloseGuard检测游标是否正常关闭:
当使用ContentResolver来查询数据库的时候,会返回一个CursorWrapperInner类型的Cursor对象.

mCursor = mResolver.query(mUri, null, null, null, null);

CloseGuard对CursorWrapperInner是否正常关闭的检测的逻辑在finalize()函数中,finalize()会在gc执行垃圾回收的时候被调用(垃圾回收使用了GcRoot算法)

如果没有执行CursorWrapperInnerclose()函数,仅将CursorWrapperInner对象置为null,当主动触发gc的时候( Systemgc()),finalize()函数被调用 ,"Cursor finalized without prior close()"这段log被打印.但如果没有将CursorWrapperInner对象置为null,这时主动触发gc并不会引起 finalize()函数的执行,因为CursorWrapperInner对象被强引用,垃圾回收器在回收时不会考虑回收强引用对象,即使最后内存不足而崩溃.

经过测试程序的测试,发现"Cursor finalized without prior close()"这段log在 CursorWrapperInner对象置空并执行 System.gc()后是会打印出来的.

但是 CloseGuard中的 warnIfOpen()函数始终没有执行

CursorWrapperInner的构造函数中,mCloseGuard执行 open()函数,在 open函数中allocationSite被赋值,而 ENABLED 变量是默认为true的,唯一改变它的setEnabled()方法在源码中也并没有被调用,所以应该是会在REPORTER中打印SystemLog的,但最后SystemLog并没有打印,具体原因分析不出来.

        @Override
        protected void finalize() throws Throwable {
            try {
                if (mCloseGuard != null) {
                    mCloseGuard.warnIfOpen();
                }

                if (!mProviderReleased && mContentProvider != null) {
                    // Even though we are using CloseGuard, log this anyway so that
                    // application developers always see the message in the log.
                    Log.w(TAG, "Cursor finalized without prior close()");
                    ContentResolver.this.releaseProvider(mContentProvider);
                }
            } finally {
                super.finalize();
            }
        }
    }
    public void warnIfOpen() {
        if (allocationSite == null || !ENABLED) {
            return;
        }

        String message =    
                ("A resource was acquired at attached stack trace but never released. "
                 + "See java.io.Closeable for information on avoiding resource leaks.");

        REPORTER.report(message, allocationSite);
    }
        @Override public void report (String message, Throwable allocationSite) {
            System.logW(message, allocationSite);
        }
        CursorWrapperInner(Cursor cursor, IContentProvider icp) {
            super(cursor);
            mContentProvider = icp;
            mCloseGuard.open("close");
        }

2.

onSqliteObjectsLeaked()也是用来检测数据库游标有没有正常关闭,但这个函数检测的是通过SqliteDataBase. query()得到的SqliteCursor游标对象.

检测位置也是在 finalize()函数中.

 /**
     * Release the native resources, if they haven't been released yet.
     */
    @Override
    protected void finalize() {
        try {
            // if the cursor hasn't been closed yet, close it first
            if (mWindow != null) {
                if (mStackTrace != null) {
                    String sql = mQuery.getSql();
                    int len = sql.length();
                    StrictMode.onSqliteObjectLeaked(
                        "Finalizing a Cursor that has not been deactivated or closed. " +
                        "database = " + mQuery.getDatabase().getLabel() +
                        ", table = " + mEditTable +
                        ", query = " + sql.substring(0, (len > 1000) ? 1000 : len),
                        mStackTrace);
                }
                close();
            }
        } finally {
            super.finalize();
        }
    }

流程如下图

Android StrictMode运行流程_第1张图片
strictMode.jpg

三.StrictMode中使用到的桥接模式

桥接模式:所谓桥接模式就是将逻辑的抽象与实现分开的一种模式

你可能感兴趣的:(Android StrictMode运行流程)