Meituan-Dianping / Robust

Robust is an Android HotFix solution with high compatibility and high stability. Robust can fix bugs immediately without a reboot.
Apache License 2.0
4.42k stars 807 forks source link

与 Aspectj 存在兼容性问题导致 crash #258

Closed w4lle closed 6 years ago

w4lle commented 6 years ago

异常类型:app运行时异常

手机型号:Nexus6

手机系统版本:Android 8.0

Robust版本:0.4.78

Gradle版本:gradle 3.3,build:gradle:2.2.3

系统:Mac

堆栈/日志:

03-30 08:39:53.328  2906  2906 D robust  : assemble method number  is  11849
03-30 08:39:53.328  2906  2906 D robust  : invoke method is com.meituan.robust.patch.MainFragmentActivityPatch.onCreate(android.os.Bundle) 
03-30 08:39:53.328  2906  2906 D robust  : get static  value is ajc$tjp_0     No:  96
03-30 08:39:53.329  2906  2906 D robust  :   inner Class new      No:  97
03-30 08:39:53.330  2906  2906 D robust  : invoke  method is       No:  98 linkClosureAndJoinPoint
03-30 08:39:53.332  2906  2906 W System.err: java.lang.reflect.InvocationTargetException
03-30 08:39:53.333  2906  2906 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
03-30 08:39:53.333  2906  2906 W System.err:    at com.meituan.robust.utils.EnhancedRobustUtils.invokeReflectMethod(Proguard:34)
03-30 08:39:53.333  2906  2906 W System.err:    at com.meituan.robust.patch.MainFragmentActivityPatch.onCreate(MainFragmentActivityPatch.java:211)
03-30 08:39:53.333  2906  2906 W System.err:    at com.meituan.robust.patch.MainFragmentActivityPatchControl.accessDispatch(Unknown Source:147)
03-30 08:39:53.333  2906  2906 W System.err:    at com.meituan.robust.PatchProxy.accessDispatch(Proguard:94)
03-30 08:39:53.333  2906  2906 W System.err:    at com.meituan.robust.PatchProxy.proxy(Proguard:44)
03-30 08:39:53.333  2906  2906 W System.err:    at com.zhangdan.app.activities.MainFragmentActivity.onCreate(Unknown Source:19)
03-30 08:39:53.333  2906  2906 W System.err:    at android.app.Activity.performCreate(Activity.java:6954)
03-30 08:39:53.333  2906  2906 W System.err:    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
03-30 08:39:53.333  2906  2906 W System.err:    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2768)
03-30 08:39:53.333  2906  2906 W System.err:    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2890)
03-30 08:39:53.333  2906  2906 W System.err:    at android.app.ActivityThread.-wrap11(Unknown Source:0)
03-30 08:39:53.333  2906  2906 W System.err:    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1591)
03-30 08:39:53.333  2906  2906 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:105)
03-30 08:39:53.333  2906  2906 W System.err:    at android.os.Looper.loop(Looper.java:164)
03-30 08:39:53.333  2906  2906 W System.err:    at android.app.ActivityThread.main(ActivityThread.java:6535)
03-30 08:39:53.333  2906  2906 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
03-30 08:39:53.333  2906  2906 W System.err:    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
03-30 08:39:53.333  2906  2906 W System.err:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
03-30 08:39:53.333  2906  2906 W System.err: Caused by: java.lang.ClassCastException: com.meituan.robust.patch.MainFragmentActivityPatch cannot be cast to com.zhangdan.app.activities.MainFragmentActivity
03-30 08:39:53.334  2906  2906 W System.err:    at com.zhangdan.app.activities.f.a(Proguard:1)
03-30 08:39:53.334  2906  2906 W System.err:    at org.aspectj.a.b.c.d(Proguard:149)
03-30 08:39:53.334  2906  2906 W System.err:    at com.zhangdan.app.activities.a.a(Proguard:41)
03-30 08:39:53.334  2906  2906 W System.err:    ... 19 more
03-30 08:39:53.334  2906  2906 D robust  : invoke  method is       No:  99 onCreate
03-30 08:39:53.345  2906  2906 D AndroidRuntime: Shutting down VM
--------- beginning of crash
03-30 08:39:53.345  2906  2906 E AndroidRuntime: FATAL EXCEPTION: main
03-30 08:39:53.345  2906  2906 E AndroidRuntime: Process: com.zhangdan.app, PID: 2906
03-30 08:39:53.345  2906  2906 E AndroidRuntime: android.util.SuperNotCalledException: Activity {com.zhangdan.app/com.zhangdan.app.activities.MainFragmentActivity} did not call through to super.onCreate()
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2771)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2890)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at android.app.ActivityThread.-wrap11(Unknown Source:0)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1591)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at android.os.Handler.dispatchMessage(Handler.java:105)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at android.os.Looper.loop(Looper.java:164)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at android.app.ActivityThread.main(ActivityThread.java:6535)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at java.lang.reflect.Method.invoke(Native Method)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
03-30 08:39:53.345  2906  2906 E AndroidRuntime:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
03-30 08:39:53.550  2906  2906 D XgStat  : [main(2): Proguard:141] - has caught the following uncaught exception:
03-30 08:39:53.551  2906  2906 E XgStat  : 
03-30 08:39:53.551  2906  2906 E XgStat  : android.util.SuperNotCalledException: Activity {com.zhangdan.app/com.zhangdan.app.activities.MainFragmentActivity} did not call through to super.onCreate()
03-30 08:39:53.551  2906  2906 E XgStat  :  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2771)
03-30 08:39:53.551  2906  2906 E XgStat  :  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2890)
03-30 08:39:53.551  2906  2906 E XgStat  :  at android.app.ActivityThread.-wrap11(Unknown Source:0)
03-30 08:39:53.551  2906  2906 E XgStat  :  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1591)
03-30 08:39:53.551  2906  2906 E XgStat  :  at android.os.Handler.dispatchMessage(Handler.java:105)
03-30 08:39:53.551  2906  2906 E XgStat  :  at android.os.Looper.loop(Looper.java:164)
03-30 08:39:53.551  2906  2906 E XgStat  :  at android.app.ActivityThread.main(ActivityThread.java:6535)
03-30 08:39:53.551  2906  2906 E XgStat  :  at java.lang.reflect.Method.invoke(Native Method)
03-30 08:39:53.551  2906  2906 E XgStat  :  at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
03-30 08:39:53.551  2906  2906 E XgStat  :  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
03-30 08:39:53.551  2906  2906 D XgStat  : [main(2): Proguard:145] - Call the original uncaught exception handler.
03-30 08:39:53.571   665   784 I ThermalEngine: ACTION: GPU - Setting GPU[0] to 450000000
03-30 08:39:53.625  2906  2906 E Error   : [Ljava.lang.StackTraceElement;@d4fc4a1 ex: Activity {com.zhangdan.app/com.zhangdan.app.activities.MainFragmentActivity} did not call through to super.onCreate()null
03-30 08:39:53.625  2906  2906 I Process : Sending signal. PID: 2906 SIG: 9
03-30 08:39:53.626  3349  3351 E jsengine: original owner has die
03-30 08:39:53.667  2166  2263 E FrameworkListener: read() failed (Connection reset by peer)
03-30 08:39:53.668  2352  2425 W InputDispatcher: channel '2085b34 com.zhangdan.app/com.zhangdan.app.activities.SplashActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x9
03-30 08:39:53.668  2352  2425 E InputDispatcher: channel '2085b34 com.zhangdan.app/com.zhangdan.app.activities.SplashActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
03-30 08:39:53.670  2352  3198 I ActivityManager: Process com.zhangdan.app (pid 2906) has died

aspectj 声明代码,其中的 MainFragmentActivity$$Injector 是APT生成的代码:

@Aspect
public class MainFragmentActivity$$Injector {
  @Around("execution(* com.zhangdan.app.activities.MainFragmentActivity.onCreate(..))")
  public void onCreate(ProceedingJoinPoint joinPoint) throws Throwable {
    MainFragmentActivity target = (MainFragmentActivity)joinPoint.getTarget();
    Bundle dataBundle = new Bundle();
    Bundle saveBundle = (Bundle)joinPoint.getArgs()[0];
    Bundle targetBundle = BundleCompact.getSupportBundle(target);
    if(targetBundle != null) {
      dataBundle.putAll(targetBundle);
    }
    if(saveBundle != null) {
      dataBundle.putAll(saveBundle);
    }
    String[] initTabIndexKeys = new String[1];
    initTabIndexKeys[0] = "index";
    for (String key : initTabIndexKeys) {
      boolean hasKey =  BundleCompact.hasKey(dataBundle,key);
      if(hasKey) {
        try {
          target.initTabIndex= BundleCompact.getValue(dataBundle,key,int.class,target.initTabIndex);
        } catch (Exception e) {
          e.printStackTrace();
        }
        break;
      }
    }
    joinPoint.proceed();
  }

基础包反编译后代码:


    public void onCreate(Bundle bundle) {
        if (!PatchProxy.proxy(new Object[]{bundle}, this, c, false, 11849, new Class[]{Bundle.class}, Void.TYPE).isSupported) {
            org.aspectj.lang.a a = org.aspectj.a.b.b.a(t, this, this, bundle);
            a.a().a(new f(new Object[]{this, bundle, a}).a(69648));
        }
    }

    private static final void b(MainFragmentActivity mainFragmentActivity, Bundle bundle, org.aspectj.lang.a aVar) {
        if (!PatchProxy.proxy(new Object[]{mainFragmentActivity, bundle, aVar}, null, c, true, 11887, new Class[]{MainFragmentActivity.class, Bundle.class, org.aspectj.lang.a.class}, Void.TYPE).isSupported) {
            super.onCreate(bundle);
            com.u51.android.judgeemulatorlib.b.a((Context) mainFragmentActivity);
            h = mainFragmentActivity;
            mainFragmentActivity.setContentView(R.layout.main_activity);
            ButterKnife.bind((Activity) mainFragmentActivity);
            mainFragmentActivity.r();
            mainFragmentActivity.i = new com.zhangdan.app.main.presenter.b(mainFragmentActivity, mainFragmentActivity.getSupportFragmentManager());
            mainFragmentActivity.j = new e();
            mainFragmentActivity.k = new com.zhangdan.app.main.presenter.c();
            mainFragmentActivity.l = new d(mainFragmentActivity);
            mainFragmentActivity.m = new com.zhangdan.app.main.presenter.a(mainFragmentActivity);
            mainFragmentActivity.j.a(mainFragmentActivity);
            mainFragmentActivity.j.a();
            e = 1;
            com.zhangdan.app.j.a.a(ZhangdanApplication.c()).b();
            mainFragmentActivity.g();
            mainFragmentActivity.t();
            mainFragmentActivity.a(bundle);
            mainFragmentActivity.f();
        }
    }

patch 包代码

package com.meituan.robust.patch;

import android.os.Bundle;
import android.util.Log;
import com.meituan.robust.utils.EnhancedRobustUtils;
import com.zhangdan.app.activities.MainFragmentActivity;
import com.zhangdan.app.activities.f;
import org.aspectj.a.b.b;
import org.aspectj.lang.a.a;

public class MainFragmentActivityPatch {
    MainFragmentActivity originClass;

    public MainFragmentActivityPatch(Object obj) {
        this.originClass = (MainFragmentActivity) obj;
    }

    public Object[] getRealParameter(Object[] objArr) {
        if (objArr == null || objArr.length < 1) {
            return objArr;
        }
        Object[] objArr2 = new Object[objArr.length];
        for (int i = 0; i < objArr.length; i++) {
            if (objArr[i] == this) {
                objArr2[i] = this.originClass;
            } else {
                objArr2[i] = objArr[i];
            }
        }
        return objArr2;
    }

    protected void onCreate(Bundle savedInstanceState) {
        a aVar = (a) EnhancedRobustUtils.getStaticFieldValue("t", MainFragmentActivity.class);
        Log.d("robust", "get static  value is ajc$tjp_0     No:  96");
        org.aspectj.lang.a aVar2 = (org.aspectj.lang.a) EnhancedRobustUtils.invokeReflectStaticMethod("a", b.class, getRealParameter(new Object[]{aVar, this, this, savedInstanceState}), new Class[]{a.class, Object.class, Object.class, Object.class});
        Object obj = (com.zhangdan.app.activities.a) EnhancedRobustUtils.invokeReflectStaticMethod("a", com.zhangdan.app.activities.a.class, getRealParameter(new Object[0]), null);
        Object[] objArr = new Object[]{this, savedInstanceState, aVar2};
        Log.d("robust", "  inner Class new      No:  97");
        Object obj2 = (f) EnhancedRobustUtils.invokeReflectConstruct("com.zhangdan.app.activities.f", getRealParameter(new Object[]{objArr}), new Class[]{Object[].class});
        if (obj2 == this) {
            obj2 = ((MainFragmentActivityPatch) obj2).originClass;
        }
        org.aspectj.lang.b bVar = (org.aspectj.lang.b) EnhancedRobustUtils.invokeReflectMethod("a", obj2, getRealParameter(new Object[]{new Integer(69648)}), new Class[]{Integer.TYPE}, org.aspectj.a.a.a.class);
        Log.d("robust", "invoke  method is       No:  98 linkClosureAndJoinPoint");
        if (obj == this) {
            obj = ((MainFragmentActivityPatch) obj).originClass;
        }
        EnhancedRobustUtils.invokeReflectMethod("a", obj, getRealParameter(new Object[]{bVar}), new Class[]{org.aspectj.lang.b.class}, com.zhangdan.app.activities.a.class);
        Log.d("robust", "invoke  method is       No:  99 onCreate");
    }
}
w4lle commented 6 years ago

看起来跟 #240 很像

w4lle commented 6 years ago

问题分析:

首先贴下未混淆的代码:

MainFragmentActivity.class
    static final void onCreate_aroundBody2(MainFragmentActivity mainFragmentActivity, Bundle bundle, JoinPoint joinPoint) {
        onCreate_aroundBody1$advice(mainFragmentActivity, bundle, joinPoint, MainFragmentActivity$$Injector.aspectOf(), (ProceedingJoinPoint) joinPoint);
    }
    private static final void onCreate_aroundBody1$advice(MainFragmentActivity mainFragmentActivity, Bundle bundle, JoinPoint joinPoint, MainFragmentActivity$$Injector mainFragmentActivity$$Injector, ProceedingJoinPoint proceedingJoinPoint) {
        if (!PatchProxy.proxy(new Object[]{mainFragmentActivity, bundle, joinPoint, mainFragmentActivity$$Injector, proceedingJoinPoint}, null, changeQuickRedirect, true, 11888, new Class[]{MainFragmentActivity.class, Bundle.class, JoinPoint.class, MainFragmentActivity$$Injector.class, ProceedingJoinPoint.class}, Void.TYPE).isSupported) {
            MainFragmentActivity mainFragmentActivity2 = (MainFragmentActivity) proceedingJoinPoint.getTarget();
            onCreate_aroundBody0(mainFragmentActivity, bundle, proceedingJoinPoint);
        }
    }

    public void onCreate(Bundle bundle) {
        if (!PatchProxy.proxy(new Object[]{bundle}, this, changeQuickRedirect, false, 11849, new Class[]{Bundle.class}, Void.TYPE).isSupported) {
            JoinPoint makeJP = Factory.makeJP(ajc$tjp_0, this, this, bundle);
            MainFragmentActivity$$Injector.aspectOf().onCreate(new AjcClosure3(new Object[]{this, bundle, makeJP}).linkClosureAndJoinPoint(69648));
        }
    }

    private static final void onCreate_aroundBody0(MainFragmentActivity mainFragmentActivity, Bundle bundle, JoinPoint joinPoint) {
        if (!PatchProxy.proxy(new Object[]{mainFragmentActivity, bundle, joinPoint}, null, changeQuickRedirect, true, 11887, new Class[]{MainFragmentActivity.class, Bundle.class, JoinPoint.class}, Void.TYPE).isSupported) {
            super.onCreate(bundle);
            JudgeEmulatorUtil.uploadEmulatorInfoIfNeed(mainFragmentActivity);
            instance = mainFragmentActivity;
            mainFragmentActivity.setContentView(R.layout.main_activity);
            ButterKnife.bind((Activity) mainFragmentActivity);
            mainFragmentActivity.initUserCenterManager();
            mainFragmentActivity.mainPagerAdapter = new MainPagerAdapter(mainFragmentActivity, mainFragmentActivity.getSupportFragmentManager());
            mainFragmentActivity.userInfoPresenter = new UserInfoPresenter();
            mainFragmentActivity.refreshOldDataPresenter = new RefreshOldDataPresenter();
            mainFragmentActivity.tabRedPointPresenter = new TabRedPointPresenter(mainFragmentActivity);
            mainFragmentActivity.getMsgCenterRedPresenter = new GetMsgCenterRedPresenter(mainFragmentActivity);
            mainFragmentActivity.userInfoPresenter.setUserInfoView(mainFragmentActivity);
            mainFragmentActivity.userInfoPresenter.startGetCurUserInfoDBUseCase();
            INSTANCE_FLAG = 1;
            BaiduLocation.getInstance(ZhangdanApplication.getInstance()).start();
            mainFragmentActivity.initToolBar();
            mainFragmentActivity.showImportBillDialog();
            mainFragmentActivity.onLoginCreate(bundle);
            mainFragmentActivity.getLoggerABConfig();
        }
    }

对应的patch 文件:

public class MainFragmentActivityPatch {
    MainFragmentActivity originClass;

    public MainFragmentActivityPatch(Object obj) {
        this.originClass = (MainFragmentActivity) obj;
    }

    protected void onCreate(Bundle savedInstanceState) {
        StaticPart staticPart = (StaticPart) EnhancedRobustUtils.getStaticFieldValue("ajc$tjp_0", MainFragmentActivity.class);
        Log.d("robust", "get static  value is ajc$tjp_0     No:  1");
        JoinPoint joinPoint = (JoinPoint) EnhancedRobustUtils.invokeReflectStaticMethod("makeJP", Factory.class, getRealParameter(new Object[]{staticPart, this, this, savedInstanceState}), new Class[]{StaticPart.class, Object.class, Object.class, Object.class});
        Object obj = (Injector) EnhancedRobustUtils.invokeReflectStaticMethod("aspectOf", Injector.class, getRealParameter(new Object[0]), null);
        Object[] objArr = new Object[]{this, savedInstanceState, joinPoint};
        Log.d("robust", "  inner Class new      No:  2");
        Object obj2 = (AjcClosure3) EnhancedRobustUtils.invokeReflectConstruct("com.zhangdan.app.activities.MainFragmentActivity$AjcClosure3", getRealParameter(new Object[]{objArr}), new Class[]{Object[].class});
        if (obj2 == this) {
            obj2 = ((MainFragmentActivityPatch) obj2).originClass;
        }
        ProceedingJoinPoint proceedingJoinPoint = (ProceedingJoinPoint) EnhancedRobustUtils.invokeReflectMethod("linkClosureAndJoinPoint", obj2, getRealParameter(new Object[]{new Integer(69648)}), new Class[]{Integer.TYPE}, AroundClosure.class);
        Log.d("robust", "invoke  method is       No:  3 linkClosureAndJoinPoint");
        if (obj == this) {
            obj = ((MainFragmentActivityPatch) obj).originClass;
        }
        EnhancedRobustUtils.invokeReflectMethod("onCreate", obj, getRealParameter(new Object[]{proceedingJoinPoint}), new Class[]{ProceedingJoinPoint.class}, Injector.class);
        Log.d("robust", "invoke  method is       No:  4 onCreate");
    }
}

MainFragmentActivity.onCreate方法,MainFragmentActivity$$Injector.aspectOf().onCreate(new AjcClosure3(new Object[]{this, bundle, makeJP}).linkClosureAndJoinPoint(69648)); 调用了Injector 对象的onCreate 方法,参数是一个AjcClosure.linkClosureAndJoinPoint后获得的对象。 我们往下跟下 Aspectj 的调用流程。

AjcClosure 类
public abstract class AroundClosure {
   ...
    protected Object[] state;

    public AroundClosure(Object[] state) {
        this.state = state;
    }

    public Object[] getState() {
      return state;
    }

    /**
     * This takes in the same arguments as are passed to the proceed
     * call in the around advice (with primitives coerced to Object types)
     */
    public abstract Object run(Object[] args) throws Throwable;

    public ProceedingJoinPoint linkClosureAndJoinPoint(int flags) {
        //TODO is this cast safe ?
        ProceedingJoinPoint jp = (ProceedingJoinPoint)state[state.length-1];
        jp.set$AroundClosure(this);
        this.bitflags = flags;
        return jp;
    }
}

AjcClosure 接收一个 Object 的对象数组,在基础包中,它的实现是

new Object[]{this, bundle, makeJP}

注意这个 this,代表的是MainFragmentActivity 对象。 相对应的看下patch包中的实现

Object[] objArr = new Object[]{this, savedInstanceState, joinPoint};

注意这个 this 代表的是MainFragmentActivityPatch 对象。 这里调用了下 getRealParameter(new Object[]{objArr}) 进行了 this 转换,所以这里的this 也是MainFragmentActivity对象,这里是没问题的。 然后调用 linkClosureAndJoinPoint 方法得到 ProceedingJoinPoint 对象,当做参数传递给 MainFragmentActivity$$Injector.onCreate(ProceedingJoinPoint joinPoint) 方法,看下这个方法的实现:

MainFragmentActivity$$Injector.class
@Aspect
public class MainFragmentActivity$$Injector {
  @Around("execution(* com.zhangdan.app.activities.MainFragmentActivity.onCreate(..))")
  public void onCreate(ProceedingJoinPoint joinPoint) throws Throwable {
    MainFragmentActivity target = (MainFragmentActivity)joinPoint.getTarget();
    ...
    joinPoint.proceed();
  }

调用了ProceedingJoinPoint.proceed抽象方法,实现在

    JoinPointImpl.java
    public Object proceed() throws Throwable {
        // when called from a before advice, but be a no-op
            return arc.run(arc.getState());
    }

这里的 arc 是 AroundClosure,arc.getState() 返回的是构造AroundClosure时传递过来的对象数组。 最后调用了抽象方法 run(Object[] args),实现在


public class MainFragmentActivity$AjcClosure3 extends AroundClosure {
    public static ChangeQuickRedirect changeQuickRedirect;

    public MainFragmentActivity$AjcClosure3(Object[] objArr) {
        super(objArr);
    }

    public Object run(Object[] objArr) {
        PatchProxyResult proxy = PatchProxy.proxy(new Object[]{objArr}, this, changeQuickRedirect, false, 11911, new Class[]{Object[].class}, Object.class);
        if (proxy.isSupported) {
            return proxy.result;
        }
        Object[] objArr2 = this.state;
        MainFragmentActivity.onCreate_aroundBody2((MainFragmentActivity) objArr2[0], (Bundle) objArr2[1], (JoinPoint) objArr2[2]);
        return null;
    }
}

最后调用 MainFragmentActivity.onCreate_aroundBody2((MainFragmentActivity) objArr2[0], (Bundle) objArr2[1], (JoinPoint) objArr2[2]);,整个 AOP 的流程就走通了。 ~~关注这个方法的第一个参数,从对象数组中拿出第一个对象,将其强转为 MainFragmentActivity,但是这个参数在 Patch 中传递过来的是MainFragmentActivityPatch对象,所以出现了强转错误。 也可以说,由于在Patch中提前把还未转换的patch对象传递到了Aspectj 中,Aspectj 使用了缓存的patch对象,导致出现了强转的错误。~~

最后总结下 Aspectj 的调用流程:

MainFragmentActivity.onCreate -> 
MainFragmentActivity$$Injector.onCreate(ProceedingJoinPoint joinPoint) -> 
ProceedingJoinPoint.proceed() -> 
AroundClosure.run(Object[] args) ->  
MainFragmentActivity$AjcClosure3.run(Object[] objArr) -> 
MainFragmentActivity.onCreate_aroundBody2((MainFragmentActivity) objArr2[0], (Bundle) objArr2[1], (JoinPoint) objArr2[2]); -> 
MainFragmentActivity.onCreate_aroundBody1$advice -> 
MainFragmentActivity.onCreate_aroundBody0();

最后的 MainFragmentActivity.onCreate_aroundBody0();方法实际上就是onCreate()的原始方法逻辑。

w4lle commented 6 years ago

~~根据上面的分析,也就能找到解决办法了。 Object[] objArr = new Object[]{this, savedInstanceState, joinPoint}; -> Object[] objArr = getRealParameter(new Object[]{this, savedInstanceState, joinPoint});~~ 上面已经调了 getRealParameter(new Object[]{this, savedInstanceState, joinPoint}); 所以还是没有找到问题所在啊 😢

w4lle commented 6 years ago

另外,对于修改后的代码,没有被打入补丁,也是可以解释的。 对于auto-path-plugin,Transform 的顺序是 Aspectj -> auto-patch. 那么,对于标记修改的onCreate方法来说,Aspectj 处理完后,onCreate 方法被替换成了代理,真正的方法实现被新生成的方法隐藏起来了。 而我们仅仅标记了旧的onCreate 方法,其结果就是,Aspectj 的代理onCreate方法被patch了,而实际的方法虽然方法体内有我们的修复,但是由于没有标记@Modify 而被忽略。

w4lle commented 6 years ago

因为是强转crash,所以在 $$Injector 代码中插入一些 Log

MainFragmentActivity$$Injector.class
    @Around("execution(* com.zhangdan.app.activities.MainFragmentActivity.onCreate(..))")
    public void onCreate(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        if (!PatchProxy.proxy(new Object[]{proceedingJoinPoint}, this, changeQuickRedirect, false, 11892, new Class[]{ProceedingJoinPoint.class}, Void.TYPE).isSupported) {
            MainFragmentActivity target = (MainFragmentActivity) proceedingJoinPoint.getTarget();
            ...
            Log.d("robust-wll", "test for aspectj start ");
            ...
            Log.d("robust-wll", "getTarget : " + proceedingJoinPoint.getTarget().getClass().getSimpleName());
            Log.d("robust-wll", "getThis : " + proceedingJoinPoint.getThis().getClass().getSimpleName());
            Log.d("robust-wll", "getArgs[0] : " + (proceedingJoinPoint.getArgs()[0] != null ? proceedingJoinPoint.getArgs()[0].getClass().getSimpleName() : null));
            Field arcField = proceedingJoinPoint.getClass().getDeclaredField("arc");
            arcField.setAccessible(true);
            AroundClosure arc = (AroundClosure) arcField.get(proceedingJoinPoint);
            if (!(arc == null || arc.getState() == null || arc.getState().length < 3)) {
                Object[] states = arc.getState();
                Log.d("robust-wll", "states[0] : " + (states[0] != null ? states[0].getClass().getSimpleName() : null));
                Log.d("robust-wll", "states[1] : " + (states[1] != null ? states[1].getClass().getSimpleName() : null));
                Log.d("robust-wll", "states[2] : " + (states[2] != null ? states[2].getClass().getSimpleName() : null));
            }
            Log.d("robust-wll", "test for aspectj end ");
            proceedingJoinPoint.proceed();
        }
    }

在不加载补丁情况下的 log 如下:

04-03 17:13:41.184 15469 15469 D robust-wll: test for aspectj start 
04-03 17:13:41.184 15469 15469 D robust-wll: getTarget : MainFragmentActivity
04-03 17:13:41.185 15469 15469 D robust-wll: getThis : MainFragmentActivity
04-03 17:13:41.185 15469 15469 D robust-wll: getArgs[0] : null
04-03 17:13:41.185 15469 15469 D robust-wll: states[0] : MainFragmentActivity
04-03 17:13:41.185 15469 15469 D robust-wll: states[1] : null
04-03 17:13:41.186 15469 15469 D robust-wll: states[2] : JoinPointImpl
04-03 17:13:41.186 15469 15469 D robust-wll: test for aspectj end 

加载补丁后,log 如下:

04-03 17:27:41.885 18490 18490 D robust-wll: test for aspectj start 
04-03 17:27:41.885 18490 18490 D robust-wll: getTarget : MainFragmentActivity
04-03 17:27:41.886 18490 18490 D robust-wll: getThis : MainFragmentActivity
04-03 17:27:41.886 18490 18490 D robust-wll: getArgs[0] : null
04-03 17:27:41.886 18490 18490 D robust-wll: states[0] : MainFragmentActivityPatch
04-03 17:27:41.886 18490 18490 D robust-wll: states[1] : null
04-03 17:27:41.886 18490 18490 D robust-wll: states[2] : JoinPointImpl
04-03 17:27:41.886 18490 18490 D robust-wll: test for aspectj end 

states[0] : MainFragmentActivityPatch 这个明显是不对的,所以我们知道了原因,是因为在构造 AroundClosure 时候传进来的参数不对。 报错地方对应于上面的分析,也就是

        Object[] objArr = new Object[]{this, savedInstanceState, joinPoint};
        Object obj2 = (AjcClosure3) EnhancedRobustUtils.invokeReflectConstruct("com.zhangdan.app.activities.MainFragmentActivity$AjcClosure3", getRealParameter(new Object[]{objArr}), new Class[]{Object[].class});

结果就是,把一个含有3个对象的一维数据,编程了含有一个对象的二维数组,然后去 getRealParameter。

    public Object[] getRealParameter(Object[] objArr) {
        if (objArr == null || objArr.length < 1) {
            return objArr;
        }
        Object[] objArr2 = new Object[objArr.length];
        for (int i = 0; i < objArr.length; i++) {
            if (objArr[i] == this) {
                objArr2[i] = this.originClass;
            } else {
                objArr2[i] = objArr[i];
            }
        }
        return objArr2;
    }

而这个方法只判断了一维数组的情况,没有判断二维或多维数组的情况。终于找到原因了 😃 对应的修改方法:

    public Object[] getRealParameter(Object[] objArr) {
        if (objArr == null || objArr.length < 1) {
            return objArr;
        }
        Object[] objArr2 = new Object[objArr.length];
        for (int i = 0; i < objArr.length; i++) {
            if (objArr[i] instanceof Object[]) {
                objArr2[i] = getRealParameter(((Object[]) objArr[i]));
            } else {
                if (objArr[i] == this) {
                    objArr2[i] = this.originClass;
                } else {
                    objArr2[i] = objArr[i];
                }
            } 
        }
        return objArr2;
    }

castException 终于是搞定了。mr #259 但是还有个问题没有想明白,就是明明是个二维数组,为什么反射构造 AroundClosure 时,结果就把二维数组转为一维数组了??debug 跟了下,反射构造方法那里,传进去的就是个二维数组,反射完 AroundClosure 中由构造方法传递进来的数组参数,就变成一维的了。想不通啊想不通。@hedex 可以解释下吗?

w4lle commented 6 years ago

至于被Aspectj 处理过的方法无法被打入patch的问题,理论上来说跟泛型的桥方法是类似的,具体参考 Android热更新方案Robust开源,新增自动化补丁工具——Java编译器的优化一节 解决方案也是 @Modify -> RobustModify.modify();待验证 经验证,会报错。log如下

Caused by: javassist.CannotCompileException: [source error] not-available: this
        at javassist.expr.MethodCall.replace(MethodCall.java:241)
        at javassist.expr.MethodCall$replace$2.call(Unknown Source)
        at com.meituan.robust.autopatch.PatchesFactory$1.edit(PatchesFactory.groovy:144)
        at javassist.expr.ExprEditor.loopBody(ExprEditor.java:224)
        at javassist.expr.ExprEditor.doit(ExprEditor.java:91)
        at javassist.CtBehavior.instrument(CtBehavior.java:712)
        at javassist.CtBehavior$instrument$1.call(Unknown Source)
        at com.meituan.robust.autopatch.PatchesFactory.createPatchClass(PatchesFactory.groovy:76)
        at com.meituan.robust.autopatch.PatchesFactory.createPatch(PatchesFactory.groovy:310)
        at com.meituan.robust.autopatch.PatchesFactory$createPatch.call(Unknown Source)
        at robust.gradle.plugin.AutoPatchTransform.generatPatch(AutoPatchTransform.groovy:190)
        at robust.gradle.plugin.AutoPatchTransform$generatPatch$0.callCurrent(Unknown Source)
        at robust.gradle.plugin.AutoPatchTransform.autoPatch(AutoPatchTransform.groovy:138)
        at robust.gradle.plugin.AutoPatchTransform$autoPatch.callCurrent(Unknown Source)
        at robust.gradle.plugin.AutoPatchTransform.transform(AutoPatchTransform.groovy:97)
        at com.android.build.api.transform.Transform.transform(Transform.java:290)
        at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:185)
        at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:181)
        at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102)
        ... 27 more
