在应用启动后通过融合插件dex的方式实现插件化的弊端:当插件太多的时候回造成app卡顿,用户体验不好,这时候可以采用按需加载插件的方式实现。
app在内存中的表现形式是LoadApk对象。
当调用ActivityThread的scheduleLaunchActivity方法会通过handler发送一个LAUNCH_ACTIVITY消息,handler在handlemessage中处理这个消息时会调用getPackageInfoNoCheck生成一个LoadApk,ActivityThread继续调用getPackageInfo 检查目前操作的是不是同一个用户和之前实例化没有new LoadApk。
LoadApk在ActivityThread中的存在形式:ArrayMap<String, WeakReference<LoadedApk>> mPackages
其中key为包名,我们只需要将插件对应的loadApk 添加到这个mPackages,加载的使用使用对应的dexclassLoader加载即可
public class HookUtil {
private Context context;
public void hookHookMh(Context context ) {
try {
Class<?> forName = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = forName.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
// 还原系统的ActivityTread mH
Object activityThreadObj=currentActivityThreadField.get(null);
Field handlerField = forName.getDeclaredField("mH");
handlerField.setAccessible(true);
// hook点找到了
Handler mH= (Handler) handlerField.get(activityThreadObj);
Field callbackField = Handler.class.getDeclaredField("mCallback");
callbackField.setAccessible(true);
callbackField.set(mH,new ActivityMH(mH));
} catch (Exception e) {
e.printStackTrace();
}
}
public void hookStartActivity(Context context) {
// 还原 gDefault 成员变量 反射 调用一次
this.context = context;
try {
Class<?> ActivityManagerNativecls=Class.forName("android.app.ActivityManagerNative");
Field gDefault = ActivityManagerNativecls.getDeclaredField("gDefault");
gDefault.setAccessible(true);
// 因为是静态变量 所以获取的到的是系统值 hook 伪hook
Object defaltValue=gDefault.get(null);
//mInstance对象
Class<?> SingletonClass=Class.forName("android.util.Singleton");
Field mInstance = SingletonClass.getDeclaredField("mInstance");
// 还原 IactivityManager对象 系统对象
mInstance.setAccessible(true);
Object iActivityManagerObject=mInstance.get(defaltValue);
Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
startActivty startActivtyMethod = new startActivty(iActivityManagerObject);
// 第二参数 是即将返回的对象 需要实现那些接口
Object oldIactivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader()
, new Class[]{IActivityManagerIntercept, View.OnClickListener.class}
, startActivtyMethod);
// 将系统的iActivityManager 替换成 自己通过动态代理实现的对象 oldIactivityManager对象 实现了 IActivityManager这个接口的所有方法
mInstance.set(defaltValue, oldIactivityManager);
} catch (Exception e) {
e.printStackTrace();
}
}
class ActivityMH implements Handler.Callback{
private Handler mH;
public ActivityMH(Handler mH) {
this.mH = mH;
}
@Override
public boolean handleMessage(Message msg) {
//LAUNCH_ACTIVITY ==100 即将要加载一个activity了
if (msg.what == 100) {
//加工 --完 一定丢给系统 secondActivity -hook->proxyActivity---hook-> secondeActivtiy 2
handleLuachActivity(msg);
}
//做了真正的跳转
mH.handleMessage(msg);
return true;
}
private void handleLuachActivity(Message msg) {
// 还原 ActivityClientRecord obj
Object obj = msg.obj;
try {
Field intentField=obj.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
// ProxyActivity 2
Intent realyIntent = (Intent) intentField.get(obj);
// sconedActivity 1
Intent oldIntent = realyIntent.getParcelableExtra("oldIntent");
if (oldIntent != null) {
MyApplication.isPlugin = true;
// 集中式登录
SharedPreferences share = context.getSharedPreferences("xxx",
Context.MODE_PRIVATE);
if (share.getBoolean("login",false)) {
// 登录 还原 把原有的意图 放到realyIntent SecondeActivity
realyIntent.setComponent(oldIntent.getComponent());
}else {
MyApplication.isPlugin = false;
ComponentName componentName = new ComponentName(context,LoginActivity.class);
realyIntent.putExtra("extraIntent", oldIntent.getComponent()
.getClassName());
realyIntent.setComponent(componentName);
}
realyIntent.setComponent(oldIntent.getComponent());
Field activityInfoField= obj.getClass().getDeclaredField("activityInfo");
activityInfoField.setAccessible(true);
ActivityInfo activityInfo= (ActivityInfo) activityInfoField.get(obj);
// 插件的class packageName--->loadeApk 系统 第一次 IPackageManager ----》activitry -——》包名 ---》
// 不够 IPackageManage.getPackageInfo()
activityInfo.applicationInfo.packageName = oldIntent.getPackage() == null ? oldIntent.getComponent().getPackageName()
: oldIntent.getPackage();
hookPackgeManager();
}else {
MyApplication.isPlugin = false;
}
// msg.obj 真实类型 ActivityClientRecord
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void hookPackgeManager() {
// hook 方法 IPackageManager.getPackgeInfo
// 这一步是因为 initializeJavaContextClassLoader 这个方法内部无意中检查了这个包是否在系统安装
// 如果没有安装, 直接抛出异常, 这里需要临时Hook掉 PMS, 绕过这个检查.
Class<?> activityThreadClass = null;
try {
activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
// 获取ActivityThread里面原始的 sPackageManager
Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
sPackageManagerField.setAccessible(true);
Object sPackageManager = sPackageManagerField.get(currentActivityThread);
Log.i("xxx", " handleMessage之前发生啦 ");
// 准备好代理对象, 用来替换原始的对象
Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager");
Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader()
, new Class[]{iPackageManagerInterface},new IPackageManagerHandler(sPackageManager) );
sPackageManagerField.set(currentActivityThread,proxy);
} catch (Exception e) {
e.printStackTrace();
}
}
class startActivty implements InvocationHandler {
private Object iActivityManagerObject;
public startActivty(Object iActivityManagerObject) {
this.iActivityManagerObject = iActivityManagerObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.i("INFO","invoke "+method.getName());
if ("startActivity".equals(method.getName())) {
Log.i("INFO","-----------------startActivity--------------------------");
//瞒天过海
// 寻找传进来的intent
Intent intent = null;
int index=0;
for (int i=0;i<args.length;i++) {
// intent
Object arg = args[i];
if (arg instanceof Intent) {
intent = (Intent) args[i];
index = i;
}
}
//目的 ---载入acgtivity 将它还原
Intent newIntent = new Intent();
ComponentName componentName = new ComponentName(context, ProxyActivity.class);
newIntent.setComponent(componentName);
// 真实的意图 被我隐藏到了 键值对
newIntent.putExtra("oldIntent", intent);
args[index] = newIntent;
}
return method.invoke(iActivityManagerObject, args);
}
}
// 通过 修改 mPackages ArrayMap<String, WeakReference<LoadedApk>>
// LoadedApk ---->插件
// 2 实例化 LoadedApk
// 3 put
public void putLoadedApk(String path) {
try {
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
// 先还原activityThread对象
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
// 再来还原 mPackages 对象
// 获取到 mPackages 这个静态成员变量, 这里缓存了apk包的信息
Field mPackagesField = activityThreadClass.getDeclaredField("mPackages");
mPackagesField.setAccessible(true);
Map mPackages = (Map) mPackagesField.get(currentActivityThread);
//z找到 getPackageInfoNoCheck method 方法
Class<?> compatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo");
Method getPackageInfoNoCheckMethod = activityThreadClass.getDeclaredMethod(
"getPackageInfoNoCheck", ApplicationInfo.class, compatibilityInfoClass);
// 得到 CompatibilityInfo 里面的 静态成员变量 DEFAULT_COMPATIBILITY_INFO 类型 CompatibilityInfo
Field defaultCompatibilityInfoField = compatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO");
Object defaultCompatibilityInfo = defaultCompatibilityInfoField.get(null);
ApplicationInfo applicationInfo = parseReceivers(context, path);
// 一个问题 传参 ApplicationInfo ai 一定是与插件相关 ApplicationInfo----》插件apk文件
//LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo)
Object loadedApk=getPackageInfoNoCheckMethod.invoke(currentActivityThread, applicationInfo, defaultCompatibilityInfo);
String odexPath = Utils.getPluginOptDexDir(applicationInfo.packageName).getPath();
String libDir = Utils.getPluginLibDir(applicationInfo.packageName).getPath();
ClassLoader classLoader = new CustomClassLoader(path,odexPath,libDir,context.getClassLoader());
Field mClassLoaderField = loadedApk.getClass().getDeclaredField("mClassLoader");
mClassLoaderField.setAccessible(true);
mClassLoaderField.set(loadedApk,classLoader);
WeakReference weakReference = new WeakReference(loadedApk);
// 最终目的 是要替换ClassLoader 不是替换LoaderApk
mPackages.put(applicationInfo.packageName,weakReference);
} catch (Exception e) {
e.printStackTrace();
}
}
private ApplicationInfo parseReceivers(Context context, String path) {
// Package对象
// PackageParser pp = new PackageParser();
// PackageParser.Package pkg = pp.parsePackage(scanFile, parseFlags);
try {
Class packageParserClass = Class.forName("android.content.pm.PackageParser");
Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);
Object packageParser = packageParserClass.newInstance();
Object packageObj= parsePackageMethod.invoke(packageParser, new File(path), PackageManager.GET_ACTIVITIES);
//
Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component");
Field intentsField = componentClass.getDeclaredField("intents");
// 调用generateActivityInfo 方法, 把PackageParser.Activity 转换成
Class<?> packageParser$ActivityClass = Class.forName("android.content.pm.PackageParser$Activity");
// generateActivityInfo方法
Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Object defaltUserState= packageUserStateClass.newInstance();
Method generateReceiverInfo = packageParserClass.getDeclaredMethod("generateActivityInfo",
packageParser$ActivityClass, int.class, packageUserStateClass, int.class);
Class<?> userHandler = Class.forName("android.os.UserHandle");
Method getCallingUserIdMethod = userHandler.getDeclaredMethod("getCallingUserId");
int userId = (int) getCallingUserIdMethod.invoke(null);
//目的 generateApplicationInfo 方法 生成 ApplicationInfo
// 需要调用 android.content.pm.PackageParser#generateActivityInfo(android.content.pm.ActivityInfo, int, android.content.pm.PackageUserState, int)
// generateApplicationInfo
Method generateApplicationInfoMethod = packageParserClass.getDeclaredMethod("generateApplicationInfo",
packageObj.getClass(),
int.class,
packageUserStateClass);
ApplicationInfo applicationInfo= (ApplicationInfo) generateApplicationInfoMethod.invoke(packageParser, packageObj, 0, defaltUserState);
applicationInfo.sourceDir = path;
applicationInfo.publicSourceDir = path;
return applicationInfo;
//generateActivityInfo
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//插件的apk的 dex --> Element
public void injectPluginClass() {
String cachePath = context.getCacheDir().getAbsolutePath();
String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/plugin.apk";
DexClassLoader dexClassLoader = new DexClassLoader(apkPath, cachePath, cachePath, context.getClassLoader());
// 第一步 找到 插件的Elements数组 dexPathlist ----?dexElement
try {
Class myDexClazzLoader=Class.forName("dalvik.system.BaseDexClassLoader");
Field myPathListFiled=myDexClazzLoader.getDeclaredField("pathList");
myPathListFiled.setAccessible(true);
Object myPathListObject =myPathListFiled.get(dexClassLoader);
Class myPathClazz=myPathListObject.getClass();
Field myElementsField = myPathClazz.getDeclaredField("dexElements");
myElementsField.setAccessible(true);
// 自己插件的 dexElements[]
Object myElements=myElementsField.get(myPathListObject);
// 第二步 找到 系统的Elements数组 dexElements
PathClassLoader pathClassLoader= (PathClassLoader) context.getClassLoader();
Class baseDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
Field pathListFiled = baseDexClazzLoader.getDeclaredField("pathList");
pathListFiled.setAccessible(true);
Object pathListObject = pathListFiled.get(pathClassLoader);
Class systemPathClazz = pathListObject.getClass();
Field systemElementsField = systemPathClazz.getDeclaredField("dexElements");
systemElementsField.setAccessible(true);
//系统的 dexElements[]
Object systemElements = systemElementsField.get(pathListObject);
// 第三步 上面的dexElements 数组 合并成新的 dexElements 然后通过反射重新注入系统的Field (dexElements )变量中
// 新的 Element[] 对象
// dalvik.system.Element
int systemLength = Array.getLength(systemElements);
int myLength = Array.getLength(myElements);
// 找到 Element 的Class类型 数组 每一个成员的类型
Class<?> sigleElementClazz = systemElements.getClass().getComponentType();
int newSysteLength = myLength + systemLength;
Object newElementsArray=Array.newInstance(sigleElementClazz, newSysteLength);
//融合
for (int i = 0; i < newSysteLength; i++) {
// 先融合 插件的Elements
if (i < myLength) {
Array.set(newElementsArray, i, Array.get(myElements, i));
}else {
Array.set(newElementsArray,i,Array.get(systemElements,i-myLength));
}
}
Field elementsField=pathListObject.getClass().getDeclaredField("dexElements");;
elementsField.setAccessible(true);
// 将新生成的EleMents数组对象重新放到系统中去
elementsField.set( pathListObject,newElementsArray);
} catch (Exception e) {
e.printStackTrace();
}
}
}
网友评论