美文网首页Android收藏集Android开发经验谈Android开发
[Android]如何做一个崩溃率少于千分之三噶应用app(30

[Android]如何做一个崩溃率少于千分之三噶应用app(30

作者: CangWang | 来源:发表于2018-04-07 09:30 被阅读1239次

    大家好,我系苍王。
    这个系列已经出到了第30章节了,已经开通了已经有一年半的时间了。
    在一年半里,建立了千人的QQ大群,不少编辑也找过我编辑图书,也有同行找过我合作出公众号。但是个人的时间是有限的,并不可能全部愿望都实现。
    那么上一年就选了一件对这辈子非常有意义的事情,和电子工业出版社出版一本关于组件化技术的书。非常感谢陈晓猛编辑找到了我一同出书,也感谢在技术群中不断深讨组件化技术的群友们。
    书中重点介绍了使用组件化思想去搭建一个Android项目,介绍了组件化的思想,组件化的编程技术,多人管理组件化,组件化的编译优化,以及对项目演进的思想感悟。
    此书并不是只是介绍技术,也包含了我对一些生活的理解,技术思维的理解。
    京东淘宝当当均可以购买,有兴趣可以点击链接就可以跳转了。

    Android组件化架构

    以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。

    [Android]如何做一个崩溃率少于千分之三噶应用app--章节列表

    关于spi,其全名是Service Provider Interfaces。ServiceLoder用于动态加载接口实现类的加载器。
    1.其可以动态加载一些继承某个接口的实体类
    2.需要将实体类名声明到resources/META-INF/services目录,可以取巧使用@AutoService
    3.其加载的并不是单例,而且构造方法不带任何参数,因为ServiceLoader底层是使用了反射的机制来加载。
    4.加载文件顺序应该是按照resources/META-INF/services目录中顺序加载,所以如果使用@AutoService是不可控的。
    5.ServiceLoader继承iterator接口,可以像List一样遍历实体类。
    6.其实际也是通过反射来实现初始化操作,使用接口的方式使模块,ServiceLoader装载器、启动器之间更加解耦。
    7.比较适合于组件化中,模块入口初始化的统一加载场景。

    SPI原理图

    以下借用一个Modular框架中的加载为例
    1.声明接口

    public interface IModule {
        /**
         * 模块初始化,只有组建时才调用,用于开启子线程轮训消息
         */
        void init();
    
        /**
         * 模块ID
         *
         * @return 模块ID
         */
        int getModuleId();
    
        /**
         * 模块注册并连接成功后,可以做以下事情:
         * <p>
         * 1、注册监听事件
         * 2、发送事件
         * 3、注册服务
         * 4、调用服务
         */
        void afterConnected();
    }
    

    2.使用@AutoService,将全路径名写到resources/META-INF/services目录

    @AutoService(IModule.class)
    public class Module extends BaseModule {
        @Override
        public void afterConnected() {
    
    
        }
    
        @Override
        public int getModuleId() {
            return Constants.MODULE_B;
        }
    }
    

    3.使用ServiceLoder加载模块

    @Override//只有当是组建单独运行时,才当Application运行,才会走onCreate,最终打包时根本没有这个类
        public void onCreate() {
            super.onCreate();
            ……
    
            //自动注册服务器(如果是独立模块内声明只有一个IModule)
            ServiceLoader<IModule> modules = ServiceLoader.load(IModule.class);
            mBaseModule = (BaseModule) modules.iterator().next();
    
            //模块初始化
            mBaseModule.init();
            ……
        }
    
    public void onCreate() {
            super.onCreate();
            ……
    
            //SPI自动注册服务(主module装载的时候,已经将全部META_INF文件合并)
            ServiceLoader<IModule> modules = ServiceLoader.load(IModule.class);
            for (IModule module : modules) module.afterConnected();
        }
    

    使用看起来非常简单,我们研究一下ServiceLoader源码的特别之处。

        //调用静态load方法来初始化XXXInterface接口信息。
        public static <S> ServiceLoader<S> load(Class<S> service) {
            //获取当前线程ClassLoader
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            return ServiceLoader.load(service, cl);
        }
    
        //构建ServiceLoader对象
        public static <S> ServiceLoader<S> load(Class<S> service,
                                                ClassLoader loader)
        {
            return new ServiceLoader<>(service, loader);
        }
    
        private ServiceLoader(Class<S> svc, ClassLoader cl) {
            //检测接口是否否存在
            service = Objects.requireNonNull(svc, "Service interface cannot be null");
            //检测classloader是否为空,为空使用系统classloader加载器
            loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
            // Android-changed: Do not use legacy security code.
            // On Android, System.getSecurityManager() is always null.
            // acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
            reload();
        }
       
        public void reload() {
            //清理provides配置加载器
            providers.clear();
            //初始化懒加载迭代器
            lookupIterator = new LazyIterator(service, loader);
        }
    

    可以看到使用的是懒加载的迭代器,只有迭代器被使用的时候,才会真正初始化每一个继承接口的实体类。

       //判断是否有下一个对象
       private boolean hasNextService() {
                if (nextName != null) {
                    return true;
                }
                if (configs == null) {
                    try {
                        //PREFIX = "META-INF/services/"
                        //加载配置地址
                        String fullName = PREFIX + service.getName();
                        if (loader == null)
                            //加载配置
                            configs = ClassLoader.getSystemResources(fullName);
                        else
                            configs = loader.getResources(fullName);
                    } catch (IOException x) {
                        fail(service, "Error locating configuration files", x);
                    }
                }
               //解析配置文件,只要找到一个需要解析的接口就跳出
                while ((pending == null) || !pending.hasNext()) {
                    if (!configs.hasMoreElements()) {
                        return false;
                    }
                    //解析config的节点
                    pending = parse(service, configs.nextElement());
                }
               
                nextName = pending.next();
                return true;
            }
    

    通过反射完成接口类的初始化

            private S nextService() {
                if (!hasNextService())
                    throw new NoSuchElementException();
                String cn = nextName;
                nextName = null;
                Class<?> c = null;
                try {
                    //通过类路径名,加载类信息
                    c = Class.forName(cn, false, loader);
                } catch (ClassNotFoundException x) {
                    fail(service,
                         // Android-changed: Let the ServiceConfigurationError have a cause.
                         "Provider " + cn + " not found", x);
                         // "Provider " + cn + " not found");
                }
                if (!service.isAssignableFrom(c)) {
                    // Android-changed: Let the ServiceConfigurationError have a cause.
                    ClassCastException cce = new ClassCastException(
                            service.getCanonicalName() + " is not assignable from " + c.getCanonicalName());
                    fail(service,
                         "Provider " + cn  + " not a subtype", cce);
                    // fail(service,
                    //        "Provider " + cn  + " not a subtype");
                }
                try {
                   //反射初始化类,并转化成接口
                    S p = service.cast(c.newInstance());
                    //记录映射关系
                    providers.put(cn, p);
                    //返回接口实体
                    return p;
                } catch (Throwable x) {
                    fail(service,
                         "Provider " + cn + " could not be instantiated",
                         x);
                }
                throw new Error();          // This cannot happen
            }
    

    ServiceLoader实际还是通过路径名反射来完成,只是其通过配置到META_INF中的目录文件来完成解耦。
    ServiceLoader使用场景是用于不需要区分module加载顺序的情况,如果有加载顺序,还需要重新排序后再初始化方法,这里最后还是使用优先级机制。

    SPI的原理上,还是通过配置文件反射加载类,而在开编的时候已经介绍了SPI的优点和局限性,跳出SPI,依然能做一个更灵活更可控的加载机制,例如json脚本,xml脚本动态更新。

    相关文章

      网友评论

        本文标题:[Android]如何做一个崩溃率少于千分之三噶应用app(30

        本文链接:https://www.haomeiwen.com/subject/lxivnxtx.html