什么是插件化開發(fā)
宿主app+插件app的模式,一個(gè)宿主可以有多個(gè)插件,根據(jù)不同業(yè)務(wù)需求,動(dòng)態(tài)更新替換插件.
很多大廠都出了自己的插件化框架,我們?cè)谑褂玫耐瑫r(shí),也需要簡(jiǎn)單了解其原理
需要解決的第一個(gè)問題
插件化開發(fā)需要解決的第一個(gè)難題,就是四大組件如何繞過清單文件配置檢測(cè),我們已a(bǔ)ctivity為例,舉個(gè)栗子,學(xué)習(xí)插件化開發(fā)之前,我們需要了解activity的啟動(dòng)流程,請(qǐng)參看源碼.我們的思路是在清單文件中注冊(cè)一個(gè)代理activity,通過它繞過檢測(cè)機(jī)制,最終替換它為真正需要加載的activity,這里需要用到hook技術(shù)
什么是hook
hook的意思是勾住。比如A發(fā)送消息給B,在消息過去之前,可以先把消息勾住,不讓其傳遞,你可以先執(zhí)行你的操作(或者說先執(zhí)行你的鉤子函數(shù))。
專業(yè)的說法就是:hook技術(shù),能夠改變API執(zhí)行的結(jié)果,將系統(tǒng)的API函數(shù)執(zhí)行重定向。
為什么需要hook
android系統(tǒng)的沙箱機(jī)制使我們不能通過一個(gè)程序改變其他程序的某些行為,但是hook技術(shù)正好可以解決此類問題。沙箱是一個(gè)虛擬系統(tǒng)程序,沙箱提供的環(huán)境相對(duì)于每一個(gè)運(yùn)行的程序的進(jìn)程空間都是獨(dú)立的,程序的運(yùn)行互不干擾,而且不會(huì)對(duì)現(xiàn)有的系統(tǒng)產(chǎn)生影響。
需要注意的問題
hook的源碼版本,與手機(jī)的系統(tǒng)版本有關(guān),也就是說,手機(jī)的android系統(tǒng)是什么版本的,就需要hook什么版本你的代碼,不同版本之間的源碼是有區(qū)別的,簡(jiǎn)單說來(lái),本例適配了6.0以上源碼
舉個(gè)栗子
先從啟動(dòng)activity開始吧,了解activity啟動(dòng)流程的童鞋對(duì)如下代碼肯定不陌生,它是啟動(dòng)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);
真正啟動(dòng)activity的是IActivityManager,基于接口,我們利用動(dòng)態(tài)代理來(lái)hook,我們看到,它是一個(gè)隱藏的類,所以我們只能通過反射來(lái)實(shí)例化對(duì)象
/**
* 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實(shí)例
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設(shè)置CallBack回掉,也只能通過發(fā)射
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");
// 每發(fā)一個(gè)消息都會(huì)走一次這個(gè)CallBack發(fā)放
if(msg.what == 100){
handleLaunchActivity(msg);
}
return false;
}
/**
* 開始啟動(dòng)創(chuàng)建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中獲取原來(lái)的originIntent
Intent originIntent = safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT);
// 3.重新設(shè)置回去
if(originIntent != null){
intentField.set(record,originIntent);
}
// 兼容AppCompatActivity報(bào)錯(cuò)問題
Class<?> forName = Class.forName("android.app.ActivityThread");
Field field = forName.getDeclaredField("sCurrentActivityThread");
field.setAccessible(true);
Object activityThread = field.get(null);
// 我自己執(zhí)行一次那么就會(huì)創(chuàng)建PackageManager,系統(tǒng)再獲取的時(shí)候就是下面的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 需要判斷手機(jī)系統(tǒng)的版本,如果小于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");
}
// 設(shè)置權(quán)限
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 必須執(zhí)行者,誰(shuí)去執(zhí)行iamInstance
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e(TAG,method.getName());
// 替換Intent,過AndroidManifest.xml檢測(cè)
if(method.getName().equals("startActivity")){
// 1.首先獲取原來(lái)的Intent
Intent originIntent = (Intent) args[2];
// 2.創(chuàng)建一個(gè)安全的Intent,即代理activity的intent
Intent safeIntent = new Intent(mContext, ProxyActivity.class);
args[2] = safeIntent;
// 3.綁定原來(lái)的真正的Intent
safeIntent.putExtra(EXTRA_ORIGIN_INTENT,originIntent);
}
return method.invoke(finalIamInstance,args);
}
});
// 3.重新指定
mInstanceField.set(gDefault,iamInstance);
}
}