转载请注明出处:http://blog.csdn.net/llew2011/article/details/78540911
我们在开发中经常用到一些优秀的第三方库,比如okhttp,glide,butterknife等。这些库不仅提高了开发效率而且避免踩坑,假如在应用中这些开源库出现了bug,我们随时可以从GitHub下载源码进行bug修改。但是项目中使用的库不是开源的并且该库又存在bug,由于没有源码也就无法进行bug的修复,一般做法就是给非开源库的作者或组织反馈bug等他们进行修复,如果他们修复的及时还好说,一旦他们更新不及时就会给我们APP造成不好影响(比如用户流失)……这篇文章我就给小伙伴们讲解一下如何自定义Gradle Plugin来彻底解决第三方Jar包中的bug(*^__^*) ……
入职新公司以来一直把精力放在了新公司原有项目的重构上,与其说是重构还不如说是重写了一遍,重构期间整个Android团队压力还是蛮大的,一方面在开发新需求另一方面在进行项目重构,经过几个月的辛劳重构后,新项目顺利上线了(在这里对整个团队表示感谢),重构后的新版本上线后我一直在Bugly上关注着它的稳定情况,尤其是崩溃率。从Bugly的日志上我发现有几个crash从旧版本到重构后的新版本一直存在着,且这些crash都是发生在第三方Jar包中的,其中一个crash日志如下所示:
日志信息清楚的表明该crash是用户没有授权导致的,由于我们APP集成了企鹅厂家的直播SDK,在用户退出直播间后都会调用SDK的退出直播间方法,也就是说崩溃发生在鹅厂的SDK内部,尽管我们在进入APP中都有向用户申请READ_PHONE_STATE权限,但还是存在部分用户不予授权的情况,以上crash就是用户没有授权导致的。因此我把这个问题反馈给了鹅厂的SDK研发组那里,并问他们能不能做下兼容(就是添加权限的判断),他们很快给予了回复说没法做兼容并给了个解决办法:如果用户没授权就给用户提示然后强制退出APP。如果按照鹅厂的解决办法那就代表着用户不授予权限就不能使用我们APP了,这显然是不可接受的,因为这样很容易造成用户流失……
既然鹅厂那边不愿意解决该问题,那我自己来解决!!!在解决该问题前我们先看看他们SDK中NetworkHelp类下的getAPInfo()方法和getMobileAPInfo()方法实现逻辑是什么样的,因为从Bugly日志看崩溃前调用了NetworkHelp中的这俩方法。反编译NetworkHelp.class文件,核心代码如下所示:
public class NetworkHelp {
protected static NetworkHelp.APInfo getAPInfo(Context var0) {
NetworkHelp.APInfo var1 = new NetworkHelp.APInfo();
if(var0 == null) {
QLog.e("NetworkHelp", 0, "getAPInfo initial context is null");
return var1;
} else {
ConnectivityManager var2 = (ConnectivityManager)var0.getSystemService("connectivity");
NetworkInfo var3 = var2.getActiveNetworkInfo();
if(var3 != null && var3.isConnected()) {
switch(var3.getType()) {
case 0:
// 在这里调用了getMobileAPInfo()方法
var1 = getMobileAPInfo(var0, var3.getSubtype());
break;
// 省略相关代码......
}
return var1;
}
}
private static NetworkHelp.APInfo getMobileAPInfo(Context var0, int var1) {
TelephonyManager var2 = (TelephonyManager)var0.getSystemService("phone");
NetworkHelp.MobileCarrier var3 = NetworkHelp.MobileCarrier.UNKNOWN;
String var4 = var2.getSubscriberId(); // getSubscriberId()内部抛的异常
// 省略相关代码...
NetworkHelp.APInfo var5 = new NetworkHelp.APInfo();
// 省略相关代码...
return var5;
}
public static class APInfo {
public int apType;
public String apName;
public APInfo() {// 当创建该对象的时候,属性会赋予默认值
this.apType = NetworkHelp.APType.AP_UNKNOWN.value();
this.apName = "AP_UNKNOWN";
}
}
// 省略相关代码...
}
根据反编译的NetworkHelp源码可以看出在getAPInfo()方法中首先判断Context类型的参数var0是否为null,如果为null则直接返回APInfo对象,而APInfo对象在创建的时候会把内部属性赋予默认值,也就是说如果我们在getAPInfo()方法中想办法让参数var0为null不就可以避免那些没有授权的用户发生崩溃了么?这是一个Hook点,简单粗暴(*^__^*) ……我们继续往下读getAPInfo()的代码,如果参数var0不为空且网络是OK的,就会走到getMobileInfo()方法中,在getMobileInfo()方法中调用了TelephoneManager的getSubscriberId()方法,而getSubscriberId()方法内部执行过程中会做权限校验,在未授权的情况下会抛出异常,因此只要在getMobileInfo()方法中调用getSubscriberId()前添加判断是否有权限的代码块就OK了,这又是一个Hook点(*^__^*) ……根据以上两个Hook点,我们对NetworkHelp做Hook后的代码应该是如下的样子:
public class NetworkHelp {
protected static NetworkHelp.APInfo getAPInfo(Conte