Java高級之注解、反射


Java的注解、反射等機制的產生,讓動態代理成為可能,一般通過全限定名+類名,找到類,可以invoke它的構造方法以及其他方法,可以獲取它的參數(Field)名稱和值。

注解一般用在代碼的注釋上、代碼審查上(有沒有按標准寫,比如inspect)、代碼注入(hook,asbectj),需要考慮的是,在何時注入(編譯期還運行期)

反射一般用在動態將json和Object互相轉化,執行相關底層代碼,比如設置某個類的Accessible為false,防止別人hook修改

例:阿里的FastJson解析:

 

@Override public <T> T json2Object(String json, Class<T> clazz) {
return JSON.parseObject(json, clazz);
}
@Override public String object2Json(Object instance) {
return JSON.toJSONString(instance);
}

 

 

例Java的默認注解策略:

 

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
	默認,編譯時被拋棄
     */
SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
	默認被編譯器保解釋,但在運行時拋棄
     */
CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
       被編譯時解釋,運行時仍保存,可以直接被使用
     * @see java.lang.reflect.AnnotatedElement
     */
RUNTIME
}
 
        
例Hook:

 

 

hook一事看似神秘,其實並不是那么難,希望各位看官看過本文之后能有所收獲。

本次是hook Android的點擊事件,也就是OnClickListener,hook的意義在於你能在調用setOnClickListener后做些其他的事,其他一些你想和所有點擊事件一起處理的事,那么在這里,我就以埋點為例吧。

先來展示下效果:

public void onClick(View view) { Map map = new HashMap(); switch (view.getId()) { case R.id.btn_hook1: map.put("巴", "掌"); map.put("菜", "比"); break; case R.id.btn_hook2: map.put("TF-Boys", "嘿嘿嘿"); map.put("id", "111"); break; } view.setTag(R.id.id_hook, map); }

我在onClick內干了三件事:

1、new HashMap

2、map塞你想埋點的數據

3、把數據傳到對應的view里

然后點擊按鈕會彈出一個Toast,如下圖:

 

hook
 

 

那么有意思的地方來了,我們並沒有在點擊事件里彈Toast,那這個Toast哪來的呢?嘿嘿嘿,當然是hook的啦。

Hook

下面開始hook過程:

整個過程濃縮下來就是四個字--移花接木!

分析源代碼

 

static class
 

 

首先來看看android.view.View中的這塊代碼,mOnClickListener變量靜靜的在這里(這里還有別的事件哦,比如OnLongClickListener等,大家學完之后可以試着hook下別的),我們需要做的就是移花接木,把自己的花替換掉這個木,mOnClickListener是ListenerInfo這個類的成員變量,那繼續看看ListenerInfo在View的哪里被初始化了,因為我們最開始拿到的只有View這一個對象。

 

ListenerInfo
 

 

沒錯,找到了,getListenerInfo()干了這件事,我們從這個方法入手先把ListenerInfo拿下,然后再移花接木。

技術方案已經有了,那么就開始着手擼碼。

實現

hook的過程就是充分利用java反射機制的過程,幾行代碼搞定,我們來看看:

//先拿下View的Class對象 Class clazzView = Class.forName("android.view.View"); //再把getListenerInfo拿到 Method method = clazzView.getDeclaredMethod("getListenerInfo"); //由於getListenerInfo並不是pulic方法,所以需要修改為可訪問 method.setAccessible(true); //繼續拿下ListenerInfo內部類的Class對象 Class clazzInfo = Class.forName("android.view.View$ListenerInfo"); //拿到主角mOnClickListener成員變量 Field field = clazzInfo.getDeclaredField("mOnClickListener"); //截止到這,我們已經完成了百分之95了,只剩最后一步,那就是把我們的木接進來 //那么這里先暫時停留下,我們把木給創建好。 //挖個坑 --> 待會填

由於移花接木有個本質不能忘,那就是尊重原有類型,因此,我們的木也得實現View.OnClickListener接口:

public static class HookListener implements View.OnClickListener { private View.OnClickListener mOriginalListener; //直接在構造函數中傳進來原來的OnClickListener public HookListener(View.OnClickListener originalListener) { mOriginalListener = originalListener; } @Override public void onClick(View v) { if (mOriginalListener != null) { mOriginalListener.onClick(v); } StringBuilder sb = new StringBuilder(); sb.append("hook succeed.\n"); //拿到之前傳遞的參數 Object obj = v.getTag(R.id.id_hook); //下面的操作可以猥瑣欲為了 if (obj != null && obj instanceof HashMap && !((Map) obj).isEmpty()) { for (Map.Entry<String, String> entry : ((Map<String, String>) obj).entrySet()) { sb.append("key => ") .append(entry.getKey()) .append(" ") .append("value => ") .append(entry.getValue()) .append("\n"); } } else { sb.append("params => null\n"); } Toast.makeText(v.getContext(), sb.toString(), Toast.LENGTH_LONG).show(); } }

以上代碼就是我們的木,為了看起來更簡單,我直接通過構造函數把原來的花(OnClickListener)給傳過來了,然后在新的HookListener的onClick()里把原來的事件繼續完成,並加上自己想猥瑣欲為的一些事情。

那么繼續填上之前埋的坑:

field.set(listenerInfo, new HookListener((View.OnClickListener) field.get(listenerInfo)));

接木的過程干了兩件事,一個是把原有的OnClickListener傳給HookListener,二是把新的HookListener替換進ListenerInfo,perfect。

至此,移花接木就完成了,簡單吧。

合適的調用hook

我們把hook方法都寫好了,最后就是調用你需要hook的View了,在大多數情況下,你可以把hook這件事交給Base去做,遍歷當前rootView所有的View,然后每個都調用hook,本文的重點不是這,我就不贅述了。

小結

本文僅僅以埋點為例,= = 其實我覺得埋點這個栗子並不太好,妹的都傳了這么多參數過來了,還在乎在這里調用一下自己的tracker?不管了,沒有栗子會讓本次hook感覺很無力,希望各位同學看過后能對hook不再懵逼,其實和自定義View一樣簡單的啦。

Sample代碼已同步到github上,有問題可以提issue => https://github.com/JeasonWong/ClickTracker

http://nanning.xjwy.cn/f/bencandy.php?fid=43&id=117777
http://nanning.xjwy.cn/f/bencandy.php?fid=43&id=117890
http://nanning.xjwy.cn/f/bencandy.php?fid=43&id=117994
http://nanning.xjwy.cn/f/bencandy.php?fid=43&id=118376

http://www.woaipu.com/shops/zuzhuan/61406


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM