Skip to content

关于使用腾讯热补丁库Tinker的问题 #129

@chengfangpeng

Description

@chengfangpeng

如果接入Tinker,Application中所有的业务代码都被移除去了,也不能实现App接口,因为Tinker自己也有一个ApplicationLike的代理类,这样的结果是,类似与框架中这样的代码就会报错

((App) mActivity.getApplication()).getAppComponent()

Activity

JessYanCoding

JessYanCoding commented on Aug 17, 2017

@JessYanCoding
Owner

简单啊,你自己自定义一个 Application 类,继承 ApplicationLike 类,并实现 App 接口,在将 BaseApplication 里的代码复制进去,这不是 Java 最基本的解决方案吗

chengfangpeng

chengfangpeng commented on Aug 17, 2017

@chengfangpeng
Author

Tinker的这个ApplicationLike其实并不是真正的Application,它只是个代理,和你的AppDelegate类功能类似,所以强转的时候会有问题,

chengfangpeng

chengfangpeng commented on Aug 17, 2017

@chengfangpeng
Author

tinker为了实现可以修复Application中的bug的功能,使用了代理Application, 有全套的Application的方法,但是它并不是Application, 所以在MVPArm框架中对getApplication()强转的时候会出错

JessYanCoding

JessYanCoding commented on Aug 17, 2017

@JessYanCoding
Owner

我虽然没用过 Tinker ,但是花了几分钟看了下源码哈, ApplicationLike 实现类,需要在注解上声明, 需要被代理的 Application 类名,然后 ApplicationLike 初始化的时候,这个 Application 会被传入构造器, 调用 getApplication() 就可以获得这个 Application ,那就很简单了,只要这个 Application 实现于 App , getApplication()就可以强转为 App , getAppComponent() ,你这个 ApplicationLike 实现类自己提供出去不就可以了

chengfangpeng

chengfangpeng commented on Aug 17, 2017

@chengfangpeng
Author

你的意思是,我自己实现个MyApplication 继承Applicaiton 实现App接口,但是这个MyApplication并不是我manifest中的Applicaiton,也不是程序的入口,只是用于在ApplicaitonLike中接受一下变量,然后在ApplicationLike中调用MyApplication相应的生命周期方法.我的理解对吗 ? @JessYanCoding

JessYanCoding

JessYanCoding commented on Aug 17, 2017

@JessYanCoding
Owner

你可以自己写个方法将 ((App)getApplication()).getAppComponent() 提供出去,也可以将你的 ApplicationLike 实现类,也实现 App 然后这样:

@Override
    public AppComponent getAppComponent() {
        return ((App)getApplication()).getAppComponent();
    }
JessYanCoding

JessYanCoding commented on Aug 17, 2017

@JessYanCoding
Owner

你肯定要声明啊,Tinker 你就按 README 的用法自己弄吧,应该不难吧

JessYanCoding

JessYanCoding commented on Aug 17, 2017

@JessYanCoding
Owner
public class SampleApplication extends TinkerApplication {
    public SampleApplication() {
      super(
        //tinkerFlags, which types is supported
        //dex only, library only, all support
        ShareConstants.TINKER_ENABLE_ALL,
        // This is passed as a string so the shell application does not
        // have a binary dependency on your ApplicationLifeCycle class. 
        "tinker.sample.android.app.SampleApplicationLike");
    }  
}
Use tinker-android-anno to generate your Application is recommended, you just need to add an annotation for your SampleApplicationLike class

@DefaultLifeCycle(
application = "tinker.sample.android.app.SampleApplication",             //application name to generate
flags = ShareConstants.TINKER_ENABLE_ALL)                                //tinkerFlags above
public class SampleApplicationLike extends DefaultApplicationLike 

这不是 README 上的用法吗, SampleApplication 和 SampleApplicationLike 都是实现 App 就可以了,我这没用过 Tinker 的人,还要教你用过了,我也是醉了

chengfangpeng

chengfangpeng commented on Aug 17, 2017

@chengfangpeng
Author

Tinker是不推荐往SampleApplication中写自己的代码的,这样就没法修复了

JessYanCoding

JessYanCoding commented on Aug 17, 2017

@JessYanCoding
Owner

