Android微信授權登錄內存泄漏問題

最近有個APP中使用了微信授權登錄功能,項目中我們采用leakcanary來檢測內存泄漏,發(fā)現(xiàn)微信登錄有內存泄漏的問題?,F(xiàn)將解決過程記錄如下,不確定與微信SDK版本有沒關系,歡迎討論指正。

一般我們是這樣使用微信登錄的,包括微信給出的demo也是如此,代碼片段如下:

    private IWXAPI mIWXAPI;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mIWXAPI = WXAPIFactory.createWXAPI(this, WX_APP_ID);
        mIWXAPI.registerApp(WX_APP_ID);

        findViewById(R.id.btn_wx_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final SendAuth.Req req = new SendAuth.Req();
                req.scope = "snsapi_userinfo";
                mIWXAPI.sendReq(req);
            }
        });
    }

代碼邏輯很簡單,就是創(chuàng)建一個IWXAPI對象,然后發(fā)送一個授權請求。leakcanary檢測到的內存泄漏截圖如下所示:

內存泄漏1

從圖中可以看到WXApiImplV10持有了一個名為ActivityLifecycleCb的引用,ActivityLifecycleCb又持有了MainActivity的引用,這樣導致MainActivity內存得不到釋放,如果多次重復進入該界面,則會引起嚴重的內存泄漏。

查看微信SDK代碼我們發(fā)現(xiàn):

    public final boolean registerApp(String var1, long var2) {
        if(this.detached) {
            throw new IllegalStateException("registerApp fail, WXMsgImpl has been detached");
        } else if(!WXApiImplComm.validateAppSignatureForPackage(this.context, "com.tencent.mm", this.checkSignature)) {
            Log.e("MicroMsg.SDK.WXApiImplV10", "register app failed for wechat app signature check failed");
            return false;
        } else {
            Log.d("MicroMsg.SDK.WXApiImplV10", "registerApp, appId = " + var1);
            if(var1 != null) {
                this.appId = var1;
            }

            if(activityCb == null && VERSION.SDK_INT >= 14) {
                if(this.context instanceof Activity) {
                    this.initMta(this.context, var1);
                    activityCb = new WXApiImplV10.ActivityLifecycleCb(this.context);
                    ((Activity)this.context).getApplication().registerActivityLifecycleCallbacks(activityCb);
                } else if(this.context instanceof Service) {
                    this.initMta(this.context, var1);
                    activityCb = new WXApiImplV10.ActivityLifecycleCb(this.context);
                    ((Service)this.context).getApplication().registerActivityLifecycleCallbacks(activityCb);
                } else {
                    Log.w("MicroMsg.SDK.WXApiImplV10", "context is not instanceof Activity or Service, disable WXStat");
                }
            }

            Log.d("MicroMsg.SDK.WXApiImplV10", "registerApp, appId = " + var1);
            if(var1 != null) {
                this.appId = var1;
            }

            Log.d("MicroMsg.SDK.WXApiImplV10", "register app " + this.context.getPackageName());
            a var4;
            (var4 = new a()).W = "com.tencent.mm";
            var4.X = "com.tencent.mm.plugin.openapi.Intent.ACTION_HANDLE_APP_REGISTER";
            var4.content = "weixin://registerapp?appid=" + this.appId;
            var4.Y = var2;
            return com.tencent.mm.opensdk.channel.a.a.a(this.context, var4);
        }
    }

在調用registerApp()方法時,里面有句代碼 registerActivityLifecycleCallbacks(activityCb),該方法注冊了一個Activity的生命周期回調方法,activityCb持有了我們對應的Activity的引用,Activity在退出時并沒有解綁,所以內存泄漏的罪魁禍首應該就是這個。通常情況下,有注冊的方法必然會有解綁的方法,果不其然找到了下面這個方法:

    public final void detach() {
        Log.d("MicroMsg.SDK.WXApiImplV10", "detach");
        this.detached = true;
        if(activityCb != null && VERSION.SDK_INT >= 14) {
            if(this.context instanceof Activity) {
                ((Activity)this.context).getApplication().unregisterActivityLifecycleCallbacks(activityCb);
            } else if(this.context instanceof Service) {
                ((Service)this.context).getApplication().unregisterActivityLifecycleCallbacks(activityCb);
            }

            activityCb.detach();
        }

        this.context = null;
    }

是不是很坑,微信的demo里并沒有提及這個,我們在開發(fā)時通常都是對著demo來一遍,一不小心就采坑了。接下來修改代碼如下,在Activity的onDestroy()方法里進行解綁操作:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mIWXAPI.detach();
    }

我已經(jīng)迫不及待地再次測試了,很遺憾的是這個問題解決了,確又蹦出來另外一個問題:

內存泄漏2

剛才的喜悅之情瞬間蕩然無存,怎么還是有內存泄漏。從leakcanary上能看到,是一個com.tencent.a.a.a.a.g.V最終持有了MainActivity的引用,從包名上可以看到這也是微信SDK里的一個類。由于這是個被混淆的類,實在是不知道這個地方怎么會有內存泄漏(有興趣的同學可以去仔細分析下),既然內存泄漏是因為MainActivity被一直引用,那如果我們手動切斷這種引用關系,是不是就可以解決這個問題呢。那動手來試驗一下,通過反射來將這種引用關系置空。

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mIWXAPI.detach();
        cleanWXLeak();
    }

    /**
     * 清除微信memory leak
     */
    public static void cleanWXLeak() {
        try {
            Class clazz = com.tencent.a.a.a.a.g.class;
            Field field = clazz.getDeclaredField("V");
            field.setAccessible(true);
            Object obj = field.get(clazz);
            if (obj != null) {
                com.tencent.a.a.a.a.g g = (com.tencent.a.a.a.a.g) obj;
                Field mapField = clazz.getDeclaredField("U");
                mapField.setAccessible(true);
                Map map = (Map) mapField.get(g);
                map.clear();
            }
            field.set(clazz, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

經(jīng)過反復測試,內存泄漏的問題終于解決了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容