新博客地址
註解初始化控件(XUtils方式)
DEMO鏈接地址
在第一次潭州教育的公開課上接觸了這個框架的講解,我動手研究了一下,結果一出手就停不下來,先後被我碰上了(Glow公司的技術博客——動態Android編程 )、從幾個大牛的博客(學到了github pages + Jekyll 免費製作博客網站)
我發現不寫博客,很多東西就會忘記(代碼如何上傳到jcenter我已經忘記了)
Just Do it!真的會有意想不到的收穫!
1. IOC概念介紹
http://www.cnblogs.com/qqlin/archive/2012/10/09/2707075.html我是從這邊文章學習的IOC概念的,寫的淺顯易懂
- 控件反轉(IOC):創建何種對象的控制權,轉移到第三方
- 依賴注入(DI):是由IOC容器在運行期間,動態地將某種依賴關係注入到對象之中。
- IOC與DI之間的關係:DI是一種IOC的具體思想,(編譯運行期,動態注入依賴關係);使用配置文件實現依賴關係的配置也是一種IOC思想(依賴拖拽)。
- 約定優於配置 這個是什麼鬼??
- 依賴注入/依賴查找/依賴拖拽
- 依賴拖拽是通過對注入對象的集中配置實現的
- 依賴查找是在業務組件代碼中進行的(EJB和Apache Avalon )
- XUtils的實現方式XUtils實際上是通過 註解 + 反射 + 動態代理實現的。
layout文件注入:
- 使用:
@ContentViewInject(R.layout.activity_main)
public class MainActivity extends AppCompatActivity
- 註解:
@Target(ElementType.TYPE)// 使用對象:類
@Retention(RetentionPolicy.RUNTIME)// 生命範圍:運行期
public @interface ContentViewInject {
int value();// 存放佈局id
}
- 注入代碼:
private static void injectLayout(Activity context) {
try {
// UIClz
Class<? extends Context> uiClass = context.getClass();
// 類上的註解
ContentViewInject annotation = uiClass.getAnnotation(ContentViewInject.class);
if (annotation != null) {
// 註解中的layout id值
int value = annotation.value();
// 通過反射使用activity中的setContentView方法進行 佈局設置
// 侷限性:這個方法僅僅適用於activity
Method setContentView = uiClass.getMethod("setContentView", int.class);
setContentView.invoke(context, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
控件注入:
- 使用:
@ViewInject(R.id.textView)
TextView textView;
......
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TurInject.bind(this);
textView.setText("徠帝嘎嘎");
}
- 註解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
}
- 注入:
private static void injectViews(Context context) {
try {
Class<? extends Context> uiClass = context.getClass();
// 獲取成員變量
Field[] fields = uiClass.getDeclaredFields();
for (Field field : fields) {
ViewInject annotation = field.getAnnotation(ViewInject.class);
if (annotation != null) {
int value = annotation.value();
Method findViewById = uiClass.getMethod("findViewById", int.class);
Object invoke = findViewById.invoke(context, value);
// 設置允許訪問
field.setAccessible(true);
field.set(context, invoke);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
事件注入
- 使用代碼:
@ClickEvent(value = {R.id.textView}, type = View.OnClickListener.class)
public void ccClick(View view) {
Toast.makeText(getBaseContext(), "ccClick", Toast.LENGTH_LONG).show();
}
@ClickEvent(value = {R.id.textView}, type = View.OnLongClickListener.class)
public void longClick(View view) {
Toast.makeText(getBaseContext(), "longClick", Toast.LENGTH_LONG).show();
}
- 註解
public static void injectMethod(final Context context) {
try {
Class<? extends Context> uiClass = context.getClass();
// 獲取所有的方法
Method[] methods = uiClass.getMethods();
for (final Method method : methods) {
ClickEvent annotation = method.getAnnotation(ClickEvent.class);
// 塞選含有ClickEvent註解的方法
if (annotation != null) {
int[] values = annotation.value();
Class type = annotation.type();
// 拿到控件
for (int viewId : values) {
// 初始化控件
Method findViewById = uiClass.getMethod("findViewById", int.class);
// view
final Object viewObj = findViewById.invoke(context, viewId);
Class<?> viewClass = viewObj.getClass();
// setOnClickListener/setOnLongClickListener等等
String viewSetMethodName = "set" + type.getSimpleName();
Method viewMethod = viewClass.getMethod(viewSetMethodName, type);
// 動態代理就是針對 任意 一個對象的接口方法的 管理/攔截/AOP
ViewEventInvocationHandler viewEventInvocationHandler = new ViewEventInvocationHandler(context, method);
// type.getClassLoader : 類加載器 new Class[]{type} : type爲接口類
Object eventInterfaceInstance = Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, viewEventInvocationHandler);
// 動態代理 OnClickListener 中的
// 執行了setOnClickListener這個方法,那麼在響應這個參數OnClickListener接口中的,onclick方法的時候會響應InvocationHandler中的invoke方法
viewMethod.invoke(viewObj, eventInterfaceInstance);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static class ViewEventInvocationHandler implements InvocationHandler {
private Context context;
private Method contentMethod;
public ViewEventInvocationHandler(Context context, Method contentMethod) {
this.context = context;
this.contentMethod = contentMethod;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// method 爲OnClickListener 中的 onClick
// 系統調用參數接口中的 onClick方法的時候,會響應這個方法
// 響應這個方法的時候我們需要響應(activity中被ClickEvent標記過的方法)
// contentMethod 爲activity中被 ClickEvent標記過的方法
contentMethod.invoke(context, args);
return true;
}
}
- 主要用來做方法的增強,讓你可以在不修改源碼的情況下,增強一些方法,在方法執行前後做任何你想做的事情(甚至根本不去執行這個方法),因爲在InvocationHandler的invoke方法中,你可以直接獲取正在調用方法對應的Method對象,具體應用的話,比如可以添加調用日誌,做事務控制等。
- 還有一個有趣的作用是可以用作遠程調用
- 在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
- 爲其他對象提供一種代理以控制對這個對象的訪問
動態代理的缺憾:Proxy已經設計得非常優美,但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持interface代理的桎梏
到這裏呢,XUtils的佈局注入、控件注入、事件注入就全部介紹完了!