xUtils2.x(ViewUtils)

来源:互联网 发布:返利软件是什么 编辑:程序博客网 时间:2024/06/05 07:47

虽然现在都以3.x为主,但是我还是希望能够从源码上学习xUtils的技术,可以从中学到更多的东西,所以先从2.x版本的xUtils入手,深度学习这款综合性框架。

GitHub地址:https://github.com/wyouflf/xUtils ----- 这是xUtils2.x版本

GitHub地址:https://github.com/wyouflf/xUtils3 ----- 这是xUtils3.x版本

xUtils3.x版本依赖:

compile 'org.xutils:xutils:3.3.44'
官方文档已经详细的介绍了其所有功能包括:ViewUtils,DbUtils,HttpUtils,BitmapUtils四大模块。

这里首先学习ViewUtils:


ViewUtils是以注解和反射的方式来完成Android界面的UI绑定和事件绑定。首先我们要大概了解注解和反射的概念和大概原理。

@ContentView(R.layout.activity_main)public class MainActivity extends Activity {    @ViewInject(R.id.t)    private TextView t;    @ViewInject(R.id.t1)    private TextView t1;    @ViewInject(R.id.t2)    private TextView t2;    private FragmentTransaction fragmentTransaction;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ViewUtils.inject(this);        fragmentTransaction = getFragmentManager().beginTransaction();        fragmentTransaction.add(R.id.fl, new TestFragment());        fragmentTransaction.commit();    }    @OnClick({R.id.t, R.id.t1, R.id.t2})    public void onClick(View view) {        Toast.makeText(this, "111111111", Toast.LENGTH_SHORT).show();    }}


这是activity中的绑定,当然还有fragment的。因为两者调用的方法不同,所以注意两者之间的区别:

public class TestFragment extends Fragment {    @ViewInject(R.id.ttt)    private TextView textView;    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);    }    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.fragment_test, container, false);        ViewGroup viewGroup = (ViewGroup) view.getParent();        if (viewGroup != null)            viewGroup.removeView(view);        ViewUtils.inject(this, view);        return  view;    }    @OnClick(R.id.ttt)    public void onclick(View view) {        Toast.makeText(getActivity(), "1qqqqqqqqq", Toast.LENGTH_SHORT).show();    }



在代码中可以看到三个文本控件和一个fragment的布局,大概知道了整个activity的整个界面,这里就不铺xml了。这里首先可以看到有许多的注解,稍后我们可以在源码中看到这个的实现原理。还有一个主要的方法 ViewUtils.inject(this); 在此一定要在使用ViewUtils的界面中调用这个方法,因为所有对注解的解释都在这里面执行。我们可以进去看一下源码:

public class ViewUtils {    ........    public static void inject(Activity activity) {        injectObject(activity, new ViewFinder(activity));    }    public static void inject(PreferenceActivity preferenceActivity) {        injectObject(preferenceActivity, new ViewFinder(preferenceActivity));    }    public static void inject(Object handler, View view) {        injectObject(handler, new ViewFinder(view));    }    public static void inject(Object handler, Activity activity) {        injectObject(handler, new ViewFinder(activity));    }.......    private static void injectObject(Object handler, ViewFinder finder) {        Class handlerType = handler.getClass();        ContentView contentView = (ContentView)handlerType.getAnnotation(ContentView.class);        if(contentView != null) {            try {                Method fields = handlerType.getMethod("setContentView", new Class[]{Integer.TYPE});                fields.invoke(handler, new Object[]{Integer.valueOf(contentView.value())});            } catch (Throwable var28) {                LogUtils.e(var28.getMessage(), var28);            }        }        Field[] var30 = handlerType.getDeclaredFields();        int var7;        if(var30 != null && var30.length > 0) {            Field[] var8 = var30;            var7 = var30.length;            for(int method = 0; method < var7; ++method) {                Field methods = var8[method];                ViewInject viewInject = (ViewInject)methods.getAnnotation(ViewInject.class);                if(viewInject != null) {                    try {                        View annotations = finder.findViewById(viewInject.value(), viewInject.parentId());                        if(annotations != null) {                            methods.setAccessible(true);                            methods.set(handler, annotations);                        }                    } catch (Throwable var27) {                        LogUtils.e(var27.getMessage(), var27);                    }                } else {                    ResInject var35 = (ResInject)methods.getAnnotation(ResInject.class);                    if(var35 != null) {                        try {                            Object annotation = ResLoader.loadRes(var35.type(), finder.getContext(), var35.id());                            if(annotation != null) {                                methods.setAccessible(true);                                methods.set(handler, annotation);                            }                        } catch (Throwable var26) {                            LogUtils.e(var26.getMessage(), var26);                        }                    } else {                        PreferenceInject var37 = (PreferenceInject)methods.getAnnotation(PreferenceInject.class);                        if(var37 != null) {                            try {                                Preference e = finder.findPreference(var37.value());                                if(e != null) {                                    methods.setAccessible(true);                                    methods.set(handler, e);                                }                            } catch (Throwable var25) {                                LogUtils.e(var25.getMessage(), var25);                            }                        }                    }                }            }        }        Method[] var31 = handlerType.getDeclaredMethods();        if(var31 != null && var31.length > 0) {            Method[] var34 = var31;            int var33 = var31.length;            for(var7 = 0; var7 < var33; ++var7) {                Method var32 = var34[var7];                Annotation[] var36 = var32.getDeclaredAnnotations();                if(var36 != null && var36.length > 0) {                    Annotation[] var14 = var36;                    int var13 = var36.length;                    for(int var39 = 0; var39 < var13; ++var39) {                        Annotation var38 = var14[var39];                        Class annType = var38.annotationType();                        if(annType.getAnnotation(EventBase.class) != null) {                            var32.setAccessible(true);                            try {                                Method e1 = annType.getDeclaredMethod("value", new Class[0]);                                Method parentIdMethod = null;                                try {                                    parentIdMethod = annType.getDeclaredMethod("parentId", new Class[0]);                                } catch (Throwable var24) {                                    ;                                }                                Object values = e1.invoke(var38, new Object[0]);                                Object parentIds = parentIdMethod == null?null:parentIdMethod.invoke(var38, new Object[0]);                                int parentIdsLen = parentIds == null?0:Array.getLength(parentIds);                                int len = Array.getLength(values);                                for(int i = 0; i < len; ++i) {                                    ViewInjectInfo info = new ViewInjectInfo();                                    info.value = Array.get(values, i);                                    info.parentId = parentIdsLen > i?((Integer)Array.get(parentIds, i)).intValue():0;                                    EventListenerManager.addEventMethod(finder, info, var38, handler, var32);                                }                            } catch (Throwable var29) {                                LogUtils.e(var29.getMessage(), var29);                            }                        }                    }                }            }        }    }}

