使用
定义模块
在使用路由之前,首先需要对业务模块的入口进行定义。对于一个组件化的项目,通常是由用于打包 apk 的主模块以及一系列业务模块构成的。每个业务模块需要定义且仅定义一个入口类用于生成该模块的路由表以及接收应用的生命周期分发。入口类应当实现 com.tencent.tmf.portal.Module
接口,并使用 @SingleModule
进行注解。如果模块希望进一步接收 Application 的生命周期事件,则应当实现 com.tencent.tmf.portal.Module.LifecycleModule
接口:
// 使用 SingleModule 注解声明这是一个模块的入口类,注解需要提供 name 参数作为模块的模块名,模块名建议使用全小写,并用'-'符号进行分割
@SingleModule(name = "module-portal")
// 如果需要接收Application的生命周期应当实现 Module.LifecycleModule 接口,否则可以直接实现 Module 接口
public class ModulePortal implements Module.LifecycleModule {
// 路由挂载该模块时的回调
@Override
public void attach(Context context, Bundle bundle) {
// 如果路由初始化时传入了初始化参数,则可以从此处的bundle获取
String messageFromInit = bundle.getString("message");
}
// 路由卸载该模块时的回调
@Override
public void detach() {
}
// 返回一个包含模块支持的Service类的列表
@Override
public List<Class<?>> supportedServices() {
return null;
}
// 返回用于创建模块支持的Service实例的Factory类
@Override
public ServiceFactory serviceFactory() {
return null;
}
// 如果该模块依赖其他一些模块,需要在其他模块挂载之后再加载,可以在此处返回依赖的模块列表
@Override
public List<String> dependsOn() {
List<String> depends = new ArrayList<>();
depends.add("module-base");
return depends;
}
// Application的生命周期回调,非LifecycleModule无需实现
@Override
public void onAttachBaseContext(Context context) {
}
// Application的生命周期回调,非LifecycleModule无需实现
@Override
public void onCreate() {
}
// Application的生命周期回调,非LifecycleModule无需实现
@Override
public void onTerminate() {
}
// Application的生命周期回调,非LifecycleModule无需实现
@Override
public void onLowMemory() {
}
// Application的生命周期回调,非LifecycleModule无需实现
@Override
public void onTrimMemory(int i) {
}
}
如果模块不需要任何配置,也可以简单地继承默认的模块入口实现:
@SingleModule(name = "module-portal")
public class ModulePortal extends Module.BaseModule {
// 默认实现没有任何回调操作,可以在此重写
}
说明:TMF 会在模块编译期间对
SingleModule
注解进行扫描,然后为模块生成对应的路由表类。默认情况下路由 SDK 会在初始化时自动加载所有模块的路由表。
初始化
在进行路由之前需要先对路由组件进行初始化,通常建议在 Application 的 onAttachBaseContext 回调或者 onCreate 回调中进行初始化:
/**
* 初始化路由框架
*/
private void initPortal(Context applicationContext) {
// 设置可输出log
Portal.setDebuggable(true);
Portal.init(applicationContext);
}
初始化时默认所有的模块都会被自动加载,如果希望在初始化时不加载某些模块,可以传入一个包含要排除的模块名的字符串数组:
private void initPortal(Context applicationContext) {
...
Portal.init(applicationContext, new String[]{
"module-portal-dynamic"
});
}
如果希望在初始化时为模块配置透传参数,可以再在初始化时传入一个 Bundle 参数。模块将会在挂载时接收到这个 Bundle (参考定义模块):
private void initPortal(Context applicationContext) {
...
Bundle param = new Bundle();
param.putString("message", "This is a message from: CommonApp");
Portal.init(application, new String[]{
"module-portal-dynamic"
}, param);
}
对于需要将Application的生命周期分发到各个模块的情况,应当在Application的生命周期回调里调用Portal对应的方法:
public class SampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Portal.onCreate();
...
}
@Override
public void onAttachedBaseContext(Context context) {
Portal.onAttachBaseContext(context);
...
}
...
}
定义路由
路由通过 @Destination
注解进行定义,TMF 会在编译期间对模块下所有的 @Destination
注解进行扫描,最终生成模块的路由表:
@Destination(url = "portal://com.tencent.tmf.module.portal/portal-no-result-activity",
launcher = Launcher.ACTIVITY,
parameters = {@Parameter(name = "param", type = Integer.class, optional = true, description = "参数1")},
description = "Portal演示")
public class NoResultActivity extends TopBarActivity {
...
}
注解包含以下几个参数:
url
:不可为空,String类型,声明路由的url,全局唯一。launcher
:不可为空,String类型,指定当前路由的类型,不可为空。description
:可选,String类型,当前路由的描述。interceptors
:可选,Class数组类型,路由拦截器,指用于在启动当前路由前需要被执行的拦截过程,可定义多个拦截器。parameters
:可选,@Parameter
注解类型数组,用于声明路由所需的参数,路由启动时将会根据注解对参数进行校验,如果参数不符合注解的定义路由将会启动失败。returns
:可选,@Return
注解类型数组,用于声明路由返回的参数,仅在方法调用时会对方法返回的结果进行校验,如果结果不符合注解的定义路由将会启动失败。
路由模块默认的laucher包含以下几种:
ACTIVITY
:表示当前路由为页面,此时注解的对象应当为Activity的子类。FRAGMENT
:表示当前路由为Fragment,此时注解的对象应当为Fragment的子类。METHOD
:表示当前路由为一个方法调用,此时注解的对象应当为PortalMethod的实现类。
@Parameter
和 @Return
的参数如下:
name
:不可为空,String
类型,参数的keytype
:不可为空,Class
类型,参数的类型optional
:不可为空,boolean
类型,参数是否可选(是否允许为空)description
:不可为空,String
类型,当前参数的描述
启动路由
启动Activity
启动Activity只需要Activity对应的url即可:
Portal.from(SampleActivity.this)
.url("portal://com.tencent.tmf.module.portal/portal-no-result-activity")
.launch();
首先调用 Portal 的 from
方法建立启动页面所需要的上下文,这里传入的可以是一个 Context,也可以是一个Fragment。然后指定要启动的 Activity 的 url ,最后调用 launch
方法。
如果希望改变 Activity 的转场动画,可以在 launch
之前调用 activityTransition
方法:
Portal.from(SampleActivity.this)
.url("portal://com.tencent.tmf.module.portal/portal-no-result-activity")
.activityTransition(R.anim.push_left_in, R.anim.push_left_out)
.launch();
使用 activityOptionsCompat
方法可以在启动时为Activity指定 activityOptions:
ActivityOptionsCompat options =
ActivityOptionsCompat.makeClipRevealAnimation(getWindow().getDecorView(),
getWindow().getDecorView().getWidth() / 2, getWindow().getDecorView().getHeight() / 2, 1000, 1000);
Portal.from(SampleActivity.this)
.url("portal://com.tencent.tmf.module.portal/portal-no-result-activity")
.activityOptionsCompat(options)
.launch();
param
方法可以为启动的Activity传入参数,这些参数可以在启动的 Activity 的 extra 中获取:
Portal.from(SampleActivity.this)
.url("portal://com.tencent.tmf.module.portal/portal-no-result-activity")
.param("param", text)
.launch();
如果需要 startActivityForResult 从开启的页面获得返回结果,则需要在启动时传入对应的 request code,然后再在Activity内实现 onActivityResult 方法:
public void startSample() {
Portal.from(SampleActivity.this)
.url("portal://com.tencent.tmf.module.portal/portal-no-result-activity")
.startForActivityResult(REQUEST_CODE)
.launch();
}
...
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK && data != null) {
showAlert("onActivityResult返回值: " + data.getStringExtra("result"));
}
}
您也可以通过传入 callback 的方式获取页面的返回结果,该方式无需实现 onActivityResult:
Portal.from(SampleActivity.this)
.url("portal://com.tencent.tmf.module.portal/portal-no-result-activity")
.startActivityWithCallback((i, intent) -> {
if (i == RESULT_OK && intent != null) {
showAlert("Callback返回值: " + intent.getStringExtra("result"));
}
})
.launch();
启动页面的时候也可以在launch中传入一个回调,以监听启动页面是否成功:
Portal.from(SampleActivity.this)
.url("portal://com.tencent.tmf.module.portal/portal-no-result-activity")
.launch(new LaunchCallback() {
@Override
public void beforeLaunch(Request request) {
// 页面准备启动前
}
@Override
public void afterLaunch(Request request, Response response) {
// 页面成功启动后
}
@Override
public void launchInterrupt(Request request, Response response) {
// 页面启动失败
}
});
您可以使用 SimpleLaunchCallback
简化回调:
Portal.from(SampleActivity.this)
.url("portal://com.tencent.tmf.module.portal/portal-no-result-activity")
.launch(new SimpleLaunchCallback() {
@Override
public void onLaunchComplete(Bundle bundle) {
// 启动成功,bundle为返回值
}
@Override
public void onLaunchFailed(int errorCode, String msg) {
// 启动失败,errorCode为错误码,msg为错误信息
}
});
获取Fragment
如果需要通过路由获取Fragment的实例,需要在启动路由时传入一个回调,用于接收获取到的Fragment实例:
Portal.from(this)
.url("portal://com.tencent.tmf.module.portal/portal-simple-fragment")
.param("param", input.getText().toString())
.getTargetInstance(instance -> {
if (instance != null) {
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out)
.add(R.id.main, (Fragment) instance)
.addToBackStack(null)
.commitAllowingStateLoss();
}
}).launch();
所有通过 param
方法传入的参数最终都可以在 Fragment 内通过 getArguments
方法获取。
调用方法
通过路由调用一个路由方法,首先需要定义实现PortalMethod接口的方法类:
@Destination(url = PortalConst.TIME_METHOD,
launcher = Launcher.METHOD,
description = "Portal演示")
public class TimeMethod implements PortalMethod {
@Override
public Bundle invoke(Bundle bundle) {
SimpleDateFormat format = new SimpleDateFormat(bundle.containsKey("format") ? bundle.getString("format") :
"EEE, MMM d, yyyy hh:mm:ss a z", Locale.getDefault());
Bundle data = new Bundle();
data.putString("result", format.format(new Date()));
return data;
}
}
参数 bundle 即调用方传入的参数。invoke 方法处理完业务逻辑后需要返回一个 bundle作为方法的返回值。该返回值可以为空。
调用方使用 PortalMethod 定义的url调用方法即可:
Portal.from(this).url(PortalConst.TIME_METHOD).launch(new SimpleLaunchCallback() {
@Override
public void onLaunchComplete(Bundle bundle) {
showAlert(bundle.getString("result"));
}
@Override
public void onLaunchFailed(int i, String s) {
showAlert(s);
}
});
默认情况下所有 launch 操作均为异步执行,如需同步执行 PortalMethod, 可以在 launch 之前调用 synchronously 方法:
Portal.from(this).url(PortalConst.ASYNC_METHOD).synchronously().launch(callback);
注意:异步的 launch 操作会在工作线程上单独执行,但是传入的 callback 会在 launch 结束后切换回主线程通知调用方。
打开网页
路由启动的 url 若为 H5 链接,默认情况下会使用浏览器打开链接:
Portal.from(this).url("https://www.qq.com").launch();
如果不希望使用浏览器打开链接,需要自定义 HTTP 类型的 Launcher,具体的用法参考 模块加载或卸载。