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

歡迎訪問 生活随笔!

生活随笔

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

Android

【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )

發布時間:2025/6/17 Android 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 ) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Android 插件化系列文章目錄

【Android 插件化】插件化簡介 ( 組件化與插件化 )
【Android 插件化】插件化原理 ( JVM 內存數據 | 類加載流程 )
【Android 插件化】插件化原理 ( 類加載器 )

【Android 插件化】“ 插樁式 “ 插件化框架 ( 原理與實現思路 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 類加載器創建 | 資源加載 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 獲取插件入口 Activity 組件 | 加載插件 Resources 資源 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 運行應用 | 代碼整理 )

【Android 插件化】Hook 插件化框架 ( Hook 技術 | 代理模式 | 靜態代理 | 動態代理 )
【Android 插件化】Hook 插件化框架 ( Hook 實現思路 | Hook 按鈕點擊事件 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動過程 | 靜態代理 )

【Android 插件化】Hook 插件化框架 ( 從 Hook 應用角度分析 Activity 啟動流程 一 | Activity 進程相關源碼 )
【Android 插件化】Hook 插件化框架 ( 從 Hook 應用角度分析 Activity 啟動流程 二 | AMS 進程相關源碼 | 主進程相關源碼 )

【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )
【Android 插件化】Hook 插件化框架 ( 通過反射獲取 “插件包“ 中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 通過反射獲取 “宿主“ 應用中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 與 “宿主“ 中的 Element[] dexElements | 設置合并后的 Element[] 數組 )
【Android 插件化】Hook 插件化框架 ( 創建插件應用 | 拷貝插件 APK | 初始化插件包 | 測試插件 DEX 字節碼 )

【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動流程 | Hook 點分析 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動流程 | 反射獲取 IActivityManager 對象 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動流程 | AMS 啟動前使用動態代理替換掉插件 Activity 類 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動流程 | 主線程創建 Activity 實例之前使用插件 Activity 類替換占位的組件 )

【Android 插件化】Hook 插件化框架 ( 反射工具類 | 反射常用操作整理 )

【Android 插件化】Hook 插件化框架 ( 插件包資源加載 )
【Android 插件化】Hook 插件化框架 ( 從源碼角度分析加載資源流程 | Hook 點選擇 | 資源沖突解決方案 )


文章目錄

  • Android 插件化系列文章目錄
  • 前言
  • 一、從源碼角度分析加載資源流程
    • 1、ActivityThread 入口
    • 2、LaunchActivityItem
    • 3、ActivityThread.performLaunchActivity
    • 4、ContextImpl
  • 二、Hook 點選擇
  • 三、資源沖突解決方案
  • 四、博客資源

前言

在之前的博客 【Android 插件化】Hook 插件化框架 ( 插件包資源加載 ) 中 , 實現了從插件包中獲取資源 ;

但是這種方法對代碼的侵入性較大 , 使用這種方式開發 , 插件應用 和 宿主應用 , 都需要對 Resources 進行特別處理 , 如重寫 Activity 和 Application 的 public Resources getResources() 方法 ;

最好是使用 Hook 方式加載資源文件 , 實現插件包代碼 0 侵入 , 開發插件應用 與 開發普通應用 , 基本一致 ;





一、從源碼角度分析加載資源流程



在插件包中的 Activity , 如果加載 R.layout.activity_main , 拿到的是 “宿主” 應用中的資源 , 無法拿到插件包中的資源 ;


1、ActivityThread 入口


在 【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動流程 | 主線程創建 Activity 實例之前使用插件 Activity 類替換占位的組件 ) 博客中 , 分析了 Activity 在主線程啟動前的一些操作 ;


在 ActivityThread 中的 mH 處理 EXECUTE_TRANSACTION 信號時 , 其中 ClientTransaction 中有 LaunchActivityItem ;

public final class ActivityThread extends ClientTransactionHandler {final H mH = new H();/** Reference to singleton {@link ActivityThread} */private static volatile ActivityThread sCurrentActivityThread;class H extends Handler {public static final int EXECUTE_TRANSACTION = 159;public void handleMessage(Message msg) {if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));switch (msg.what) {case EXECUTE_TRANSACTION:final ClientTransaction transaction = (ClientTransaction) msg.obj;mTransactionExecutor.execute(transaction);if (isSystem()) {// Client transactions inside system process are recycled on the client side// instead of ClientLifecycleManager to avoid being cleared before this// message is handled.transaction.recycle();}// TODO(lifecycler): Recycle locally scheduled transactions.break;}}} }

源碼路徑 : /frameworks/base/core/java/android/app/ActivityThread.java


2、LaunchActivityItem


LaunchActivityItem 中的 execute 方法中的 ClientTransactionHandler client 參數 就是 ActivityThread ;

public class LaunchActivityItem extends ClientTransactionItem {@Overridepublic void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,mPendingResults, mPendingNewIntents, mIsForward,mProfilerInfo, client);client.handleLaunchActivity(r, pendingActions, null /* customIntent */);Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);} }

源碼路徑 : /frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java


3、ActivityThread.performLaunchActivity


在 LaunchActivityItem 的 execute 方法中 , 調用 client.handleLaunchActivity 方法就是執行的 ActivityThread 的 handleLaunchActivity 方法 ;

在 ActivityThread 中的 handleLaunchActivity 方法中 , 調用了 performLaunchActivity 方法 ;

