返回顶部
首页 > 资讯 > 移动开发 >亲自动手实现Android App插件化
  • 547
分享到

亲自动手实现Android App插件化

自动appAndroid 2022-06-06 05:06:24 547人浏览 安东尼
摘要

Android插件化目前国内已经有很多开源的工程了,不过如果不实际开发一遍,很难掌握的很好。 下面是自己从0开始,结合目前开源的项目和博客,动手开发插件化方案。 按照需要插件化

Android插件化目前国内已经有很多开源的工程了,不过如果不实际开发一遍,很难掌握的很好。

下面是自己从0开始,结合目前开源的项目和博客,动手开发插件化方案。

按照需要插件化主要解决下面的几种问题:

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();
}

public static Object getPathList(ClassLoader classLoader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
 return FieldUtils.readField(classLoader, "pathList");
}

public static Object getDexElements(Object pathList) throws NoSuchFieldException, IllegalAccessException {
 LogUtils.d("Reflect To Get DexElements");
 return FieldUtils.readField(pathList, "dexElements");
}

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,是有生命周期的,应该是使用系统默认的创建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.进行访问。

具体原理参照://www.jb51.net/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) ;
 }
 }
}
您可能感兴趣的文章:iOS和Android用同一个二维码实现跳转下载链接的方法Android中Textview超链接实现方式Android中捕获TTextView文本中的链接点击事件方法Android开发之获取网络链接状态Android下保存简单网页到本地(包括简单图片链接转换)实现代码Android实现TextView中文字链接的4种方式介绍及代码Android开发技巧之在a标签或TextView控件中单击链接弹出Activity(自定义动作)Android如何动态改变App桌面图标Android小挂件(APP Widgets)设计指导Android实现使用微信登录第三方APP的方法Android编程实现点击链接打开APP功能示例


--结束END--

本文标题: 亲自动手实现Android App插件化

本文链接: https://lsjlt.com/news/22915.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 亲自动手实现Android App插件化
    Android插件化目前国内已经有很多开源的工程了,不过如果不实际开发一遍,很难掌握的很好。 下面是自己从0开始,结合目前开源的项目和博客,动手开发插件化方案。 按照需要插件化...
    99+
    2022-06-06
    自动 app Android
  • Android微信自动抢红包插件优化和实现
    又是兴趣系列 网上有很多自动强红包的例子和代码,笔者也是做了一些优化。 先说说自己的两个个优势 1.可以在聊天界面自动强不依赖于通知栏推送 2.可以在屏幕熄灭的时候的时候点亮...
    99+
    2022-06-06
    自动 插件 抢红包 优化 Android
  • 亲自动手编写Android通用刷新控件
    项目中我们经常有上拉、下拉刷新的需求,几乎所有的listView、RecyclerView都会伴随着上拉、下拉刷新的需求,如果我们使用一些开源控件,换了控件我们就要更新,现在我们自己撸起袖子写一个通用的刷新控件项目地址:https://gi...
    99+
    2023-05-31
    android 刷新控件 roi
  • Android如何实现APP自动更新
    先来看看要实现的效果图: 对于安卓用户来说,手机应用市场说满天飞可是一点都不夸张,比如小米,魅族,百度,360,机锋,应用宝等等,当我们想上线一款新版本APP时,先不说渠道打...
    99+
    2022-06-06
    更新 app Android
  • Android实现app开机自启动功能
    本文实例为大家分享了Android实现app开机自启动的具体代码,供大家参考,具体内容如下 最近要做个大屏的开发板程序,需要长期稳定运行,并开机自启运行此软件。 废话不多说,上...
    99+
    2022-06-07
    启动 app Android
  • Android广播实现App开机自启动
    本文实例为大家分享了Android广播实现App开机自启动的具体代码,供大家参考,具体内容如下 一、概括 在安卓中,想要实现app开机自动启动,需要实现拦截广播android....
    99+
    2022-06-07
    启动 android广播 app Android
  • maven实现docker自动化部署插件的使用
    构建开发项目 首先打开我们的IDEA构建一个我们的开发项目,配置如下: 接着打开我们的pom.xml修改build的配置,修改以后的代码如下: <build> ...
    99+
    2024-04-02
  • 怎样用Python实现自动化操作Android手机
    本篇文章给大家分享的是有关怎样用Python实现自动化操作Android手机,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。一、【必须】安装adb工具adb全称Android D...
    99+
    2023-06-02
  • android实现App活动定时自动跳转效果
    App的小功能点,很简单几十行代码就可以实现 主页面代码 package com.buildingbuilding; import android.content.Inte...
    99+
    2022-06-06
    自动跳转 自动 app Android
  • Android App自动化性能测试探究
      前言   Android App的性能测试是移动测试过程中必不可少的一个环节。在我们项目组内,性能测试的过程是这样的,先设置测试场景,然后一边手工执行场景,一边通过工...
    99+
    2022-06-06
    性能 性能测试 app 测试 Android
  • idea使用docker插件实现一键自动化部署
    目录环境:一、 docker开启远程连接访问安装配置idea的docker插件创建项目,并配置一 、 创建项目二、 配置项目maven打包,生成镜像创建容器,项目部署到docker修...
    99+
    2024-04-02
  • Python与Appium实现手机APP自动化测试的示例代码
    目录1.什么是Appium2.启动一个app自动化程序的步骤3.appium服务介绍4. appium客户端使用5.adb的使用6.Appium启动过程分析1.什么是Appium a...
    99+
    2024-04-02
  • VSCode自动化插件有哪些
    这篇文章主要讲解了“VSCode自动化插件有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“VSCode自动化插件有哪些”吧!Live Server一般情况下,当你在VSCode修改代码后...
    99+
    2023-06-29
  • Android广播怎么实现App开机自启动
    今天小编给大家分享一下Android广播怎么实现App开机自启动的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、概括在安卓...
    99+
    2023-06-30
  • Android实现开机自动启动Service或app的方法
    本文实例讲述了Android实现开机自动启动Service或app的方法。分享给大家供大家参考,具体如下: 第一步:首先创建一个广播接收者,重构其抽象方法 onReceive(...
    99+
    2022-06-06
    自动 service 方法 app Android
  • Android自定义View实现随手势滑动控件
    本文控件为大家分享了Android随手势滑动控件的具体代码,供大家参考,具体内容如下 1.新建自定义控件类:MyView public class MyView extend...
    99+
    2022-06-06
    view 手势 Android
  • Angular.js中ng-app如何实现自动绑定与手动绑定
    这篇文章主要介绍Angular.js中ng-app如何实现自动绑定与手动绑定,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、传统的绑定初始化<html> <he...
    99+
    2024-04-02
  • Android应用APP自动更新功能的代码实现
    由于Android项目开源所致,市面上出现了N多安卓软件市场。为了让我们开发的软件有更多的用户使用,我们需要向N多市场发布,软件升级后,我们也必须到安卓市场上进行更新,给我们增...
    99+
    2022-06-06
    更新 app Android
  • Android App中用Handler实现ViewPager页面的自动切换
    在很多电商网页及app上都有自动切换的商品的推广快,感觉体验挺不错的,正好今天学习使用ViewPager,因此也实现了一个功能类似的demo。 下面是其中的两个截图: 实现...
    99+
    2022-06-06
    自动 viewpager handler app Android
  • Maven Web项目怎么使用Cargo插件实现自动化部署
    这篇文章主要讲解了“Maven Web项目怎么使用Cargo插件实现自动化部署”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Maven Web项目怎么使用Cargo插件实...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作