亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

親自動手實現Android App插件化

 更新時間:2016年12月16日 09:35:00   作者:xingjm8511  
這篇文章主要教大家親自動手實現Android App插件化,具有一定的參考價值,感興趣的小伙伴們可以參考一下

Android插件化目前國內已經有很多開源的工程了,不過如果不實際開發(fā)一遍,很難掌握的很好。

下面是自己從0開始,結合目前開源的項目和博客,動手開發(fā)插件化方案。

按照需要插件化主要解決下面的幾種問題:

1. 代碼的加載

(1) 要解決純Java代碼的加載

(2) Android組件加載,如Activity、Service、Broadcast Receiver、ContentProvider,因為它們是有生命周期的,所以要特殊處理

(3) Android Native代碼的加載

(4) Android 特殊控件的處理,如Notification等

2. 資源加載

不同插件的資源如何管理,是公用一套還是插件獨立管理?

因為在Android中訪問資源,都是通過R. 實現的, 

下面就一步步解決上面的問題

1. 純Java代碼的加載

主要就是通過ClassLoader、更改DexElements將插件的路徑添加到原來的數組中。

詳細的分析可以參考我轉載的一篇文章,因為感覺原貼命名和結構有點亂,所以轉載記錄下。

https://my.oschina.net/android520/blog/794715

Android提供DexClassLoader和PathClassLoader,都繼承BaseDexClassLoader,只是構造方法的參數不一樣,即optdex的路徑不一樣,源碼如下

// DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
 public DexClassLoader(String dexPath, String optimizedDirectory,
  String libraryPath, ClassLoader parent) {
 super(dexPath, new File(optimizedDirectory), libraryPath, parent);
 }
}

// PathClassLoader.java
public class PathClassLoader extends BaseDexClassLoader {
 public PathClassLoader(String dexPath, ClassLoader parent) {
 super(dexPath, null, null, parent);
 }

 public PathClassLoader(String dexPath, String libraryPath,
  ClassLoader parent) {
 super(dexPath, null, libraryPath, parent);
 }
}

其中,optimizedDirectory是用來存儲opt后的dex目錄,必須是內部存儲路徑。

DexClassLoader可以加載外部的dex或apk,只要opt的路徑通過參數設置一個內部存儲路徑即可。

PathClassLoader只能加載已安裝的apk,因為opt路徑會使用默認的dex路徑,外部的不可以。

下面介紹下如何通過DexClassLoader實現加載Java代碼,參考Nuwa

這種方式類似于熱修復,如果插件和宿主代碼有相互訪問,則需要在打包中使用插樁技術實現。

public static boolean injectDexAtFirst(String dexPath, String dexOptPath) {

 // 獲取系統的dexElements
 Object baseDexElements = getDexElements(getPathList(getPathClassLoader()));

 // 獲取patch的dexElements
 DexClassLoader patchDexClassLoader = new DexClassLoader(dexPath, dexOptPath, dexPath, getPathClassLoader());
 Object patchDexElements = getDexElements(getPathList(patchDexClassLoader));

 // 組合最新的dexElements
 Object allDexElements = combineArray(patchDexElements, baseDexElements);

 // 將最新的dexElements添加到系統的classLoader中
 Object pathList = getPathList(getPathClassLoader());
 FieldUtils.writeField(pathList, "dexElements", allDexElements);
}

public static ClassLoader getPathClassLoader() {
 return DexUtils.class.getClassLoader();
}

/**
 * 反射調用getPathList方法,獲取數據
 * @param classLoader
 * @return
 * @throws ClassNotFoundException
 * @throws NoSuchFieldException
 * @throws IllegalAccessException
 */
public static Object getPathList(ClassLoader classLoader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
 return FieldUtils.readField(classLoader, "pathList");
}

/**
 * 反射調用pathList對象的dexElements數據
 * @param pathList
 * @return
 * @throws NoSuchFieldException
 * @throws IllegalAccessException
 */
public static Object getDexElements(Object pathList) throws NoSuchFieldException, IllegalAccessException {
 LogUtils.d("Reflect To Get DexElements");
 return FieldUtils.readField(pathList, "dexElements");
}

/**
 * 拼接dexElements,將patch的dex插入到原來dex的頭部
 * @param firstElement
 * @param secondElement
 * @return
 */
public static Object combineArray(Object firstElement, Object secondElement) {

 LogUtils.d("Combine DexElements");

 // 取得一個數組的Class對象, 如果對象是數組,getClass只能返回數組類型,而getComponentType可以返回數組的實際類型
 Class objTypeClass = firstElement.getClass().getComponentType();

 int firstArrayLen = Array.getLength(firstElement);
 int secondArrayLen = Array.getLength(secondElement);
 int allArrayLen = firstArrayLen + secondArrayLen;

 Object allObject = Array.newInstance(objTypeClass, allArrayLen);
 for (int i = 0; i < allArrayLen; i++) {
 if (i < firstArrayLen) {
  Array.set(allObject, i, Array.get(firstElement, i));
 } else {
  Array.set(allObject, i, Array.get(secondElement, i - firstArrayLen));
 }
 }
 return allObject;
}

使用上面的方式啟動的Activity,是有生命周期的,應該是使用系統默認的創(chuàng)建Activity方式,而不是自己new Activity對象,所以打開的Activity生命周期正常。

但是上面的方式,必須保證Activity在宿主AndroidManifest.xml中注冊。

2. 下面介紹下如何加載未注冊的Activity功能

Activity的加載原理參考 https://my.oschina.net/android520/blog/795599

主要通過Hook系統的IActivityManager完成

3. 資源加載

