Android微信授权登录内存泄漏问题
最近有个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检测到的内存泄漏截图如下所示:
从图中可以看到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(); }
我已经迫不及待地再次测试了,很遗憾的是这个问题解决了,确又蹦出来另外一个问题:
刚才的喜悦之情瞬间荡然无存,怎么还是有内存泄漏。从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(); } }
经过反复测试,内存泄漏的问题终于解决了。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
仿百度地图小度语音助手的贝塞尔曲线动画
废话不多说,看下面的动图,和百度的还是有点点差别,我也不修改了,很简单,我实在是没有多余的时间,还要学习其他的东西,累啊,(复杂的动态View,可以使用SurfaceView,效率更高,我这里就简单使用View了) 效果图: 仔细观察一下百度那个动画,其实是由三层曲线组成的;每层曲线又是由三个贝塞尔曲线组成的。所以一层一层叠加,加上颜色的渐变,就可以绘制出来类似于山峰的曲线;最后,动态改变曲线的高度就可以达到那种效果(你也可以加上声音的波动值,那就看起来更加逼真了) 一个草图(见笑了): package com.example.helang.volumewave; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import...
- 下一篇
Android端使用阿里OSS上传
最近一直没更新博客,因为都写到了公司的wiki上,我觉得博客是有必要的,记录一下自己成长过程中的一点一滴吧。 这节课,说一下阿里的OSS,可能会有一点坑。 还有我自己封装的一个比较low的工具类。 我们选择了将文件直接上传到OSS,然后拿到文件的url,将url交给服务器保存。这样一来就大大减少了服务器的压力。 直接上自己封装的工具类代码吧。(直接拿去就可以用) 1.配置依赖: compile'com.aliyun.[dpa:oss-android-sdk:+](http://dpaoss-android-sdk+/)'//这里用sdk+ 是为了随时跟随oss的版本更新,oss也会随时修复bug。 compile'com.squareup.[okhttp3:okhttp:3.4.1](http://okhttp3okhttp:3.4.1/)'// 由于OSS底层封装是ok,所以ok的依赖也得有 compile'com.squareup.[okio:okio:1.9.0](http://okiookio:1.9.0/)' 2.********这里权限就不贴代码了,什么网络权限,文件读取权...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS7安装Docker,走上虚拟化容器引擎之路