Archmage是一个android组件化框架
- 组件之间可以用类似OSGI的导入、导出部分包实现provided依赖
- 组件之间通过接口方式通信
- scheme路由方式实现Activity跳转、Fragment获取
- 组件可以自定义轻量、重量启动项,并声明依赖关系
- 自动注册组件
- 支持Android gradle plugin 3.0+
原理
module | archmage-gradle-plugin | archmage-runtime | archmage-annotation |
---|---|---|---|
latest version |
在根工程的build.gradle中添加插件
buildscript {
dependencies {
classpath "com.mricefox.archmage.build.gradle:archmage-gradle-plugin:1.1.1"
}
}
在宿主组件(一般是app)build.gradle中应用插件
apply plugin: 'archmage-build-plugin'
对于两个相互没有依赖却又有通信需求的组件来说,服务提供方可以导出包给其他组件使用
apply plugin: 'archmage-build-plugin'
archmage {
exportPackages = ['com.mricefox.archmage.sample.hotel.export']
}
而需要调用服务的组件可以从其他组件导入包
apply plugin: 'archmage-build-plugin'
archmage {
//project
provided(project(':hotel')) {
importPackages = ['com.mricefox.archmage.sample.hotel.export']
}
//aar
provided('com.mricefox.archmage.sample.ticket:ticket:1.0.0') {
importPackages = ['com.mricefox.archmage.sample.ticket.export']
}
}
之后点击Android Studio中的Gradle Sync按钮即可 演示Gif
dependencies {
compile 'com.mricefox.archmage.runtime:archmage-runtime:1.0.0'
compile 'com.mricefox.archmage.annotation:archmage-annotation:1.0.1'
}
public class SampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Archmage.install(this, false);
}
@Override
public void onTerminate() {
super.onTerminate();
Archmage.terminate(this);
}
}
public interface HotelService extends IService {
//...
}
//直接获取服务,失败将抛出ServiceNotFoundException,不建议采用这种方式
HotelBean bean = Archmage.service(HotelService.class).getHotelDetail(3);
//通过回调获取服务,建议采用这种方式
Archmage.service(HotelService.class, new ServiceFindCallback<HotelService>() {
@Override
public void found(HotelService hotelService) {
HotelBean bean = hotelService.getHotelDetail(5);
}
@Override
public void notFound(Class<HotelService> alias) {
//Service no found
}
});
@ServiceImpl
public class HotelServiceImpl implements HotelService {
//...
}
//Fragment
@Target(path = "/share/ShareArea")
public class ShareFragment extends Fragment {
//...
}
//Activity
@Target(path = "/pay/PayPage")
public class PayActivity extends AppCompatActivity {
//...
}
也就是在Fragment或者Activity前面加上@Target(path = "/组件前缀/页面名称")
//直接跳转Activity,失败将抛出TargetNotFoundException
Archmage.transfer(DefaultTargetUriParser.createUri("pay", "PayPage"))
.activity()
.intent(new Intent().putExtra("source", "ticket"))
.startForResult(TicketListActivity.this, 5);
//通过回调跳转Activity
Archmage.transfer(DefaultTargetUriParser.createUri("pay", "PayPage"))
.activity(new TargetFindCallback<Transfer.TargetActivity>() {
@Override
public void found(Transfer.TargetActivity targetActivity) {
targetActivity.intent(new Intent().putExtra("source", "ticket"))
.startForResult(TicketListActivity.this, 5);
}
@Override
public void notFound(Uri uri) {
//Activity not found
}
});
//直接获取,失败将抛出TargetNotFoundException
Bundle bundle = new Bundle();
bundle.putString("source", "ticket");
Fragment fragment = Archmage.transfer(DefaultTargetUriParser.createUri("share", "ShareArea"))
.fragmentV4()
.arguments(bundle)
.get();
//通过回调获取
Archmage.transfer(DefaultTargetUriParser.createUri("share", "ShareArea"))
.fragmentV4(new TargetFindCallback<Transfer.TargetFragmentV4>() {
@Override
public void found(Transfer.TargetFragmentV4 targetFragmentV4) {
Bundle bundle = new Bundle();
bundle.putString("source", "ticket");
Fragment fragment = targetFragmentV4.arguments(bundle).get();
}
@Override
public void notFound(Uri uri) {
//Fragment not found
}
});
除白色方块表示的基础组件代码对上层所有组件可见外,其他上层组件(酒店、门票、账号)互相之间并没有依赖关系。这里的酒店组件并不知道账户组件有些什么启动任务,账户组件需要给自己的启动任务设置一个别名暴露给酒店组件
public interface AccountBootAlias extends LightBootTaskAlias {
//这个接口放在export包里
}
然后账号组件需要一个入口来声明自己的启动任务,注意需要添加com.mricefox.archmage.annotation.Module
这个注解
@Module
public class AccountModule extends ArchmageModule {
@Override
protected Class<? extends LightBootTaskAlias> alias() {
//设置别名,默认是this.getClass()
return AccountBootAlias.class;
}
//...
}
酒店组件的启动入口,声明启动顺序,需要导入账号的export包,以便引用AccountBootAlias
@Module
public class HotelModule extends ArchmageModule {
@Override
protected void declareBootDependency() {
//在AccountModule之后启动
dependsOn(AccountBootAlias.class);
}
}
可以定义LightBootTask作为启动任务,上面的组件入口ArchmageModule本身也是一个启动任务。注意轻量启动任务的boot方法都是在主线程上被调用
public class HotelSdkInitTask extends LightBootTask {
@Override
protected void boot(Application application, Bundle extra) {
//具体的任务
}
}
在组件的启动入口declareBootDependency方法中加入
@Module
public class HotelModule extends ArchmageModule {
@Override
protected void declareBootDependency() {
//添加一个轻量启动任务
addLightBootTask(new HotelSdkInitTask());
}
}
具体的使用可以参考sample
一些比较耗时的任务可以放在后台线程执行,减少app启动时间,使用上类似AsyncTask,doInBackground方法在后台线程调用,onPostExecute方法在UI线程调用
public class PushSdkInitTask extends HeavyBootTask<String> {
@Override
protected String doInBackground(Application application, Bundle extra) {
//后台耗时任务
Log.d(Constants.BOOT_TASK_TAG, "push sdk boot, run in thread:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Channel_X";
}
@Override
protected void onPostExecute(Application application, String s) {
//后台任务结果转到前台
Log.d(Constants.BOOT_TASK_TAG, "push sdk boot, bg result:" + s);
Log.d(Constants.BOOT_TASK_TAG, "push sdk boot, run in thread:" + Thread.currentThread().getName());
}
@Override
protected boolean bootBesideMainProcess() {
//是否在app进程以外的进程启动该任务
return false;
}
}
这里HeavyBootTask之间也可以用before、after设置启动顺序,注意HeavyBootTask将在所有的LightBootTask执行完毕之后开始执行 具体的使用可以参考sample
对Archmage框架执行的各种节点进行监控,注意需要在Archmage.install之前注册
ArchmageAspectPlugins.inst().registerDependencyLookupHook
ArchmageAspectPlugins.inst().registerDependencyInjectionHook
ArchmageAspectPlugins.inst().registerLifecycleHook
-keep public class * extends com.mricefox.archmage.runtime.ModuleActivator
- 组件独立安装调试
- 不支持组件间共享资源文件
- 组件导出包能够引用的类只有android.jar、support-v4、archmage-runtime当中的类
This project is licensed under the Apache License, Version 2.0 - see the LICENSE file for details