我是一名插件化爱好者,也是Replugin的铁粉。
公司项目需要FacebookSDK,但是用Replugin进行插件化加载,发现有Crash,花了点时间做了兼容。有需要的同学可以看一下方案.
crash 关键字 1
Caused by: The SDK has not been initialized, make sure to call FacebookSdk.sdkInitialize() first.
复制代码
上面是因为,直接安装插件APK,facebook会被自动初始化。但是如果变成插件,需要我们这边手动初始化 代码如下:
FacebookContextWrapper contextWrapper = new FacebookContextWrapper(this);
FacebookSdk.setApplicationId("your id");
FacebookSdk.sdkInitialize(contextWrapper, new FacebookSdk.InitializeCallback() {
@Override
public void onInitialized() {
}
});
复制代码
这里用FacebookContextWrapper是为了避免出现资源访问的问题,因为里面会变成ApplicationContext,也就是宿主的context
public class FacebookContextWrapper extends ContextWrapper {
public FacebookContextWrapper(Context base) {
super(base);
}
//把这个方法重写,返回是插件的上下文
@Override
public Context getApplicationContext() {
return super.getBaseContext();
}
}
复制代码
crash 关键字 2
Caused by: java.lang.VerifyError: Verifier rejected class com.facebook.AccessTokenManager:
com.facebook.AccessTokenManager com.facebook.AccessTokenManager.getInstance() failed to verify:
com.facebook.AccessTokenManager com.facebook.AccessTokenManager.getInstance(): [0x1A] register v2 has
type Reference: java.lang.Object but expected Precise Reference:
android.support.v4.content.LocalBroadcastManager (declaration of 'com.facebook.AccessTokenManager'
appears in /data/data/com.ostudio.issue.sdk/app_plugins_v3/login-10-10-1.jar:classes2.dex)
复制代码
校验dex失败了,首先我们先看看为什么会失败
我们将打好的包,使用apktool反编译看一下smali ,定位到出问题的地方
.method static getInstance()Lcom/facebook/AccessTokenManager;
.locals 4
sget-object v0, Lcom/facebook/AccessTokenManager;->instance:Lcom/facebook/AccessTokenManager;
if-nez v0, :cond_1
const-class v1, Lcom/facebook/AccessTokenManager;
monitor-enter v1
:try_start_0
sget-object v0, Lcom/facebook/AccessTokenManager;->instance:Lcom/facebook/AccessTokenManager;
if-nez v0, :cond_0
invoke-static {}, Lcom/facebook/FacebookSdk;->getApplicationContext()Landroid/content/Context;
move-result-object v0
//这句有问题
invoke-static {v0}, Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;->getInstance(Landroid/content/Context;)Ljava/lang/Object;
move-result-object v0
new-instance v2, Lcom/facebook/AccessTokenCache;
invoke-direct {v2}, Lcom/facebook/AccessTokenCache;-><init>()V
new-instance v3, Lcom/facebook/AccessTokenManager;
invoke-direct {v3, v0, v2}, Lcom/facebook/AccessTokenManager;-><init>(Landroid/support/v4/content/LocalBroadcastManager;Lcom/facebook/AccessTokenCache;)V
sput-object v3, Lcom/facebook/AccessTokenManager;->instance:Lcom/facebook/AccessTokenManager;
:cond_0
monitor-exit v1
:try_end_0
.catchall {:try_start_0 .. :try_end_0} :catchall_0
:cond_1
sget-object v0, Lcom/facebook/AccessTokenManager;->instance:Lcom/facebook/AccessTokenManager;
return-object v0
:catchall_0
move-exception v0
:try_start_1
monitor-exit v1
:try_end_1
.catchall {:try_start_1 .. :try_end_1} :catchall_0
throw v0
.end method
复制代码
invoke-static {v0}, Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;->getInstance(Landroid/content/Context;)Ljava/lang/Object;
复制代码
很明显,这个单例的返回值应该是Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager类型,但是返回了Ljava/lang/Object;类型
所以应该改为
invoke-static {v0}, Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;->getInstance(Landroid/content/Context;)Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;
复制代码
另一个地方也会触发dex校验的问题,就是Replugin插件化只改掉了方法体内部的(LocalBroadcastManager改为PluginLocalBroadcastManager),但是有个全局变量没有改
.field private final localBroadcastManager:Landroid/support/v4/content/LocalBroadcastManager;
复制代码
所以这里也要改为这个样子
.field private final localBroadcastManager:Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;
复制代码
所以暂时建议大家先写个脚本,遍历smali每一行代码,然后自动化修改
大概思路,就是生成插件后->解压插件->使用baksmali将class.dex变为smali->自动化修改smali->压缩apk
//代码写的不好,见谅
public class FacebookDealLine implements IDealLine {
@Override
public String deal(String line, String fileNme) {
if (fileNme.contains("facebook")) {
if (line.contains("Landroid/support/v4/content/LocalBroadcastManager;")) {
// System.out.print(fileNme);
line = line.replace("Landroid/support/v4/content/LocalBroadcastManager;", "Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;");
}
if (line.contains("invoke-static {v0}, Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;->getInstance(Landroid/content/Context;)Ljava/lang/Object;")) {
line = line.replace("invoke-static {v0}, Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;->getInstance(Landroid/content/Context;)" +
"Ljava/lang/Object;", "invoke-static {v0}, Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;->getInstance" +
"(Landroid/content/Context;)Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;");
}
} else if (fileNme.contains("PluginLocalBroadcastManager")) {
if (line.contains(".method public static getInstance(Landroid/content/Context;)Ljava/lang/Object;")) {
line = line.replace(".method public static getInstance(Landroid/content/Context;)Ljava/lang/Object;", ".method public static getInstance" +
"(Landroid/content/Context;)Lcom/qihoo360/replugin/loader/b/PluginLocalBroadcastManager;");
}
}
return line;
}
}
复制代码
按上面几步操作完,还会有些问题
crash 关键字 3
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Boolean.booleanValue()' on a null object reference
at com.facebook.FacebookSdk.getAutoLogAppEventsEnabled(Unknown Source:5)
at com.facebook.appevents.internal.AutomaticAnalyticsLogger.logActivateAppEvent(Unknown Source:8)
at com.facebook.internal.FetchedAppSettingsManager$1.run(Unknown Source:64)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:784)
复制代码
报Facebook内部空指针了,看源码
public static boolean getAutoLogAppEventsEnabled() {
Validate.sdkInitialized();
return autoLogAppEventsEnabled;
}
复制代码
返回的autoLogAppEventsEnabled为空,那我们找到定义的地方
if (autoLogAppEventsEnabled == null) {
autoLogAppEventsEnabled = ai.metaData.getBoolean(
AUTO_LOG_APP_EVENTS_ENABLED_PROPERTY,
true);
}
复制代码
OK,很明显,是从metadata中取值,改的方法很多,简单点直接在宿主的mainfest中添加
<meta-data
android:name="com.facebook.sdk.AutoLogAppEventsEnabled"
android:value="true"/>
复制代码
或者如果你是更新插件的话,可以直接在facebook的源码中修改字节码,来解决这个问题。