Caused by: compile error: not-available: this
        at javassist.compiler.CodeGen.atKeyword(CodeGen.java:1908)
        at javassist.compiler.ast.Keyword.accept(Keyword.java:35)
        at javassist.compiler.JvstCodeGen.atMethodArgs(JvstCodeGen.java:358)
        at javassist.compiler.MemberCodeGen.atMethodCallCore(MemberCodeGen.java:569)
        at javassist.compiler.MemberCodeGen.atCallExpr(MemberCodeGen.java:537)
        at javassist.compiler.JvstCodeGen.atCallExpr(JvstCodeGen.java:244)
        at javassist.compiler.ast.CallExpr.accept(CallExpr.java:46)
        at javassist.compiler.CodeGen.atStmnt(CodeGen.java:338)
        at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
        at javassist.compiler.CodeGen.atStmnt(CodeGen.java:351)
        at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
        at javassist.compiler.Javac.compileStmnt(Javac.java:569)
        at javassist.expr.MethodCall.replace(MethodCall.java:235)
        ... 45 more

问题分析,根据堆栈显示,这里是在做替换super 方法的逻辑,跟了下plugin 的 debug,生成需要replace 的javassist 代码为 {staticRobustonCreate(this,originClass,$$);},然后在replace 后,javac编译这条语句的时候跪了。 分析下需要替换的super的方法,这个方法实际上是Aspectj 处理后的方法,根据上面分析的Aspectj的调用流程得知,该方法实际上是onCreate方法原始的逻辑,反编译出来如下:

    private static final void onCreate_aroundBody0(MainFragmentActivity mainFragmentActivity, Bundle bundle, JoinPoint joinPoint) {
        if (!PatchProxy.proxy(new Object[]{mainFragmentActivity, bundle, joinPoint}, null, changeQuickRedirect, true, 11887, new Class[]{MainFragmentActivity.class, Bundle.class, JoinPoint.class}, Void.TYPE).isSupported) {
            super.onCreate(bundle);//这里是需要替换的地方
            ...
        }
    }

