句柄泄漏(Fd leak)不是什麼新鮮問題了,網上一搜這種問題,有不少種解決辦法。 但沒有系統性,致使排查問題效率極低。筆者但願經過這一篇文章,幫助你們理清思路,快速解決問題。java
本文也將按照這幾步,逐步深刻到問題中。linux
通常發生這種問題時,堆棧以下:android
java.lang.RuntimeException
Could not read input channel file descriptors from parcel.
android.view.InputChannel.nativeReadFromParcel(Native Method)
android.view.InputChannel.readFromParcel(InputChannel.java:148)
android.view.IWindowSession$Stub$Proxy.addToDisplay(IWindowSession.java:804)
android.view.ViewRootImpl.setView(ViewRootImpl.java:770)
android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:356)
android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3716)
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2901)
android.app.ActivityThread.-wrap11(Unknown Source:0)
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1616)
android.os.Handler.dispatchMessage(Handler.java:106)
android.os.Looper.loop(Looper.java:173)
android.app.ActivityThread.main(ActivityThread.java:6653)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:821)android.view.InputChannel.nativeReadFromParcel(Native Method)
複製代碼
顯然這種堆棧是沒什麼卵用的,惟一的用處就是,你把這log去網上一搜,你們說這多是句柄泄漏(Fd leak)問題。shell
這一步就有些學問了。 你有2種手段來找到這個問題的復現步驟:bash
嚴格模式的具體使用,你們能夠移步官網:developer.android.com/reference/a…app
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
複製代碼
咱們只要加入detectLeakedClosableObjects() penaltyLog()
這兩個方法,logcat裏面搜索「Strick Mode」關鍵字,就能直接定位到是哪一個Closable沒有關閉。而每每大量的Closeable沒有關閉,會致使句柄泄漏(Fd leak)的發生。oop
可是,嚴格模式不是那麼的可靠,他並不能幫你發現全部的問題。我就經歷過靠嚴格模式排查了一遍事後,問題依然存在的狀況。最後仍是靠找到復現步驟、人工查代碼解決的。因此接下來的東西也很重要:ui
找復現步驟,能夠經過靜置手機、跑monkey等等手段,「等待」crash的發生。但,這太沒效率。本文但願經過如下步驟,縮短這個「等待」的時間spa
咱們知道致使句柄泄漏(Fd leak)的緣由是:當前進程所持有的fd(file descriptor)數量,超過了操做系統所設置的上限。那咱們首先要知道發生crash的手機對每一個進程設置的fd上限是怎樣的。操作系統
根據個人調研,華爲mate10 fd上限是3萬多,這種手機是不大容易復現問題的,上限過高。小米note1,華爲p9,上限是1024,這類手機是比較容易復現問題的。另外,從發生crash時先後的logcat來看,有些機型,會在發生句柄泄漏(Fd leak)問題後,動態的調高fd上限。好比小米mix 6。
那怎麼找呢?執行
adb shell ulimit -a
(無需root)
你將會看到:
nofiles(descriptors)一項所示的數量,就是每一個進程可持有的fd上限。
首先,你須要一部root的手機。 進入adb shell,su,如下命令,能夠查看你的app進程所佔用的fd數量:
lsof | grep <進程號> | wc -l
你每作一些操做(或者靜置手機不動),就運行一下這個命令,對比先後幾回fd數量的變化,能夠大大縮小問題的範圍。
by the way:
每次都運行命令笨不笨呢?有沒有更好的辦法呢?能夠嘗試使用linux中的watch命令。例如:
watch -n 2 'adb shell su lsof | grep <進程號> | wc -l'
不過若是直接adb shell su lsof
的話,查出來的結果爲0。爲什麼adb shell su這命令直接運行,不能正確的給adb提權到root?我也沒深究。有經驗的朋友能夠在評論區回覆,筆者不勝感激。
在進行了上面的排查步驟後,查代碼的範圍也被大大的縮小了。你能夠經過搜索各類「Stream」字段,來看他們有沒有正確的關閉。或者,經過刪代碼這種「作減法」的方式,逐漸定位問題。筆者就是經過「作減法」的方式,定位到是公司內部的一個組件庫的問題。