日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android热补丁之Robust(三)坑和解

發布時間:2025/3/21 Android 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android热补丁之Robust(三)坑和解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在前兩篇文章中,分析了 Android 熱補丁框架 Robust 中,幾個重要的流程包括:

  • 補丁加載過程
  • 基礎包插樁過程
  • 補丁包自動化生成過程

本篇文章主要分析下集成過程中遇到的坑以及分析問題的思路和最終的解決方案。包含:

  • 打補丁包出錯?
  • Robust 定義的 API 不夠用怎么辦?
  • 插件 Plugin Transform 的順序問題?
  • 與 Aspectj 沖突怎么辦?
  • static 方法中包含 super 方法怎么辦?

系列文章:

  • Android熱補丁之Robust原理解析(一)
  • Android熱補丁之Robust(二)自動化補丁原理解析
  • Android熱補丁之Robust(三)坑和解

打補丁包出錯?

在打補丁包過程中,碰到了一個錯誤 execute command java -jar /Users/wanglinglong/Develop/u51/Credit51/CreditCardManager/robust/dx.jar --dex --output=classes.dex meituan.jar error,找了一大圈最后發現是jdk老版本在Mac上的一個bug,升級jdk就好了,參考 Class JavaLaunchHelper is implemented in two places

Robust 定義的 API 不夠用怎么辦?

Robust 提供了一些 API 可供開發者擴展使用,比如: 添加類庫依賴 compile 'com.meituan.robust:robust:0.4.82',其中 PatchManipulateImp 類的一些可擴展方法

protected List<Patch> fetchPatchList(Context context);protected boolean verifyPatch(Context context, Patch patch);protected boolean ensurePatchExist(Patch patch); 復制代碼

但是在一些情況下,這些可擴展方法并不能滿足我們的需求。

為了滿足定制化需求,可以棄用 com.meituan.robust:robust,自己實現一套補丁加載邏輯,這個實現起來難度并不太大,主要補丁加載流程都可以參考 Robust 官方實現,具體加載邏輯可參考本系列第一篇文章,這里不再深入。

插件 Plugin Transform 的順序問題?

首先,要找到 Gradle Plugin 編譯過程中對于自定義 Transform 的處理,具體流程讀者可以自行搜索查看,這里只給出關鍵代碼

