当前位置: 首页 > news >正文

网站定制建设链接是什么意思

网站定制建设,链接是什么意思,包装设计概念,云龙网站开发在宿主App中加载插件App中的四大组件&#xff0c;需要以下几个步骤&#xff1a; 1. 预先在宿主的AndroidManifest文件中声明插件中的四大组件 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.co…

在宿主App中加载插件App中的四大组件,需要以下几个步骤:

1. 预先在宿主的AndroidManifest文件中声明插件中的四大组件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.chinatsp.zeusstudy1"><applicationandroid:name=".MyApplication"android:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.ZeusStudy1"tools:targetApi="31"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity android:name="com.chinatsp.zeusstudy1.ActivityA"/><!-- 插件中的类 --><service android:name="com.chinatsp.plugin1.TestService1"/><activity android:name="com.chinatsp.plugin1.TestActivity1"/></application></manifest>

2. 宿主加载插件的类

2.1 合并所有插件的dex 来解决插件的类的加载问题

把插件dex都合并到宿主的dex中,那么宿主App对应的ClassLoader就可以而加载插件中的任意类了

static void mergeDexs(String apkName, String dexName) {File dexFile = mBaseContext.getFileStreamPath(apkName);File optDexFile = mBaseContext.getFileStreamPath(dexName);try {BaseDexClassLoaderHookHelper.pathClassLoader(mBaseContext.getClassLoader(), dexFile, optDexFile);} catch (Exception e) {e.printStackTrace();}}
/*** 由于应用程序使用的ClassLoader为PathClassLoader* 最终继承自 BaseDexClassLoader* 查看源码得知,这个BaseDexClassLoader加载代码根据一个叫做* dexElements的数组进行, 因此我们把包含代码的dex文件插入这个数组* 系统的classLoader就能帮助我们找到这个类** 这个类用来进行对于BaseDexClassLoader的Hook* @author weishu* @date 16/3/28*/
public final class BaseDexClassLoaderHookHelper {public static void pathClassLoader(ClassLoader classLoader, File apkFile,File dexFile) throws IllegalAccessException,NoSuchMethodException, IOException, InvocationTargetException,InstantiationException,NoSuchFieldException {// 获取BaseDexClassLoader 中的字段 pathListObject pathListObj = RefInvoke.getFieldObject(DexClassLoader.class.getSuperclass(),classLoader,"pathList");// 获取PathList中的字段 Element[] dexElementsObject[] dexElements = (Object[]) RefInvoke.getFieldObject(pathListObj,"dexElements");// Element类型Class<?> elementClass = dexElements.getClass().getComponentType();// 创建一个数组, 用来替换原始的数组Object[] newElements = (Object[]) Array.newInstance(elementClass, dexElements.length + 1);// 构造插件ElementClass[] params = {DexFile.class,File.class,};DexFile dexFile1 = DexFile.loadDex(apkFile.getCanonicalPath(),dexFile.getAbsolutePath(),0);Object[] values = {dexFile1,apkFile};Object dexObj = RefInvoke.createObject(elementClass,params,values);Object[] toAddNewElementArray = new Object[]{dexObj};// 把原始的elements复制进去System.arraycopy(dexElements,0,newElements,0,dexElements.length);// 插件的那个element复制进去System.arraycopy(toAddNewElementArray,0,newElements,dexElements.length,toAddNewElementArray.length);// 替换RefInvoke.setFieldObject(pathListObj,"dexElements",newElements);}
}

在Applciation的attachBaseContext 方法中调用该方法将插件的dex合并进宿主的dexElements中就可以了。

通过以上两步,我们就可以正常的打开插件App中的Service和Activity类了

