2021-07-29尝试手撸安卓框架

闲来无事,尝试写写框架吧

前言

最近呢,准备换公司了。平时都是折腾后台框架,暂时很累,不想折腾了,没思路。拿安卓练练手吧。
但是呢,安卓的代码,给我的感觉就是雍总,杂乱无章,可能我还活在上个世纪吧。安装最新的android studio,安装ButterKnife,安装code generator,重启

	Plugin Error
					Plugin "Android ButterKnife Injections (Support Kotlin)" is incompatible (supported only in IntelliJ IDEA).
					Plugin "Android Code Generator" is incompatible (supported only in IntelliJ IDEA).

android studio 不就是idea的吗?难道不兼容?好吧,我自己来撸一套吧,毕竟java都大同小异
当然,这套框架不能全部手撸,部分工具直接从我源项目拿来了,比如ReflectUtil,StringUtil,ParameterUtil,Assert等等。

目标

一个高度解耦,高度扩展,高兼容性,高便利性,低代码量的框架
目前的效果

package com.yanan.todo;

import com.chaychan.library.BottomBarItem;
import com.chaychan.library.BottomBarLayout;
import com.yanan.framework.StringHolder;
import com.yanan.framework.classhandler.Theme;
import com.yanan.framework.event.BindEvent;
import com.yanan.framework.event.EventContext;
import com.yanan.framework.event.Synchronized;
import com.yanan.framework.fieldhandler.BindFragment;
import com.yanan.framework.fieldhandler.MainFragment;
import com.yanan.framework.fieldhandler.SqlLite;
import com.yanan.framework.fieldhandler.Values;
import com.yanan.framework.message.MessageBus;
import com.yanan.framework.methodhandler.AfterInjected;
import com.yanan.framework.classhandler.ContextView;
import com.yanan.framework.classhandler.NoActionBar;
import com.yanan.framework.Plugin;
import com.yanan.framework.fieldhandler.Service;
import com.yanan.framework.fieldhandler.Value;
import com.yanan.framework.fieldhandler.Views;
import com.yanan.framework.event.Click;
import com.yanan.todo.ui.TestFragment;


@ContextView(R.layout.activity_main)
@NoActionBar
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Main_Activity";
    @Views(R.id.main_bottom_bar) 
    private BottomBarLayout mBottomBarLayout;
    @Service //通过Plugin获取Fragment对象
    private FragmentManager fragmentManager;
    @Service
    private FragmentTransaction fragmentTransaction;
    @Service //使用plugin生成一个fragment对象
    @BindFragment(R.id.fragment) //绑定到fragment
    private TestFragment testFragment;
    @MainFragment//表明当前Fragment为主Fragment
    @Service
    @BindFragment(R.id.fragment)
    private com.yanan.todo.ui.MainFragment homeFragment;
    @Value(R.string.app_name) //获取资源数据
    private String app_name;
    @Values("hello \\{{app_name}\\}") //获取资源数据
    private String app_names;
    Toast toast;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Plugin.inject(this);
        Toast.makeText(getApplication(),app_names,Toast.LENGTH_LONG);
        Log.d(TAG,"软件名称:"+app_name+","+app_names);
    }
    @AfterInjected
    public void onInited(){
        Log.d("YA_NAN_PLUGIN","test method");
    }
    @Synchronized("请勿重复点击")
    @Click(R.id.fab)
    public void onFloatClick(View view){
        startActivity(new Intent(this,FormActivity.class));
    }
    @Click(R.id.fragment)
    public void onItemSelected(FrameLayout view){
    }
    @BindEvent(view = R.id.main_bottom_bar,event = "OnItemSelectedListener")
    public void onItemSelected(BottomBarItem bottomBarItem, int previousPosition, int currentPosition) {
        //do some thing
    }
}

第一个功能:获取要处理的实例

虽然安卓的编程是java,但activity的生命周期却是从onCreate开始,那么安卓的很多属性都只有在onCreate之后才能得到。那么,我们只需要在onCreate之后调用我们的方法,并将activity传入,那我们的框架就能处理activity了。
我们把对activity的处理方式分为三种

  1. 对class头的处理
  2. 对Field的处理
  3. 对Method的处理
    那么,我们首先需要拿到类里各个地方的注解,不想写注释
    ** 任何开发完成以前写注释和优化都是耍流氓 **
    ** 什么,写blog的时间都有,不写注释 ? 肤浅,谁说有时间写博客就有时间写注释 **

public static void inject(Activity context){
        inject(context,context);
}

public static void inject(Activity context,Object instance){
        Assert.isNotNull(context);
        Assert.isNotNull(instance);
        currentActivity.set(context);
        injectClass(context,instance);
        Field[] fields = ReflectUtils.getAllFields(instance.getClass());
        for(Field field : fields){
            injectField(context,instance,field);
        }
        injectMethod(context,instance);
    }
    