而auto-patch做的工作是将super.onCreate方法包装成 static 方法,正常生成的patch代码如下:

public class SecondActivityPatch {
    SecondActivity originClass;

    public SecondActivityPatch(Object obj) {
        this.originClass = (SecondActivity) obj;
    }
    public void onCreate(Bundle bundle) {
        staticRobustonCreate(this,originClass,bundle);
       ...
    }

    public static void staticRobustonCreate(SecondActivityPatch secondActivityPatch, SecondActivity secondActivity, Bundle bundle) {
        SecondActivityPatchRobustAssist.staticRobustonCreate(secondActivityPatch, secondActivity, bundle);
    }

public class SecondActivityPatchRobustAssist extends Activity {
    public static void staticRobustonCreate(SecondActivityPatch secondActivityPatch, SecondActivity secondActivity, Bundle bundle) {
        super.onCreate(bundle);
    }
}

而对于已经被Aspectj处理过的方法,是这样的:

    private static final void onCreate_aroundBody0(MainFragmentActivity mainFragmentActivity, Bundle bundle, JoinPoint joinPoint) {
        if (!PatchProxy.proxy(new Object[]{mainFragmentActivity, bundle, joinPoint}, null, changeQuickRedirect, true, 11887, new Class[]{MainFragmentActivity.class, Bundle.class, JoinPoint.class}, Void.TYPE).isSupported) {
            //super.onCreate(bundle);//这里是需要替换的地方
            staticRobustonCreate(this,originClass,bundle);
            ...
        }
    }

在static 方法中使用了 this关键字,当然编译出错啦。同理这个 originClass 也不可以出现,因为它是非static变量。 由于 xxPatchRobustAssist.staticRobustonCreate() 方法并没有用到前两个变量(patch, activity),直接传 null 行不行呢?经验证是不行的,原因如下。 看了下生成xxxPatchRobustAssist类的代码:

class PatchesAssistFactory {
    def
    static createAssistClass(CtClass modifiedClass, String patchClassName, CtMethod removeMethod) {
       ....
        StringBuilder staticMethodBuidler = new StringBuilder();
        if (removeMethod.parameterTypes.length > 0) {
            staticMethodBuidler.append("public static  " + removeMethod.returnType.name + "   " + ReflectUtils.getStaticSuperMethodName(removeMethod.getName())
                    + "(" + patchClassName + " patchInstance," + modifiedClass.getName() + " modifiedInstance," + JavaUtils.getParameterSignure(removeMethod) + "){");

        } else {
            staticMethodBuidler.append("public static  " + removeMethod.returnType.name + "   " + ReflectUtils.getStaticSuperMethodName(removeMethod.getName())
                    + "(" + patchClassName + " patchInstance," + modifiedClass.getName() + " modifiedInstance){");

        }
        staticMethodBuidler.append(" return patchInstance." + removeMethod.getName() + "(" + JavaUtils.getParameterValue(removeMethod.getParameterTypes().length) + ");");
        staticMethodBuidler.append("}");
        ...
        return assistClass;
    }
}

实际上,最终生成的调用是 xxPatch.superMethod($$); ,$$代表全部参数。对于与上面的onCreate方法就是 xxPatch.onCreate(bundle);。 所以,patch 应该不能传 null了,否则运行时会报空指针,那第二个参数activity 能不能传 null呢?继续往下看。 首先,根据常识,static 方法中肯定是不能调用 super方法的。从最终生成的代码也能看出,这并不是最终反编译出的的 super.onCreate(bundle)方法调用。所以处理的地方肯定在javassist修改编译之后,对应处理的地方在smali 层,代码:

SmaliTool.java
    private String invokeSuperMethodInSmali(final String line, String fullClassName) {
                    ...
                    result = line.replace(Constants.SMALI_INVOKE_VIRTUAL_COMMAND, Constants.SMALI_INVOKE_SUPER_COMMAND);
                    try {
                        if (!ctMethod.getReturnType().isPrimitive()) {
                            returnType = "L" + ctMethod.getReturnType().getName().replaceAll("\\.", "/");
                        } else {
                            returnType = String.valueOf(((CtPrimitiveType) ctMethod.getReturnType()).getDescriptor());
                        }
                        if (NameManger.getInstance().getPatchNameMap().get(fullClassName).equals(fullClassName)) {
                            result = result.replace("p0", "p1");
                        }
                       ...
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                ...
    }

实际上就是把方法调用从 invoke-invoke-virtual {p0, p2}, Lcom/meituan/robust/patch/SecondActivityPatch;->onCreate(Landroid/os/Bundle;)V 转换成 invoke-super {p1, p2}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V,这步处理完才会真正的调用父类的super方法。 也就是说,在smali 处理完后,参数从 p0 -> p1,也就是参数从 xxpatch 换成了 Activity,第二个参数会在运行时用到,所以也不能传null。 分析完了总结下,第二个参数 originClass 肯定不能传 null,否则会空指针;第一个参数 xxPatch,由于在smali 被替换成了第二个参数,所以有可能是可以传 null 的。 关于super 方法的处理,参考官方文章 Android热更新方案Robust

解决方案:

  1. 修改originClass 为static,并新增一个static patch 变量
  2. 由于目前已经是在static 方法中存在super 方法,对应的smali 代码:
    .method private static final onCreate_aroundBody0(Lcom/zhangdan/app/activities/MainFragmentActivity;Landroid/os/Bundle;Lorg/aspectj/lang/JoinPoint;)V
    ...
    invoke-super {p0, p1}, Lcom/zhangdan/app/activities/WrapperAppCompatFragmentActivity;->onCreate(Landroid/os/Bundle;)V

    所以只要不处理就好了,需要做的就是在auto-plugin 中增加条件判断,符合 static 方法中带有super 的不处理,一共有三处,一处是生成 xxPatchRobustAssist辅助类,第二处在javassit替换super方法,第三处在smali处理补丁中的super方法。

对于方案1,问题: 如果patch 和 originClass 都是static ,那么就会有内存泄露的风险。 并且如果被 patch 方法是static 方法,那么在初始化 patch 时,originClass 会传 null。

    public Object accessDispatch(String methodName, Object[] paramArrayOfObject) {
        try {
            Log.d("robust", new StringBuffer().append("arrivied in AccessDispatch ").append(methodName).append(" paramArrayOfObject  ").append(paramArrayOfObject).toString());
            MainFragmentActivityPatch mainFragmentActivityPatch;
            if (!methodName.split(":")[2].equals("false")) {
                Log.d("robust", "static method forward ");
                mainFragmentActivityPatch = new MainFragmentActivityPatch(null);

同样应该也是为了避免内存泄露,每修复一个方法就会生成一个 patch 对象并持有static 的originClass引用。 对于方案2 ,问题: 首先,Aspectj 在static 方法中插了个super方法,这个是怎么做到的暂时不知道,直接写的话编译时会报错,smaili 处理吧还没到这一步。不管Aspectj怎么处理的,被修复后,这个static方法是在xxPatch类中的,auto-patch即使不处理,运行时也不能正常运行,因为xxPatch 不是originClass 父类的子类,不能调用super 方法。

观察Aspectj生成的方法,所有的static 方法,第一个参数都是当前类的引用,比如 private static final void onCreate_aroundBody0(MainFragmentActivity mainFragmentActivity, Bundle bundle, JoinPoint joinPoint) {。 所以比如根据上面的分析,得出一个可行的方案:

    def static String invokeSuperString(MethodCall m, String originClass) {
           ...
           stringBuilder.append(getStaticSuperMethodName(m.methodName) + "(" + null + "," + originClass + ",\$\$);");
           ...
    }

如果是static 方法中含有 super 方法,就如下处理。 第一个xxPatch 对象传空,最后在smali 处理的时候会被替换掉。 第二个参数是从类似onCreate_aroundBody0()中传过来的,后面的是其他参数。 最终结果:

    public static void staticRobustonCreate(MainFragmentActivityPatch mainFragmentActivityPatch, MainFragmentActivity mainFragmentActivity, Bundle bundle) {
        MainFragmentActivityPatchRobustAssist.staticRobustonCreate(mainFragmentActivityPatch, mainFragmentActivity, bundle);
    }

    private static final void onCreate_aroundBody0(MainFragmentActivity ajc$this, Bundle savedInstanceState, JoinPoint joinPoint) {
        ...
        staticRobustonCreate(null, ajc$this, savedInstanceState);
    }
hedex commented 6 years ago

我们看看哈~ 感谢反馈

w4lle commented 6 years ago

@hedex 已经解决,见pr #265

hedex commented 6 years ago

👍