我是一名插件化爱好者,也是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的源码中修改字节码,来解决这个问题。