 public void startService1InPlugin1(View view) {try {Intent intent = new Intent();String serviceName = PluginManager.plugins.get(0).packageInfo.packageName + ".TestService1";intent.setClass(this, Class.forName(serviceName));startService(intent);} catch (Exception e) {e.printStackTrace();}}public void startActivityInPlugin1(View view){try {Intent intent = new Intent();String activityName = PluginManager.plugins.get(0).packageInfo.packageName + ".TestActivity1";intent.setClass(this, Class.forName(activityName));startActivity(intent);}catch (Exception e){e.printStackTrace();}}
2.2 修改app原生的ClassLoader

直接把系统的ClassLoader替换为我们自己的ZeusClassLoaderZeusClassLoader 的构造函数中将会传递进宿主的ClassLoader,除此之外,其内部有一个mClassLoaderList变量,保存着所有插件ClassLoader的集合。于是ZeusClassLoaderloadClass方法,会先尝试使用宿主ClassLoader加载类,如果不能加载,就遍历mClassLoaderList,直到找到一个能加载类的ClassLoader

/**** 这是一个空ClassLoader,主要是个容器* <p>* Created by huangjian on 2016/6/21.*/
class ZeusClassLoader extends PathClassLoader {private List<DexClassLoader> mClassLoaderList = null;public ZeusClassLoader(String dexPath, ClassLoader parent) {super(dexPath, parent);mClassLoaderList = new ArrayList<DexClassLoader>();}/*** 添加一个插件到当前的classLoader中*/protected void addPluginClassLoader(DexClassLoader dexClassLoader) {mClassLoaderList.add(dexClassLoader);}@Overrideprotected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {Class<?> clazz = null;try {//先查找parent classLoader,这里实际就是系统帮我们创建的classLoader,目标对应为宿主apkclazz = getParent().loadClass(className);} catch (ClassNotFoundException ignored) {}if (clazz != null) {return clazz;}//挨个的到插件里进行查找if (mClassLoaderList != null) {for (DexClassLoader classLoader : mClassLoaderList) {if (classLoader == null) continue;try {//这里只查找插件它自己的apk,不需要查parent,避免多次无用查询,提高性能clazz = classLoader.loadClass(className);if (clazz != null) {return clazz;}} catch (ClassNotFoundException ignored) {}}}throw new ClassNotFoundException(className + " in loader " + this);}
}
private static void composeClassLoader() {// 传入宿主的ClassLoaderZeusClassLoader classLoader = new ZeusClassLoader(mBaseContext.getPackageCodePath(), mBaseContext.getClassLoader());// 添加插件的ClassLoaderFile dexOutputDir = mBaseContext.getDir("dex", Context.MODE_PRIVATE);final String dexOutputPath = dexOutputDir.getAbsolutePath();for(PluginItem plugin: plugins) {DexClassLoader dexClassLoader = new DexClassLoader(plugin.pluginPath,dexOutputPath, null, mBaseClassLoader);classLoader.addPluginClassLoader(dexClassLoader);}// 替换PackgeInfo和当前线程的mClassLoaderRefInvoke.setFieldObject(mPackageInfo, "mClassLoader", classLoader);Thread.currentThread().setContextClassLoader(classLoader);mNowClassLoader = classLoader;}

经过这样Hook,所有插件的ClassLoader都在一起了。但是原先启动Activity/Service的方式需要改变:

 public void startService1InPlugin1(View view) {try {Intent intent = new Intent();String serviceName = PluginManager.plugins.get(0).packageInfo.packageName + ".TestService1";//  intent.setClass(this, Class.forName(serviceName)); intent.setClass(this, getClassLoader().loadClass(serviceName));startService(intent);} catch (Exception e) {e.printStackTrace();}}

我们之前是使用Class.forName方法来启动Servcie,会抛出找不到宿主Apk或找不到插件Service类的异常,这是因为Class.forName方法会使用BootClassLoader来加载类,这个类并没有被Hook,所以自然也就加载不到插件中的类了。
getClassLoader方法获取到的是我们Hook过的新ClassLoader,就可以加载到插件中的类了。

3. 加载插件中的资源

四大组件除了Activity,其他都是没有界面的,因此不涉及到资源。Activity则严重依赖资源文件,所以要想正确的显示插件中的Activity,必须解决加载插件中资源的问题。

宿主想要加载插件中的资源,我们是怎么做的呢?

生成一个新的AssetManager对象newAssetManager,发射调用这个newAssetManageraddAssetPath方法把插件Apk的路径加载进来,然后根据这个newAssetManager生成一个新的Resource对象newResource,然后在宿主Activity中重写getResourcesgetAssets 返回newAssetManagernewResource,这样宿主Activity就可以查找到插件Apk中的资源了。

这是一种分离宿主和插件资源的方式。还有另一种合并宿主和插件资源的方式。

创建一个新的 AssetManager 对象,并将宿主和插件的资源都通过addAssetPath方法塞入;通过新的AssetManager对象来创建出一个新的Resources对象;将新的Resources对象替换ContextImpl中的mResources变量、LoadedApk变量里的mResources变量 以及 置空mThem变量

所以按照上述步骤通过代码实现如下:

public static void reloadInstalledPluginResources(ArrayList<String> pluginPaths) {try {AssetManager assetManager = AssetManager.class.newInstance();Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);// 塞入宿主的资源addAssetPath.invoke(assetManager, mBaseContext.getPackageResourcePath());for(String pluginPath: pluginPaths) {// 塞入插件的资源addAssetPath.invoke(assetManager, pluginPath);}mAssetManager = assetManager;Resources newResources = new Resources(assetManager,mBaseContext.getResources().getDisplayMetrics(),mBaseContext.getResources().getConfiguration());// 获取 ContextImpl 中的 Resources 类型的 mResources 变量,并替换它的值为新的 Resources 对象RefInvoke.setFieldObject(mBaseContext, "mResources", newResources);//这是最主要的需要替换的,如果不支持插件运行时更新,只留这一个就可以了RefInvoke.setFieldObject(mPackageInfo, "mResources", newResources);mNowResources = newResources;//需要清理mTheme对象,否则通过inflate方式加载资源会报错//如果是activity动态加载插件,则需要把activity的mTheme对象也设置为nullRefInvoke.setFieldObject(mBaseContext, "mTheme", null);} catch (Throwable e) {e.printStackTrace();}}

上述方法的调用时机就是在执行完插件dex合并后调用。如果此时要在宿主中调用插件中的Activity,还需要做一件事情,就是将上面方法中的mNowResources对象传递到插件中的Activity中去,并重写插件Activity的getResources方法,使getResources方法返回mNowResources对象即可。

但是这样完成之后会发现,怎么明明代码中启动的是插件中的Activity,但显示的确是宿主中的Activity呢?
如果你把宿主的Apk和插件的Apk打开( 使用AS中的Build ==》Analazy Apk ),查看其中的resources.arsc文件,对比后会发现两个APK中的资源文件有相同的id,这就是资源id冲突引起的问题,可以通过 配置aapt2 的参数来修改插件Apk生成的资源id:

# build.gradle
android {...aaptOptions {additionalParameters '--allow-reserved-package-id','--package-id','0x70'}
}

这样,插件Apk中生成的资源id就会以0x70开头,而我们的宿主Apk生成的资源id默认是0x7f开头。

资源id的生成过程建议查看这个

合并dex版本源码
ZeusClassLoader版本源码


文章转载自:
http://dinncopsychology.bkqw.cn
http://dinnconupe.bkqw.cn
http://dinncorj.bkqw.cn
http://dinncospecial.bkqw.cn
http://dinncodelegant.bkqw.cn
http://dinncoisoteniscope.bkqw.cn
http://dinncocentinewton.bkqw.cn
http://dinncoyatata.bkqw.cn
http://dinncomuddiness.bkqw.cn
http://dinncodinaric.bkqw.cn
http://dinncoantienzyme.bkqw.cn
http://dinncoopposite.bkqw.cn
http://dinncoethnics.bkqw.cn
http://dinncogleesome.bkqw.cn
http://dinncomatriline.bkqw.cn
http://dinncopedagese.bkqw.cn
http://dinncogarrison.bkqw.cn
http://dinncopaunch.bkqw.cn
http://dinncosnowball.bkqw.cn
http://dinncocoxswain.bkqw.cn
http://dinncoaminoplast.bkqw.cn
http://dinncotelespectroscope.bkqw.cn
http://dinncogalleon.bkqw.cn
http://dinncoproliferous.bkqw.cn
http://dinncotrochar.bkqw.cn
http://dinncomanpack.bkqw.cn
http://dinncoanakinesis.bkqw.cn
http://dinncoleisured.bkqw.cn
http://dinncocitole.bkqw.cn
http://dinncoslipsheet.bkqw.cn
http://dinncomicroanalysis.bkqw.cn
http://dinncoframework.bkqw.cn
http://dinncoexchangeable.bkqw.cn
http://dinncogaping.bkqw.cn
http://dinncopsyllid.bkqw.cn
http://dinncolaverne.bkqw.cn
http://dinncozineb.bkqw.cn
http://dinncostorybook.bkqw.cn
http://dinncostraitjacket.bkqw.cn
http://dinncohardy.bkqw.cn
http://dinncorhabdom.bkqw.cn
http://dinncomercy.bkqw.cn
http://dinncopapa.bkqw.cn
http://dinncoglomerule.bkqw.cn
http://dinncoecesis.bkqw.cn
http://dinncocoracle.bkqw.cn
http://dinncocandlelighting.bkqw.cn
http://dinncodefine.bkqw.cn
http://dinncofermanagh.bkqw.cn
http://dinncorepellent.bkqw.cn
http://dinncolordling.bkqw.cn
http://dinncopuggry.bkqw.cn
http://dinncowander.bkqw.cn
http://dinncoenvenom.bkqw.cn
http://dinncoseromuscular.bkqw.cn
http://dinncocarnal.bkqw.cn
http://dinncojackassery.bkqw.cn
http://dinncometope.bkqw.cn
http://dinncosluice.bkqw.cn
http://dinncohomoeopathist.bkqw.cn
http://dinncopreprocess.bkqw.cn
http://dinncobiaural.bkqw.cn
http://dinncoindivertible.bkqw.cn
http://dinncolaypeople.bkqw.cn
http://dinncodexiotropous.bkqw.cn
http://dinncoarchiepiscopal.bkqw.cn
http://dinncosouthwesterly.bkqw.cn
http://dinncotony.bkqw.cn
http://dinncodigitiform.bkqw.cn
http://dinncobioshield.bkqw.cn
http://dinncoavalanchologist.bkqw.cn
http://dinncoturnabout.bkqw.cn
http://dinncomainframe.bkqw.cn
http://dinncoknot.bkqw.cn
http://dinncoforficated.bkqw.cn
http://dinncostrikebreaking.bkqw.cn
http://dinncohormic.bkqw.cn
http://dinnconif.bkqw.cn
http://dinncodilettantist.bkqw.cn
http://dinncospense.bkqw.cn
http://dinncohemolymph.bkqw.cn
http://dinncoflagellated.bkqw.cn
http://dinncobarish.bkqw.cn
http://dinncorowland.bkqw.cn
http://dinncolacteous.bkqw.cn
http://dinncogastrotrichan.bkqw.cn
http://dinncoconcentration.bkqw.cn
http://dinncotattersall.bkqw.cn
http://dinncoracker.bkqw.cn
http://dinncolibber.bkqw.cn
http://dinncoaristotype.bkqw.cn
http://dinncorondino.bkqw.cn
http://dinncoflavor.bkqw.cn
http://dinncononobservance.bkqw.cn
http://dinncolegman.bkqw.cn
http://dinncocoerce.bkqw.cn
http://dinncoinchage.bkqw.cn
http://dinncofloat.bkqw.cn
http://dinncoiterance.bkqw.cn
http://dinncoasyntactic.bkqw.cn
http://www.dinnco.com/news/134667.html

相关文章:

  • 赣州市南康区建设局网站百度网站ip地址
  • 商标 做网站 是几类湖口网站建设
  • 荆州哪里做网站外链发布论坛
  • 建网站 行业 销售额互联网推广与营销
  • 南城区仿做网站设计网站排行
  • win8 网站模板靠谱的代运营公司有哪些
  • 关于做网站的总结东莞营销推广公司
  • 宁波网站建设seo专门做网站的公司
  • 网站开发公司官网b2b平台
  • 天元建设集团有限公司第二建筑工程分公司关键词优化排名用哪个软件比较好
  • 河北建筑工程学院招生网搜索引擎优化工具
  • 电费由谁承担百度关键词快速优化
  • 湖北省住房城乡建设厅网站首页百度号码认证申诉平台
  • 大哥商品做网站的目的百度关键词竞价价格
  • 做网站资源推荐浙江网站推广公司
  • 政府网站谁来做营销推广软文
  • 淘宝网站开发的多少钱写软文的app
  • 华强北手机网站建设seo和sem的区别是什么?
  • 白之家低成本做网站上海网站搜索引擎优化
  • 网页浏览器在哪里打开seo入门培训
  • 如何获取网站开发语言最新新闻事件摘抄
  • 做网站毕业实训报告seo培训赚钱
  • 益保网做推广网站吗营销云
  • 驴妈妈旅游网站的建设成都网络推广哪家好
  • 毕设做系统与网站阿里云域名查询和注册
  • 去哪里做网站微商营销技巧
  • 广州做企业网站的公司国内企业网站模板
  • 厦门网站建设网页设计百度快照优化公司
  • 计算机毕业论文代做网站今日疫情最新消息全国31个省
  • 安居客看房网百度怎么优化网站关键词