日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

如何解决Binder泄漏问题

發(fā)布時(shí)間:2023/12/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何解决Binder泄漏问题 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

作者:王小二C? 2019/09/06

前言

[011]一個(gè)看似是系統(tǒng)問(wèn)題的應(yīng)用問(wèn)題的解決過(guò)程[1]中我們解決了一個(gè)注冊(cè)過(guò)多的BroadcastReceiver導(dǎo)致的某一次發(fā)送廣播失敗的問(wèn)題。我這邊遇到了一個(gè)類(lèi)似的問(wèn)題,但是我用了一個(gè)可能網(wǎng)絡(luò)上從來(lái)沒(méi)有提出過(guò)的方法,解決了這個(gè)問(wèn)題,寫(xiě)下這個(gè)文章記錄一下,如果三年前的我肯定想不出這種解決手段。

問(wèn)題

簡(jiǎn)單看了一下log,發(fā)現(xiàn)和[011]一個(gè)看似是系統(tǒng)問(wèn)題的應(yīng)用問(wèn)題的解決過(guò)程[2]的root cause是一樣的,還是在這次發(fā)廣播的Binder通信中無(wú)法申請(qǐng)足夠的buffer。

1143 1297 W BroadcastQueue: Can't deliver broadcast to com.android.systemui (pid 2107). Crashing it. 1143 1297 W BroadcastQueue: Failure sending broadcast Intent { act=android.intent.action.BATTERY_CHANGED flg=0x60000010 (has extras) } 1143 1297 W BroadcastQueue: android.os.DeadObjectException: Transaction failed on small parcel; remote process probably died 1143 1297 W BroadcastQueue: at android.os.BinderProxy.transactNative(Native Method) 1143 1297 W BroadcastQueue: at android.os.BinderProxy.transact(Binder.java:1129) 1143 1297 W BroadcastQueue: at android.app.IApplicationThread$Stub$Proxy.scheduleRegisteredReceiver(IApplicationThread.java:1237) 1143 1297 W BroadcastQueue: at com.android.server.am.BroadcastQueue.performReceiveLocked(BroadcastQueue.java:496) 1143 1297 W BroadcastQueue: at com.android.server.am.BroadcastQueue.deliverToRegisteredReceiverLocked(BroadcastQueue.java:715) 1143 1297 W BroadcastQueue: at com.android.server.am.BroadcastQueue.processNextBroadcastLocked(BroadcastQueue.java:875) 1143 1297 W BroadcastQueue: at com.android.server.am.BroadcastQueue.processNextBroadcast(BroadcastQueue.java:834) 1143 1297 W BroadcastQueue: at com.android.server.am.BroadcastQueue$BroadcastHandler.handleMessage(BroadcastQueue.java:172) 1143 1297 W BroadcastQueue: at android.os.Handler.dispatchMessage(Handler.java:106) 1143 1297 W BroadcastQueue: at android.os.Looper.loop(Looper.java:193) 1143 1297 W BroadcastQueue: at android.os.HandlerThread.run(HandlerThread.java:65) 1143 1297 W BroadcastQueue: at com.android.server.ServiceThread.run(ServiceThread.java:44) 1143 1297 W BroadcastQueue: Can't deliver broadcast to com.android.systemui (pid 2107). Crashing it.

初步分析

首先我按照之前解決問(wèn)題的思路,看systemui是否注冊(cè)了過(guò)多的BATTERY_CHANGED的廣播,但是排查了好多遍代碼,好像systemui并沒(méi)有注冊(cè)過(guò)多的廣播,這條路走不通。

查看內(nèi)存信息

通過(guò)反復(fù)的測(cè)試,我發(fā)現(xiàn)systemui中存在大量的Local Binder,這個(gè)代表systemui創(chuàng)建了2207個(gè)Binder的Server端,這明顯是不正常的。

** MEMINFO in pid 2558 [com.android.systemui] ** Objects Views: 4976 ViewRootImpl: 5 AppContexts: 201 Activities: 1 Assets: 21 AssetManagers: 0 Local Binders: 2207 Proxy Binders: 267 Parcel memory: 91 Parcel count: 434 Death Recipients: 196 OpenSSL Sockets: 0 WebViews: 0

如果找到Binder對(duì)象莫名增長(zhǎng)的原因?

方案1:抓Hprof的文件

通過(guò)抓Hprof的文件,想查看Binder這個(gè)類(lèi)的引用名,發(fā)現(xiàn)都是臨時(shí)變量,并沒(méi)有引用名,這條路走不通。

方案2:純看代碼

由于這個(gè)模塊不是我負(fù)責(zé)的,我也不是特別熟悉,這條路也走不通

重要發(fā)現(xiàn)

