什么是IOC
IOC(Inversion of Control):控制反轉(zhuǎn)娄周。開發(fā)過程中類里面需要用到很多個成員變量
傳統(tǒng)的寫法:你要用這些成員變量的時候,那么你就new出來用
IOC的寫法:你要用這些成員變量的時候羹奉,使用注解的方式自動注入進去
優(yōu)點:代碼量減少秒旋,加速開發(fā)
缺點:性能消耗加大,閱讀性差诀拭,加速65535
框架的思路
框架例子
//實現(xiàn)Button自動findViewById的工作
@ViewById(R.id.bt_ioc)
private Button bt_ioc;1
實現(xiàn)思路
?創(chuàng)建自定義注解 @ViewById
?通過某個字節(jié)碼文件獲取對應的自定義注解
?通過反射迁筛,獲取注解和注解的值 (R.id.bt_ioc)
?通過對注解的值做相應的操作,并設置回對象自身
實現(xiàn)內(nèi)容
?實現(xiàn)通過Id找到控件的功能
?實現(xiàn)通過Id找到Color耕挨、String資源
?實現(xiàn)綁定view的點擊事件细卧、長按事件
?實現(xiàn)綁定SetContentView
?實現(xiàn)綁定網(wǎng)絡的檢測功能
框架的結(jié)構(gòu)
20171020001616734.png
包含的注解介紹
OnClick:實現(xiàn)點擊事件
OnLongClick:實現(xiàn)長按事件
ColorById:找到對應的Color值
ContentViewById:實現(xiàn)SetContentView
StringById:找到對應的String值
ViewById:實現(xiàn)findViewById
CheckNet:實現(xiàn)網(wǎng)絡檢查功能
框架的使用
下面的這個Activity實現(xiàn)了框架的所有內(nèi)容
@ContentViewById(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@ViewById(R.id.bt_ioc)
private Button bt_ioc;
@StringById(R.string.app_name)
private String app_name;
@ColorById(R.color.colorAccent)
private int color;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//IOC演示
InjectManager.inject(this);
bt_ioc.setText(app_name);
bt_ioc.setBackgroundColor(color);
}
//支持數(shù)組形式的綁定,綁定多個控件
@OnClick({R.id.open_ioc})
@OnLongClick({R.id.open_ioc})
@CheckNet()
public void open_ioc() {
Toast.makeText(this, "網(wǎng)絡可用", Toast.LENGTH_SHORT).show();
}
}1234567891011121314151617181920212223242526272829
框架的實現(xiàn)
框架的實現(xiàn)分為兩步:自定義注解的創(chuàng)建和通過反射進行注入
一筒占、自定義注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
int[] value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnLongClick {
int[] value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColorById {
int value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentViewById {
int value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface StringById {
int value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewById {
int value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
}
Target注解的介紹
@Target(ElementType.XXX):代表的是注解放在XXX位置
@Target(ElementType.TYPE):接口贪庙、類、枚舉翰苫、注解
@Target(ElementType.FIELD):字段止邮、枚舉的常量
@Target(ElementType.METHOD):方法
@Target(ElementType.PARAMETER):方法參數(shù)
@Target(ElementType.CONSTRUCTOR):構(gòu)造函數(shù)
@Target(ElementType.LOCAL_VARIABLE):局部變量
@Target(ElementType.ANNOTATION_TYPE):注解
@Target(ElementType.PACKAGE):包
Retention注解的介紹
@Retention(Policy.RUNTIME):代表運行時檢測,class文件中存在
@Retention(Policy.CLASS):代表編譯時檢測奏窑,存在于class文件中导披,運行時無法獲取
@Retention(Policy.SOURCE):代表在源文件中有效(在.java文件中有效)
二、注入步驟
從使用中可以看到埃唯,注入中最重要的步驟的是:InjectManager.inject(this)撩匕,這里主要負責的事情有
?注入ContentView
?注入變量
?注入事件
···
public class InjectManager {
public static void inject(Activity activity) {
inject(new ViewManager(activity), activity);
}
public static void inject(Fragment fragment) {
inject(new ViewManager(fragment), fragment);
}
/**
* 注入
*
* @param viewManager
* @param object
*/
private static void inject(ViewManager viewManager, Object object) {
InjectManagerService.injectContentView(viewManager, object);
InjectManagerService.injectField(viewManager, object);
InjectManagerService.injectEvent(viewManager, object);
}
···
這里會使用到ViewManager輔助類,代碼很簡單筑凫,后面會用到
···
public class ViewManager {
private Activity mActivity;
private Fragment mFragment;
private View mView;
public ViewManager(Activity activity) {
this.mActivity = activity;
}
public ViewManager(View view) {
this.mView = view;
}
public ViewManager(Fragment fragment) {
this.mFragment = fragment;
}
/**
* 通過Id查詢View
*
* @param resId
* @return
*/
public View findViewById(int resId) {
View view = null;
if (mActivity != null) {
view = mActivity.findViewById(resId);
}
if (mFragment != null) {
view = mFragment.getActivity().findViewById(resId);
}
if (mView != null) {
view = mView.findViewById(resId);
}
return view;
}
/**
* 設置根布局滑沧,僅限Activity
*
* @param resId
*/
public void setContentView(int resId) {
if (mActivity != null) {
mActivity.setContentView(resId);
}
}
/**
* 獲取顏色
*
* @param resId
*/
public int getColor(int resId) {
int color = -1;
if (mActivity != null) {
color = mActivity.getResources().getColor(resId);
}
if (mFragment != null) {
color = mFragment.getActivity().getResources().getColor(resId);
}
return color;
}
/**
* 獲取字符串
*
* @param resId
*/
public String getString(int resId) {
String str = "";
if (mActivity != null) {
str = mActivity.getString(resId);
}
if (mFragment != null) {
str = mFragment.getActivity().getString(resId);
}
return str;
}
···
在InjectManagerService中,也是上面的三個主要步驟巍实,主要還是下面通過反射實現(xiàn)其真正的效果
···
public class InjectManagerService {
/**
* 注入根布局
*
* @param viewManager
* @param object
*/
public static void injectContentView(ViewManager viewManager, Object object) {
injectContentViewById(viewManager, object);
}
/**
* 注入變量
*
* @param viewManager
* @param object
*/
public static void injectField(ViewManager viewManager, Object object) {
injectFieldById(viewManager, object);
}
/**
* 注入事件
*
* @param viewManager
* @param object
*/
public static void injectEvent(ViewManager viewManager, Object object) {
injectOnClick(viewManager, object);
injectOnLongClick(viewManager, object);
}
/**
* 注入根布局
*
* @param viewManager
* @param object
*/
private static void injectContentViewById(ViewManager viewManager, Object object) {
Class<?> clazz = object.getClass();
ContentViewById contentView = clazz.getAnnotation(ContentViewById.class);
if (contentView != null) {
int layoutId = contentView.value();
viewManager.setContentView(layoutId);
}
}
/**
* 注入findViewById事件
*
* @param viewManager
* @param object
*/
public static void injectFieldById(ViewManager viewManager, Object object) {
//1. 獲取Activity字節(jié)碼滓技,這里以Activity為例
Class<?> clazz = object.getClass();
//2. 獲取字節(jié)碼中所有的成員變量
Field[] fields = clazz.getDeclaredFields();
if (fields != null) {
//3. 遍歷所有變量
for (Field field : fields) {
//4. 找到對應的注解
ViewById viewById = field.getAnnotation(ViewById.class);
StringById stringById = field.getAnnotation(StringById.class);
ColorById colorById = field.getAnnotation(ColorById.class);
if (viewById != null) {
//5. 獲取注解中的值
int viewId = viewById.value();
//6. findViewById并設置訪問權(quán)限
View view = viewManager.findViewById(viewId);
field.setAccessible(true);
try {
//7. 動態(tài)注入到變量中
field.set(object, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
if (stringById != null) {
int viewId = stringById.value();
String string = viewManager.getString(viewId);
field.setAccessible(true);
try {
field.set(object, string);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
if (colorById != null) {
int viewId = colorById.value();
int color = viewManager.getColor(viewId);
field.setAccessible(true);
try {
field.set(object, color);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 注入點擊事件
*
* @param viewManager
* @param object
*/
public static void injectOnClick(ViewManager viewManager, Object object) {
Class<?> clazz = object.getClass();
Method[] methods = clazz.getDeclaredMethods();
if (methods != null) {
for (Method method : methods) {
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
int[] viewIds = onClick.value();
for (int viewId : viewIds) {
View view = viewManager.findViewById(viewId);
//檢查網(wǎng)絡
boolean isCheckNet = method.getAnnotation(CheckNet.class) != null;
if (view != null) {
view.setOnClickListener(new DeclaredOnClickListener(method, object, isCheckNet));
}
}
}
}
}
}
/**
* 注入長按事件
*
* @param viewManager
* @param object
*/
public static void injectOnLongClick(ViewManager viewManager, Object object) {
Class<?> clazz = object.getClass();
Method[] methods = clazz.getDeclaredMethods();
if (methods != null) {
for (Method method : methods) {
OnLongClick onLongClick = method.getAnnotation(OnLongClick.class);
if (onLongClick != null) {
int[] viewIds = onLongClick.value();
for (int viewId : viewIds) {
View view = viewManager.findViewById(viewId);
//檢查網(wǎng)絡
boolean isCheckNet = method.getAnnotation(CheckNet.class) != null;
if (view != null) {
view.setOnLongClickListener(new DeclaredOnLongClickListener(method, object, isCheckNet));
}
}
}
}
}
}
···
這里用到兩個點擊事件,并且將檢查網(wǎng)絡作為參數(shù)傳進去到事件中處理棚潦,由于長按事件和點擊事件大同小異令漂,這里只貼一處代碼
···
public class DeclaredOnLongClickListener implements View.OnLongClickListener {
private Method mMethod;
private Object mObject;
private boolean mIsCheckNet;
public DeclaredOnLongClickListener(Method method, Object object, boolean isCheckNet) {
this.mMethod = method;
this.mObject = object;
this.mIsCheckNet = isCheckNet;
}
@Override
public boolean onLongClick(View v) {
if (mIsCheckNet) {
if (!NetUtils.isNetworkAvailable(v.getContext())) {
Toast.makeText(v.getContext(), "網(wǎng)絡不可用", Toast.LENGTH_SHORT).show();
return true;
}
}
//執(zhí)行點擊事件
try {
mMethod.setAccessible(true);
mMethod.invoke(mObject, v);
} catch (Exception e) {
e.printStackTrace();
try {
mMethod.invoke(mObject, null);
} catch (Exception e1) {
e1.printStackTrace();
}
}
return true;
}
···
結(jié)語
到這里IOC框架就結(jié)束了,其中比較重要的兩點是注解的自定義和通過反射獲取屬性值并注入丸边,其實代碼挺簡單的叠必,反復看看還是挺容易理解的,大家可以結(jié)合源碼進行閱讀妹窖,其實在IOC路上還有權(quán)限的申請等功能可以實現(xiàn)纬朝,不過已經(jīng)有第三方框架已經(jīng)做好了