一、什么是自定义api及其有何作用
自定义api,顾名思义为开发者为满足自身需求而自己创建的一个api。那么自己创建的这个api能起到什么效果和作用呢。
如果小程序里需要调用一些宿主app提供的能力,而FinClip SDK未实现或无法实现时,就可以注册一些自定义API。然后小程序里就可以像调用其他API一样调用注册的API了。
简单来说这个自定义api能起到小程序或者h5与原生app的交互作用。
注册自定义API分两个场景:1.注册给原生小程序使用的自定义API;2.注册给小程序中web-view组件加载的H5使用的自定义API。
二、如何注册及使用finclip小程序自定义api
1、ios端注册小程序自定义api
注册自定义的小程序API的函数如下所示:
/**
注册扩展Api
@param extApiName 扩展的api名称
@param handler 回调
@return 返回注册结果
*/
- (BOOL)registerExtensionApi:(NSString *)extApiName handle:(void (^)(id param, FATExtensionApiCallback callback))handler;
比如,我这里注册一个小程序APIcustomEvent:
[[FATClient sharedClient] registerExtensionApi:@"customEvent" handle:^(id param, FATExtensionApiCallback callback) {
// xxxx
callback(FATExtensionCodeSuccess, nil);
}];
然后,在小程序的根目录创建 FinClipConf.js 文件,配置实例如下:
module.exports = {
extApi:[
{ //普通交互API
name: 'customEvent', //扩展api名 该api必须Native方实现了
params: { //扩展api 的参数格式,可以只列必须的属性
url: ''
}
}
]
}
extApi 是个数组,所以,您可以注册多个自定义API。
最后,在小程序里调用自定义的API,示例代码:
ft.customEvent({
url:'https://www.baidu.com',
success: function (res) {
console.log("调用customEvent success");
console.log(res);
},
fail: function (res) {
console.log("调用customEvent fail");
console.log(res);
}
});
2、ios端注册小程序web-view组件api
小程序里加载的H5,如果也想调用宿主API的某个能力,就可以利用该方法注册一个API
/// 为HTML 注册要调用的原生 api
/// @param webApiName 原生api名字
/// @param handler 回调
- (BOOL)fat_registerWebApi:(NSString *)webApiName handle:(void (^)(id param, FATExtensionApiCallback callback))handler;
我这里为小程序里的H5注册了一个叫js2AppFunction的方法,
[[FATClient sharedClient] fat_registerWebApi:@"js2AppFunction" handle:^(id param, FATExtensionApiCallback callback) {
NSString *name = param[@"name"];
// id params = param[@"data"];
if ([name isEqualToString:@"getLocation"]) {
// 执行定位逻辑
// 返回结果给HTML
NSDictionary *dict = @{@"errno":@"403", @"errmsg":@"无权限", @"result": @{@"address":@"广东省深圳市南山区航天科技广场"}};
callback(FATExtensionCodeSuccess, dict);
} else if ([name isEqualToString:@"getColor"]) {
// 执行其他逻辑
// 返回结果给HTML
NSDictionary *dict = @{@"r":@"110",@"g":@"150",@"b":@"150"};
callback(FATExtensionCodeSuccess, dict);
}
}];
在H5内引用我们的桥接JSSDK文件,即可调用上面的注册的方法了。
HTML内调用注册的方法示例:
window.ft.miniProgram.callNativeAPI('js2AppFunction', {name:'getLocation'}, (result) => {
console.log(result)
});
3、android端注册小程序自定义api
自定义api示例:
public class CustomApi extends BaseApi {
public CustomApi(Context context) {
super(context);
}
@Override
public String[] apis() {
return new String[]{"customEvent"}; //api名称
}
@Override
public void invoke(String event, JSONObject param, ICallback callback) {
// 调用方法时原生对应的操作
}
}
然后将其注册到extensionApiManager中,支持单个注册和批量注册。
·Kotlin
单个注册
FinAppClient.extensionApiManager.registerApi(CustomApi(this))
批量注册
val apis = listOf<IApi>(CustomApi1(), CustomApi2(), CustomApi3())
FinAppClient.extensionApiManager.registerApis(apis)
·Java
单个注册
FinAppClient.INSTANCE.getExtensionApiManager().registerApi(new CustomApi(this));
批量注册
List<IApi> apis = new ArrayList<>();
IApi customApi1 = new CustomApi1();
apis.add(customApi1);
IApi customApi2 = new CustomApi2();
apis.add(customApi2);
IApi customApi3 = new CustomApi3();
apis.add(customApi3);
FinAppClient.INSTANCE.getExtensionApiManager().registerApis(apis);
然后,在小程序的根目录创建 FinClipConf.js 文件,配置实例如下:
module.exports = {
extApi:[
{ //普通交互API
name: 'customEvent', //扩展api名 该api必须Native方实现了
params: { //扩展api 的参数格式,可以只列必须的属性
url: ''
}
},
{
name: 'customEvent1',
params: {
foo: ''
}
},
{
// foo
}
]
}
最后,在小程序里调用自定义的API,示例代码:
ft.customEvent({
url:'https://www.xxx.com',
success: function (res) {
console.log("customEvent call succeeded");
console.log(res)
},
fail: function (res) {
console.log("customEvent call failed");
console.log(res)
}
})
4、android端注册小程序web-view组件api
小程序里加载的H5,如果也想调用宿主API的某个能力,就可以利用该方法注册一个API。
public class WebApi extends BaseApi {
public WebApi(Context context) {
super(context);
}
@Override
public String[] apis() {
return new String[]{"webApiName"}; //api名称
}
@Override
public void invoke(String event, JSONObject param, ICallback callback) {
// 调用方法时原生对应的操作
}
}
然后将其注册到extensionWebApiManager中,同样也支持单个注册和批量注册。
·Kotlin
单个注册
FinAppClient.extensionWebApiManager.registerApi(WebApi(this))
批量注册
val apis = listOf<IApi>(WebApi1(), WebApi2(), WebApi3())
FinAppClient.extensionWebApiManager.registerApis(apis)
·Java
单个注册
FinAppClient.INSTANCE.getExtensionWebApiManager().registerApi(new WebApi(this));
批量注册
List<IApi> apis = new ArrayList<>();
IApi webApi1 = new WebApi1();
apis.add(webApi1);
IApi webApi2 = new WebApi2();
apis.add(webApi2);
IApi webApi3 = new WebApi3();
apis.add(webApi3);
FinAppClient.INSTANCE.getExtensionWebApiManager().registerApis(apis);
在H5内引用我们的桥接JSSDK文件,即可调用上面的注册的方法了。
HTML内调用注册的方法示例:
window.ft.miniProgram.callNativeAPI('js2AppFunction', {name:'getLocation'}, (result) => {
console.log(result)
});
三、FinClip小程序自定义api常见问题
1、为什么注册的自定义小程序API不起作用
在注册自定义API时,会判断当前的小程序SDK是否初始化成功了。如果没有初始化成功,那么注册自定义Api就不会成功。
所以,注册自定义API前,一定要保证小程序已经初始化成功了。
2、在finclip fide中如何mock使用自定义api
在fide中,有mock功能可以方便开发者在开发的途中mock模拟自定义api的返回结果。如下图:
在mock中定义api接口字段及返回结果(需要注意的是,这里的JSON数据包的返回结果需要的是双引号””)
然后在小程序根目录下。
然后,在小程序的根目录创建 FinClipConf.js 文件,配置实例如下:
module.exports = {
extApi: [{
name: 'kkshy',
}]
}
最后就是小程序中的调用
ft.kkshy({
success: function(res) {
console.log("success");
console.log(res);
},
fail: function(res) {
console.log("fail");
console.log(res);
}
});
3、使用flutter接入的话,自定义api是否支持通过success方法接收flutter的回调数据
答案是支持的,
typedef ExtensionApiHandler = Future Function(dynamic params)
自定义的方法返回的结果会返回给小程序
4、在自定义接口的invoke()方法中跳转到宿主App的其它页面,做完一系列操作之后,按系统返回键想返回小程序,结果却返回到了宿主App中启动小程序的页面,为什么?
- 原因:跳转到宿主App其它页面这一步,是通过宿主App中的Context实例来启动Activity的,并且没有把Activity压入新的任务栈中。Android小程序SDK是多进程架构的,小程序和宿主App处于不同进程中,所处的任务栈自然也是不同的。小程序跳转到宿主App的页面,新打开的页面是添加到宿主App原有的任务栈中的,当从页面返回时,执行的逻辑是在原生App中原有的任务栈中弹出页面,因此会看到原生App的页面被逐个关闭,最后返回到原生应用启动小程序的页面,并没有返回小程序。
- 解决方案:
方案1(推荐):
通过ICallback的startActivity或startActivityForResult来跳转到宿主App的其它页面。
这是推荐的方案,因为这样做是在小程序所在的任务栈打开新宿主App的Activity的,Activity的入栈出栈都是在同一个任务栈中完成的,没有任务栈切换的过程。
更重要的一个原因是:如果需要通过startActivityForResult来启动Activity并在页面返回时获取到回传的数据,只有使用这种方案,自定义接口的onActivityResult才会执行,才能拿到返回的数据。
此方案使用示例:
@Override
public void invoke(String event, JSONObject param, ICallback callback) {
Intent intent = new Intent();
intent.setClass(mContext, SecondActivity.class);
callback.startActivityForResult(intent, 100);
}
方案2(不推荐):
如果一定要使用宿主App中的Context实例来启动Activity,就需要对启动原生页面的Intent设置”支持多任务栈”和“开启新任务栈”的Flag,这样可以在原生App的进程中新开一个任务栈,开启新任务栈之后,新打开的页面将被逐个压入这个新任务栈中,当结束完原生页面的所有操作之后逐个页面返回时,便会从这个新任务栈中将页面逐个弹出,当这个新任务栈中的所有页面都被弹出后,便会回到小程序进程的任务栈。
因此,在自定义接口的invoke()方法中,如果需要跳转到原生应用的其它页面执行某些操作,并期望当关闭这些原生页面后能够返回小程序,那么建议在执行跳转的时候为Intent对象同时增加Intent.FLAG_ACTIVITY_MULTIPLE_TASK和FLAG_ACTIVITY_NEW_TASK,如下:
Intent intent = new Intent();
intent.setClass(context, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); // context是宿主App中的Context实例
使用此方案,如果通过startActivityForResult来启动Activity,当页面返回时,自定义接口的onActivityResult不会被调用,因此不推荐。
5、taro中如何给打包后文件添加FinChatConf.js
taro中可以使用copy配置项,将FinChatConf.js复制到打包后的文件之中,具体写法可参考如下:
module.exports = {
// ...
copy: {
patterns: [
{ from: 'FinChatConf.js', to: 'dist/FinChatConf.js' } // 指定需要 copy 的文件
]
}
}
具体可参考taro文档http://taro-docs.jd.com/taro/docs/config-detail#copy
四、FinClip小程序自定义api示例
自定义api相关示例代码可见:https://github.com/finogeeks/auth_demo_android
[