正當(dāng)我一籌莫展的時(shí)候,同事發(fā)現(xiàn)反復(fù)進(jìn)行某個(gè)操作的時(shí)候,會(huì)導(dǎo)致Binder增加,這個(gè)給了我一些線索,這個(gè)時(shí)候其實(shí)如果去反復(fù)看這個(gè)操作的代碼,我相信肯定可以找到原因,但是這個(gè)也只是把大海撈針變成了游泳池?fù)漆?#xff0c;還是挺費(fèi)時(shí)間的,對(duì)于代碼不熟悉的我來(lái)說(shuō),這個(gè)難度有點(diǎn)大。

#這樣的Binder對(duì)象對(duì)系統(tǒng)有威脅嗎?假如我按照以下的代碼,創(chuàng)建多個(gè)Binder對(duì)象,其實(shí)對(duì)系統(tǒng)沒(méi)有威脅,因?yàn)檫@樣子的Binder對(duì)象并不會(huì)在Binder驅(qū)動(dòng)中創(chuàng)建Binder Node,說(shuō)白了就是一個(gè)普通類(lèi),其他進(jìn)程并不會(huì)持有這個(gè)Binder的BinderProxy對(duì)象。

while(true) { Binder binder = new Binder(); }

怎么的Binder對(duì)象對(duì)系統(tǒng)有威脅?

首先我們可以確認(rèn),systemui創(chuàng)建的Binder對(duì)象肯定是匿名的Binder對(duì)象,匿名的Binder對(duì)象只有通過(guò)Binder的接口傳遞的時(shí)候才會(huì)創(chuàng)建Binder Node,這樣子才有威脅。既然要通過(guò)Binder的接口,必定要走以下代碼,所以我在下面加了這個(gè)Debug Log,我前面所說(shuō)的關(guān)鍵方法,這個(gè)Debug Log。

