Chapter 8 The Simplest Plug-in Solution
This chapter introduces the simplest plug-in solution that are applicable to the four major components. This solution involves the following aspects:
1) Combine the dex of all plugins to solve the problem of loading plug-in's class.
2) Declare the four major components of the plugin in the host app’s AndroidManifest file. It’s a nuisance for hundreds of activities in the plug-in.
3) Merge all the resources in the plug-in into the host's resources in one time. Of course, this may result in conflict of resource ids.
8.1 Declaring Components in a Plug-in in Android Manifest
As we mentioned earlier, the four major components in the plug-in are just ordinary classes that the system does not recognize at all.
To make the host app to recognize them, you must declare the four major components in the host app's AndroidManifest.xml.
Then there is the simplest plug-in solution in history. The four components in the plug-in are declared in the host app.
Look at the example ZeusStudy1.0, as shown in the figure below. Plugin1 has a TestService1 component
.
Figure 8-1 Project structure of Plugin1
Correspondingly, in the Host app’s AndroidManifest file, the statement is as follows:
<service android:name="jianqiang.com.plugin1.TestService1" />
8.2 Combine the dex
Host app loads classes in plug-ins, there are two methods to make it. [1]
1.Use the class of plug-in’s ClassLoader;
2.Combine the dexs of host app and plug-in.
The second one is simpler.
Once the plug-in dex is merged into the host's dex, then the ClassLoader corresponding to the host App loads all the classes in the plug-in as follows:
public final class BaseDexClassLoaderHookHelper {
public static void patchClassLoader(ClassLoader cl, File apkFile, File optDexFile)
throws IllegalAccessException, NoSuchMethodException, IOException, InvocationTargetException, InstantiationException, NoSuchFieldException {
// Obtain BaseDexClassLoader : pathList
Object pathListObj = RefInvoke.getFieldObject(DexClassLoader.class.getSuperclass(), cl, "pathList");
// Obtain PathList: Element[] dexElements
Object[] dexElements = (Object[]) RefInvoke.getFieldObject(pathListObj, "dexElements");
// Element type
Class<?> elementClass = dexElements.getClass().getComponentType();
// Create an array to replace the original array
Object[] newElements = (Object[]) Array.newInstance(elementClass, dexElements.length + 1);
// Construct a plugin Element(File file, boolean isDirectory, File zip, DexFile dexFile) This constructor
Class[] p1 = {File.class, boolean.class, File.class, DexFile.class};
Object[] v1 = {apkFile, false, apkFile, DexFile.loadDex(apkFile.getCanonicalPath(), optDexFile.getAbsolutePath(), 0)};
Object o = RefInvoke.createObject(elementClass, p1, v1);
Object[] toAddElementArray = new Object[] { o };
// Copy the original elements
System.arraycopy(dexElements, 0, newElements, 0, dexElements.length);
// The element of the plugin is copied in
System.arraycopy(toAddElementArray, 0, newElements, dexElements.length, toAddElementArray.length);
// replace
RefInvoke.setFieldObject(pathListObj, "dexElements", newElements);
}
}
8.3 Start the Service of plug-in
Combined with the previous two parts, the host App can start a service that in plug-in.
Intent intent = new Intent();
String serviceName = "jianqiang.com.plugin1.TestService1";
intent.setClassName(this, serviceName);
startService(intent);
8.4Activity resources [2]
Not just Service, four major components can be implemented as plug-in program. Both ContentProvider and Receiver are relatively simple, you can try to implement it by yourself.
Service, ContentProvider, and Receiver just need to merge dex, because they have no resources.
The solution for Activity here is a bit complicated.
Activity is heavily dependent on resources. Therefore, if you want to implement the plug-in of the Activity, you must solve the problem of loading the resources in the plug-in.
Chapter 7 introduced the relationship between AssetManager and Resources. AssetManager has an addAssetPath method that can populate the plugin's path all at once, and then generates a "super" Resource based on this "super" AssetManager.
Save this Super Resources in the global variable PluginManager. After you find the plugin or the host resource, you can find it too.
The above logic is implemented as follows (located in MyApplication):
private static void reloadInstalledPluginResources() {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, mBaseContext.getPackageResourcePath());
addAssetPath.invoke(assetManager, pluginItem1.pluginPath);
Resources newResources = new Resources(assetManager,
mBaseContext.getResources().getDisplayMetrics(),
mBaseContext.getResources().getConfiguration());
RefInvoke.setFieldObject (mBaseContext, "mResources", newResources);
// This is the main need to replace, if you do not support the plug-in runtime update, just leave this one
RefInvoke.setFieldObject (mPackageInfo, "mResources", newResources);
mNowResources = newResources;
// Need to clean up the mTheme object, otherwise it will report an error when loading resources through inflate mode
// If the activity dynamically loads the plugin, you need to set the activity's mTheme object to null RefInvoke.setFieldObject (mBaseContext, "mTheme", null);
} catch (Throwable e) {
e.printStackTrace();
}
The plug-in Activity must implement the base class ZeusBaseActivity. In this base class, the getResource method is overridden, thereby ensuring that the plug-in Activity is fetched from the "super" Resources each time it fetches resources.
public class ZeusBaseActivity extends Activity {
@Override
public Resources getResources() {
return PluginManager.mNowResources;
}
}
Here is the code for TestActivity1 in plug-in which called Plugin1. It uses the layout xml resource called activity_test1 in the plug-in:
public class TestActivity1 extends ZeusBaseActivity {
private final static String TAG = "TestActivity1";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Intent intent = new Intent();
String activityName = "jianqiang.com.hostapp.ActivityA";
intent.setComponent(new ComponentName("jianqiang.com.hostapp", activityName));
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
At this point, the simplest plug-in solution of activity is complete, and we can even jump from the plug-in activity to the activity in the host.
However, there has a fatal problem: The four major components in the plugin must be declared in the host's AndroidManifest file beforehand and cannot be added.
In fact, for most of the App, rarely use Service, Receiver and ContentProvider, so the plug-in has these three components, is also typically updated logic, and will not add new.
However, there are many activities in the plug-in, and also add new, at this moment, we cannot pre-empt in the host AndroidManifest file.
This issue will be completely resolved in Chapter 9.
8.5Summary
This chapter introduces the simplest plug-in solution. Although the plug-in can be successfully loaded, there are many problems:
1) Because the plugin and host resources are merged together, the resource ids will conflict.
2) Activity in the plug-in cannot be expected in advance, especially add new Activity1 in the plug-in.
These problems will be solved in the subsequent chapters.
[1] This section example code, please refer to https://github.com/Baobaojianqiang/ZeusStudy1.0
[2] This section example code, please refer to https://github.com/Baobaojianqiang/ZeusStudy1.1
Chapter 8 The Simplest Plug-in Solution的更多相关文章
- C++ 之 策略模式
1 会飞的鸭子 Duck 基类,包含两个成员函数 swim() 和 display():派生类 MallardDuck,RedheadDuck 和 RubberDuck,各自重写 display() ...
- (转)几种范数的解释 l0-Norm, l1-Norm, l2-Norm, … , l-infinity Norm
几种范数的解释 l0-Norm, l1-Norm, l2-Norm, - , l-infinity Norm from Rorasa's blog l0-Norm, l1-Norm, l2-Norm, ...
- 如何创建Asp.net MVC ViewModel
ASP.NET MVC View Model Patterns Since MVC has been released I have observed much confusion about how ...
- C#主要字典集合性能对比[转]
A post I made a couple days ago about the side-effect of concurrency (the concurrent collections in ...
- 2D Rotated Rectangle Collision
Introduction While working on a project for school, I found it necessary to perform a collision chec ...
- TIJ——Chapter One:Introduction to Objects
///:~容我对这个系列美其名曰"读书笔记",其实shi在练习英文哈:-) Introduction to Objects Object-oriented programming( ...
- Think Python - Chapter 18 - Inheritance
In this chapter I present classes to represent playing cards, decks of cards, and poker hands.If you ...
- Think Python - Chapter 11 - Dictionaries
Dictionaries A dictionary is like a list, but more general. In a list, the indices have to be intege ...
- Chapter 4: Spring and AOP:Spring's AOP Framework -- draft
Spring's AOP Framework Let's begin by looking at Spring's own AOP framework - a proxy-based framewor ...
随机推荐
- 4-18 class与id的区别
1. class是设置标签的类, class属性用于指定元素属于何种样式的类. 如样式表可以加入.content1 { color: red; background: #ff80c0 } 使用方法:c ...
- java虚拟机和内存优化总结
前一段时间总结了spring和springmvc相关的知识,面试中常问到的除了这些基本的框架之外,还有底层的基础知识,比如与java虚拟机相关的知识点,这一部分也是面试中经常问到的,在面试中高级jav ...
- docker mysql 主主同步
转发自:https://blog.csdn.net/money9sun/article/details/85099134 第一步:安装docker https://www.cnblogs.com/ ...
- 子元素z-index高于父元素兄弟元素z-index被遮挡问题
问题:最近在写样式时,遇到一个这样的问题,子元素的z-index值大于父元素兄弟元素z-index值,结果子元素超出父元素部分被父元素兄弟元素遮挡解决:将父元素的z-index值设置为大于兄弟元素z- ...
- centos7+mariadb+防火墙,允许远程
centos7 已安装mariadb,想要允许数据库远程==数据库权限允许+系统允许 mariadb:允许数据库用户在所有ip使用某个用户远程 GRANT ALL PRIVILEGES ON *(数据 ...
- [转]vue跨域解决方法
vue跨域解决方法 vue项目中,前端与后台进行数据请求或者提交的时候,如果后台没有设置跨域,前端本地调试代码的时候就会报“No 'Access-Control-Allow-Origin' hea ...
- Swoole 心跳检测
Swoole的心跳检测特别简单,只需要配置 heartbeat_check_interval,heartbeat_idle_time就可以了. heartbeat_check_interval:表示服 ...
- 内嵌圆角CSS实现
前言 开发项目时,经常会遇到如上图左上角和右上角这种内嵌的圆角效果,在以前css3还没有普及时不用说一张图片搞定,但是到现在我们完全可以用css去实现. 实现 第一步:思路 仔细观察这个小缺角,它其实 ...
- MySQL 聚合函数 控制流程函数
常用的聚合函数 1. AVG() 求平均值 mysql> AVG([DISTINCT] expr) -- 返回 expr 的平均值 mysql> select AVG(age) from ...
- Java中String字符串常量池
首先看一个例子,通过这个例子更能快速理解String常量池 public static void main(String[] args) { String a = "ab"; St ...