ProcessRoute for Android 跨进程事件路由框架

用于解决安卓组件化下各组件间交叉通讯的问题

支持插件化和组件化(注意:插件化需要引导用户安装插件,介意的话可以无视插件化)

与ARoater不同的是,ProcessRoute是依托AIDL作为通讯纽带,以接口协议为中间件,采取事件机制进行通讯的框架

支持跨进程发送消息,且自由回调

通讯协议为Interface接口,可指定通讯进程、输入参数(数量最大10个)、回调参数

全程对象化传输,无需关注对象的序列化问题(前提:各组件共同依赖于模型包)

自动处理跨进程多线程异步问题

可跟踪通讯过程中及接口协议关联的独立组件执行过程中所有异常

无需配置,只需在各组件依赖的Common包引入即可集成

更新日志

2019-04-24

更新了processroute到1.0.15,修复了部分已知bug

2019-04-23

更新了hookgradle到1.0.2,修复了部分已知bug

2019-03-20

更新了processroute到1.0.13,新增了【组件化 转 插件化】解决方案

2019-03-19

更新了hookgradle插件,新增了组件化R非常量解决方案

2019-02-27

优化了错误日志的输出方式,可通过控制台看到错误信息

修复了部分已知bug

使用方式

【前提】

组件单独打包测试时,测试机请关闭链式启动的控制

您将各组件包括主工程均引用了一个公用库(下面统称:Common)

您将各组件包括主工程的模型均放入了引用的Common包中

您创建了一个组件名为Plug1,并以独立运行APK的方式配置好了

