Android StrictMode運(yùn)行流程(推薦)
什么是 StrictMode(嚴(yán)苛模式)
strictmode是android在 API9后引入的檢測(cè)影響app運(yùn)行流暢性的一種機(jī)制,例如我們都知道的主線程中不允許有網(wǎng)絡(luò)操作這條規(guī)則就是嚴(yán)苛模式規(guī)則的一種.
strictmode.java 這個(gè)類(lèi)中設(shè)定了許多detect標(biāo)志位例如 DETECT_NETWORK ,還有許多 penalty標(biāo)志位例如 PENALTY_NETWORK , DETECT標(biāo)志位決定strictmode是否要對(duì)這項(xiàng)內(nèi)容進(jìn)行檢測(cè),PENALTY標(biāo)志位決定了在這項(xiàng)內(nèi)容發(fā)生時(shí)是否要拋出異常(相當(dāng)于一種懲罰機(jī)制,PENALTY的意思就是懲罰).
StrictMode 類(lèi)的作用之一就是對(duì)這些標(biāo)志位進(jìn)行管理,通過(guò) setThreadPolicy() 方法可以設(shè)定 Policy 變量中的mask值.
之后會(huì)將 POLICY 變量傳入 BlockGuard 中,BlockGuard 運(yùn)行在 Dalvik虛擬機(jī)中,對(duì)所有的異常操作進(jìn)行統(tǒng)一的管理.
Android官方文檔中對(duì)于strict mode 給出的解釋
strictmode 是一種開(kāi)發(fā)工具,引入它可以使你發(fā)現(xiàn)在開(kāi)發(fā)過(guò)程中產(chǎn)生的問(wèn)題,并修復(fù)它們.
在 application main thread中常有UI相關(guān)的操作和動(dòng)畫(huà)發(fā)生,strictmode可以在主線程中檢測(cè)硬盤(pán)和網(wǎng)絡(luò)相關(guān)的操作.將硬盤(pán)讀寫(xiě)操作和網(wǎng)絡(luò)相關(guān)操作挪出主線程可以使你的app更加流暢和具有響應(yīng)性.同時(shí)為了使app更加響應(yīng)性,你可以屏蔽ANR發(fā)生時(shí)彈出的dialog.
需要注意的是,盡管android設(shè)備的硬盤(pán)類(lèi)型大多為 flash memory,建立在這種存儲(chǔ)介質(zhì)上的文件系統(tǒng)的并發(fā)性仍然是非常有限的(速度上肯定是RAM比較快).
大部分情況下,硬盤(pán)的讀寫(xiě)操作都是非??斓模谀承┣闆r下,后臺(tái)進(jìn)程中會(huì)運(yùn)行耗費(fèi)很大的I/O操作,在這種情況下,app的響應(yīng)速度會(huì)下降很多.
一.setThreadPolicy()流程
StrictMode類(lèi)的文檔中給出的strictmode啟動(dòng)方式
* 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.
在執(zhí)行了 setThreadPolicy()函數(shù)后會(huì)調(diào)用 setThreadPolicyMask()方法.
public static void setThreadPolicy(final ThreadPolicy policy) { setThreadPolicyMask(policy.mask); }
2.
在 setThreadPolicyMask()方法中,除了在java層的threadLocal中設(shè)置外,還需要在Native層也進(jìn)行一個(gè)設(shè)置.
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,會(huì)返回一個(gè)默認(rèn)policy,默認(rèn)policy不進(jìn)行任何設(shè)置和檢測(cè),policy對(duì)象存儲(chǔ)在threadLocal變量中(每個(gè)線程保存一個(gè)policy的對(duì)象),首次運(yùn)行該方法會(huì)生成一個(gè)默認(rèn)policy(mMask=0)保存在threadLocal中,這里的policy對(duì)象是AndroidBlockGuardPolicy類(lèi)型.
// 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層的代碼:
設(shè)置了policy
386void IPCThreadState::setStrictModePolicy(int32_t policy) 387{ 388 mStrictModePolicy = policy; 389}
二.StrictMode如何檢測(cè)問(wèn)題.
1.
CloseGuard檢測(cè)游標(biāo)是否正常關(guān)閉:
當(dāng)使用ContentResolver來(lái)查詢數(shù)據(jù)庫(kù)的時(shí)候,會(huì)返回一個(gè)CursorWrapperInner類(lèi)型的Cursor對(duì)象.
mCursor = mResolver.query(mUri, null, null, null, null);
CloseGuard對(duì)CursorWrapperInner是否正常關(guān)閉的檢測(cè)的邏輯在finalize()函數(shù)中,finalize()會(huì)在gc執(zhí)行垃圾回收的時(shí)候被調(diào)用(垃圾回收使用了GcRoot算法)
如果沒(méi)有執(zhí)行CursorWrapperInner的close()函數(shù),僅將CursorWrapperInner對(duì)象置為null,當(dāng)主動(dòng)觸發(fā)gc的時(shí)候( Systemgc()),finalize()函數(shù)被調(diào)用 ,"Cursor finalized without prior close()"這段log被打印.但如果沒(méi)有將CursorWrapperInner對(duì)象置為null,這時(shí)主動(dòng)觸發(fā)gc并不會(huì)引起 finalize()函數(shù)的執(zhí)行,因?yàn)镃ursorWrapperInner對(duì)象被強(qiáng)引用,垃圾回收器在回收時(shí)不會(huì)考慮回收強(qiáng)引用對(duì)象,即使最后內(nèi)存不足而崩潰.
經(jīng)過(guò)測(cè)試程序的測(cè)試,發(fā)現(xiàn)"Cursor finalized without prior close()"這段log在 CursorWrapperInner對(duì)象置空并執(zhí)行 System.gc()后是會(huì)打印出來(lái)的.
但是 CloseGuard中的 warnIfOpen()函數(shù)始終沒(méi)有執(zhí)行
在 CursorWrapperInner的構(gòu)造函數(shù)中,mCloseGuard執(zhí)行 open()函數(shù),在 open函數(shù)中allocationSite被賦值,而 ENABLED 變量是默認(rèn)為true的,唯一改變它的setEnabled()方法在源碼中也并沒(méi)有被調(diào)用,所以應(yīng)該是會(huì)在REPORTER中打印SystemLog的,但最后SystemLog并沒(méi)有打印,具體原因分析不出來(lái).
@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()也是用來(lái)檢測(cè)數(shù)據(jù)庫(kù)游標(biāo)有沒(méi)有正常關(guān)閉,但這個(gè)函數(shù)檢測(cè)的是通過(guò)SqliteDataBase. query()得到的SqliteCursor游標(biāo)對(duì)象.
檢測(cè)位置也是在 finalize()函數(shù)中.
/** * 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(); } }
流程如下圖
三.StrictMode中使用到的橋接模式
橋接模式:所謂橋接模式就是將邏輯的抽象與實(shí)現(xiàn)分開(kāi)的一種模式
總結(jié)
以上所述是小編給大家介紹的Android StrictMode運(yùn)行流程,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Android通話默認(rèn)打開(kāi)揚(yáng)聲器的方法
這篇文章主要介紹了Android通話默認(rèn)打開(kāi)揚(yáng)聲器的方法.小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08微信或手機(jī)瀏覽器在線顯示office文件(已測(cè)試ios、android)
這篇文章主要介紹了微信或手機(jī)瀏覽器在線顯示office文件,已測(cè)試ios、android,感興趣的小伙伴們可以參考一下2016-06-06Android自定義頂部導(dǎo)航欄控件實(shí)例代碼
這篇文章主要介紹了Android自定義頂部導(dǎo)航欄控件實(shí)例代碼,需要的朋友可以參考下2017-12-12android應(yīng)用開(kāi)發(fā)之spinner控件的簡(jiǎn)單使用
Android的控件有很多種,其中就有一個(gè)Spinner的控件,這個(gè)控件其實(shí)就是一個(gè)下拉顯示列表。本文通過(guò)腳本之家平臺(tái)給大家介紹android應(yīng)用開(kāi)發(fā)之spinner控件的簡(jiǎn)單使用,感興趣的朋友可以參考下2015-11-11Android開(kāi)發(fā)使用strings.xml多語(yǔ)言翻譯解決方案
這篇文章主要為大家介紹了Android開(kāi)發(fā)使用strings.xml多語(yǔ)言翻譯解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Android中TelephonyManager類(lèi)的用法案例詳解
這篇文章主要介紹了Android中TelephonyManager類(lèi)的用法,以獲取Android手機(jī)硬件信息為例詳細(xì)分析了TelephonyManager類(lèi)的使用技巧,需要的朋友可以參考下2015-09-09Android實(shí)現(xiàn)二級(jí)列表購(gòu)物車(chē)功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)二級(jí)列表購(gòu)物車(chē)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10