无所谓啊, 我看 ApplicationLike 里面不是有 Application 的所有生命周期吗,你让 SampleApplicationLike 实现 App ,在new 一个 Appdelegate ,然后在对应的声明周期中调用 attachBaseContext() onCreate() ,然后在实现 getAppComponent 不就 Ok 了,何必绕那么大圈子, 所有逻辑都在 Appdelegate 中,只要让他初始化不就可以了

chengfangpeng

chengfangpeng commented on Aug 17, 2017

@chengfangpeng
Author

怎么又绕回来了啊, 按你上面的逻辑,MVPArm框架中((App) mActivity.getApplication()).getAppComponent()这样的代码就会报错,除非我修改你的框架

JessYanCoding

JessYanCoding commented on Aug 17, 2017

@JessYanCoding
Owner

你修改我的框架也没用,不让改 Application ,那 mActivity.getApplication() 肯定是不可能能获得 AppComponent , Tinker 如果没有提供某些方法,能让你拿到这个 ApplicationLike ,那你就用静态方法提供 AppComponent ,或者自己写个单例类,在 onCreate() 中实例完 Appdelegate 后,将 AppComponent 放入单例类,以后通过这个单例 Get 就可以了

chengfangpeng

chengfangpeng commented on Aug 17, 2017

@chengfangpeng
Author

其实tinker也不是不让改Application,只是Application中的代码是不能热修复的, 我的想法是还是修改Application,让它实现App,在getAppComponent方法中返回ApplicationLike中的getAppComponent(), 其余逻辑还在ApplicationLike中.这样涉及到的代码最少. 否则的话我就得改动你的框架.

JessYanCoding

JessYanCoding commented on Aug 17, 2017

@JessYanCoding
Owner

随便你吧,反正不管写到哪里要做的就是初始化 AppDelegate ,再将 AppDelegate.getAppComponent() 提供出去就可以了,至于怎么提供你怎么高兴怎么来

chengfangpeng

chengfangpeng commented on Aug 17, 2017

@chengfangpeng
Author

好的,多谢,而且回复真的很及时 ^v^

Yanqilong

Yanqilong commented on Feb 3, 2018

@Yanqilong

这个问题最终还是没有得到解决方案,MVPArms 框架获取 getAppComponent() 是需要 Application 子类实现 App 接口, 然后在 obtainAppComponentFromContext 方法内 ((App) context.getApplicationContext()).getAppComponent() 强转上下文对象获取 AppConponent,但是现在问题来了, Tinker 不让自己直接操作 Application 子类, 而且这个子类是有 Tinker 生成,如果重新编译会重置,所以不能在 里面实现 APP接口, 问下有什么方案? @chengfangpeng @JessYanCoding

Yanqilong

Yanqilong commented on Feb 6, 2018

@Yanqilong

问题已经解决,框架不修改一行代码的情况下兼容 Tinker,亲测有效!
首先说明最根本的一个的问题,Application 不是非得使用 Tinker 注解生成,可以自己自定义 Application 并继承 TinkerApplication,这也是官方问答有说的,不是投机取巧。

解决方案:
1、自定义 Application 并继承 TinkerApplication,构造器中需要自己手动指定参数,并实现框架 App 接口,撸上代码

public class NewSeaXApplication extends TinkerApplication implements App{

    public NewSeaXApplication() {
        super(7,
                "com.newseax.tutor.application.NewSeaXApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader",
                false);

    }

    @NonNull
    @Override
    public AppComponent getAppComponent() {
        return ((App) TinkerManager.getTinkerApplicationLike()).getAppComponent();
    }
}

可能有人会有疑问,tinker 建议除了构造器以外的代码不写在 Application 内,会造成 Applicaton 内的代码不能实现热更新,是这样的这没有问题。那为啥还能实现 App 接口呢?因为 App 接口内的代码是获取 AppComponent 组件代码,只要写一次,之后都不会变动,所以不存在问题。

2.拷贝框架的 BaseApplication 代码到 CustomApplicationLike,CustomApplicationLike 是继承自 Tinker的 DefaultApplicationLike,具有和 Application 的部分生命周期,所以将框架的生命周期代理给 AppDelegate 且也满足需求,同时需要实现 App 接口,在接口方法中通过 AppDelegate 返回 AppComponent 给上面 TinkerApplication 的 getAppComponent 使用。CustomApplicationLike 和 NewSeaXApplication 的关系通过 TinkerManager 来关联, 代码如下:

