轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/lmj623565791/article/details/39269193,本文出自:【張鴻洋的博客】
1、概述
首先我們來吹吹牛,什么叫IoC,控制反轉(zhuǎn)(Inversion of Control,英文縮寫為IoC),什么意思呢?
就是你一個類里面需要用到很多個成員變量,傳統(tǒng)的寫法,你要用這些成員變量,那么你就new 出來用唄~~
IoC的原則是:NO,我們不要new,這樣耦合度太高;你配置個xml文件,里面標(biāo)明哪個類,里面用了哪些成員變量,等待加載這個類的時候,我?guī)湍阕⑷?#xff08;new)進(jìn)去;
這樣做有什么好處呢?
回答這個問題,剛好可以回答另一個問題,很多人問,項目分層開發(fā)是吧,分為控制層、業(yè)務(wù)層、DAO層神馬的。然后每一層為撒子要一個包放接口,一個包放實(shí)現(xiàn)呢?只要一個實(shí)現(xiàn)包不行么~剛好,如果你了解了IoC,你就知道這些個接口的作用了,上面不是說,你不用new,你只要聲明了成員變量+寫個配置文件,有人幫你new;此時,你在類中,就可以把需要使用到的成員變量都聲明成接口,然后你會發(fā)現(xiàn),當(dāng)實(shí)現(xiàn)類發(fā)生變化的時候,或者切換實(shí)現(xiàn)類,你需要做什么呢?你只要在配置文件里面做個簡單的修改。如果你用的就是實(shí)實(shí)在在的實(shí)現(xiàn)類,現(xiàn)在換實(shí)現(xiàn)類,你需要找到所有聲明這個實(shí)現(xiàn)類的地方,手動修改類名;如果你遇到了一個多變的老大,是吧,呵呵~
當(dāng)然了,很多會覺得,寫個配置文件,臥槽,這多麻煩。于是乎,又出現(xiàn)了另一種方案,得,你閑配置文件麻煩,你用注解吧。你在需要注入的成員變量上面給我加個注解,例如:@Inject,這樣就行了,你總不能說這么個單詞麻煩吧~~
當(dāng)然了,有了配置文件和注解,那么怎么注入呢?其實(shí)就是把字符串類路徑變成類么,當(dāng)然了,反射上場了;話說,很久很久以前,反射很慢啊,嗯,那是很久很久以前,現(xiàn)在已經(jīng)不是太慢了,當(dāng)然了肯定達(dá)不到原生的速度~~無反射,沒有任何框架。
如果你覺得注解,反射神馬的好高級。我說一句:Just Do It ,你會發(fā)現(xiàn)注解就和你寫一個普通JavaBean差不多;反射呢?API就那么幾行,千萬不要被震懾住~
2、框架實(shí)現(xiàn)
得進(jìn)入正題了,Android IOC框架,其實(shí)主要就是幫大家注入所有的控件,布局文件什么的。如果你用過xUtils,afinal類的框架,你肯定不陌生~
注入View
假設(shè):我們一個Activity,里面10來個View。
傳統(tǒng)做法:我們需要先給這個Activity設(shè)置下布局文件,然后在onCreate里面一個一個的findViewById把~
目標(biāo)的做法:Activity類上添加個注解,幫我們自動注入布局文科;聲明View的時候,添加一行注解,然后自動幫我們findViewById;
于是乎我們的目標(biāo)類是這樣的:
[java]?view plaincopy
@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;??
3、編碼
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.TYPE)?? @Retention(RetentionPolicy.RUNTIME)?? public?@interface?ContentView?? {?? ????int?value();?? }??
ContentView用于在類上使用,主要用于標(biāo)明該Activity需要使用的布局文件。
[java]?view plaincopy
@ContentView(value?=?R.layout.activity_main)?? public?class?MainActivity??
[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.FIELD)?? @Retention(RetentionPolicy.RUNTIME)?? public?@interface?ViewInject?? {?? ????int?value();?? }??
在成員變量上使用,用于指定View的Id
[java]?view plaincopy
@ViewInject(R.id.id_btn)?? ????private?Button?mBtn1;??
簡單說一下注解:定義的關(guān)鍵字@interface ;?@Target表示該注解可以用于什么地方,可能的類型TYPE(類),FIELD(成員變量),可能的類型:
[java]?view plaincopy
public?enum?ElementType?{?? ????? ? ?? ????TYPE,?? ????? ? ?? ????FIELD,?? ????? ? ?? ????METHOD,?? ????? ? ?? ????PARAMETER,?? ????? ? ?? ????CONSTRUCTOR,?? ????? ? ?? ????LOCAL_VARIABLE,?? ????? ? ?? ????ANNOTATION_TYPE,?? ????? ? ?? ????PACKAGE?? }??
就是這些個枚舉。
@Retention表示:表示需要在什么級別保存該注解信息;我們這里設(shè)置為運(yùn)行時。
可能的類型:
[java]?view plaincopy
public?enum?RetentionPolicy?{?? ????? ? ?? ????SOURCE,?? ????? ? ? ?? ????CLASS,?? ????? ? ? ?? ????RUNTIME?? }??
這些個枚舉~
2、MainActivity
[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;?? ????????}?? ????}?? ?? }??
注解都寫好了,核心的代碼就是ViewInjectUtils.inject(this)了~
3、ViewInjectUtils
1、首先是注入主布局文件的代碼:
[java]?view plaincopy
? ? ? ? ?? ????private?static?void?injectContentView(Activity?activity)?? ????{?? ????????Class<??extends?Activity>?clazz?=?activity.getClass();?? ?????????? ????????ContentView?contentView?=?clazz.getAnnotation(ContentView.class);?? ????????if?(contentView?!=?null)?? ????????{?? ????????????int?contentViewLayoutId?=?contentView.value();?? ????????????try?? ????????????{?? ????????????????Method?method?=?clazz.getMethod(METHOD_SET_CONTENTVIEW,?? ????????????????????????int.class);?? ????????????????method.setAccessible(true);?? ????????????????method.invoke(activity,?contentViewLayoutId);?? ????????????}?catch?(Exception?e)?? ????????????{?? ????????????????e.printStackTrace();?? ????????????}?? ????????}?? ????}??
通過傳入的activity對象,獲得它的Class類型,判斷是否寫了ContentView這個注解,如果寫了,讀取它的value,然后得到setContentView這個方法,使用invoke進(jìn)行調(diào)用;
有個常量:
[java]?view plaincopy
private?static?final?String?METHOD_SET_CONTENTVIEW?=?"setContentView";??
2、接下來是注入Views
[java]?view plaincopy
private?static?final?String?METHOD_FIND_VIEW_BY_ID?=?"findViewById";?? ????? ? ? ? ?? ????private?static?void?injectViews(Activity?activity)?? ????{?? ????????Class<??extends?Activity>?clazz?=?activity.getClass();?? ????????Field[]?fields?=?clazz.getDeclaredFields();?? ?????????? ????????for?(Field?field?:?fields)?? ????????{?? ?????????????? ????????????ViewInject?viewInjectAnnotation?=?field?? ????????????????????.getAnnotation(ViewInject.class);?? ????????????if?(viewInjectAnnotation?!=?null)?? ????????????{?? ????????????????int?viewId?=?viewInjectAnnotation.value();?? ????????????????if?(viewId?!=?-1)?? ????????????????{?? ????????????????????Log.e("TAG",?viewId+"");?? ?????????????????????? ????????????????????try?? ????????????????????{?? ????????????????????????Method?method?=?clazz.getMethod(METHOD_FIND_VIEW_BY_ID,?? ????????????????????????????????int.class);?? ????????????????????????Object?resView?=?method.invoke(activity,?viewId);?? ????????????????????????field.setAccessible(true);?? ????????????????????????field.set(activity,?resView);?? ????????????????????}?catch?(Exception?e)?? ????????????????????{?? ????????????????????????e.printStackTrace();?? ????????????????????}?? ?? ????????????????}?? ????????????}?? ?? ????????}?? ?? ????}??
獲取聲明的所有的屬性,遍歷,找到存在ViewInject注解的屬性,或者其value,然后去調(diào)用findViewById方法,最后把值設(shè)置給field~~~
好了,把這兩個方法寫到inject里面就好了。
[java]?view plaincopy
public?static?void?inject(Activity?activity)?? ????{?? ?????????? ????????injectContentView(activity);?? ????????injectViews(activity);?? ?????????? ????}??
本文主要了解了如何打造這么個框架,下一篇,將教大家如何注入事件 ,不要再寫什么setXXXListener了~~~
效果圖:
源碼點(diǎn)擊下載
總結(jié)
以上是生活随笔為你收集整理的Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。