FrameWork内核解析之XMS内核管理(一)上篇
阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680
本篇文章将继续从以下两个内容来介绍XMS内核管理之AMS:
- [Activity的管理]
- [ Android插件化开发之运行未安装apk的activity]
一、Activity的管理
1、ActivityRecord是Activity管理的最小单位,它对应着一个用户界面;
2、TaskRecord也是一个栈式管理结构,每一个TaskRecord都可能存在一个或多个ActivityRecord,栈顶的ActivityRecord表示当前可见的界面;
3、ActivityStack是一个栈式管理结构,每一个ActivityStack都可能存在一个或多个TaskRecord,栈顶的TaskRecord表示当前可见的任务;
4、ActivityStackSupervisor管理着多个ActivityStack,但当前只会有一个获取焦点(Focused)的ActivityStack;
5、ProcessRecord记录着属于一个进程的所有ActivityRecord,运行在不同TaskRecord中的ActivityRecord可能是属于同一个 ProcessRecord。
二、Android插件化开发之运行未安装apk的activity
2.1、介绍
我们知道PathClassLoader是一个应用的默认加载器(而且他只能加载data/app/xxx.apk的文件),但是我们加载插件一般使用DexClassLoader加载器,所以这里就有问题了,其实如果对于开始的时候,每个人都会认为很简单,很容易想到使用DexClassLoader来加载Activity获取到class对象,在使用Intent启动
2.2、替换LoadApk里面的mClassLoader
我们知道我们可以将我们使用的DexClassLoader加载器绑定到系统加载Activity的类加载器上就可以了,这个是我们的思路。也是最重要的突破点。下面我们就来通过源码看看如何找到加载Activity的类加载器。加载Activity的时候,有一个很重要的类:LoadedApk.Java,这个类是负责加载一个Apk程序的,我们可以看一下他的源码:
我们知道内部有个mClassLoader成员变量,我们只需要获取它就可以了,因为它不是静态的,所以我们需要先获取LoadApk这个类的对象,我们再去看看ActivityThread.java这个类。
我们可以发现ActivityThread里面有个静态的成员变量sCurrentActivityThread,然后还有一个ArrayMap存放Apk包名和LoadedApk映射关系的数据结构,我们通过反射来获取mClassLoader对象。
如果对ActivityThread.java这个类不熟悉的可以看我这篇博客,http://blog.csdn.net/u011068702/article/details/53207039 (Android插件化开发之AMS与应用程序(客户端ActivityThread、Instrumentation、Activity)通信模型分析)非常重要,是app程序的入口处。
2.3、实现具体代码
1)我们先需要一个测试apk,然后把这个测试的test.apk,放到手机sdcard里面去,关键代码如下
package com.example.testapkdemo;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
public static View parentView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (parentView == null) {
setContentView(R.layout.activity_main);
} else {
setContentView(parentView);
}
findViewById(R.id.button).setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "我是来自插件", Toast.LENGTH_SHORT).show();
}
});
}
public void setView(View view) {
this.parentView = view;
}
}
效果图如下:
接下来是我宿主代码:
ReflectHelper.java 这个是的我的反射帮助类
package com.example.dexclassloaderactivity;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 反射辅助函数
* @author
*
*/
public class ReflectHelper {
public static final Class<?>[] PARAM_TYPE_VOID = new Class<?>[]{};
public static Object invokeStaticMethod(String className, String methodName, Class<?>[] paramTypes, Object...params) {
try {
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod(methodName, paramTypes);
method.setAccessible(true);
return method.invoke(null, params);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static Object invokeMethod(String className, String methodName, Class<?>[] paramTypes, Object obj, Object...params) {
try {
Class<?> clazz = Class.forName(className);
Method method = clazz.getMethod(methodName, paramTypes);
method.setAccessible(true);
return method.invoke(obj, params);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static Object getStaticField(String className, String fieldName) {
try {
Class<?> clazz = Class.forName(className);
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static Object getField(String className, String fieldName, Object obj) {
try {
Class<?> clazz = Class.forName(className);
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void setStaticField(String className, String fieldName, Object value) {
try {
Class<?> clazz = Class.forName(className);
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(null, value);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setField(String className, String fieldName, Object obj, Object value) {
try {
Class<?> clazz = Class.forName(className);
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object createInstance(String className, Class<?>[] paramTypes, Object...params) {
Object object = null;
try {
Class<?> cls = Class.forName(className);
Constructor<?> constructor = cls.getConstructor(paramTypes);
constructor.setAccessible(true);
object = constructor.newInstance(params);
}catch (Exception e) {
e.printStackTrace();
}
return object;
}
/**
* Locates a given field anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the field into.
* @param name field name
* @return a field object
* @throws NoSuchFieldException if the field cannot be located
*/
public static Field findField(Object instance, String name) throws NoSuchFieldException {
for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
try {
Field field = clazz.getDeclaredField(name);
if (!field.isAccessible()) {
field.setAccessible(true);
}
return field;
} catch (NoSuchFieldException e) {
// ignore and search next
}
}
throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
}
/**
* Locates a given field anywhere in the class inheritance hierarchy.
*
* @param cls to search the field into.
* @param name field name
* @return a field object
* @throws NoSuchFieldException if the field cannot be located
*/
public static Field findField2(Class<?> cls, String name) throws NoSuchFieldException {
Class<?> clazz = null;
for (clazz = cls; clazz != null; clazz = clazz.getSuperclass()) {
try {
Field field = clazz.getDeclaredField(name);
if (!field.isAccessible()) {
field.setAccessible(true);
}
return field;
} catch (NoSuchFieldException e) {
// ignore and search next
}
}
throw new NoSuchFieldException("Field " + name + " not found in " + clazz);
}
/**
* Locates a given method anywhere in the class inheritance hierarchy.
*
* @param instance an object to search the method into.
* @param name method name
* @param parameterTypes method parameter types
* @return a method object
* @throws NoSuchMethodException if the method cannot be located
*/
public static Method findMethod(Object instance, String name, Class<?>... parameterTypes)
throws NoSuchMethodException {
for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
try {
Method method = clazz.getDeclaredMethod(name, parameterTypes);
if (!method.isAccessible()) {
method.setAccessible(true);
}
return method;
} catch (NoSuchMethodException e) {
// ignore and search next
}
}
throw new NoSuchMethodException("Method " + name + " with parameters " +
Arrays.asList(parameterTypes) + " not found in " + instance.getClass());
}
}
MyApplication.java 这个类用实现替换mClassLoader
package com.example.dexclassloaderactivity;
import java.io.File;
import java.lang.ref.WeakReference;
import dalvik.system.DexClassLoader;
import android.annotation.SuppressLint;
import android.app.Application;
import android.os.Environment;
import android.util.ArrayMap;
import android.util.Log;
public class MyApplication extends Application{
public static final String TAG = "MyApplication";
public static final String AppName = "test.apk";
public static int i = 0;
public static DexClassLoader mClassLoader;
@Override
public void onCreate() {
Log.d(TAG, "替换之前系统的classLoader");
showClassLoader();
try {
String cachePath = this.getCacheDir().getAbsolutePath();
String apkPath = /*Environment.getExternalStorageState() + File.separator*/"/sdcard/"+ AppName;
mClassLoader = new DexClassLoader(apkPath, cachePath,cachePath, getClassLoader());
loadApkClassLoader(mClassLoader);
} catch (Exception e) {
e.printStackTrace();
}
Log.d(TAG, "替换之后系统的classLoader");
showClassLoader();
}
@SuppressLint("NewApi")
public void loadApkClassLoader(DexClassLoader loader) {
try {
Object currentActivityThread = ReflectHelper.invokeMethod("android.app.ActivityThread", "currentActivityThread", new Class[] {},new Object[] {});
String packageName = this.getPackageName();
ArrayMap mpackages = (ArrayMap) ReflectHelper.getField("android.app.ActivityThread", "mPackages", currentActivityThread);
WeakReference wr= (WeakReference)mpackages.get(packageName);
Log.e(TAG, "mClassLoader:" + wr.get());
ReflectHelper.setField("android.app.LoadedApk", "mClassLoader", wr.get(), loader);
Log.e(TAG, "load:" + loader);
} catch (Exception e) {
Log.e(TAG, "load apk classloader error:" + Log.getStackTraceString(e));
}
}
/**
* 打印系统的classLoader
*/
public void showClassLoader() {
ClassLoader classLoader = getClassLoader();
if (classLoader != null){
Log.i(TAG, "[onCreate] classLoader " + i + " : " + classLoader.toString());
while (classLoader.getParent()!=null){
classLoader = classLoader.getParent();
Log.i(TAG,"[onCreate] classLoader " + i + " : " + classLoader.toString());
i++;
}
}
}
}
然后就是MainActivity.java文件,里面包含了下面另外一种方式,打开activity,所以我把函数 inject(DexClassLoader loader)先注释掉。
package com.example.dexclassloaderactivity;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
public class MainActivity extends ActionBarActivity{
public static final String TAG = "MainActivity";
public static final String AppName = "test.apk";
public static DexClassLoader mDexClassLoader = null;
public static final String APPName = "test.apk";
public TextView mText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.text2).setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
try {
// inject(MyApplication.mClassLoader);
String apkPath = Environment.getExternalStorageDirectory().toString() + File.separator + APPName;
Class clazz = MyApplication.mClassLoader.loadClass("com.example.testapkdemo.MainActivity");
Intent intent = new Intent(MainActivity.this, clazz);
startActivity(intent);
finish();
} catch (Exception e) {
Log.e(TAG, "name:" + Log.getStackTraceString(e));
}
}
});
}
private void inject(DexClassLoader loader){
PathClassLoader pathLoader = (PathClassLoader) getClassLoader();
try {
Object dexElements = combineArray(
getDexElements(getPathList(pathLoader)),
getDexElements(getPathList(loader)));
Object pathList = getPathList(pathLoader);
setField(pathList, pathList.getClass(), "dexElements", dexElements);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static Object getPathList(Object baseDexClassLoader)
throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
ClassLoader bc = (ClassLoader)baseDexClassLoader;
return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
}
private static Object getField(Object obj, Class<?> cl, String field)
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field localField = cl.getDeclaredField(field);
localField.setAccessible(true);
return localField.get(obj);
}
private static Object getDexElements(Object paramObject)
throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
return getField(paramObject, paramObject.getClass(), "dexElements");
}
private static void setField(Object obj, Class<?> cl, String field,
Object value) throws NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field localField = cl.getDeclaredField(field);
localField.setAccessible(true);
localField.set(obj, value);
}
private static Object combineArray(Object arrayLhs, Object arrayRhs) {
Class<?> localClass = arrayLhs.getClass().getComponentType();
int i = Array.getLength(arrayLhs);
int j = i + Array.getLength(arrayRhs);
Object result = Array.newInstance(localClass, j);
for (int k = 0; k < j; ++k) {
if (k < i) {
Array.set(result, k, Array.get(arrayLhs, k));
} else {
Array.set(result, k, Array.get(arrayRhs, k - i));
}
}
return result;
}
}
这里一定要注意,我们犯了3个错,
1、test.apk的路径写错了,下次写文件路径的时候,我们应该需要加上File file = new File(path); 用file.exist()函数来判断是否存在
2、从sdcard卡里面读取test.apk,没加上权限, <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
3、写了Application,忘了在AndroidManifest.xml文件里面声明。
我们还需要注意要加上开启activity 在AndroidManifest.xml里面注册,切记,希望下次不要再次犯错。
AndroidManifest.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.dexclassloaderactivity"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:name="com.example.dexclassloaderactivity.MyApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.testapkdemo.MainActivity">
</activity>
</application>
</manifest>
运行结果如下:
然后这里又出现了插件资源和宿主资源冲突问题,我们后面再来研究。
4、合并PathClassLoader和DexClassLoader中的dexElements数组
我们首先来看一下PathClassLoader和DexClassLoader类加载器的父类BaseDexClassloader的源码:
(这里需要注意的是PathClassLoader和DexClassLoader类的父加载器是BaseClassLoader,他们的父类是BaseDexClassLoader)
这里有一个DexPathList对象,在来看一下DexPathList.java源码:
Elements数组,我们看到这个变量他是专门存放加载的dex文件的路径的,系统默认的类加载器是PathClassLoader,本身一个程序加载之后会释放一个dex出来,这时候会将dex路径放到里面,当然DexClassLoader也是一样的,那么我们会想到,我们是否可以将DexClassLoader中的dexElements和PathClassLoader中的dexElements进行合并,然后在设置给PathClassLoader中呢?这也是一个思路。我们来看代码:
/**
* 以下是一种方式实现的
* @param loader
*/
private void inject(DexClassLoader loader){
PathClassLoader pathLoader = (PathClassLoader) getClassLoader();
try {
Object dexElements = combineArray(
getDexElements(getPathList(pathLoader)),
getDexElements(getPathList(loader)));
Object pathList = getPathList(pathLoader);
setField(pathList, pathList.getClass(), "dexElements", dexElements);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static Object getPathList(Object baseDexClassLoader)
throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
ClassLoader bc = (ClassLoader)baseDexClassLoader;
return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
}
private static Object getField(Object obj, Class<?> cl, String field)
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field localField = cl.getDeclaredField(field);
localField.setAccessible(true);
return localField.get(obj);
}
private static Object getDexElements(Object paramObject)
throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
return getField(paramObject, paramObject.getClass(), "dexElements");
}
private static void setField(Object obj, Class<?> cl, String field,
Object value) throws NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field localField = cl.getDeclaredField(field);
localField.setAccessible(true);
localField.set(obj, value);
}
private static Object combineArray(Object arrayLhs, Object arrayRhs) {
Class<?> localClass = arrayLhs.getClass().getComponentType();
int i = Array.getLength(arrayLhs);
int j = i + Array.getLength(arrayRhs);
Object result = Array.newInstance(localClass, j);
for (int k = 0; k < j; ++k) {
if (k < i) {
Array.set(result, k, Array.get(arrayLhs, k));
} else {
Array.set(result, k, Array.get(arrayRhs, k - i));
}
}
return result;
}
然后运行的时候把MyApplication.java文件里面的函数loadApkClassLoader(mClassLoader);注释掉,然后把MainActivity.java文件里面的inject(MyApplication.mClassLoader)不要注释,运行效果一样。
阿里P7Android高级架构进阶视频免费学习请点击:https://space.bilibili.com/474380680
参考https://blog.csdn.net/u011068702/article/details/53523609
https://www.jianshu.com/p/0bbcffbc7796
FrameWork内核解析之XMS内核管理(一)上篇的更多相关文章
- 解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】
转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的 ...
- xenomai内核解析之xenomai的组成结构
@ 目录 一.xenomai 3 二.xenomai3 结构 这是第二篇笔记. 一.xenomai 3 从xenomai3开始支持两种方式构建linux实时系统,分别是cobalt 和 mercury ...
- xenomai内核解析之信号signal(二)---xenomai信号处理机制
xenomai信号 上篇文章讲了linux的信号在内核的发送与处理流程,现在加入了cobalt核,Cobalt内核为xenomai线程提供了信号机制.下面一一解析xenomai内核的信号处理机制. 1 ...
- xenomai内核解析---内核对象注册表—xnregistry(重要组件)
1. 概述 上篇文章xenomai内核解析--同步互斥机制(一)--优先级倒置讲到,对于所有内核对象: xnregistry:保存内核对象,提供内核对象存储和快速检索. xnsynch:资源抽象,提供 ...
- 【原创】xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 1.概述 上篇文章xenomai内核解析--实时IP ...
- 【原创】xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(二)--实时与非实时关联(bind流程)
版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 1.概述 上篇文章介绍了实时端socket创建和配置 ...
- 【xenomai内核解析】系列文章大纲
xenomai内核解析 本博客为本人学习linux实时操作系统框架xenomai的一些记录,主要剖析xenomai内核实现,以及与linux相关的知识.方便读者定位具体文章,现列出本博客大纲,后续会陆 ...
- v72.01 鸿蒙内核源码分析(Shell解析) | 应用窥伺内核的窗口 | 百篇博客分析OpenHarmony源码
子曰:"苟正其身矣,于从政乎何有?不能正其身,如正人何?" <论语>:子路篇 百篇博客系列篇.本篇为: v72.xx 鸿蒙内核源码分析(Shell解析篇) | 应用窥视 ...
- c#网络通信框架networkcomms内核解析 序言
NetworkComms网络通信框架序言 networkcomms是我遇到的写的最优美的代码,很喜欢,推荐给大家:) 基于networkcomms2.3.1开源版本( gplv3)协议,写了一些文章, ...
随机推荐
- 初学Selenium遇上的问题
1.IWebDriver driver = new InternetExplorerDriver();运行时报关于protecte model的错误 解决办法就是用如下代码设置IEDriverOpit ...
- ingress-nginx配置https文件访问
1.先将证书文件上传至服务器特定目录.比如:/root/ssl 假设证书名称为:server.crt和server.key 2.现在主节点后台创建私密文件. kubectl create secret ...
- Vue证明题
看来我需要对我的vue能力做一个证明了~~ 最近辞职了,又逢病重,找工作的时候发现对vue要求蛮高的,说会不行,还必须要有过vue的项目. 我这种半路出家的哪里来的vue的项目,公司又不是那种一线互联 ...
- 联想think system sr550信息
带外管理口 默认IP地址:192.168.70.125 默认用户名密码 USERID PASSW0RD 0是数字0
- 力扣——Partition List(分隔链表) python实现
题目描述: 中文: 给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前. 你应当保留两个分区中每个节点的初始相对位置. 示例: 输入: head = ...
- docker-ce创建gitlab-ce容器笔记
前言 vagrant + ubuntu 16.04 设置 apt 源 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo vim ...
- MySQL-事件总结
是什么?事件是一组SQL集合,简单说就是mysql中的定时器,时间到了就执行. 一:查询事件变量,如果查询不到变量,说明数据库版本过低,不支持事件. SHOW VARIABLES LIKE 'even ...
- Maven介绍及安装
1.maven是一个管理第三方库的jar package 2.从该页面下载相应的Maven jar包(http://maven.apache.org/download.cgi),linux OS下 ...
- 如何给 List 集合排序
一,List<Integer>的排序示例代码:List<Integer> list = new ArrayList<Integer>();list.add(6);l ...
- 【leetcode】1006. Clumsy Factorial
题目如下: Normally, the factorial of a positive integer n is the product of all positive integers less t ...