public class CustomApplicationLike extends DefaultApplicationLike implements App {

    private static final String TAG = "Tinker.TinkerApplicationLike";
    // Application 生命周期代理
    private AppLifecycles mAppDelegate; //TinkerApplicationLike

    public NewSeaXApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
                                  long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

    /**
     * install multiDex before install tinker
     * so we don't need to put the tinker lib classes in the main dex
     *
     * @param base
     */
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);

        SampleApplicationContext.application = getApplication();
        SampleApplicationContext.context = getApplication();
        TinkerManager.setTinkerApplicationLike(this);

        TinkerManager.initFastCrashProtect();
        //should set before tinker is installed
        TinkerManager.setUpgradeRetryEnable(true);

        //optional set logIml, or you can use default debug log
        TinkerInstaller.setLogIml(new MyLogImp());

        //installTinker after load multiDex
        //or you can put com.tencent.tinker.** to main dex
        TinkerManager.installTinker(this);
        Tinker tinker = Tinker.with(getApplication());

        //---------- start 框架 ----------
        if (mAppDelegate == null) {
            this.mAppDelegate = new AppDelegate(base, this);
        }
        this.mAppDelegate.attachBaseContext(base);
        //---------- end 框架 ----------
    }

    @Override
    public void onCreate() {
        super.onCreate();

        //---------- start 框架 ----------
        if (mAppDelegate != null) {
            this.mAppDelegate.onCreate(getApplication());
        }
        KitsUtils.init(getApplication());
        //---------- end 框架 ----------
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        //---------- start 框架 ----------
        if (mAppDelegate != null) {
            this.mAppDelegate.onTerminate(getApplication());
        }
        //---------- end 框架 ----------
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
        getApplication().registerActivityLifecycleCallbacks(callback);
    }

    @NonNull
    @Override
    public AppComponent getAppComponent() {
        Preconditions.checkNotNull(mAppDelegate, "%s cannot be null", AppDelegate.class.getName());
        Preconditions.checkState(mAppDelegate instanceof App, "%s must be implements %s", AppDelegate.class.getName(), App.class.getName());
        return ((App) mAppDelegate).getAppComponent();
    }
}
runnchild

runnchild commented on Nov 6, 2018

@runnchild

问题已经解决,框架不修改一行代码的情况下兼容 Tinker,亲测有效!
首先说明最根本的一个的问题,Application 不是非得使用 Tinker 注解生成,可以自己自定义 Application 并继承 TinkerApplication,这也是官方问答有说的,不是投机取巧。

解决方案:
1、自定义 Application 并继承 TinkerApplication,构造器中需要自己手动指定参数,并实现框架 App 接口,撸上代码

public class NewSeaXApplication extends TinkerApplication implements App{

    public NewSeaXApplication() {
        super(7,
                "com.newseax.tutor.application.NewSeaXApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader",
                false);

    }

    @NonNull
    @Override
    public AppComponent getAppComponent() {
        return ((App) TinkerManager.getTinkerApplicationLike()).getAppComponent();
    }
}

可能有人会有疑问,tinker 建议除了构造器以外的代码不写在 Application 内,会造成 Applicaton 内的代码不能实现热更新,是这样的这没有问题。那为啥还能实现 App 接口呢?因为 App 接口内的代码是获取 AppComponent 组件代码,只要写一次,之后都不会变动,所以不存在问题。

2.拷贝框架的 BaseApplication 代码到 CustomApplicationLike,CustomApplicationLike 是继承自 Tinker的 DefaultApplicationLike,具有和 Application 的部分生命周期,所以将框架的生命周期代理给 AppDelegate 且也满足需求,同时需要实现 App 接口,在接口方法中通过 AppDelegate 返回 AppComponent 给上面 TinkerApplication 的 getAppComponent 使用。CustomApplicationLike 和 NewSeaXApplication 的关系通过 TinkerManager 来关联, 代码如下:

public class CustomApplicationLike extends DefaultApplicationLike implements App {

    private static final String TAG = "Tinker.TinkerApplicationLike";
    // Application 生命周期代理
    private AppLifecycles mAppDelegate; //TinkerApplicationLike

    public NewSeaXApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
                                  long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