Parcel.java public final void writeStrongBinder(IBinder val) { if(Binder.getCallingUid() == 10049) {//10049是systemui的uid android.util.Log.v("kobewang", "writeStrongBinder", new Exception("kobewang")); } nativeWriteStrongBinder(mNativePtr, val); }

發(fā)現(xiàn)了異常的log

從下面的異常堆棧,發(fā)現(xiàn)一個(gè)問(wèn)題不管是addCallback,還是removeCallback,都會(huì)調(diào)用registerSoftApCallback,這不是明顯的錯(cuò)誤了嗎,不應(yīng)該是removeCallback調(diào)用unregisterSoftApCallback才對(duì)嘛。

kobewang: writeStrongBinder kobewang: java.lang.Exception: kobewang kobewang: at android.os.Parcel.writeStrongBinder(Parcel.java:738) kobewang: at android.net.wifi.IWifiManager$Stub$Proxy.registerSoftApCallback(IWifiManager.java:2341) kobewang: at android.net.wifi.WifiManager.registerSoftApCallback(WifiManager.java:2664) kobewang: at com.android.systemui.statusbar.policy.HotspotControllerImpl.updateWifiStateListeners(HotspotControllerImpl.java:128) kobewang: at com.android.systemui.statusbar.policy.HotspotControllerImpl.addCallback(HotspotControllerImpl.java:94) kobewang: at com.android.systemui.statusbar.policy.HotspotControllerImpl.addCallback(HotspotControllerImpl.java:35) kobewang: at com.android.systemui.qs.tiles.HotspotTile.handleSetListening(HotspotTile.java:84) kobewang: at com.android.systemui.qs.tileimpl.QSTileImpl.handleSetListeningInternal(QSTileImpl.java:401) kobewang: at com.android.systemui.qs.tileimpl.QSTileImpl.access$700(QSTileImpl.java:70) kobewang: at com.android.systemui.qs.tileimpl.QSTileImpl$H.handleMessage(QSTileImpl.java:544) kobewang: at android.os.Handler.dispatchMessage(Handler.java:106) kobewang: at android.os.Looper.loop(Looper.java:193) kobewang: at android.os.HandlerThread.run(HandlerThread.java:65) kobewang: writeStrongBinder kobewang: java.lang.Exception: kobewang kobewang: at android.os.Parcel.writeStrongBinder(Parcel.java:738) kobewang: at android.net.wifi.IWifiManager$Stub$Proxy.registerSoftApCallback(IWifiManager.java:2341) kobewang: at android.net.wifi.WifiManager.registerSoftApCallback(WifiManager.java:2664) kobewang: at com.android.systemui.statusbar.policy.HotspotControllerImpl.updateWifiStateListeners(HotspotControllerImpl.java:128) kobewang: at com.android.systemui.statusbar.policy.HotspotControllerImpl.removeCallback(HotspotControllerImpl.java:105) kobewang: at com.android.systemui.statusbar.policy.HotspotControllerImpl.removeCallback(HotspotControllerImpl.java:35) kobewang: at com.android.systemui.qs.tiles.HotspotTile.handleSetListening(HotspotTile.java:88) kobewang: at com.android.systemui.qs.tileimpl.QSTileImpl.handleSetListeningInternal(QSTileImpl.java:407) kobewang: at com.android.systemui.qs.tileimpl.QSTileImpl.access$700(QSTileImpl.java:70) kobewang: at com.android.systemui.qs.tileimpl.QSTileImpl$H.handleMessage(QSTileImpl.java:544) kobewang: at android.os.Handler.dispatchMessage(Handler.java:106) kobewang: at android.os.Looper.loop(Looper.java:193) kobewang: at android.os.HandlerThread.run(HandlerThread.java:65)

代碼分析

從代碼中可以發(fā)現(xiàn)removeCallback方法中updateWifiStateListeners(!mCallbacks.isEmpty());是有問(wèn)題的。他這樣子寫(xiě)代碼會(huì)導(dǎo)致removeCallback,最后走的也是registerSoftApCallback。

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @Override public void addCallback(Callback callback) { synchronized (mCallbacks) { if (callback == null || mCallbacks.contains(callback)) return; if (DEBUG) Log.d(TAG, "addCallback " + callback); mCallbacks.add(callback); updateWifiStateListeners(!mCallbacks.isEmpty()); } } @Override public void removeCallback(Callback callback) { if (callback == null) return; if (DEBUG) Log.d(TAG, "removeCallback " + callback); synchronized (mCallbacks) { mCallbacks.remove(callback); //問(wèn)題點(diǎn):如果mCallbacks永遠(yuǎn)存在一個(gè)callback, //那么!mCallbacks.isEmpty()就永遠(yuǎn)是true。 updateWifiStateListeners(!mCallbacks.isEmpty()); } } private void updateWifiStateListeners(boolean shouldListen) { mWifiStateReceiver.setListening(shouldListen); if (shouldListen) { //永遠(yuǎn)只會(huì)走registerSoftApCallback mWifiManager.registerSoftApCallback( this, Dependency.get(Dependency.MAIN_HANDLER)); } else { mWifiManager.unregisterSoftApCallback(this); } }

查看了registerSoftApCallback的代碼,發(fā)現(xiàn)這個(gè)接口,會(huì)創(chuàng)建不止一個(gè)Binder對(duì)象,而是兩個(gè)Binder對(duì)象,一個(gè)Binder,一個(gè)SoftApCallbackProxy。

public void registerSoftApCallback(@NonNull SoftApCallback callback, @Nullable Handler handler) { if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", handler=" + handler); Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper(); Binder binder = new Binder();//一個(gè)binder對(duì)象 try { //SoftApCallbackProxy也是一個(gè)Binder對(duì)象 mService.registerSoftApCallback(binder, new SoftApCallbackProxy(looper, callback), callback.hashCode()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }

總結(jié)

其實(shí)這是一個(gè)android 9.0的原生bug,所以有時(shí)候谷歌工程師也會(huì)犯錯(cuò)誤,那如果來(lái)解決這個(gè)問(wèn)題,其實(shí)這個(gè)問(wèn)題已經(jīng)在android 10上被谷歌工程師修復(fù)了,修復(fù)的方式,由于保密協(xié)議,我無(wú)法貼出android 10的代碼,等代碼正式釋放了,你們可以看看如何修復(fù)這個(gè)問(wèn)題,當(dāng)然你們自己也可以想想如何解決這個(gè)bug,其實(shí)也不是特別難。

PS

經(jīng)過(guò)解決了兩個(gè)Binder申請(qǐng)buffer失敗的問(wèn)題,我覺(jué)得最近幾年持續(xù)不斷的研究Binder驅(qū)動(dòng)是非常值得的,換做2年前的我,可能就會(huì)和測(cè)試扯皮了,讓他monitor這些問(wèn)題,然后然后最后無(wú)法復(fù)現(xiàn)或者低概率,這個(gè)bug就被close掉了。當(dāng)然我現(xiàn)在還會(huì)遇到一些低概率input ANR難以解決的問(wèn)題,以我現(xiàn)在的水平,還是無(wú)法解決這類(lèi)問(wèn)題,我相信在我不斷的學(xué)習(xí)之下,肯定最后會(huì)被我攻克的。

應(yīng)用開(kāi)發(fā)的建議

1.register和unregister一定要成對(duì)出現(xiàn)

2.對(duì)于注冊(cè)callback到system_server進(jìn)程,一定要注意,因?yàn)橐话氵@種callback就是一個(gè)binder對(duì)象,所以最好注冊(cè)一次,如果多處代碼需要注冊(cè)這個(gè)callback,請(qǐng)?jiān)谕ㄟ^(guò)你的應(yīng)用層注冊(cè)一個(gè)callbackmanager到system_server,然后其他callback,都注冊(cè)到你的callbackmanager,這樣子system_server和你的應(yīng)用跨進(jìn)程通信就只需要一次。

References

[1]?[011]一個(gè)看似是系統(tǒng)問(wèn)題的應(yīng)用問(wèn)題的解決過(guò)程:?/p/160290f38ee9

掃碼或長(zhǎng)按關(guān)注

總結(jié)

以上是生活随笔為你收集整理的如何解决Binder泄漏问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。