日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下)

發布時間:2023/12/15 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上一篇博客我們已經帶大家簡單的吹了一下IoC,實現了Activity中View的布局以及控件的注入,如果你不了解,請參考:Android 進階 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)。

本篇博客將帶大家實現View的事件的注入。

1、目標效果

上篇博客,我們的事件的代碼是這么寫的:

[java]?view plaincopy
  • package?com.zhy.zhy_xutils_test;??
  • ??
  • import?android.app.Activity;??
  • import?android.os.Bundle;??
  • import?android.view.View;??
  • import?android.view.View.OnClickListener;??
  • import?android.widget.Button;??
  • import?android.widget.Toast;??
  • ??
  • import?com.zhy.ioc.view.ViewInjectUtils;??
  • import?com.zhy.ioc.view.annotation.ContentView;??
  • import?com.zhy.ioc.view.annotation.ViewInject;??
  • ??
  • @ContentView(value?=?R.layout.activity_main)??
  • public?class?MainActivity?extends?Activity?implements?OnClickListener??
  • {??
  • ????@ViewInject(R.id.id_btn)??
  • ????private?Button?mBtn1;??
  • ????@ViewInject(R.id.id_btn02)??
  • ????private?Button?mBtn2;??
  • ??
  • ????@Override??
  • ????protected?void?onCreate(Bundle?savedInstanceState)??
  • ????{??
  • ????????super.onCreate(savedInstanceState);??
  • ??????????
  • ????????ViewInjectUtils.inject(this);??
  • ??
  • ????????mBtn1.setOnClickListener(this);??
  • ????????mBtn2.setOnClickListener(this);??
  • ????}??
  • ??
  • ????@Override??
  • ????public?void?onClick(View?v)??
  • ????{??
  • ????????switch?(v.getId())??
  • ????????{??
  • ????????case?R.id.id_btn:??
  • ????????????Toast.makeText(MainActivity.this,?"Why?do?you?click?me??",??
  • ????????????????????Toast.LENGTH_SHORT).show();??
  • ????????????break;??
  • ??
  • ????????case?R.id.id_btn02:??
  • ????????????Toast.makeText(MainActivity.this,?"I?am?sleeping?!!!",??
  • ????????????????????Toast.LENGTH_SHORT).show();??
  • ????????????break;??
  • ????????}??
  • ????}??
  • ??
  • }??

  • 光有View的注入能行么,我們寫View的目的,很多是用來交互的,得可以點擊神馬的吧。摒棄傳統的神馬,setOnClickListener,然后實現匿名類或者別的方式神馬的,我們改變為:

    [java]?view plaincopy
  • package?com.zhy.zhy_xutils_test;??
  • ??
  • import?android.view.View;??
  • import?android.widget.Button;??
  • import?android.widget.Toast;??
  • ??
  • import?com.zhy.ioc.view.annotation.ContentView;??
  • import?com.zhy.ioc.view.annotation.OnClick;??
  • import?com.zhy.ioc.view.annotation.ViewInject;??
  • ??
  • @ContentView(value?=?R.layout.activity_main)??
  • public?class?MainActivity?extends?BaseActivity??
  • {??
  • ????@ViewInject(R.id.id_btn)??
  • ????private?Button?mBtn1;??
  • ????@ViewInject(R.id.id_btn02)??
  • ????private?Button?mBtn2;??
  • ??
  • ????@OnClick({?R.id.id_btn,?R.id.id_btn02?})??
  • ????public?void?clickBtnInvoked(View?view)??
  • ????{??
  • ????????switch?(view.getId())??
  • ????????{??
  • ????????case?R.id.id_btn:??
  • ????????????Toast.makeText(this,?"Inject?Btn01?!",?Toast.LENGTH_SHORT).show();??
  • ????????????break;??
  • ????????case?R.id.id_btn02:??
  • ????????????Toast.makeText(this,?"Inject?Btn02?!",?Toast.LENGTH_SHORT).show();??
  • ????????????break;??
  • ????????}??
  • ????}??
  • ??
  • }??

  • 直接通過在Activity中的任何一個方法上,添加注解,完成1個或多個控件的事件的注入。這里我把onCreate搬到了BaseActivity中,里面調用了ViewInjectUtils.inject(this);

    2、實現

    1、注解文件

    [java]?view plaincopy
  • package?com.zhy.ioc.view.annotation;??
  • ??
  • import?java.lang.annotation.ElementType;??
  • import?java.lang.annotation.Retention;??
  • import?java.lang.annotation.RetentionPolicy;??
  • import?java.lang.annotation.Target;??
  • ??
  • @Target(ElementType.ANNOTATION_TYPE)??
  • @Retention(RetentionPolicy.RUNTIME)??
  • public?@interface?EventBase??
  • {??
  • ????Class<?>?listenerType();??
  • ??
  • ????String?listenerSetter();??
  • ??
  • ????String?methodName();??
  • }??

  • [java]?view plaincopy
  • package?com.zhy.ioc.view.annotation;??
  • ??
  • import?java.lang.annotation.ElementType;??
  • import?java.lang.annotation.Retention;??
  • import?java.lang.annotation.RetentionPolicy;??
  • import?java.lang.annotation.Target;??
  • ??
  • import?android.view.View;??
  • ??
  • @Target(ElementType.METHOD)??
  • @Retention(RetentionPolicy.RUNTIME)??
  • @EventBase(listenerType?=?View.OnClickListener.class,?listenerSetter?=?"setOnClickListener",?methodName?=?"onClick")??
  • public?@interface?OnClick??
  • {??
  • ????int[]?value();??
  • }??

  • EventBase主要用于給OnClick這類注解上添加注解,畢竟事件很多,并且設置監聽器的名稱,監聽器的類型,調用的方法名都是固定的,對應上面代碼的:

    listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick"

    Onclick是用于寫在Activity的某個方法上的:

    [java]?view plaincopy
  • @OnClick({?R.id.id_btn,?R.id.id_btn02?})??
  • ????public?void?clickBtnInvoked(View?view)??

  • 如果你還記得,上篇博客我們的ViewInjectUtils.inject(this);里面已經有了兩個方法,本篇多了一個:

    [java]?view plaincopy
  • public?static?void?inject(Activity?activity)??
  • ????{??
  • ????????injectContentView(activity);??
  • ????????injectViews(activity);??
  • ????????injectEvents(activity);??
  • ????}??

  • 2、injectEvents

    [java]?view plaincopy
  • /**?
  • ?????*?注入所有的事件?
  • ?????*??
  • ?????*?@param?activity?
  • ?????*/??
  • ????private?static?void?injectEvents(Activity?activity)??
  • ????{??
  • ??????????
  • ????????Class<??extends?Activity>?clazz?=?activity.getClass();??
  • ????????Method[]?methods?=?clazz.getMethods();??
  • ????????//遍歷所有的方法??
  • ????????for?(Method?method?:?methods)??
  • ????????{??
  • ????????????Annotation[]?annotations?=?method.getAnnotations();??
  • ????????????//拿到方法上的所有的注解??
  • ????????????for?(Annotation?annotation?:?annotations)??
  • ????????????{??
  • ????????????????Class<??extends?Annotation>?annotationType?=?annotation??
  • ????????????????????????.annotationType();??
  • ????????????????//拿到注解上的注解??
  • ????????????????EventBase?eventBaseAnnotation?=?annotationType??
  • ????????????????????????.getAnnotation(EventBase.class);??
  • ????????????????//如果設置為EventBase??
  • ????????????????if?(eventBaseAnnotation?!=?null)??
  • ????????????????{??
  • ????????????????????//取出設置監聽器的名稱,監聽器的類型,調用的方法名??
  • ????????????????????String?listenerSetter?=?eventBaseAnnotation??
  • ????????????????????????????.listenerSetter();??
  • ????????????????????Class<?>?listenerType?=?eventBaseAnnotation.listenerType();??
  • ????????????????????String?methodName?=?eventBaseAnnotation.methodName();??
  • ??
  • ????????????????????try??
  • ????????????????????{??
  • ????????????????????????//拿到Onclick注解中的value方法??
  • ????????????????????????Method?aMethod?=?annotationType??
  • ????????????????????????????????.getDeclaredMethod("value");??
  • ????????????????????????//取出所有的viewId??
  • ????????????????????????int[]?viewIds?=?(int[])?aMethod??
  • ????????????????????????????????.invoke(annotation,?null);??
  • ????????????????????????//通過InvocationHandler設置代理??
  • ????????????????????????DynamicHandler?handler?=?new?DynamicHandler(activity);??
  • ????????????????????????handler.addMethod(methodName,?method);??
  • ????????????????????????Object?listener?=?Proxy.newProxyInstance(??
  • ????????????????????????????????listenerType.getClassLoader(),??
  • ????????????????????????????????new?Class<?>[]?{?listenerType?},?handler);??
  • ????????????????????????//遍歷所有的View,設置事件??
  • ????????????????????????for?(int?viewId?:?viewIds)??
  • ????????????????????????{??
  • ????????????????????????????View?view?=?activity.findViewById(viewId);??
  • ????????????????????????????Method?setEventListenerMethod?=?view.getClass()??
  • ????????????????????????????????????.getMethod(listenerSetter,?listenerType);??
  • ????????????????????????????setEventListenerMethod.invoke(view,?listener);??
  • ????????????????????????}??
  • ??
  • ????????????????????}?catch?(Exception?e)??
  • ????????????????????{??
  • ????????????????????????e.printStackTrace();??
  • ????????????????????}??
  • ????????????????}??
  • ??
  • ????????????}??
  • ????????}??
  • ??
  • ????}??

  • 嗯,注釋盡可能的詳細了,主要就是遍歷所有的方法,拿到該方法省的OnClick注解,然后再拿到該注解上的EventBase注解,得到事件監聽的需要調用的方法名,類型,和需要調用的方法的名稱;通過Proxy和InvocationHandler得到監聽器的代理對象,顯示設置了方法,最后通過反射設置監聽器。

    這里有個難點,就是關于DynamicHandler和Proxy的出現,如果不理解沒事,后面會詳細講解。

    3、DynamicHandler

    這里用到了一個類DynamicHandler,就是InvocationHandler的實現類:

    [java]?view plaincopy
  • package?com.zhy.ioc.view;??
  • ??
  • import?java.lang.ref.WeakReference;??
  • import?java.lang.reflect.InvocationHandler;??
  • import?java.lang.reflect.Method;??
  • import?java.util.HashMap;??
  • ??
  • public?class?DynamicHandler?implements?InvocationHandler??
  • {??
  • ????private?WeakReference<Object>?handlerRef;??
  • ????private?final?HashMap<String,?Method>?methodMap?=?new?HashMap<String,?Method>(??
  • ????????????1);??
  • ??
  • ????public?DynamicHandler(Object?handler)??
  • ????{??
  • ????????this.handlerRef?=?new?WeakReference<Object>(handler);??
  • ????}??
  • ??
  • ????public?void?addMethod(String?name,?Method?method)??
  • ????{??
  • ????????methodMap.put(name,?method);??
  • ????}??
  • ??
  • ????public?Object?getHandler()??
  • ????{??
  • ????????return?handlerRef.get();??
  • ????}??
  • ??
  • ????public?void?setHandler(Object?handler)??
  • ????{??
  • ????????this.handlerRef?=?new?WeakReference<Object>(handler);??
  • ????}??
  • ??
  • ????@Override??
  • ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)??
  • ????????????throws?Throwable??
  • ????{??
  • ????????Object?handler?=?handlerRef.get();??
  • ????????if?(handler?!=?null)??
  • ????????{??
  • ????????????String?methodName?=?method.getName();??
  • ????????????method?=?methodMap.get(methodName);??
  • ????????????if?(method?!=?null)??
  • ????????????{??
  • ????????????????return?method.invoke(handler,?args);??
  • ????????????}??
  • ????????}??
  • ????????return?null;??
  • ????}??
  • }??
  • 好了,代碼就這么多,這樣我們就實現了,我們事件的注入~~

    效果圖:


    效果圖其實沒撒好貼的,都一樣~~~

    3、關于代理

    那么,本文結束了么,沒有~~~關于以下幾行代碼,相信大家肯定有困惑,這幾行干了什么?

    [java]?view plaincopy
  • //通過InvocationHandler設置代理??
  • ????????????????????????DynamicHandler?handler?=?new?DynamicHandler(activity);??
  • ????????????????????????handler.addMethod(methodName,?method);??
  • ????????????????????????Object?listener?=?Proxy.newProxyInstance(??
  • ????????????????????????????????listenerType.getClassLoader(),??
  • ????????????????????????????????new?Class<?>[]?{?listenerType?},?handler);??

  • InvocationHandler和Proxy成對出現,相信大家如果對Java比較熟悉,肯定會想到Java的動態代理~~~

    關于InvocationHandler和Proxy的文章,大家可以參考:http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/?ps:IBM的技術文章還是相當不錯的,畢竟有人審核還有獎金~

    但是我們的實現有一定的區別,我為什么說大家疑惑呢,比如反射實現:

    mBtn2.setOnClickListener(this);這樣的代碼,難點在哪呢?

    1、mBtn2的獲取?so easy?

    2、調用setOnClickListener ? so easy?

    but , 這個 this,這個this是OnClickListener的實現類的實例,OnClickListener是個接口~~你的實現類怎么整,聽說過反射newInstance對象的,但是你現在是接口!

    是吧~現在應該明白上述幾行代碼做了什么了?實現了接口的一個代理對象,然后在代理類的invoke中,對接口的調用方法進行處理。

    4、代碼是最好的老師

    光說誰都理解不了,你在這xx什么呢??下面看代碼,我們模擬實現這樣一個情景:

    Main類中實現一個Button,Button有兩個方法,一個setOnClickListener和onClick,當調用Button的onClick時,觸發的事件是Main類中的click方法

    涉及到4個類:

    Button

    [java]?view plaincopy
  • package?com.zhy.invocationhandler;??
  • ??
  • public?class?Button??
  • {??
  • ????private?OnClickListener?listener;??
  • ??
  • ????public?void?setOnClickLisntener(OnClickListener?listener)??
  • ????{??
  • ??
  • ????????this.listener?=?listener;??
  • ????}??
  • ??
  • ????public?void?click()??
  • ????{??
  • ????????if?(listener?!=?null)??
  • ????????{??
  • ????????????listener.onClick();??
  • ????????}??
  • ????}??
  • }??

  • OnClickListener接口

    [java]?view plaincopy
  • package?com.zhy.invocationhandler;??
  • ??
  • public?interface?OnClickListener??
  • {??
  • ????void?onClick();??
  • }??

  • OnClickListenerHandler , InvocationHandler的實現類

    [java]?view plaincopy
  • package?com.zhy.invocationhandler;??
  • ??
  • import?java.lang.reflect.InvocationHandler;??
  • import?java.lang.reflect.Method;??
  • import?java.util.HashMap;??
  • import?java.util.Map;??
  • ??
  • public?class?OnClickListenerHandler?implements?InvocationHandler??
  • {??
  • ????private?Object?targetObject;??
  • ??
  • ????public?OnClickListenerHandler(Object?object)??
  • ????{??
  • ????????this.targetObject?=?object;??
  • ????}??
  • ??
  • ????private?Map<String,?Method>?methods?=?new?HashMap<String,?Method>();??
  • ??
  • ????public?void?addMethod(String?methodName,?Method?method)??
  • ????{??
  • ????????methods.put(methodName,?method);??
  • ????}??
  • ??
  • ????@Override??
  • ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)??
  • ????????????throws?Throwable??
  • ????{??
  • ??
  • ????????String?methodName?=?method.getName();??
  • ????????Method?realMethod?=?methods.get(methodName);??
  • ????????return?realMethod.invoke(targetObject,?args);??
  • ????}??
  • ??
  • }??

  • 我們的Main

    [java]?view plaincopy
  • package?com.zhy.invocationhandler;??
  • ??
  • import?java.lang.reflect.InvocationTargetException;??
  • import?java.lang.reflect.Method;??
  • import?java.lang.reflect.Proxy;??
  • ??
  • public?class?Main??
  • {??
  • ????private?Button?button?=?new?Button();??
  • ??????
  • ????public?Main()?throws?SecurityException,?IllegalArgumentException,?NoSuchMethodException,?IllegalAccessException,?InvocationTargetException??
  • ????{??
  • ????????init();??
  • ????}??
  • ??
  • ????public?void?click()??
  • ????{??
  • ????????System.out.println("Button?clicked!");??
  • ????}??
  • ??
  • ????public?void?init()?throws?SecurityException,??
  • ????????????NoSuchMethodException,?IllegalArgumentException,??
  • ????????????IllegalAccessException,?InvocationTargetException??
  • ????{??
  • ????????OnClickListenerHandler?h?=?new?OnClickListenerHandler(this);??
  • ????????Method?method?=?Main.class.getMethod("click",?null);??
  • ????????h.addMethod("onClick",?method);??
  • ????????Object?clickProxy?=?Proxy.newProxyInstance(??
  • ????????????????OnClickListener.class.getClassLoader(),??
  • ????????????????new?Class<?>[]?{?OnClickListener.class?},?h);??
  • ????????Method?clickMethod?=?button.getClass().getMethod("setOnClickLisntener",??
  • ????????????????OnClickListener.class);??
  • ????????clickMethod.invoke(button,?clickProxy);??
  • ??????????
  • ????}??
  • ??
  • ????public?static?void?main(String[]?args)?throws?SecurityException,??
  • ????????????IllegalArgumentException,?NoSuchMethodException,??
  • ????????????IllegalAccessException,?InvocationTargetException??
  • ????{??
  • ??
  • ????????Main?main?=?new?Main();??
  • ??????????
  • ????????main.button.click();??
  • ????}??
  • ??
  • }??

  • 我們模擬按鈕點擊:調用main.button.click(),實際執行的卻是Main的click方法。

    看init中,我們首先初始化了一個OnClickListenerHandler,把Main的當前實例傳入,然后拿到Main的click方法,添加到OnClickListenerHandler中的Map中。

    然后通過Proxy.newProxyInstance拿到OnClickListener這個接口的一個代理,這樣執行這個接口的所有的方法,都會去調用OnClickListenerHandler的invoke方法。

    但是呢?OnClickListener畢竟是個接口,也沒有方法體~~那咋辦呢?這時候就到我們OnClickListenerHandler中的Map中大展伸手了:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable
    {

    String methodName = method.getName();
    Method realMethod = methods.get(methodName);
    return realMethod.invoke(targetObject, args);
    }

    我們顯示的把要執行的方法,通過鍵值對存到Map里面了,等調用到invoke的時候,其實是通過傳入的方法名,得到Map中存儲的方法,然后調用我們預設的方法~。

    這樣,大家應該明白了,其實就是通過Proxy得到接口的一個代理,然后在InvocationHandler中使用一個Map預先設置方法,從而實現Button的onClick,和Main的click關聯上。

    現在看我們InjectEvents中的代碼:

    [java]?view plaincopy
  • //通過InvocationHandler設置代理??
  • ????????????????????????DynamicHandler?handler?=?new?DynamicHandler(activity);??
  • ????????????????????????//往map添加方法??
  • ????????????????????????handler.addMethod(methodName,?method);??
  • ????????????????????????Object?listener?=?Proxy.newProxyInstance(??
  • ????????????????????????????????listenerType.getClassLoader(),??
  • ????????????????????????????????new?Class<?>[]?{?listenerType?},?handler);??

  • 是不是和我們init中的類似~~

    好了,關于如何把接口的回調和我們Activity里面的方法關聯上我們也解釋完了~~~


    注:部分代碼參考了xUtils這個框架,畢竟想很完善的實現一個完整的注入不是一兩篇博客就可以搞定,但是核心和骨架已經實現了~~大家有興趣的可以繼續去完善~




    源碼點擊下載


    總結

    以上是生活随笔為你收集整理的Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。