在这里源码过长,我们可以看看主要的几个过程:

1.调用 ViewUtils 的 inject 方法,顺便也创建了一个 ViewFinder。

2.通过反射找到所在此上下文中的 ContentView 注解,并找到该上下文的布局,可以看到所有的解释都在 injectObject方法中。

3.同上,通过反射找到此上下文中的 ViewInject 注解,并绑定其注解的控件上、包括资源id和监听事件绑定。


在做一切事情之前就是找到主界面:

Class handlerType = handler.getClass();        ContentView contentView = (ContentView)handlerType.getAnnotation(ContentView.class);        if(contentView != null) {            try {                Method fields = handlerType.getMethod("setContentView", new Class[]{Integer.TYPE});                fields.invoke(handler, new Object[]{Integer.valueOf(contentView.value())});            } catch (Throwable var28) {                LogUtils.e(var28.getMessage(), var28);            }        }
在一开始就通过getAnnotation的方法寻找该上下文中指定类型的注解,也就是ContentView类型。这是什么类型呢?就是我们在Activity中最顶层的第一行注解:

@ContentView(R.layout.activity_main)

在没有找到该注解执行接下来的代码,否则会通过反射机制来完成setContentView的界面绑定过程。

接下来就是要绑定控件和资源,因为这里需要使用ViewFinder类,所以我们先看看这个类中提供了什么功能:

public class ViewFinder {    private View view;    private Activity activity;    private PreferenceGroup preferenceGroup;    private PreferenceActivity preferenceActivity;    public ViewFinder(View view) {        this.view = view;    }    public ViewFinder(Activity activity) {        this.activity = activity;    }    public ViewFinder(PreferenceGroup preferenceGroup) {        this.preferenceGroup = preferenceGroup;    }    public ViewFinder(PreferenceActivity preferenceActivity) {        this.preferenceActivity = preferenceActivity;        this.activity = preferenceActivity;    }    public View findViewById(int id) {        return this.activity == null?this.view.findViewById(id):this.activity.findViewById(id);    }    public View findViewByInfo(ViewInjectInfo info) {        return this.findViewById(((Integer)info.value).intValue(), info.parentId);    }    public View findViewById(int id, int pid) {        View pView = null;        if(pid > 0) {            pView = this.findViewById(pid);        }        View view = null;        if(pView != null) {            view = pView.findViewById(id);        } else {            view = this.findViewById(id);        }        return view;    }    public Preference findPreference(CharSequence key) {        return this.preferenceGroup == null?this.preferenceActivity.findPreference(key):this.preferenceGroup.findPreference(key);    }    public Context getContext() {        return (Context)(this.view != null?this.view.getContext():(this.activity != null?this.activity:(this.preferenceActivity !    = null?this.preferenceActivity:null)));}}

其中可以看到熟悉的findViewById等方法,没错,这里就给ViewUtils提供查找控件的功能。

之后也可以看出在ViewUtils中查找ViewInject的解释对象并使用viewFinder.findViewById来完成控件绑定,所以在控件上看到@ViewInject(R.id.t1)
 的控件绑定注解,所有的解释工作都是在这里完成的。在这里也可以看出可以给出父控件的重载方法。