資源訪問都是通過R.方式,實際上Android會生成一個0x7f******格式的int常量值,關聯對應的資源。

如果資源有更改,如layout、id、drawable等變化,會重新生成R.java內容,int常量值也會變化。

因為插件中的資源沒有參與宿主程序的資源編譯,所以無法通過R.進行訪問。

具體原理參照:http://chabaoo.cn/article/100245.htm

使用addAssetPath方式將插件路徑添加到宿主程序后,因為插件是獨立打包的,所以資源id也是從1開始,而宿主程序也是從1開始,可能會導致插件和宿主資源沖突,系統加載資源時以最新找到的資源為準,所以無法保證界面展示的是宿主的,還是插件的。

針對這種方式,可以在打包時,更改每個插件的資源id生成的范圍,可以參考public.xml介紹。

代碼參考Amigo

public static void loadPatchResources(Context context, String apkPath) throws Exception {
 AssetManager newAssetManager = AssetManager.class.newInstance();
 invokeMethod(newAssetManager, "addAssetPath", apkPath);
 invokeMethod(newAssetManager, "ensureStringBlocks");
 replaceAssetManager(context, newAssetManager);
}

private static void replaceAssetManager(Context context, AssetManager newAssetManager)
  throws Exception {
 Collection<WeakReference<Resources>> references;
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
 Class<?> resourcesManagerClass = Class.forName("android.app.ResourcesManager");
 Object resourcesManager = invokeStaticMethod(resourcesManagerClass, "getInstance");

 if (getField(resourcesManagerClass, "mActiveResources") != null) {
  ArrayMap<?, WeakReference<Resources>> arrayMap =
   (ArrayMap) readField(resourcesManager, "mActiveResources", true);
  references = arrayMap.values();
 } else {
  references = (Collection) readField(resourcesManager, "mResourceReferences", true);
 }
 } else {
 HashMap<?, WeakReference<Resources>> map =
   (HashMap) readField(ActivityThreadCompat.instance(), "mActiveResources", true);
 references = map.values();
 }

 AssetManager assetManager = context != null ? context.getAssets() : null;
 for (WeakReference<Resources> wr : references) {
 Resources resources = wr.get();
 if (resources == null) continue;

 try {
  writeField(resources, "mAssets", newAssetManager);
  originalAssetManager = assetManager;
 } catch (Throwable ignore) {
  Object resourceImpl = readField(resources, "mResourcesImpl", true);
  writeField(resourceImpl, "mAssets", newAssetManager);
 }

 resources.updateConfiguration(resources.getConfiguration(),
   resources.getDisplayMetrics());
 }

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 for (WeakReference<Resources> wr : references) {
  Resources resources = wr.get();
  if (resources == null) continue;

  // android.util.Pools$SynchronizedPool<TypedArray>
  Object typedArrayPool = readField(resources, "mTypedArrayPool", true);

  // Clear all the pools
  while (invokeMethod(typedArrayPool, "acquire") != null) ;
 }
 }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • 淺析Android.mk

    淺析Android.mk

    Android.mk是Android提供的一種makefile文件,用來指定諸如編譯生成so庫名、引用的頭文件目錄、需要編譯的.c/.cpp文件和.a靜態(tài)庫文件等。要掌握jni,就必須熟練掌握Android.mk的語法規(guī)范
    2016-01-01
  • JS實現點擊參數面板按鈕顯示或隱藏數據

    JS實現點擊參數面板按鈕顯示或隱藏數據

    本文主要介紹JS實現點擊參數面板按鈕顯示或隱藏數據的方法,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-03-03
  • Android實現實時滑動ViewPager的2種方式

    Android實現實時滑動ViewPager的2種方式

    這篇文章主要為大家詳細介紹了Android實現實時滑動ViewPager的2種方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Android本地搜索業(yè)務優(yōu)化方案

    Android本地搜索業(yè)務優(yōu)化方案

    這篇文章主要為大家介紹了Android本地搜索業(yè)務優(yōu)化方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • Android實現側滑只需一步

    Android實現側滑只需一步

    這篇文章主要介紹了Android實現側滑只需一步,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • Android中簡單的電話管理與短信管理App編寫實例

    Android中簡單的電話管理與短信管理App編寫實例

    這篇文章主要介紹了Android中簡單的電話管理與短信管理App編寫實例,包括監(jiān)聽電話的呼叫狀態(tài)以及短信群發(fā)聯系人選擇等基本功能的實現,代碼突出要點,需要的朋友可以參考下
    2016-04-04
  • Android編程中Activity的四種啟動模式

    Android編程中Activity的四種啟動模式

    這篇文章主要介紹了Android編程中Activity的四種啟動模式,較為詳細的分析了Activity四種啟動模式的原理與功能,需要的朋友可以參考下
    2016-04-04
  • android完美實現 拍照 選擇圖片 剪裁等代碼分享

    android完美實現 拍照 選擇圖片 剪裁等代碼分享

    本文給大家分享了2個安卓實現實現 拍照 選擇圖片 剪裁等的代碼,都是從正式項目中提取出來了,非常實用,有需要的小伙伴可以參考下。
    2016-01-01
  • Android開發(fā)中解析xml文件XmlUtils工具類與用法示例

    Android開發(fā)中解析xml文件XmlUtils工具類與用法示例

    這篇文章主要介紹了Android開發(fā)中解析xml文件XmlUtils工具類與用法,結合實例形式分析了Android開發(fā)中解析xml文件工具類定義與相關使用技巧,需要的朋友可以參考下
    2018-01-01
  • Android實現透明動畫

    Android實現透明動畫

    這篇文章主要為大家詳細介紹了Android實現透明動畫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05

最新評論