public static void injectMethod(Activity context, Object instance) {
        Class<?> classes = instance.getClass();
        Method[] methods = classes.getMethods();
        for(Method method : methods){
            Annotation[] annotations = method.getAnnotations();
            for(Annotation annotation : annotations){
                Class<? extends Annotation> annoType = annotation.annotationType();
                //do some thing
            }
        }
    }

    public static void injectClass(Activity context, Object instance) {
        Class<?> classes = instance.getClass();
        setInstance(classes,instance);
        Annotation[] annotations = order(classes.getAnnotations());
        if(annotations != null){
            for(Annotation annotation : annotations){
                Class<? extends Annotation> annoType = annotation.annotationType();
                //do some thing
            }
        }
    }
    public static void injectField(Activity context,Object instance,Field field){
        Annotation[] annotations = order(field.getAnnotations());
        Log.d(TAG,field+"==>"+Arrays.toString(annotations));
        if(annotations == null || annotations.length == 0)
            return;
        for(Annotation annotation : annotations){
            Class<? extends Annotation> annoType = annotation.annotationType();
            //do some thing
        }
    }

这样,我们就可以拿到实例类所有方法,属性和头部的注解,这个类的主要功能就完成了一半了。下一步呢,就要根据类的注解去处理这些注解了。
我有想过使用我自己Plugin框架的那一套。放弃了,因为相对于安卓应用来说,太雍总了。那么,要实现即轻量级,又要高扩展的方式,那么就要用一些原始方法了。
因此,我们需要在这个类里提供一个容器(Container ),其实就一个HashMap,他的功能就是用于保存各种注释对应的处理器
嗯~应该是这样的

//属性处理集合
    private static final Map<Class<? extends Annotation>,FieldHandler<? extends Annotation>> handlerMap = new HashMap<>();
    //类处理集合
    private static final Map<Class<? extends Annotation>,ClassHandler<? extends Annotation>> classHandlerMap = new HashMap<>();
    //方法处理集合
    private static final Map<Class<? extends Annotation>,MethodHandler<? extends Annotation>> methodHandlerMap = new HashMap<>();
    //下面是注册功能
    public static <T extends Annotation> void register(Class<T> type, FieldHandler<T> handler){
        handlerMap.put(type,handler);
    }
    public static <T extends Annotation> void register(Class<T> type, ClassHandler<T> handler){
        classHandlerMap.put(type,handler);
    }
    public static <T extends Annotation> void register(Class<T> type, MethodHandler<T> handler){
        methodHandlerMap.put(type,handler);
    }

看吧,就是这么简单又朴实无华。
然后我们就需要有各自类型的接口的定义

public interface ClassHandler<T extends Annotation> {
    void process(Activity activity,Object instance,T annotation);
}
public interface FieldHandler<T extends Annotation> {
    void process(Activity activity,Object instance,Field field, T annotaion);
}
public interface MethodHandler<T extends Annotation> {
    void process(Activity activity, Object instance, Method method, T annotaion);
}

从新修改一下inject这写方法

 public static void injectMethod(Activity context, Object instance) {
        Class<?> classes = instance.getClass();
        Method[] methods = classes.getMethods();
        for(Method method : methods){
            Annotation[] annotations = method.getAnnotations();
            for(Annotation annotation : annotations){
                Class<? extends Annotation> annoType = annotation.annotationType();
                MethodHandler handler = methodHandlerMap.get(annoType);
                if(handler != null){
                    Log.d(TAG,"Annotations "+annoType.getSimpleName()+" use handler "+handler.getClass());
                    handler.process(context,instance,method,annotation);
                }
            }
        }
    }

    public static void injectClass(Activity context, Object instance) {
        Class<?> classes = instance.getClass();
        setInstance(classes,instance);
        Annotation[] annotations = order(classes.getAnnotations());
        if(annotations != null){
            for(Annotation annotation : annotations){
                Class<? extends Annotation> annoType = annotation.annotationType();
                ClassHandler handler = classHandlerMap.get(annoType);
                if(handler != null){
                    Log.d(TAG,"Annotations "+annoType.getSimpleName()+" use handler "+handler.getClass());
                    handler.process(context,instance,annotation);
                }
            }
        }
    }
    public static void injectField(Activity context,Object instance,Field field){
        Annotation[] annotations = order(field.getAnnotations());
        Log.d(TAG,field+"==>"+Arrays.toString(annotations));
        if(annotations == null || annotations.length == 0)
            return;
        for(Annotation annotation : annotations){
            Class<? extends Annotation> annoType = annotation.annotationType();
            FieldHandler handler = handlerMap.get(annoType);
            if(handler != null){
                Log.d(TAG,"Annotations "+annoType.getSimpleName()+" use handler "+handler.getClass());
                handler.process(context,instance,field,annotation);
            }
        }
    }

