Android-模块化通信-简单实用的android spi机制

2023-11-12


前言

为了实现Android-模块化通信-需要一套简单实用的android spi机制。


一、spi是什么?

SPI即 Service Provider Interface,它是为接口提供寻找实现服务类,类似IOC的思想。

二、ServiceLoader

java中的spi, ServiceLoader尤为重要, 抛开注解生成代码和Transform注册

  • load 可以使用map实现,需要Class<T> baseClass,String servicePath构建返回约束和访问参数
  • register 项目经常需要一些全局唯一的Service以及使用时创建Service,注册参数有 path 索引唯一路径interfaceClass 接口类,obj 单实例/实例/惰性

·简单实现代码如下·

1.ExportTableLoader

/**
 * 作用描述: 导出表加载程序接口
 * 组件描述: #基础组件 #组件名 (子组件)
 * 组件版本: v1
 * 创建人 rentl
 * 创建日期 2022/2/4 21:23
 * 修改日期 2022/2/4 21:23
 */
public interface ExportTableLoader {

    /**
     * 输出
     *
     * @param exporter
     * @implNote {@link ExportTable#initTables(cn.mashang.export_table_java.ExportTableLoader[])}
     */
    void export(ExportTable.Exporter exporter);
}

2.ExportTable


/**
 * 作用描述: 导出表
 * 组件描述: #基础组件 #组件名 (子组件)
 * 组件版本: v1
 * 创建人 rentl
 * 创建日期 2022/2/4 21:21
 * 修改日期 2022/2/4 21:21
 */
public final class ExportTable {
    /**
     * 服务列表map
     */
    final static Map<Class<?>, List<Object>> serviceListMap = new HashMap<>();
    /**
     * 新服务地图
     */
    final static Map<String, Object> newServiceMap = new HashMap<>();
    /**
     * 单例服务图
     */
    final static Map<String, Object> singletonServiceMap = new HashMap<>();

    private static ExportTableLoader[] tables = null;
    private static final ExportTable.Exporter exporter = new ExportTable.Exporter();

    private ExportTable() {
    }

    public static void initTables(ExportTableLoader[] tables) {
        ExportTable.tables = tables;
        init();
    }

    public static Exporter getExporter() {
        return exporter;
    }

    private static void init() {
        if (tables == null) {
            return;
        }
        for (ExportTableLoader table : tables) {
            if (table == null) {
                continue;
            }
            table.export(exporter);
        }
    }

    /**
     * 导出表 出口商
     */
    public final static class Exporter {
        Exporter() {
        }

        /**
         * 将单个实例放入路线图
         *
         * @param path           索引唯一路径
         * @param interfaceClass 接口类
         * @param proxyObj       单实例代理对象
         */
        private void putSingletonToRoadMap(final String path, @SuppressWarnings("rawtypes") final Class interfaceClass, final Object proxyObj) {
            if (path == null || path.isEmpty()) {
                throw new IllegalArgumentException("putSingleInstance args: path is null");
            }
            if (proxyObj == null) {
                throw new IllegalArgumentException("putSingleInstance args: proxyObj is null");
            }
            if (singletonServiceMap.containsKey(path)) {
                throw new UnsupportedOperationException("putSingleInstance args: path is exist");
            }
            if (singletonServiceMap.containsValue(proxyObj)) {
                throw new UnsupportedOperationException("putSingleInstance args: proxyObj is exist");
            }
            singletonServiceMap.put(path, proxyObj);

            //all
            List<Object> objects = serviceListMap.get(interfaceClass);
            //noinspection Java8MapApi
            if (objects == null) {
                objects = new ArrayList<>();
                serviceListMap.put(interfaceClass, objects);
            }
            objects.add(proxyObj);
        }

        /**
         * 设置单实例
         *
         * @param path           索引唯一路径
         * @param interfaceClass 接口类
         * @param obj            单实例
         */
        public <T> void setSingleton(final String path, final Class<T> interfaceClass, final T obj) {
            putSingletonToRoadMap(path, interfaceClass, obj);
        }

        /**
         * 使用惰性设置单实例
         *
         * @param path           索引唯一路径
         * @param interfaceClass 接口类
         * @param obj            单实例
         * @param <T>            泛型类
         */
        public <T> void setSingletonByLazy(final String path, final Class<T> interfaceClass, final LazyLoader<T> obj) {
            putSingletonToRoadMap(path, interfaceClass, obj);
        }


