什么是插件化开发
宿主app+插件app的模式,一个宿主可以有多个插件,根据不同业务需求,动态更新替换插件.
很多大厂都出了自己的插件化框架,我们在使用的同时,也需要简单了解其原理
需要解决的第一个问题
插件化开发需要解决的第一个难题,就是四大组件如何绕过清单文件配置检测,我们已activity为例,举个栗子,学习插件化开发之前,我们需要了解activity的启动流程,请参看源码.我们的思路是在清单文件中注册一个代理activity,通过它绕过检测机制,最终替换它为真正需要加载的activity,这里需要用到hook技术
什么是hook
hook的意思是勾住。比如A发送消息给B,在消息过去之前,可以先把消息勾住,不让其传递,你可以先执行你的操作(或者说先执行你的钩子函数)。
专业的说法就是:hook技术,能够改变API执行的结果,将系统的API函数执行重定向。
为什么需要hook
android系统的沙箱机制使我们不能通过一个程序改变其他程序的某些行为,但是hook技术正好可以解决此类问题。沙箱是一个虚拟系统程序,沙箱提供的环境相对于每一个运行的程序的进程空间都是独立的,程序的运行互不干扰,而且不会对现有的系统产生影响。
需要注意的问题
hook的源码版本,与手机的系统版本有关,也就是说,手机的android系统是什么版本的,就需要hook什么版本你的代码,不同版本之间的源码是有区别的,简单说来,本例适配了6.0以上源码
举个栗子
先从启动activity开始吧,了解activity启动流程的童鞋对如下代码肯定不陌生,它是启动activity的代码,我们从这里开始hook
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
真正启动activity的是IActivityManager,基于接口,我们利用动态代理来hook,我们看到,它是一个隐藏的类,所以我们只能通过反射来实例化对象
/**
* System private API for talking with the activity manager service. This
* provides calls from the application back to the activity manager.
*
* {@hide}
*/
public interface IActivityManager extends IInterface {
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags,
ProfilerInfo profilerInfo, Bundle options) throws RemoteException;
}
开始撸代码
<activity android:name=".ProxyActivity"/>
public class ProxyActivity extends Activity{
}
public class BaseApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
HookUtils hookUtils =
new HookUtils(this);
try {
hookUtils.hookStartActivity();
hookUtils.hookLaunchActivity();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class HookStartActivityUtil {
private String TAG = "HookUtils";
private Context mContext;
private final String EXTRA_ORIGIN_INTENT = "EXTRA_ORIGIN_INTENT";
public HookUtils(Context context){
this.mContext = context.getApplicationContext();
}
public void hookLaunchActivity() throws Exception{
// 1 获取ActivityThread实例
Class<?> atClass = Class.forName("android.app.ActivityThread");
Field scatField = atClass.getDeclaredField("sCurrentActivityThread");
scatField.setAccessible(true);
Object sCurrentActivityThread = scatField.get(null);
// 2 获取ActivityThread中的mH
Field mhField = atClass.getDeclaredField("mH");
mhField.setAccessible(true);
Object mHandler = mhField.get(sCurrentActivityThread);
// 3 hook handleLaunchActivity
// 给Handler设置CallBack回掉,也只能通过发射
Class<?> handlerClass = Class.forName("android.os.Handler");
Field mCallbackField = handlerClass.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mHandler,new HandlerCallBack());
}
private class HandlerCallBack implements Handler.Callback{
@Override
public boolean handleMessage(Message msg) {
Log.e(TAG,"handleMessage");
// 每发一个消息都会走一次这个CallBack发放
if(msg.what == 100){
handleLaunchActivity(msg);
}
return false;
}
/**
* 开始启动创建Activity拦截
* @param msg
*/
private void handleLaunchActivity(Message msg) {
try {
Object record = msg.obj;
// 1.从record 获取过安检的Intent
Field intentField = record.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
Intent safeIntent = (Intent) intentField.get(record);
// 2.从safeIntent中获取原来的originIntent
Intent originIntent = safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT);
// 3.重新设置回去
if(originIntent != null){
intentField.set(record,originIntent);
}
// 兼容AppCompatActivity报错问题
Class<?> forName = Class.forName("android.app.ActivityThread");
Field field = forName.getDeclaredField("sCurrentActivityThread");
field.setAccessible(true);
Object activityThread = field.get(null);
// 我自己执行一次那么就会创建PackageManager,系统再获取的时候就是下面的iPackageManager
Method getPackageManager = activityThread.getClass().getDeclaredMethod("getPackageManager");
Object iPackageManager = getPackageManager.invoke(activityThread);
PackageManagerHandler handler = new PackageManagerHandler(iPackageManager);
Class<?> iPackageManagerIntercept = Class.forName("android.content.pm.IPackageManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[]{iPackageManagerIntercept}, handler);
// 获取 sPackageManager 属性
Field iPackageManagerField = activityThread.getClass().getDeclaredField("sPackageManager");
iPackageManagerField.setAccessible(true);
iPackageManagerField.set(activityThread, proxy);
}catch (Exception e){
e.printStackTrace();
}
}
}
class PackageManagerHandler implements InvocationHandler {
private Object mActivityManagerObject;
public PackageManagerHandler(Object iActivityManagerObject) {
this.mActivityManagerObject = iActivityManagerObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Log.e("TAG", "methodName = " + method.getName());
if (method.getName().startsWith("getActivityInfo")) {
ComponentName componentName = new ComponentName(mContext, ProxyActivity.class);
args[0] = componentName;
}
return method.invoke(mActivityManagerObject, args);
}
}
public void hookStartActivity() throws Exception{
// 1 需要判断手机系统的版本,如果小于m则获取ActivityManagerNative里面的gDefault,
//大于m则获取ActivityManager中IActivityManagerSingleton
Class<?> amnClass = null;
Field gDefaultField = null;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
amnClass = Class.forName("android.app.ActivityManager");
// 获取属性
gDefaultField = amnClass.getDeclaredField("IActivityManagerSingleton");
}else {
amnClass = Class.forName("android.app.ActivityManagerNative");
gDefaultField = amnClass.getDeclaredField("gDefault");
}
// 设置权限
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null);
// 2 获取gDefault中的mInstance属性
Class<?> singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
Object iamInstance = mInstanceField.get(gDefault);
Class<?> iamClass = Class.forName("android.app.IActivityManager");
Object finalIamInstance = iamInstance;
iamInstance = Proxy.newProxyInstance(HookUtils.class.getClassLoader(),
new Class[]{iamClass},
// InvocationHandler 必须执行者,谁去执行iamInstance
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e(TAG,method.getName());
// 替换Intent,过AndroidManifest.xml检测
if(method.getName().equals("startActivity")){
// 1.首先获取原来的Intent
Intent originIntent = (Intent) args[2];
// 2.创建一个安全的Intent,即代理activity的intent
Intent safeIntent = new Intent(mContext, ProxyActivity.class);
args[2] = safeIntent;
// 3.绑定原来的真正的Intent
safeIntent.putExtra(EXTRA_ORIGIN_INTENT,originIntent);
}
return method.invoke(finalIamInstance,args);
}
});
// 3.重新指定
mInstanceField.set(gDefault,iamInstance);
}
}