1 前言
前面的文章注解框架源碼分析(XUtils百侧、ButterKnife)和ButterKnife編譯時生成代碼原理:butterknife-compiler源碼分析老赤,按照源碼的思路我們自己打造一款IOC注解框架唤崭,因為ButterKnife的源碼實現(xiàn)難度過大士聪,我先仿照xUtils的源碼方式某饰,反射注解實現(xiàn)。
雖說反射注解對性能有影響剃袍,但是影響是極小黄刚,相比I渲染和Bitmap以及Service和Handler上的內(nèi)存泄露不是一個量級的,編程一開始不糾結完美化民效,實現(xiàn)這個IOC框架是為了提升自己的編碼能力憔维,也是提高自己對項目整體代碼提高可控性涛救。
2 控件屬性注入
Annotation注解需要了解Java中Annotation用法、Java Annotation 總結
屬性注入代碼:
/*
* ElementType.FIELD 代表annotation的位置
* FIELD:屬性注解
* CONSTRUCTOR:構造器注解
* METHOD:方法注解
* TYPE:類上注解
*/
@Target(ElementType.FIELD)
/*
@Retention(RetentionPolicy.CLASS)什么時候生效
CLASS 編譯時
RUNTIME 運行時
SOURCR 源碼資源
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface FindView {
//@FindView(R.id.xxxx)
@IdRes int value();
}
public class SteadyoungIOC {
//activity使用
public static void inject(Activity activity){
inject(new ViewFinder(activity),activity);
}
//View使用
public static void inject(View view){
inject(new ViewFinder(view),view);
}
//Fragment使用
public static void inject(View view,Object object){
inject(new ViewFinder(view),object);
}
//兼容上述三種方式
private static void inject(ViewFinder finder,Object object){
injectField(finder,object);
injectEvent(finder,object);
}
/**
* 注入屬性
* @param finder
* @param object
*/
private static void injectField(ViewFinder finder, Object object) {
//1.獲取類里面的所有屬性
Class<?> clazz = object.getClass();
//獲取所有屬性包括私有和公有
Field[] fields = clazz.getDeclaredFields();
//2.獲取ViewById的里面的value值
for(Field field : fields){
FindView viewById = field.getAnnotation(FindView.class);
if(viewById != null){
int viewId = viewById.value();
//3.findViewById找到View
View view = finder.findViewById(viewId);
if (view != null) {
// 4. 反射注入View屬性
// 設置所有屬性都能注入包括私有和公有
field.setAccessible(true);
try {
field.set(object, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
throw new RuntimeException("Invalid @ViewInject for "
+ clazz.getSimpleName() + "." + field.getName());
}
}
}
}
/**
* 注入事件
* @param finder
* @param object
*/
private static void injectEvent(ViewFinder finder, Object object) {
//TODO
}
public class ViewFinder {
private Activity mActivity;
private View mView;
public ViewFinder(Activity activity) {
this.mActivity = activity;
}
public ViewFinder(View view) {
this.mView = view;
}
/**
* 判斷容器然后 根據(jù)容器和控件ID獲取控件View
* @param viewId
* @return
*/
public View findViewById(@IdRes int viewId){
return mActivity!= null ? mActivity.findViewById(viewId) : mView.findViewById(viewId);
}
}
3 點擊事件注入
我先實現(xiàn)setOnclickListener點擊事件业扒,其他事件后期實現(xiàn)检吆。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
@IdRes int[] value();
}
/**
* 注入事件
* @param finder
* @param object
*/
private static void injectEvent(ViewFinder finder, Object object) {
//TODO
//1.獲取類里面的所有方法
Class<?> clazz = object.getClass();
Method[] methods = clazz.getDeclaredMethods();
//2.獲取OnClick的里面的Value值
for ( Method method: methods ) {
OnClick onClick = method.getAnnotation(OnClick.class);
if(onClick != null){
int[] viewIds = onClick.value();
for( int viewId : viewIds){
//3.findViewById找到View
View view = finder.findViewById(viewId);
//4.view.setOnClickListener
view.setOnClickListener(new DeclaredOnClickListener(method,object));
}
}
}
}
private static class DeclaredOnClickListener implements View.OnClickListener {
private Method mMethod;
private Object mObject;
public DeclaredOnClickListener(Method mMethod, Object mObject) {
this.mMethod = mMethod;
this.mObject = mObject;
}
@Override
public void onClick(View v) {
try {
//打開權限
mMethod.setAccessible(true);
mMethod.invoke(mObject,v);
} catch (Exception e) {
e.printStackTrace();
try {
mMethod.invoke(mObject,null);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
4 擴展動態(tài)檢測網(wǎng)絡注解
最后擴展動態(tài)檢測網(wǎng)絡注解,可以在點擊某些按鈕或者圖片是需要訪問網(wǎng)絡前檢測網(wǎng)絡狀態(tài)程储,避免打不開網(wǎng)頁或者獲取不到數(shù)據(jù)等網(wǎng)絡問題的情況蹭沛!
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNet {
String value();
}
/**
* 注入事件
* @param finder
* @param object
*/
private static void injectEvent(ViewFinder finder, Object object) {
//TODO
//1.獲取類里面的所有方法
Class<?> clazz = object.getClass();
Method[] methods = clazz.getDeclaredMethods();
//2.獲取OnClick的里面的Value值
for ( Method method: methods ) {
OnClick onClick = method.getAnnotation(OnClick.class);
if(onClick != null){
int[] viewIds = onClick.value();
for( int viewId : viewIds){
//3.findViewById找到View
View view = finder.findViewById(viewId);
//判斷是否有檢測網(wǎng)絡需求
boolean isCheckNet = method.getAnnotation(CheckNet.class) != null;
String message = null;
if(isCheckNet){
//獲取無網(wǎng)絡提示信息
message = method.getAnnotation(CheckNet.class).value();
}
//4.view.setOnClickListener
view.setOnClickListener(new DeclaredOnClickListener(method,object,isCheckNet,message));
}
}
}
}
private static class DeclaredOnClickListener implements View.OnClickListener {
private Method mMethod;
private Object mObject;
private boolean isCheckNet;
private String message;
public DeclaredOnClickListener(Method mMethod, Object mObject, boolean isCheckNet, String message) {
this.mMethod = mMethod;
this.mObject = mObject;
this.isCheckNet = isCheckNet;
this.message = message;
}
@Override
public void onClick(View v) {
if(isCheckNet){
Log.d("isCheckNet", "onClick:111111111111111111 ");
if(!isNetworkConnected(v.getContext())){
Toast.makeText(v.getContext(), TextUtils.isEmpty(message) ? "網(wǎng)絡不給力,請檢查網(wǎng)絡連接章鲤!" : message ,Toast.LENGTH_SHORT).show();
return;
}
}
try {
//打開權限
mMethod.setAccessible(true);
mMethod.invoke(mObject,v);
} catch (Exception e) {
e.printStackTrace();
try {
mMethod.invoke(mObject,null);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
/**
*獲取網(wǎng)絡連接狀態(tài)
* @param context
* @return true網(wǎng)絡連接正常 false無網(wǎng)絡
*/
private static boolean isNetworkConnected(Context context){
if(context != null){
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if(networkInfo != null){
return networkInfo.isAvailable();
}
}
return false;
}
下面放上測試代碼和測試結果:
public class MainActivity extends AppCompatActivity {
/****Hello World!****/
@FindView(R.id.test_tv)
private TextView mTestTv;
@FindView(R.id.test_iv)
private ImageView mTestIv;
/****TestButton****/
@FindView(R.id.test_btn)
private Button mTestBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SteadyoungIOC.inject(this);
mTestTv.setText("測試文本");
mTestIv.setBackgroundColor(Color.RED);
mTestBtn.setText("測試按鈕");
}
@OnClick(R.id.test_tv)
private void testTvClick(TextView testTv) {
Toast.makeText(this,"點擊了文字",Toast.LENGTH_SHORT).show();
}
@OnClick(R.id.test_iv)
private void testIvClick(ImageView testIv) {
mTestIv.setBackgroundColor(Color.BLUE);
Toast.makeText(this,"點擊了圖片",Toast.LENGTH_SHORT).show();
}
@CheckNet("親摊灭!網(wǎng)絡不給力哦!")
@OnClick(R.id.test_btn)
private void testBtnClick(Button testBtn) {
Toast.makeText(this,"點擊了按鈕",Toast.LENGTH_SHORT).show();
}
}
steadyoungioctest.gif
steadyoungioc.gif
上圖中生成代碼的插件是我自己編寫的咏窿,后面的文章將講解插件代碼斟或。
附上源碼:https://github.com/Steadyoung/SteadyoungIOC
插件下載地址:SteadyoungIOC-CodePlug.jar