紙上得來終覺淺,絕知此事要躬行锭碳。
上一篇我們對注解有了初步的了解糠悯,知道了注解主要分兩種運(yùn)行時(shí)注解以及編譯時(shí)注解,這篇文章我們就一起學(xué)習(xí)一下XUtils是如何基于運(yùn)行時(shí)注解實(shí)現(xiàn)ViewInject的暴氏。
XUtils曾經(jīng)也是個風(fēng)靡一時(shí)的框架,集成了很多功能胸懈,作為一個初學(xué)者我對它也是愛不釋手垄开,對作者也是佩服得五體投地,但是隨著學(xué)習(xí)的深入也逐漸了解到了為人詬病的效率問題(基于運(yùn)行時(shí)注解以及反射)聋涨,以及如此龐大的功能與單一職責(zé)原則相悖晾浴,在實(shí)際的開發(fā)中已不可能再去使用。但是打開它的源碼去一窺究竟一直是一個心愿牍白。今天就先看看他是如何實(shí)現(xiàn)setContentView以及ViewInject的脊凰。
什么是IOC
控制反轉(zhuǎn)(Inversion of Control,縮寫為IoC)茂腥,是面向?qū)ο缶幊?/a>中的一種設(shè)計(jì)原則狸涌,可以用來減低計(jì)算機(jī)代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection础芍,簡稱DI)杈抢,還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉(zhuǎn)仑性,對象在被創(chuàng)建的時(shí)候惶楼,由一個調(diào)控系統(tǒng)內(nèi)所有對象的外界實(shí)體,將其所依賴的對象的引用傳遞給它诊杆。也可以說歼捐,依賴被注入到對象中。
IOC最主要的實(shí)現(xiàn)方式就是依賴注入晨汹,依賴注入通俗的講就是目標(biāo)類(目標(biāo)類需要進(jìn)行依賴初始化的類)中所依賴的其他的類的初始化過程豹储,不是通過手動編碼的方式創(chuàng)建,而是通過技術(shù)手段可以把其他的類的已經(jīng)初始化好的實(shí)例自動注入到目標(biāo)類中淘这。
把話再講得糙一點(diǎn)剥扣,就是通過技術(shù)手段把初始化好的對象直接注射進(jìn)目標(biāo)類中巩剖,至于對象是如何來的,目標(biāo)類并不需要知道钠怯。
XUtils中的ViewInject就是這樣一種IOC的實(shí)現(xiàn)佳魔,通過注解的方式直接將View對象注入到Activity類中。
實(shí)現(xiàn)SetContentView
@ContentView(R.layout.main)
public class MyActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewUtils.inject(this);
}
}
我們最終要達(dá)到的目的就是像上面這樣晦炊,通過注解直接setContentView鞠鲜。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
int value();
}
首先定義個ContentView注解,作用的目標(biāo)是TYPE(類)断国,有一個int返回值的value()方法用來接收layout資源id贤姆。
public static void inject(Activity activity) {
Class<?> handlerType = activity.getClass();
// inject ContentView
ContentView contentView = handlerType.getAnnotation(ContentView.class);
if (contentView != null) {
try {
Method setContentViewMethod = handlerType.getMethod("setContentView", int.class);
setContentViewMethod.invoke(handler, contentView.value());
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
}
}
上面的方法也很簡單,通過反射調(diào)用了setContentView()方法稳衬,并將resid從注解上取出后?作為參數(shù)傳遞進(jìn)去霞捡。然后你再用ContentView注解去修飾你的Activity,這樣簡單的setContentView就實(shí)現(xiàn)了,一定要動手試一下宋彼。
如果對反射一竅不通的話弄砍,可以去看看這篇文章
Java反射機(jī)制探秘
實(shí)現(xiàn)ViewInject
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
}
與ContentView相似,只是這一次注解的對象是FIELD(成員變量)
Class<?> handlerType = activity.getClass();
// inject view
//獲取到activity中的所有成員變量
Field[] fields = handlerType.getDeclaredFields();
if (fields != null && fields.length > 0) {
//循環(huán)成員變量
for (Field field : fields) {
//查看該成員變量是否有ViewInject注解
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
try {
//通過activity的findViewById方法獲取到View對象
View view = activity.findViewById(viewInject.value());
if (view != null) {
//將獲取到得view對象設(shè)置給成員變量
field.setAccessible(true);
field.set(handler, view);
}
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
}
}
還是通過反射的方法實(shí)現(xiàn)了將find到的View設(shè)置給注解了的成員變量输涕,不理解的可以看代碼中的注釋音婶。
@ViewInject(R.id.btn_change)
private Button btn_change;
這樣我們就實(shí)現(xiàn)了通過注解實(shí)例化了View對象,還是文章開頭的那句話 絕知此事要躬行 一定要去試一試莱坎,下面是我的代碼
Stride-Annotation
下一篇文章將和大家一起學(xué)習(xí)如何實(shí)現(xiàn)onClick注解