        /**
         * 将新实例放入路线图 (全局)
         *
         * @param path           索引唯一路径
         * @param interfaceClass 接口类
         * @param proxyObj       单实例代理对象
         */
        private void putNewInstanceToRoadMap(final String path, @SuppressWarnings("rawtypes") final Class interfaceClass, final Object proxyObj) {
            if (path == null || path.isEmpty()) {
                throw new IllegalArgumentException("setNewInstance args: path is null");
            }
            if (proxyObj == null) {
                throw new IllegalArgumentException("setNewInstance args: proxyObj is null");
            }
            if (newServiceMap.containsKey(path)) {
                throw new UnsupportedOperationException("setNewInstance args: path is exist");
            }
            if (newServiceMap.containsValue(proxyObj)) {
                throw new UnsupportedOperationException("setNewInstance args: proxyObj is exist");
            }
            newServiceMap.put(path, proxyObj);

            //all
            List<Object> objects = serviceListMap.get(interfaceClass);
            //noinspection Java8MapApi
            if (objects == null) {
                objects = new ArrayList<>();
                serviceListMap.put(interfaceClass, objects);
            }
            objects.add(proxyObj);
        }

        /**
         * 设置对象实例惰性创建(推荐)
         *
         * @param path           索引唯一路径
         * @param interfaceClass 接口类
         * @param obj            单实例
         * @param <T>            泛型类
         */
        public <T> void setNewInstanceByLazy(final String path, final Class<T> interfaceClass, final LazyLoader<T> obj) {
            putNewInstanceToRoadMap(path, interfaceClass, obj);
        }

        /**
         * 设置对象实例反射创建(不推荐)
         *
         * @param path           索引唯一路径
         * @param interfaceClass 接口类
         * @param implementClass 单实例
         * @param <T>            泛型类
         */
        public <T> void setNewInstanceClass(final String path, final Class<T> interfaceClass, final Class<T> implementClass) {
            putNewInstanceToRoadMap(path, interfaceClass, implementClass);
        }

    }
}

3.LazyLoader

/**
 * 作用描述: 懒加载程序
 * 组件描述: #基础组件 #组件名 (子组件)
 * 组件版本: v1
 * 创建人 rentl
 * 创建日期 2022/2/8 15:02
 * 修改日期 2022/2/8 15:02
 */
public class LazyLoader<T> {
    private final Object lock = new Object();
    private final Supplier<T> initializer;
    private volatile T value;

    public LazyLoader(Supplier<T> initializer) {
        this.initializer = initializer;
    }

    /**
     * 直接创建value
     */
    public T createValue() {
        return initializer.get();
    }

    /**
     * 获取value,如果null则创建
     */
    public T getValue() {
        if (value == null) {
            synchronized (lock) {
                if (value == null) {
                    value = initializer.get();
                }
            }
        }
        return value;
    }

    public boolean isCreated() {
        return value != null;
    }
}

4.ServiceLoader<T>

package cn.mashang.export_table_java;

import java.util.ArrayList;
import java.util.List;

/**
 * 作用描述: 服务加载程序
 * 组件描述: #基础组件 #组件名 (子组件)
 * 组件版本: v1
 * 创建人 rentl
 * 创建日期 2022/2/8 20:12
 * 修改日期 2022/2/8 20:12
 */
public final class ServiceLoader<T> {

    private final Class<T> serviceBaseClass;
    private final String serviceName;

    public static <T> ServiceLoader<T> build(Class<T> baseClass) {
        if (baseClass == null) {
            throw new RuntimeException("ServiceLoader baseClass class can't be null");
        }
        return new ServiceLoader<>(baseClass);
    }

    private ServiceLoader(Class<T> serviceBaseClass) {
        this.serviceBaseClass = serviceBaseClass;
        this.serviceName = serviceBaseClass.getName();
    }