在 ActivityThread 中的 performLaunchActivity 方法中 , 調用

ContextImpl appContext = createBaseContextForActivity(r);

方法 , 創建了 ContextImpl 對象 ;

public final class ActivityThread extends ClientTransactionHandler {private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {final int displayId;try {displayId = ActivityManager.getService().getActivityDisplayId(r.token);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();// For debugging purposes, if the activity's package name contains the value of// the "debug.use-second-display" system property as a substring, then show// its content on a secondary display if there is one.String pkgName = SystemProperties.get("debug.second-display.pkg");if (pkgName != null && !pkgName.isEmpty()&& r.packageInfo.mPackageName.contains(pkgName)) {for (int id : dm.getDisplayIds()) {if (id != Display.DEFAULT_DISPLAY) {Display display =dm.getCompatibleDisplay(id, appContext.getResources());appContext = (ContextImpl) appContext.createDisplayContext(display);break;}}}return appContext;} }

源碼路徑 : /frameworks/base/core/java/android/app/ActivityThread.java


4、ContextImpl


在 ActivityThread 中調用了

ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

方法 , 創建了 ContextImpl ;

下面分析 ContextImpl 的 createActivityContext 方法 ;

class ContextImpl extends Context {static ContextImpl createActivityContext(ActivityThread mainThread,LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,Configuration overrideConfiguration) {if (packageInfo == null) throw new IllegalArgumentException("packageInfo");String[] splitDirs = packageInfo.getSplitResDirs();ClassLoader classLoader = packageInfo.getClassLoader();if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");try {classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);} catch (NameNotFoundException e) {// Nothing above us can handle a NameNotFoundException, better crash.throw new RuntimeException(e);} finally {Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);}}ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,activityToken, null, 0, classLoader);// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)? packageInfo.getCompatibilityInfo(): CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;final ResourcesManager resourcesManager = ResourcesManager.getInstance();// Create the base resources for which all configuration contexts for this Activity// will be rebased upon.context.setResources(resourcesManager.createBaseActivityResources(activityToken,packageInfo.getResDir(),splitDirs,packageInfo.getOverlayDirs(),packageInfo.getApplicationInfo().sharedLibraryFiles,displayId,overrideConfiguration,compatInfo,classLoader));context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,context.getResources());return context;} }

源碼路徑 : /frameworks/base/core/java/android/app/ContextImpl.java

在上述方法中 , 就有創建 Resources 資源的方法 :

context.setResources(resourcesManager.createBaseActivityResources(activityToken,packageInfo.getResDir(),splitDirs,packageInfo.getOverlayDirs(),packageInfo.getApplicationInfo().sharedLibraryFiles,displayId,overrideConfiguration,compatInfo,classLoader));



二、Hook 點選擇



修改應用 Resources 的 Hook 點有很多 , 通過改變 Activity 的 Resources , 甚至修改 ActivityThread 中創建 Resources 的流程 都可以實現 ;

在插件包中 , 使用了 AppCompatActivity , 可以直接替換 AppCompatActivity 中的 private Resources mResources 成員 ;

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {private static final String DELEGATE_TAG = "androidx:appcompat";private AppCompatDelegate mDelegate;private Resources mResources; }

只要可以拿到 AppCompatActivity 實例 , 就可以通過反射 , 替換掉 private Resources mResources 成員 ;


在 Instrumentation 中 , 調用了 newActivity 創建 Activity 實例 ;

public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id,Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException {Activity activity = (Activity)clazz.newInstance();ActivityThread aThread = null;// Activity.attach expects a non-null Application Object.if (application == null) {application = new Application();}activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,info, title, parent, id,(Activity.NonConfigurationInstances)lastNonConfigurationInstance,new Configuration(), null /* referrer */, null /* voiceInteractor */,null /* window */, null /* activityConfigCallback */);return activity;}

源碼路徑 : /frameworks/base/core/java/android/app/Instrumentation.java

在 ActivityThread 中 , 有 Instrumentation mInstrumentation 成員變量 , ActivityThread 本身就是單例 , 通過獲取其靜態成員 private static volatile ActivityThread sCurrentActivityThread , 就可以獲取到 ActivityThread ;

/** Reference to singleton {@link ActivityThread} */private static volatile ActivityThread sCurrentActivityThread;Instrumentation mInstrumentation;



三、資源沖突解決方案



資源的 ID 在 AAPT 編譯資源階段就確定了 ;

固定類型的資源 , 編號是從一定的編號段開始的 , 如 layout 布局資源 , 第一個布局資源總是 2131361820 ;

不同類型的資源 , 布局 , 字符串 , 數值 , 主題 , 圖片 , drawable 等 , 都有對應的資源編號范圍 , 同時也要兼容系統的資源編號范圍 ;

如果宿主應用啟動 , 加載第一個布局資源 , 那么編號是 2131361820 ;
如果插件應用啟動 , 加載第一個布局資源 , 那么編號也是 2131361820 ;
這樣就出現了資源沖突 ;

使用不同的 AssetManager 加載不同的資源 , 可以解決資源沖突問題 ;





四、博客資源



博客資源 :

  • GitHub : https://github.com/han1202012/Plugin_Hook

總結

以上是生活随笔為你收集整理的【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )的全部內容,希望文章能夠幫你解決所遇到的問題。

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