【Let`s start】

通过接口约束远程方法、请求参数、回调参数为双方共同遵守的协议

//在Common中新建一个接口协议RemoteServiceOfPlug1,可以对外曝光很多功能,这里已登录为例
//login:该方法名用于外部组件访问时识别其调用的功能
//String username / String password: 这是实现登录需要的参数,参数支持[0-10]个,类型不限(基本类型必须换成其包装类)
//CallbackProcessor callbackProcessor: 这是回调处理器,协议的所有方法都必须将该参数放置最前,是必输参数
//CallbackProcessor callbackProcessor: 用于约束回调的类型,类型不限
//void:协议无需返回参数,无论返回什么值均为无效,所有回调均使用CallbackProcessor处理
public interface RemoteServiceOfPlug1 {
/**
* 登录
* @param username 姓名
* @param password 密码
* @param callbackProcessor @String 回调返回token
*/
void login(CallbackProcessor callbackProcessor,String username, String password);
}
通过在组件内实现协议来与宿主的解耦
//在Plug1中新建一个接口协议RemoteServiceOfPlug1Impl实现RemoteServiceOfPlug1协议
//在这里你可以尽情的玩耍了
//callbackProcessor.callback(): 改方法用于回调,如果你需要在别的地方回调可将callbackProcessor传递过去而无需关心序列化问题
//"Hello!": 该参数与依赖,您必须使用相同的类型进行回调
public class RemoteServiceOfPlug1Impl implements RemoteServiceOfPlug1 {
@Override
public void login(CallbackProcessor callbackProcessor,String username, String password) {
System.out.print("username=>"+ username);
System.out.print("password=>"+ password);
callbackProcessor.callback("Hello!");
}
}
通过@ProcessId、RemoteServiceImpl搭建组件通讯桥梁
//我们需要对RemoteServiceOfPlug1设置依赖关系
//ProcessId: 用于指定该协议从属于哪个组件,注意是组件以Application方式编译时的ApplicationId
//RemoteServiceImpl: 用于指定该协议的实现类,需输入实现类的全路径
//RemoteServiceImpl: APT注解可自动生成方法RemoteServiceOfPlug1Impl.class(需要Make一下工程),在需要传方法名的地方建议使用该自动生成的类取值,以达到规范的目的
//ProcessId, RemoteServiceImpl:缺一不可。当然你也不用太担心,如果协议上有任何的地方有问题,框架都会在调用方回调错误信息以提示您
@ProcessId("com.demo.plug1")
@RemoteServiceImpl("com.demo.plug1.PlugRemoteService")
public interface RemoteServiceOfPlug1 {
/**
* 登录
* @param username 姓名
* @param password 密码
* @param callbackProcessor @String 回调返回token
*/
void login(CallbackProcessor callbackProcessor,String username, String password);
}
在Common包中的Application中注册
//我们需要在公共的Application中注册
... extends Application {
@Override
public void onCreate() {
super.onCreate();
//isModuleAsApk: 组件是否以独立APK编译(建议配置到gradle中)
ProcessRoute.ini(Application application, boolean isModuleAsApk);
}
}
简洁高效的使用协议进行组件间通讯
//假设您在某个组件中需要Plug1模块的登录功能,使用ProcessRoute发送一个进程事件消息即可
//context:上下文
//RemoteServiceOfPlug1Impl.login: APT生成的类,可直接访问协议类的login方法名
//"18800000000", "198123545masd": 与通讯协议的login功能中要求的参数一样,但必须确保参数数量、类型、顺序一致
//RouteListener(): 与通讯协议的login功能中要求的回调参数一样
//prepare(): 事件已发出但尚且没有回调
//callback(): 事件回调了,且成功带回需要的数据。注意:只有Plug1模块调用了callbackProcessor.callback()才会回调
//fail(): 事件回调了,但失败了。如Plug1未安装、连接失败、未遵守协议、Plug1的实现过程报错都会在这个方法中将详细错误信息带过来
RouteReq.create(RemoteServiceOfPlug1Impl.login, "18800000000", "198123545masd")
.routeListener(new RouteListener() {
//可选实现
@Override
public void prepare() {}
@Override
public void callback(String obj) {}
}).send()
【end】
进阶
1.实现各组件的Application共享
在Plug1模块中新建Plug1Application 并实现ApplicationDelegate协议,并添加RegistApplication注解,框架将自动将注册共享Application
@RegistApplication
public class Plug1Application implements ApplicationDelegate {
@Override
public void onCreate(Application application) {
}
@Override
public void onTerminate(Application application) {
}
}
在Common包中的Application中注册组件的ApplicationDelegate
public class BaseApplication extends Application {
public static Application application;
@Override
public void onCreate() {
super.onCreate();
ProcessRoute.ini(this, BuildConfig.isPlugMode);
ProcessRoute.onApplicationCreate();//必须先初始化后再调用
}
@Override
public void onTerminate() {
super.onTerminate();
ProcessRoute.onApplicationTerminate();
}
}
2.实现RemoteService的远程方法AOP编程
以会话拦截为例。在Plug1模块中新建LoginProcessor 并继承RemoteServiceProcessor协议
public class LoginProcessor extends RemoteServiceProcessor {
/**
* AOP处理器
* @param method 对应RemoteService的远程方法名
* @param callbackProcessor 对应RemoteService的callbackProcessor回调处理器,即您也可以在拦截器中回调数据
* @param params 对应RemoteService的远程方法参数
* @return 是否继续执行RemoteService的远程方法,返回false则被拦截
*/
@Override
public boolean process(Method method, CallbackProcessor callbackProcessor, Object... params) {
return true;
}
}
在Plug1模块的RemoteServiceOfPlug1远程协议实现类中,在需要拦截的远程方法中加入@Processor({LoginProcessor.class})即可实现拦截
public class RemoteServiceOfPlug1Impl implements RemoteServiceOfPlug1 {
@Processor({LoginProcessor.class})//AOP处理器支持多个,以","号隔开即可。如多个处理器中有1个返回false,则被拦截
@Override
public void login(CallbackProcessor callbackProcessor,String username, String password) {
System.out.print("username=>"+ username);
System.out.print("password=>"+ password);
callbackProcessor.callback("Hello!");
}
}

3.快捷部署组件化Moudule

在Project的classpath中去掉com.android.tools.build:gradle:x.x.x,替换为com.fanjun.hookgradle:1.0.3
dependencies {
//当前插件版本基于【classpath:gradle:3.2.0】 + 【gradle-wrapper.properties:gradle-4.6-all.zip】构建
classpath 'com.fanjun:hookgradle:1.0.3'
}
与原生gradle一样,通过apply plugin: 'com.android.application' 或 'com.android.library'切换组件运行模式
1.Module无需动态设置applicationId,建议像开发Application一样设置上applicationId(无需关注运行模式)
2.Module无需动态设置sourceSets.main.manifest.srcFile,插件会自动配置
3.Module无需再创建一个Manifest.xml文件用于适配运行模式,插件会自动生成并自动关联切换
4.Module默认的Manifest.xml将用于Application模式时加载,您可以像写Application一样的编写Manifest.xml文件
4.组件化Moudule的R文件非常量解决方案
在Project的classpath中去掉com.android.tools.build:gradle:x.x.x,替换为com.fanjun.hookgradle:1.0.3
dependencies {
//当前插件版本基于【classpath:gradle:3.2.0】 + 【gradle-wrapper.properties:gradle-4.6-all.zip】构建
classpath 'com.fanjun:hookgradle:1.0.3'
}
在组件化Moudule的build.gradle文件中 apply plugin: 'com.fanjun.autor'
apply plugin: 'com.fanjun.autor'
android {
//...
}
在所有类的外部访问R文件资源id的地方使用R_访问(如 R_.layout.activity_main ),类的内部随您的喜好。(请忽略EActivity注解,这只是用于举例)
@EActivity(R_.layout.activity_ba)
public class BActivity extends Activity {
}

与原生R文件一样,插件生成的 R_ 文件会实时同步最新的id信息,并自动根据组件运行模式读取Moudule的R或者宿主的R

您无需关注R_文件的生成情况,即无需clean、build、sync等操作

注意1:当您的Moudule以Library运行时,插件默认认为它的宿主Moudule的名称为app(切记切记!!!)

注意2:您的组件Moudule的名字不要以a或A开头

5.组件化 转 插件化的解决方案

在你的需要以插件打包的组件Moudule的Manifest文件中,使用以下activity作为入口应用入口Activity

tools:ignore="AppLinkUrlError"/>

这样设置后打包成APK即可作为宿主的插件使用

1.在开发阶段你可以暂时屏蔽这段代码,使用你期望的那个activity作为启动页面,但是在打包成插件APK时一定要改回来

2.该方案是类似支付宝安装指纹支付插件的方式,用户需要像安装普通应用一般的安装你的插件,但是该插件并不会用户的手机桌面上显示图标

3.该方案的通讯机制与组件化一模一样,您无需担心切换后产生通讯问题

4.框架已绕过AIDL通讯引发手机的反链式启动程序的限制,可以自由自在的进行通讯

混淆方式

-keepattributes Signature
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
-keep class com.google.gson.* { *;}
-dontwarn com.google.gson.**
-keepclassmembers class * {
public (org.json.JSONObject);
}
-keep class **.R_$* {
*;
}
-keep @com.fanjun.processroute.annotation.RemoteServiceImpl class * {*;}
-keepnames class com.fanjun.processroute.apt.** { *; }
-keepnames class * implements com.fanjun.processroute.application.ApplicationDelegate { *; }
-keep public class * extends com.fanjun.processroute.processors.RemoteServiceProcessor { *; }
依赖
Gradle 在各个组件中引入processroute框架及配套的annotationProcessor处理器
implementation 'com.fanjun:processroute:1.0.15'
annotationProcessor 'com.fanjun:processroutecompiler:1.0.6'