TaskManager.java /*** Creates the post-compilation tasks for the given Variant.** These tasks create the dex file from the .class files, plus optional intermediary steps like* proguard and jacoco*/ public void createPostCompilationTasks(@NonNull final VariantScope variantScope) {...// ---- Code Coverage first -----...// Merge Java Resources.createMergeJavaResTransform(variantScope);// ----- External Transforms -----// apply all the external transforms.List<Transform> customTransforms = extension.getTransforms();List<List<Object>> customTransformsDependencies = extension.getTransformsDependencies();for (int i = 0, count = customTransforms.size(); i < count; i++) {Transform transform = customTransforms.get(i);List<Object> deps = customTransformsDependencies.get(i);transformManager.addTransform(taskFactory, variantScope, transform).ifPresent(t -> {if (!deps.isEmpty()) {t.dependsOn(deps);}// if the task is a no-op then we make assemble task depend on it.if (transform.getScopes().isEmpty()) {variantScope.getAssembleTask().dependsOn(t);}});}... } 復制代碼

在生成 Dex 任務之前,會處理所有的自定義 Transform 任務,邏輯是按照順序遍歷然后處理任務依賴關系,那么從這里我們可以知道,Transform 的執行順序是按照插件的聲明順序來執行的,也就是說,哪個 plugin 的聲明在前,其對應的 Transform 就在前執行,舉個例子:

apply plugin: 'pluginA' apply plugin: 'pluginB' 復制代碼

那么對應的 TransformA 任務就會先于 TransfromB 任務執行。

好了,知道了 Transform 的執行順序問題,再來看下 Robust 插件的順序問題。首先來看下基線包的處理插件 apply plugin: 'robust',其邏輯是在每個方法中前置插入補丁加載邏輯代碼,用于攔截基線包中的原有邏輯,達到修復方法的目的。 如果 robust Plugin 是先于其他插件執行的,那么會出現 Robust 插入代碼后,再執行其他插件的代碼邏輯,這樣會有問題嗎?其實要具體問題具體分析,可能會有問題,也可能沒有問題。舉個例子,我們項目中使用了聽云,而且是較老的版本(2.5.9),其插件內部插入代碼沒有用到 Transform,而是用了另外一種技術,其會導致不管在哪里聲明插件,其處理順序都是最后執行,那么對于 Robust 基線包插件來說,當基線包插件插入完代碼后,又會去執行聽云插件的插入代碼邏輯,所以可能會看到以下這種代碼:

實際上,我們最終想要的結果是這樣的:

然而,如果仔細觀察這種錯誤的代碼插入邏輯,實際上并沒有對最終的熱修復邏輯產生影響。是因為在生成補丁包過程中,他們的執行順序也是這樣的,即聽云插件最后執行,這樣的結果就是Robust 自動化補丁插件在生成插件后就強制停止了整個編譯流程,聽云插件根本就沒有機會執行。所以最后補丁包中僅僅會包含剔除了Robust 基線包插件插入的代碼以及聽云插件插入的代碼。可以理解為第一張圖中把紅框下面的代碼通過熱修復的方式移入了紅框里面,然后return。

對于聽云來說,2.8.x版本之后,插入代碼的邏輯也由 Tranform 來執行,也就是說,對于聽云來說,不管是怎么樣的執行順序,都不會與 Robust 發生兼容性問題。

所以還是要具體問題具體分析,這里僅僅提供一些排查問題的思路。

與 Aspectj 沖突怎么辦?

我們項目中大量使用了 AOP 技術,涉及到的框架有 Aspectj、javassist、ASM。其中由于 javassist 和 ASM 完全是有自己控制的,所以不會有問題,而對于 Aspectj 來說就沒這么簡單了。

剛開始介入就碰到了這樣一個問題 Caused by: java.lang.ClassCastException: com.meituan.robust.patch.MainFragmentActivityPatch cannot be cast to com.zhangdan.app.activities.MainFragmentActivity,是一個類型強轉錯誤。

問題分析,由于 Aspectj 框架為開發者省略了很多邏輯,開發者只需要編寫切面相關代碼即可,所以需要梳理清楚 Aspectj 的原理:

首先貼下未混淆的代碼:

MainFragmentActivity.classstatic 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");} } 復制代碼

我們往下跟下 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}; 復制代碼

這里調用了下 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.javapublic Object proceed() throws Throwable {// when called from a before advice, but be a no-opreturn 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 的流程就走通了。

最后總結下 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()的原始方法邏輯。

另外,對于修改后的代碼,沒有被打入補丁,也是可以解釋的。 對于 auto-path-plugin,Transform 的順序是 Aspectj -> auto-patch. 那么,對于標記修改的 onCreate 方法來說,Aspectj 處理完后,onCreate 方法被替換成了代理,真正的方法實現被新生成的方法隱藏起來了。 而我們僅僅標記了舊的 onCreate 方法,其結果就是,Aspectj 的代理 onCreate 方法被 patch 了,而實際的方法雖然方法體內有我們的修復,但是由于沒有標記 @modify 而被忽略。

因為是強轉 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 終于是搞定了。具體解決方法見 merge request #259。

static 方法中包含 super 方法怎么辦?

看到這個標題可能會一臉懵逼,static 方法中怎么可能包含 super 調用?別急慢慢往下看。

書接上節,至于被 Aspectj 處理過的方法無法被打入 patch 的問題,理論上來說跟泛型的橋方法是類似的,解決方案也是 @Modify -> RobustModify.modify();,修改后經驗證,會報錯。log如下

Caused by: javassist.CannotCompileException: [source error] not-available: thisat 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: thisat 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 {defstatic 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.javaprivate 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 的。

解決方案:

  • 修改 originClass 為static,并新增一個 static patch 變量
  • 由于目前已經是在 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 方法(猜測也是在 smali 層做的修改),直接寫的話 javac 編譯時會報錯,smali 處理吧還沒到這一步。所以被修復后,這個 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);} 復制代碼

    到這里這個問題就分析完了,詳細解決方發見 merge request #265

    總結

    這個系列到這里基本就結束了。這篇文章主要介紹了在接入 Robust 過程中碰到的一些坑以及解決思路,其實根本還是熟讀源碼,碰到問題學習從源碼中找答案。要堅信,坑踩的多了,也就不怕坑了。最后福利一張。

    總結

    以上是生活随笔為你收集整理的Android热补丁之Robust(三)坑和解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。