    /**
     * 获取所有服务
     */
    public List<T> getAllService() {
        List<Object> objects = ExportTable.serviceListMap.get(serviceBaseClass);
        List<T> tList = new ArrayList<>();
        if (objects != null && !objects.isEmpty()) {
            for (Object object : objects) {
                if (object instanceof LazyLoader) {
                    Object value = ((LazyLoader) object).getValue();
                    try {
                        tList.add((T) value);
                    } catch (ClassCastException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                try {
                    tList.add((T) object);
                } catch (ClassCastException e) {
                    e.printStackTrace();
                }
            }
        }
        return tList;
    }

    /**
     * 获取单实例
     *
     * @return <T>
     */
    public T getSingletonService(final String servicePath) {
        if (servicePath == null || servicePath.isEmpty()) {
            throw new IllegalArgumentException("getSingleInstance args: path is null");
        }
        try {
            Object o = ExportTable.singletonServiceMap.get(servicePath);
            if (o instanceof LazyLoader) {
                return (T) ((LazyLoader<?>) o).createValue();
            }
            return (T) o;
        } catch (ClassCastException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 新实例
     *
     * @param servicePath
     */
    public T newInstance(final String servicePath) {
        if (servicePath == null || servicePath.isEmpty()) {
            throw new IllegalArgumentException("newInstance args: path is null");
        }
        final Object o = ExportTable.newServiceMap.get(servicePath);
        if (o instanceof LazyLoader) {
            try {
                return (T) ((LazyLoader<?>) o).createValue();
            } catch (ClassCastException e) {
                e.printStackTrace();
            }
        }
        try {
            return (T) o.getClass().getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}


三、应用

lib_arouter

/**
 * 作用描述: ARouter应用程序生命周期
 * 组件描述:
 * 创建人 rentl
 * 创建日期 2022/2/9
 * 修改日期 2022/2/9
 */
public class ARouterApplicationLifeCycle extends BaseApplicationLifeCycle {

    public ARouterApplicationLifeCycle(int initOrder) {
        super(initOrder);
    }

    @Override
    public void onCreate() {
        if (ProcessHelper.isMainProcess()) { //根据需要
            ARouterApp.initARouter(BaseAndroid.StaticCall.sApplication);
        }
    }
}

public class LibOkHttpExportTable implements ExportTableLoader {

    @Override
    public void export(ExportTable.Exporter exporter) {
        exporter.setSingleton(TableConstants.Path.OkhttpApplicationLifeCycle, ApplicationLifeCycle.class, new OkhttpApplicationLifeCycle(999));
    }
}

public class LibARouterExportTable implements ExportTableLoader {
    @Override
    public void export(ExportTable.Exporter exporter) {
        exporter.setSingleton(TableConstants.Path.ARouterApplicationLifeCycle, ApplicationLifeCycle.class, new ARouterApplicationLifeCycle(1000));
    }
}

lib_okhttp

public class OkhttpApplicationLifeCycle extends BaseApplicationLifeCycle {
}

lib_baseAndroid

public class BaseAndroidApplication extends Application {
  @Override
    protected void attachBaseContext(Context base) {
    uper.attachBaseContext(base);
        MultiDex.install(this);
        BaseAndroid.installRun(this);
        List<ApplicationLifeCycle> allService = ServiceLoader.build(ApplicationLifeCycle.class).getAllService();
        Collections.sort(allService, (o1, o2) -> Integer.compare(o2.getInitOrder(), o1.getInitOrder()));
        mApplicationLifeCycles = allService;
        for (ApplicationLifeCycle applicationLifeCycle : mApplicationLifeCycles) {
            applicationLifeCycle.onBaseContextAttached(base);
        }
    }
}

app

public final class AppExtTable implements ExportTableLoader {

    public static void init() {
        ExportTable.initTables(new ExportTableLoader[]{
                new AppExtTable(),
                new LibOkHttpExportTable(),
                new LibARouterExportTable(),
                //动态集成模块table可以用反射
        });
    }

    @Override
    public void export(ExportTable.Exporter exporter) {
        ExportTable.getExporter().setSingleton(TableConstants.ConstantsPath.APP_BUILD_COMPUTER_TIME, long.class, BuildEvn.BUILD_COMPUTER_TIME);
    }
}

注册ExportTableLoader 相关类可以通过gradle 分配一个文件夹
baseAndroid.gradle

android {
    sourceSets {
        main {
            if (export_table) {
                def export_table_src_dir = 'src/export_table' //java代码文件夹
                if (hasAppPlugin) {
                    //默认包目录
                    def javaFiles = new File(projectDir, "$export_table_src_dir/cn/mashang/export_table")
                    if (!javaFiles.exists()) {
                        javaFiles.mkdirs()
                    }
                }
                java.srcDir(project.file(export_table_src_dir))
            }
        }
    }
}    

在这里插入图片描述

四、总结

例如:以上就是简单实用的android spi机制。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android-模块化通信-简单实用的android spi机制 的相关文章

  • 我们如何从 Android 通知中提取 bigpicturestyle 图像?

    我有一个通知侦听器服务 可以读取来自其他应用程序的通知 经用户许可 并提取所有数据 能够访问除通知展开视图中显示的图像之外的所有内容 我也在阅读 EXTRA PICTURE 意图值 if extras containsKey Notific
  • 如何使用 kotlin Android 从 Url 读取 JSON?

    我正在使用 kotlin 来开发应用程序 现在我想从服务器获取 JSON 数据 在java中 我实现了Asyntask以及Rxjava来从Url读取JSON 我也在谷歌搜索 但我无法获得满足我的要求的正确详细信息 如何使用 kotlin 从
  • Android中如何通过ViewModel类在Activity和Fragment之间共享数据?

    我想知道是否可以传递一个在 Activity 类中声明的字符串数据 并将该字符串数据传递给 ViewModel 类 然后将数据传递给 Fragment 类 视图模型类 class TimeTableViewModel extends Vie
  • 如何在再次恢复应用程序时不重新加载webview?

    我做了一个网络视图应用程序 一切都运转良好 但是 当用户点击后退按钮并打开 恢复 应用程序时 它会重新加载 Web 视图 我该如何预防呢 MainActivity java public class MainActivity extends
  • 使用 NEON 内在函数除以浮点数

    我当时正在处理四个像素的图像 这是在armv7对于 Android 应用程序 我想分一个float32x4 t向量由另一个向量组成 但其中的数字与大约不同0 7 to 3 85 在我看来 除法的唯一方法是使用右移 但这是针对一个数字2 n
  • android ndk 多点触控?

    我正在编写一个仅使用本机代码的应用程序 那么ndk中是否可以获取多点触控事件呢 我感觉我已经搜索了整个网络 但什么也没找到 有谁知道如何做到这一点 是的 您可以检查名为native activity查看如何获取输入事件 寻找engine h
  • 使用 setText 更改文本后如何更新屏幕?

    现在我正在使用以下代码来更新TextView txtMain setText new text 该代码执行后 屏幕不会更新新文本 有没有办法可以强制文本立即更新 该代码执行后 屏幕不会更新新文本 一旦您将控制权归还给 Android 就应该
  • 通过意图使用多个电话号码添加新联系人

    我想使用添加新联系人ContactsContract Intents Insert 但问题是我不知道可以有多少个电话号码 据我了解 我只能使用三个电话号码PHONE SECONDARY PHONE TERTIARY PHONE常数 有什么办
  • 多种语言的多种字体

    我最近在开发应用程序时遇到了一种情况 我必须在文本视图中显示不同的语言 目前我正在展示一些使用字体 字体像这样 Typeface tf Typeface createFromAsset this getAssets DroidHindi t
  • Google 地图位于活动的中间区域

    我正在使用 Android studio 2 1 2 我检查了一下 大多数问题要么使用旧版本的 Android studio 要么使用一些旧的类 这些类不适用于我的情况 从文件 gt 新项目 gt 我使用了选项Google Maps Act
  • RecyclerView 在聊天屏幕中的 notificationDataSetChanged 上滚动到顶部

    我正在尝试使用 recyclerView 创建消息传递类型的屏幕 该屏幕将从底部开始 并在用户到达聊天顶端时加载更多数据 但我面临着这个奇怪的问题 我的 recyclerView 在调用 notificationDataSetChanged
  • TextView 宽度匹配drawableTop 宽度

    有什么办法可以使TextView width匹配复合可绘制宽度 XML 例如对于 xml 代码
  • 表面视图+gl表面视图+框架布局

    我是 java 和 OpenGL 的新手 我正在尝试获得一个相机预览屏幕 能够 同时显示 3D 对象 浏览完样本后 api 演示 我想结合示例的代码 api 演示就足够了 但不知何故它不起作用 迫使我 启动时关闭 错误被称为空指针 例外 有
  • Android:canvas.drawBitmap() 方法无法正常工作

    我已经发布了两个与此相关的问题 请参考此自定义饼图 1 https stackoverflow com questions 28343600 customize pie chart in quarter shape at the botto
  • 在 Xamarin 中隐藏软键盘

    如何隐藏软键盘以便在聚焦时显示Entry在 Xamarin forms 便携式表单项目中 我假设我们必须为此编写特定于平台的渲染器 但以下内容不起作用 我创建自己的条目子类 public class MyExtendedEntry Entr
  • 在android中从SD卡上传图像到facebook

    我无法从 SD 卡上传 Facebook 上的图像 我使用了下面的代码 但它没有给我错误 但同时它没有上传图像 byte data null try FileInputStream fis new FileInputStream filep
  • Android:分配内存失败

    我正在尝试创建一个具有 2047 mb 内存的模拟器 当我运行它时 我收到此错误 2011 02 22 14 24 14 Emulator 2011 02 22 14 24 14 Emulator This application has
  • android系统用户和linux root用户有什么区别

    当我将手机连接到电脑并使用adb shell与我的手机通信并输入的命令ps命令输出当前在我的手机上运行的进程信息 我发现有两个特殊用户 一个是root 另一个是system 据我所知 Android是基于linux的 所以root用户是最大
  • JetPack Compose - 卡中行中的weight() 不起作用

    创建 Android 应用程序时 我将一些可组合项放在卡片的一行中 如下所示 但它没有按我的预期工作 我添加 weight 1f 的可组合项不再显示 data class Test val title String val text Str
  • 在线性布局内的 ScrollView 内并排对齐 TextView

    我有一个带有滚动视图的线性布局 我想保留它的当前格式 但只需将 textView2a 和 textView3a 并排放置 而不会破坏我当前的布局格式 我已经包含了我最近的尝试 但它们似乎不正确 提前致谢 Java菜鸟 当前有效的 XML

随机推荐