    /**
     * install multiDex before install tinker
     * so we don't need to put the tinker lib classes in the main dex
     *
     * @param base
     */
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);

        SampleApplicationContext.application = getApplication();
        SampleApplicationContext.context = getApplication();
        TinkerManager.setTinkerApplicationLike(this);

        TinkerManager.initFastCrashProtect();
        //should set before tinker is installed
        TinkerManager.setUpgradeRetryEnable(true);

        //optional set logIml, or you can use default debug log
        TinkerInstaller.setLogIml(new MyLogImp());

        //installTinker after load multiDex
        //or you can put com.tencent.tinker.** to main dex
        TinkerManager.installTinker(this);
        Tinker tinker = Tinker.with(getApplication());

        //---------- start 框架 ----------
        if (mAppDelegate == null) {
            this.mAppDelegate = new AppDelegate(base, this);
        }
        this.mAppDelegate.attachBaseContext(base);
        //---------- end 框架 ----------
    }

    @Override
    public void onCreate() {
        super.onCreate();

        //---------- start 框架 ----------
        if (mAppDelegate != null) {
            this.mAppDelegate.onCreate(getApplication());
        }
        KitsUtils.init(getApplication());
        //---------- end 框架 ----------
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        //---------- start 框架 ----------
        if (mAppDelegate != null) {
            this.mAppDelegate.onTerminate(getApplication());
        }
        //---------- end 框架 ----------
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
        getApplication().registerActivityLifecycleCallbacks(callback);
    }

    @NonNull
    @Override
    public AppComponent getAppComponent() {
        Preconditions.checkNotNull(mAppDelegate, "%s cannot be null", AppDelegate.class.getName());
        Preconditions.checkState(mAppDelegate instanceof App, "%s must be implements %s", AppDelegate.class.getName(), App.class.getName());
        return ((App) mAppDelegate).getAppComponent();
    }
}

按你的方法介入后正常安装没问题,但是打补丁后报错,提示Application does not implements App,

handsometong

handsometong commented on Nov 9, 2018

@handsometong

打补丁后报错,提示Application does not implements App,怎么解决?

DituLin

DituLin commented on Nov 9, 2018

@DituLin

@qrc0403 你找到原因了没?

1378282082

1378282082 commented on Apr 13, 2019

@1378282082

@qrc0403 @handsometong
补丁下发激活后报错Application does not implements App,请问有什么解决方案吗?

IurKwan

IurKwan commented on Apr 18, 2019

@IurKwan

@qrc0403 @handsometong
补丁下发激活后报错Application does not implements App,请问有什么解决方案吗?

把框架clone到本地使用就可以了

lujianzhao

lujianzhao commented on Jul 13, 2019

@lujianzhao

@ qrc0403 @handsometong
补丁下发激活后报错应用程序没有实现App,请问有什么解决方案吗?

把框架克隆到本地使用就可以了

请问下是tinker框架还是ArmMVP框架克隆到本地呢?

IurKwan

IurKwan commented on Jul 13, 2019

@IurKwan

@ qrc0403 @handsometong
补丁下发激活后报错应用程序没有实现App,请问有什么解决方案吗?

把框架克隆到本地使用就可以了

请问下是tinker框架还是ArmMVP框架克隆到本地呢?

MVPArms

lujianzhao

lujianzhao commented on Jul 13, 2019

@lujianzhao

@ qrc0403 @handsometong
补丁下发激活后报错应用程序没有实现App,请问有什么解决方案吗?

把框架克隆到本地使用就可以了

请问下是tinker框架还是ArmMVP框架克隆到本地呢?

MVPArms

不行。Android6.0.1设备上可以正常打补丁。。但Android8.0以上的手机还是照旧这个问题Application does not implements App

lujianzhao

lujianzhao commented on Jul 13, 2019

@lujianzhao

最终解决方案:
1、使extends DefaultApplicationLike的类implements App
2、修改ArmsUtils

public static AppComponent obtainAppComponentFromContext(Context context) {
        //Preconditions.checkNotNull(context, "%s cannot be null", Context.class.getName());
        //Preconditions.checkState(context.getApplicationContext() instanceof App, "%s must be implements %s", context.getApplicationContext().getClass().getName(), App.class.getName());
        return ((App) TinkerManager.getTinkerApplicationLike()).getAppComponent();
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @chengfangpeng@handsometong@DituLin@runnchild@JessYanCoding

        Issue actions

          关于使用腾讯热补丁库Tinker的问题 · Issue #129 · JessYanCoding/MVPArms