接口定义,注册功能都实现了,我们开发一个简单的注解 @Views把,他的作用就是根据id获取试图了

//注解定义
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER} )
public @interface Views {
    int value();
    boolean required() default false;
}
//注解的处理方法

public class ViewsHandler implements FieldHandler<Views> {
  
    @Override
    public void process(Activity activity,Object instance, Field field, Views views) {
        View view = activity.findViewById(views.value());
        Log.d(getClass().getSimpleName(),field+"==>"+view);
        if(view == null && views.required())
            throw new NullPointerException("view "+views.value()+" not found");
        try {
            ReflectUtils.setFieldValue(field,instance,view);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    
    }

搞定,但是,这个怎么注册到Plugin里的容器里呢。要不要写高级一点,搞个配置文件,搞个配置上下文。现实告诉我们,简单即是真

public class ViewsHandler implements FieldHandler<Views> {
   static {
        Plugin.register(Views.class,new ViewsHandler());
    }
    @Override
    public void process(Activity activity,Object instance, Field field, Views views) {
        View view = activity.findViewById(views.value());
        Log.d(getClass().getSimpleName(),field+"==>"+view);
        if(view == null && views.required())
            throw new NullPointerException("view "+views.value()+" not found");
        try {
            ReflectUtils.setFieldValue(field,instance,view);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

然后再启动的时候用Class.forName()一下吗?当然可以的,不过如果后面的处理器越来越多怎么办?那我可以用代码实现Class.forName()啊,在Plugin类加载的时候去扫描要加载组件包,这样只要类被加载到内存,就会执行对应的static代码块,就注册到容器了。因为安卓的打包机制和后台应用的区别,所以不能使用平时用的类扫描机制,然后百度了个dex版本的类扫描方法

static {
        try {
            loadPlugins("com.yanan");
        } catch (ClassNotFoundException e) {
            Log.e(TAG,"failed to init Plugin",e);
        } catch (IOException e) {
            Log.e(TAG,"failed to init Plugin",e);
        } catch (NoSuchFieldException e) {
            Log.e(TAG,"failed to init Plugin",e);
        } catch (IllegalAccessException e) {
            Log.e(TAG,"failed to init Plugin",e);
        }
    }
    public static void loadPlugins(String pack) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IOException {
        Log.d(TAG,"loaded plugins services:"+DexUtils.getClasses(pack).toString());
    }

dexUtils


public class DexUtils {
    private static final String TAG = "DEX_UTILS";

    private static List<DexFile> getMultiDex() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        BaseDexClassLoader dexLoader = (BaseDexClassLoader) Thread.currentThread().getContextClassLoader();
        Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
        Object pathList = ReflectUtils.getFieldValue(pathListField,dexLoader);
//        Field f = getField("pathList", ClassHelper.getClassHelper("dalvik.system.BaseDexClassLoader").getClass());
//        Object pathList = getObjectFromField(f, dexLoader);
//        Field f2 = getField("dexElements", getClassByAddressName("dalvik.system.DexPathList"));
//        Object[] list = getObjectFromField(f2, pathList);
        Object[] list = ReflectUtils.getDeclaredFieldValue("dexElements",pathList);
//        Field f3 = getField("dexFile", getClassByAddressName("dalvik.system.DexPathList$Element"));
//        Field f3 = ClassHelper.getClassHelper("dalvik.system.DexPathList$Element").getDeclaredField("dexFile");
        List<DexFile> res = new ArrayList<>();

        for (int i = 0; i < list.length; i++) {
            DexFile d = ReflectUtils.getDeclaredFieldValue("dexFile", list[i]);
            res.add(d);
        }
        return res;
    }
    /**
     *
     * @Description: 根据包名获得该包以及子包下的所有类
     * @param path 包名
     * @return List<Class>    包下所有类
     */
    public static List<Class> getClasses(String path) throws ClassNotFoundException, IOException, NoSuchFieldException, IllegalAccessException {
        List<DexFile> dexFiles = getMultiDex();
        List<Class> classes = new ArrayList<>();
        for(DexFile dexFile : dexFiles){
            Enumeration<String> enumeration = dexFile.entries();//获取df中的元素  这里包含了所有可执行的类名 该类名包含了包名+类名的方式
            while(enumeration.hasMoreElements()){
                String  className = enumeration.nextElement();
                if (className.startsWith(path)) {
                    Log.d(TAG,"find init class path :"+className);
                    classes.add(Class.forName(className));
                }
            }
        }
        return classes;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值