重點教你如何應用
- 先回顧一下代理模式
- 使用場景1:動態代理+反射 實作hook技術
- AOP(面向切面編程)
先回顧一下代理模式
代理模式分動態代理和靜態代理,寫起來也比較簡單,先上代碼:
public interface Goal {
void sayHello();//定義一個介面,宣告好要做的事兒
}
然后實作他,真實的目的
public class RealGoal implements Goal {
@Override
public void sayHello() {
System.out.println("hello! think you very much!");
}
}
這時候我們想列印hello! think you very much!,只需要 new RealGoal().sayHello(); 這樣只執行代碼就可以,但是既然是代理模式,肯定不能這樣親自動手,要找個代理人執行,所以還要創建個代理人
public class ProxyGoal implements Goal {
private Goal goal;
public ProxyGoal(Goal goal) {
this.goal = goal;
}
@Override
public void sayHello() {
goal.sayHello();
}
}
如何具體應用呢?
public class Test {
public static void main(String[] args) {
Goal realGoal = new RealGoal();
Goal proxyGoal = new ProxyGoal(realGoal);
proxyGoal.sayHello();
}
}
執行結果:

**剛開始學的時候,不知道你們有沒有和我一樣,有那種脫褲子放屁多此一舉的感覺,既然我們想執行sayHello(),直接用realGoal.sayHello()就完了唄,為什么還要搞一個代理類呢????????**在回答這個問題之前,再來看看動態代理怎么寫!
public class GoalHandler implements InvocationHandler {
Object object;
public GoalHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("I'm Proxy, I'm invoking...");
method.invoke(object, args);
System.out.println("invoke end!");
return null;
}
}
執行測驗
public static void main(String[] args) {
Goal realGoal = new RealGoal();
InvocationHandler goalHandler = new GoalHandler(realGoal);
Goal dynamicProxyGoal = (Goal) Proxy.newProxyInstance(realGoal.getClass().getClassLoader(), realGoal.getClass().getInterfaces(), goalHandler);
dynamicProxyGoal.sayHello();
}
動態代理其實也一樣,也多此一舉,所以一定要結合使用場景,設計模式才有意義,這個代理模式很簡單吧,顧名思義,多出來一個代理人,相當于你從基層員工直接變領導了,比如你想發個快遞,本來你要親自跟快遞員打交道,但是代理模式讓你擁有了一個小弟(proxy),有什么事兒你就叫你小弟去辦理,然后你小弟再去找快遞;當然這個事兒還可以是買煙,具體什么事兒無所謂,重點是多了一個小弟,而且這個小弟完全在你的掌握之中.
使用場景1:動態代理+反射 實作hook技術
hook技術很簡單,所謂hook點有點找后門的意思,只要找到hook點,就可以,現在模擬一個場景,android里面有個ActivityManagerNative,這個物件,他是和AMS進行通信的物件,所有的startActivity方法最后都要經過它里面的binder物件發送給AMS,這部分沒看過原始碼的同學可能不懂,不過不要緊這不是重點;重點是這些類 這些物件正常情況下都是遙不可及的,我們無法直接使用他們;但是通過動態代理+反射就可以,這時候我們就可以走后門了,我們可以監聽到startActvity的一些資訊,從而做自己的手腳,先來寫個小demo
代碼結構很簡單,system下代表系統級別的類,我們正常開發是無法使用的;我們能使用的只有應用層的Activity如圖

Activity就是模擬我們android里的android.app.Activity 先來看結構,system下
public interface IActivityManager {
int startActivity(String intent);
}
public class ActivityManagerNative {
public static IActivityManager binder = new ActivityManagerImpl();
public static IActivityManager gDefault() {
return binder;
}
}
public class Log {
public static void i(String tag, String value) {
System.out.println(tag+" : "+value);
}
}
public class ActivityManagerImpl implements IActivityManager {
@Override
public int startActivity(String intent) {
System.out.println("我是AMS,將要啟動activity物件是: "+intent);
return 0;
}
}
再來看application包下的activity
public class Activity {
public void statActivity(String i) {
ActivityManagerNative.gDefault().startActivity(i);
}
}
上面這些都是已經存在的類,我們這時候自定義一個activity,并且執行startActvity方法,告訴系統我們要啟動的Activity是哪個
public class MainActivity extends Activity {
public static void main(String[] args) {
MainActivity mainActivity = new MainActivity();
mainActivity.statActivity("MainActivity");
}
}
執行結果:
那么問題來了!,我要在不影響正常開發的時候,我要對運行結果進行更改如何做?接著寫一個類,里面包含了一個邏輯,檢測要啟動的activity是否包含NeedLogin,如果包含NeedLogin則直接列印LoginActivity,如果不包含就正常列印,不做改變
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class HookUtil {
private static final String TAG = "HookUtil";
/**
* 獲取IActivityManager物件 將其替換掉
* 這里需要動態代理;
* 動態代理也是個時髦的技術;
*/
public void hookActivityStart() {
try {
//獲取ActivityManager類
Class<?> activityManagerClass = Class.forName("com.example.myapplication.system.ActivityManagerNative");
//獲取其IActivityManagerSingleton屬性
Field iActivityManagerSingleton = activityManagerClass.getDeclaredField("binder");
iActivityManagerSingleton.setAccessible(true);
Object binder = iActivityManagerSingleton.get(activityManagerClass);
HookHandler hookHandler = new HookHandler(binder);
Object proxyInstance = Proxy.newProxyInstance(binder.getClass().getClassLoader(), binder.getClass().getInterfaces(), hookHandler);
//proxyInstance代理IActivityManager
iActivityManagerSingleton.set(activityManagerClass, proxyInstance);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
class HookHandler implements InvocationHandler {
private Object object;
public HookHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//攔截startActivity()方法
String name = method.getName();
Log.i(TAG, "呼叫" + name + "方法了");
if ("startActivity".contains(name)) {
// Log.i(TAG, "呼叫startActivity方法了");
//獲取intent引數
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg instanceof String) {
if(((String) arg).contains("NeedLogin"))//此處判斷邏輯,如果里面包含NeedLogin,則跳轉LoginActivity,否則就正常執行
args[i] = "LoginActivity";
}
}
}
return method.invoke(object, args);
}
}
}
然后我們寫測驗的時候這樣寫
public class MainActivity extends Activity {
public static void main(String[] args) {
HookUtil hookUtil = new HookUtil();
hookUtil.hookActivityStart();
MainActivity mainActivity = new MainActivity();
mainActivity.statActivity("MainActivity");
mainActivity.statActivity("MineActivityNeedLogin");
}
}
執行結果:

我們可以看,字串里包含NeedLogin的執行結果集完全變了,這就是典型的動態代理+反射 實作的hook結束、一般插件化、熱修復或者某些場景都需要用到,
AOP(面向切面編程)
AOP的全稱是Aspect-Oriented Programming,即面向切面編程(也稱面向方面編程),它是面向物件編程(OOP)的一種補充,目前已成為一種比較成熟的編程方式,就是一種編程思想,代碼注入是AOP中的重要部分:AOP可用于日志埋點、性能監控、動態權限控制、甚至是代碼除錯等等,實作AOP的一個方式就有動態代理的模式,未完待續…
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/263860.html
標籤:其他
