2.0版本能不能把TinkerApplication的实现代码出现抽象出来,让用户自己的Application类在方法里面调用抽象出来的接口,比如在onBaseContextAttached()调用什么方法这样的方式。或者提供接口让用户实现,然后提供tinker框架所需要的东西。让因为目前有些项目必须继承其他第三方的Application类,没法集成tinker.#793
是这个问题,不知道楼上有没有办法放弃对Application的修复,在自己application中初始化Tinker?
确实有这样的需求场景。比如我的Application.startService()想封装一层try catch
确实需要。目前我这边是自己fork tinker下来改造并维护,很麻烦
这个可以有哇,不过Application不能直接引用其他类,需要多搞一层XXLike,让Application通过反射的方式调用XXLike里对应的生命周期函数,这里我暂时还没想到怎么做得通用化。
@DengHuiJun 你那边是怎么处理的呢?
目前就是根据需要的场景,多搞一层XXLike。
好吧,我再想想看能怎么重构一下吧
我的解决方案是使用多个 Application,除了 TinkerApplication 和我们 App 的 AppApplication,主要的实现逻辑放到 WrapperApplication 中,它会被注册到 AndroidManifest.xml 中,并转发各生命周期方法等到前两个 Application,其中 AppApplication 通过类名传过来并在 onBaseContextAttached() 中通过新的 ClassLoader 构造(先调用 TinkerApplication 的 onBaseContextAttached(),因为其中可能会创建新的 ClassLoader)。
另外,WrapperApplication 可以抽象出来,只保留 getAppApplicationClassName() 接口(或按需要增加其他接口);AppApplication 几乎不用修改;不过,原应用中不能使用 (AppApplication) getApplication() 这种方式。
看看能不能完全去掉XXLike模式
XXLike有点麻烦; 我是在外面封装一个Application, 项目自己的Application保持不变;
// 外部包装一个TinkerApplication: AndroidManifest.xml 中 真正引用的是这个Application
public class OuterApplication extends TinkerApplication {
public OuterApplication() {
super(7, "项目自己的Application类名", "com.tencent.tinker.loader.TinkerLoader", false);
}
}
//
myHotFix:封装了真正的Application(TinkerApplication)和ApplicationLike
// 在自己的Application中(被代理的那个),再初始化Tinker
public class MyApplication extends Application {
// 接收TinkerApplication的传参数
public MyApplication(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent
/*, Resources[] resources, ClassLoader[] classLoader, AssetManager[] assetManager*/) {
myHotFix = new Hotfix(application, tinkerFlags, tinkerLoadVerifyFlag,
applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent
/*, resources, classLoader, assetManager*/, this);
}
// 在attachBaseContext中,初始化Tinker
@Override
protected void attachBaseContext(final Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
myHotFix.initTinker(new MyTinkerReport(), new MyLogImp());
// do others...
}
/** 代理Application的生命周期 */
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
myHotFix.getApplication().registerActivityLifecycleCallbacks(callback);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
myHotFix.getApplication().unregisterActivityLifecycleCallbacks(callback);
}
@Override
public void registerComponentCallbacks(ComponentCallbacks callback) {
myHotFix.getApplication().registerComponentCallbacks(callback);
}
@Override
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
myHotFix.getApplication().unregisterComponentCallbacks(callback);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void registerOnProvideAssistDataListener(OnProvideAssistDataListener callback) {
myHotFix.getApplication().registerOnProvideAssistDataListener(callback);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void unregisterOnProvideAssistDataListener(OnProvideAssistDataListener callback) {
myHotFix.getApplication().unregisterOnProvideAssistDataListener(callback);
}
}
唯一限制点:
项目应用中不能使用 (MyApplication) getApplication() 这种强转类型方式;
我也是准备用tinker的时候,发现Application很难继承TinkerApplication。无奈之下只有修改源码。动到的地方也不多,在tinker-android-loader这个工程里,把TinkerApplication干掉,再换成对应的调用就差不多了。虽然改动了tinker,但是原来的代码不受任何影响。
@endlessbest
这种改法有点危险哇,干掉TinkerApplication,再把里面原有的代码放到你自己的Application里,这时候你自己的Application对外部任何类的引用,包括引用XX.class,访问XX类的成员和方法都要通过反射进行,不然容易在dvm环境下报verify error或者在art环境下报class has already been loaded。
@tomystang 我是加了第三个 Application 类,然后把它的方法派发到另两个 Application,其中自己的 Application 是在加载完 Tinker 后反射创建的(因为可能使用新的 ClassLoader)。
@tomystang 如果patch只针对Application以外的class呢,这样做有问题吗,求指点?确实项目无法继承TinkerApplication了。如果除了Application本身,其他的可以修复,也算是委曲求全吧。
@tomystang 这个功能目前在开发了吗?
application的强制依赖确实有点难搞,正头疼怎么集成进去
能不能直接继承tinker生成的XXaplicaton呢,在里面做一些操作
@limingyuan 不能,因为生成的XXaplicaton是每次编译的时候生成的。
@blackberry2016 编译时生成在使用上有什么问题吗,在运行时都是已经存在的.class文件
我们是这么搞的,可以做到基本不影响原来的业务代码
public class MyApplicationLike extends DefaultApplicationLike {
//未接入tinker前我们的application就叫App
//现在App已经是一个代理application了,真正的application是tinker的application,不要把application强转成App了
private App app;
public MyApplicationLike(Application application, int i, boolean b, long l, long l1, Intent intent) {
super(application, i, b, l, l1, intent);
app = new App();
}
public void onCreate() {
super.onCreate();
app.onCreate();
}
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
//在这执行MultiDex
MultiDex.install(base);
//代理真正的ContextImpl
app.attachBaseContext(base);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
app.onConfigurationChanged(newConfig);
}
@Override
public void onLowMemory() {
super.onLowMemory();
app.onLowMemory();
}
@Override
public void onTerminate() {
super.onTerminate();
app.onTerminate();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
app.onTrimMemory(level);
}
}
2.重写App中如下方法,交给真正的application处理
public class App extend Application{
...原来的逻辑不用动,只新增如下方法
@Override
public void registerComponentCallbacks(ComponentCallbacks callback) {
Application application = (Application) getBaseContext().getApplicationContext();
application.registerComponentCallbacks(callback);
}
@Override
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
Application application = (Application) getBaseContext().getApplicationContext();
application.unregisterComponentCallbacks(callback);
}
@Override
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
Application application = (Application) getBaseContext().getApplicationContext();
application.registerActivityLifecycleCallbacks(callback);
}
@Override
public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
Application application = (Application) getBaseContext().getApplicationContext();
application.unregisterActivityLifecycleCallbacks(callback);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void registerOnProvideAssistDataListener(OnProvideAssistDataListener callback) {
Application application = (Application) getBaseContext().getApplicationContext();
application.registerOnProvideAssistDataListener(callback);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void unregisterOnProvideAssistDataListener(OnProvideAssistDataListener callback) {
Application application = (Application) getBaseContext().getApplicationContext();
application.unregisterOnProvideAssistDataListener(callback);
}
}
特别注意,现在App已经是一个代理application了,真正的application是tinker的application,不要把application强转成App了。
=========================
查看ActivityThread.handleBindApplication源码可知
app启动时通过ContextImpl.createAppContext为Application创建了一个真正的context,
然后通过mInstrumentation.newApplication反射创建了一个Application对象,并调用了app.attach(context)(Application继承来的绝大多数方法其实都是调用的这个context中的方法)。然后执行instrumentation.callApplicationOnCreate(app)调用了Application的onCreate方法。
由此看来Application只不过是一个代理,代理了contextimpl,只不过新增了上面几个registerActivityLifecycleCallbacks等方法
Most helpful comment
我们是这么搞的,可以做到基本不影响原来的业务代码
由于已经在like.onBaseContextAttached中调用了MultiDex.install(base);所以原来的App类只需要继承自Application就可以了
2.重写App中如下方法,交给真正的application处理
特别注意,现在App已经是一个代理application了,真正的application是tinker的application,不要把application强转成App了。
=========================
查看ActivityThread.handleBindApplication源码可知
app启动时通过ContextImpl.createAppContext为Application创建了一个真正的context,
然后通过mInstrumentation.newApplication反射创建了一个Application对象,并调用了app.attach(context)(Application继承来的绝大多数方法其实都是调用的这个context中的方法)。然后执行instrumentation.callApplicationOnCreate(app)调用了Application的onCreate方法。
由此看来Application只不过是一个代理,代理了contextimpl,只不过新增了上面几个registerActivityLifecycleCallbacks等方法