您现在的位置是:首页 > 文章详情

Android微信授权登录内存泄漏问题

日期:2018-07-25点击:542

最近有个APP中使用了微信授权登录功能,项目中我们采用leakcanary来检测内存泄漏,发现微信登录有内存泄漏的问题。现将解决过程记录如下,不确定与微信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); } }); } 

代码逻辑很简单,就是创建一个IWXAPI对象,然后发送一个授权请求。leakcanary检测到的内存泄漏截图如下所示:

img_4ce9c42ff1bf42e4d55ca743ea49990b.png
内存泄漏1

从图中可以看到WXApiImplV10持有了一个名为ActivityLifecycleCb的引用,ActivityLifecycleCb又持有了MainActivity的引用,这样导致MainActivity内存得不到释放,如果多次重复进入该界面,则会引起严重的内存泄漏。

查看微信SDK代码我们发现:

 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里并没有提及这个,我们在开发时通常都是对着demo来一遍,一不小心就采坑了。接下来修改代码如下,在Activity的onDestroy()方法里进行解绑操作:

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

我已经迫不及待地再次测试了,很遗憾的是这个问题解决了,确又蹦出来另外一个问题:

img_b7eefe0284cf0929d6117baf764d3672.png
内存泄漏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(); } } 

经过反复测试,内存泄漏的问题终于解决了。

原文链接:https://yq.aliyun.com/articles/663991
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章