最后就要寻找被注解的监听事件,面对源码需要了解动态代理机制,这里可以看到所使用的是var32.getDeclaredAnnotations();的方法,之前见过和这个类似的方法是以需要寻找的注解class作为参数传递,就会找到该上下文所有对应的变量等,而这里是查找到所有的被标记注解的所有方法。

Method[] var31 = handlerType.getDeclaredMethods();        if(var31 != null && var31.length > 0) {            Method[] var34 = var31;            int var33 = var31.length;            for(var7 = 0; var7 < var33; ++var7) {                Method var32 = var34[var7];                Annotation[] var36 = var32.getDeclaredAnnotations();                if(var36 != null && var36.length > 0) {                    Annotation[] var14 = var36;                    int var13 = var36.length;                    for(int var39 = 0; var39 < var13; ++var39) {                        Annotation var38 = var14[var39];                        Class annType = var38.annotationType();                        if(annType.getAnnotation(EventBase.class) != null) {                            var32.setAccessible(true);                            try {                                Method e1 = annType.getDeclaredMethod("value", new Class[0]);                                Method parentIdMethod = null;                                try {                                    parentIdMethod = annType.getDeclaredMethod("parentId", new Class[0]);                                } catch (Throwable var24) {                                    ;                                }                                Object values = e1.invoke(var38, new Object[0]);                                Object parentIds = parentIdMethod == null?null:parentIdMethod.invoke(var38, new Object[0]);                                int parentIdsLen = parentIds == null?0:Array.getLength(parentIds);                                int len = Array.getLength(values);                                for(int i = 0; i < len; ++i) {                                    ViewInjectInfo info = new ViewInjectInfo();                                    info.value = Array.get(values, i);                                    info.parentId = parentIdsLen > i?((Integer)Array.get(parentIds, i)).intValue():0;                                    EventListenerManager.addEventMethod(finder, info, var38, handler, var32);                                }                            } catch (Throwable var29) {                                LogUtils.e(var29.getMessage(), var29);                            }                        }                    }                }            }        }

还有就是在16行可以看到一个EventBase的接口,点进去可以看到他是一个接口注解,所有的监听事件注解也都被该注解注释并向其中添加属性:listenerType(监听器.class),listenerSetter(调用监听器的方法名),mehodName(监听器中方法的名字)。进入for循环后在 20行 和 24行 会去寻找注解中的参数,也就是我们在监听控件上添加参数的资源ID,之后就根据Id数组循环绑定监听器,这里运用到EventListenerManager类。

public class EventListenerManager {    private static final DoubleKeyValueMap<ViewInjectInfo, Class<?>, Object> listenerCache = new DoubleKeyValueMap();    private EventListenerManager() {    }    public static void addEventMethod(ViewFinder finder, ViewInjectInfo info, Annotation eventAnnotation, Object handler, Method method) {        try {            View e = finder.findViewByInfo(info);            if(e != null) {                EventBase eventBase = (EventBase)eventAnnotation.annotationType().getAnnotation(EventBase.class);                Class listenerType = eventBase.listenerType();                String listenerSetter = eventBase.listenerSetter();                String methodName = eventBase.methodName();                boolean addNewMethod = false;                Object listener = listenerCache.get(info, listenerType);                EventListenerManager.DynamicHandler dynamicHandler = null;                if(listener != null) {                    dynamicHandler = (EventListenerManager.DynamicHandler)Proxy.getInvocationHandler(listener);                    addNewMethod = handler.equals(dynamicHandler.getHandler());                    if(addNewMethod) {                        dynamicHandler.addMethod(methodName, method);                    }                }                if(!addNewMethod) {                    dynamicHandler = new EventListenerManager.DynamicHandler(handler);                    dynamicHandler.addMethod(methodName, method);                    listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, dynamicHandler);                    listenerCache.put(info, listenerType, listener);                }                Method setEventListenerMethod = e.getClass().getMethod(listenerSetter, new Class[]{listenerType});                setEventListenerMethod.invoke(e, new Object[]{listener});            }        } catch (Throwable var14) {            LogUtils.e(var14.getMessage(), var14);        }    }    public static class DynamicHandler implements InvocationHandler {        private WeakReference<Object> handlerRef;        private final HashMap<String, Method> methodMap = new HashMap(1);        public DynamicHandler(Object handler) {            this.handlerRef = new WeakReference(handler);        }        public void addMethod(String name, Method method) {            this.methodMap.put(name, method);        }        public Object getHandler() {            return this.handlerRef.get();        }        public void setHandler(Object handler) {            this.handlerRef = new WeakReference(handler);        }        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            Object handler = this.handlerRef.get();            if(handler != null) {                String methodName = method.getName();                method = (Method)this.methodMap.get(methodName);                if(method != null) {                    return method.invoke(handler, args);                }            }            return null;        }    }}


0 0
原创粉丝点击