Android 集成
集成样例代码
可以在这里获取集成样例代码https://github.com/finogeeks/mop-android-demo (opens new window)
1. 获取 SDK KEY 及 SDK SECRET
使用SDK需要申请 SDK KEY 及 SDK SECRET ,只有在SDK初始化的时候配置了正确的 SDK KEY 及 SDK SECRET ,才能初始化成功并正常使用。
1.1 创建应用
注册用户需要登录「应用管理-新增合作应用」,完成应用创建;
1.2 获取 SDK KEY 及 SDK SECRET
创建应用并添加Bundle ID后,若需要导出对应的SDK KEY与SDK SECRET,请选择对应 Bundle ID 后的「复制」,即可通过ctrl+v或command+v进行粘贴操作;
其中:
- SDK KEY:是合作应用能使用小程序SDK的凭证,如果 SDK KEY 校验失败,则 SDK 的所有 API 都无法使用。
- SDK SECERT:是访问服务的安全证书,不要给第三方。
提示
关于创建应用与获取 SDK KEY 及 SDK SECRET 的详细操作,见「介绍-操作指引-企业端操作指引-7.关联移动应用」一节。
2. 导入SDK
2.1 在工程的build.gradle中需要配置的内容
在工程的build.gradle
中添加maven仓库的地址:
maven {
url "https://gradle.finogeeks.club/repository/applet/"
credentials {
username "applet"
password "123321"
}
}
由于SDK中部分代码使用了Kotlin来编写,因此需要工程的build.gradle
中添加Kotlin的gradle插件:
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61"
工程的build.gradle
的完整配置如下:
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:3.5.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61"
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url "https://gradle.finogeeks.club/repository/applet/"
credentials {
username "applet"
password "123321"
}
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
2.2 在gradle中依赖SDK
在gradle文件的dependencies中添加对finapplet的依赖:
implementation 'com.finogeeks.lib:finapplet:+'
注意
SDK中的动态库是被加固过的,被加固过的动态库在编译打包时不能被压缩,否则加载的时候会报错。
因此需要在App module下的build.gradle
中增加doNotStrip
配置:
packagingOptions {
// libsdkcore.so、libyuvutil.so是被加固过的,不能被压缩,否则加载动态库时会报错
doNotStrip "*/x86/libsdkcore.so"
doNotStrip "*/x86_64/libsdkcore.so"
doNotStrip "*/armeabi/libsdkcore.so"
doNotStrip "*/armeabi-v7a/libsdkcore.so"
doNotStrip "*/arm64-v8a/libsdkcore.so"
doNotStrip "*/x86/libyuvutil.so"
doNotStrip "*/x86_64/libyuvutil.so"
doNotStrip "*/armeabi/libyuvutil.so"
doNotStrip "*/armeabi-v7a/libyuvutil.so"
doNotStrip "*/arm64-v8a/libyuvutil.so"
}
完整的配置如下:
apply plugin: 'com.android.application'
android {
buildToolsVersion '28.0.3'
compileSdkVersion 28
defaultConfig {
applicationId "com.finogeeks.finappletdemo"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
// libsdkcore.so、libyuvutil.so是被加固过的,不能被压缩,否则加载动态库时会报错
doNotStrip "*/x86/libsdkcore.so"
doNotStrip "*/x86_64/libsdkcore.so"
doNotStrip "*/armeabi/libsdkcore.so"
doNotStrip "*/armeabi-v7a/libsdkcore.so"
doNotStrip "*/arm64-v8a/libsdkcore.so"
doNotStrip "*/x86/libyuvutil.so"
doNotStrip "*/x86_64/libyuvutil.so"
doNotStrip "*/armeabi/libyuvutil.so"
doNotStrip "*/armeabi-v7a/libyuvutil.so"
doNotStrip "*/arm64-v8a/libyuvutil.so"
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.finogeeks.lib:finapplet:+'
}
2.3 配置混淆规则
集成SDK之后,为了避免SDK中部分不能被混淆的代码被混淆,需要在工程的混淆规则配置文件中增加以下配置:
-keep class com.finogeeks.** {*;}
3. SDK初始化
我们强烈建议在Application
中对SDK进行初始化,初始化SDK需要传入的各项参数如下(sdk初始化只需要调用一次,应避免重复调用):
3.1 小程序框架的配置信息
2.13.102
版本之前,SDK仅支持配置一个服务器信息,只能打开单个环境中的小程序。配置参数如下:
FinAppConfig config = new FinAppConfig.Builder()
.setSdkKey("SDK Key信息") // SDK Key
.setSdkSecret("SDK Secret信息") // SDK Secret
.setApiUrl("服务器地址") // 服务器地址
.setApiPrefix("/api/v1/mop/") // 服务器接口请求路由前缀
.setEncryptionType("加密方式") // 加密方式,国密:SM,md5: MD5
.build();
从2.13.102
版本开始支持配置多个服务器信息,可以同时打开不同环境中的小程序。配置参数如下:
// 服务器信息集合
List<FinStoreConfig> storeConfigs = new ArrayList<>();
// 服务器1的信息
FinStoreConfig storeConfig1 = new FinStoreConfig(
"SDK Key信息", // SDK Key
"SDK Secret信息", // SDK Secret
"服务器1的地址", // 服务器地址
"服务器1的数据上报服务器地址", // 数据上报服务器地址
"/api/v1/mop/", // 服务器接口请求路由前缀
"",
"加密方式" // 加密方式,国密:SM,md5: MD5
);
storeConfigs.add(storeConfig1);
// 服务器2的信息
FinStoreConfig storeConfig2 = new FinStoreConfig(
"SDK Key信息", // SDK Key
"SDK Secret信息", // SDK Secret
"服务器2的地址", // 服务器地址
"服务器2的数据上报服务器地址", // 数据上报服务器地址
"/api/v1/mop/", // 服务器接口请求路由前缀
"",
"加密方式" // 加密方式,国密:SM,md5: MD5
);
storeConfigs.add(storeConfig2);
FinAppConfig config = new FinAppConfig.Builder()
.setFinStoreConfigs(storeConfigs) // 服务器信息集合
.build();
3.2 设置SDK初始化状态回调
实现FinCallback接口,用于监听SDK初始化状态:
// SDK初始化结果回调,用于接收SDK初始化状态
FinCallback<Object> callback = new FinCallback<Object>() {
@Override
public void onSuccess(Object result) {
// SDK初始化成功
}
@Override
public void onError(int code, String error) {
// SDK初始化失败
Toast.makeText(AppletApplication.this, "SDK初始化失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onProgress(int status, String error) {
}
};
3.3 初始化SDK
调用初始化接口初始化SDK:
FinAppClient.INSTANCE.init(this, config, callback);
3.4 SDK初始化多进程处理
SDK采用多进程机制实现,每个小程序运行在独立的进程中,即一个小程序对应一个进程,在初始化SDK时,要特别注意的一点是:小程序进程在创建的时候不需要执行任何初始化操作,即使是小程序SDK的初始化,也不需要在小程序进程中执行。
例如:应用使用了一些第三方库,这些库需要在应用启动时先初始化,那么在Application
中执行初始化时,只有当前进程为宿主进程时才需要初始化这些第三方库,小程序进程是不需要初始化这些库的。
因此,在初始化SDK之前,一定要判断当前进程是哪一个进程,如果是小程序进程,就不进行任何操作了:
if (FinAppClient.INSTANCE.isFinAppProcess(this)) {
return;
}
以上即为初始化SDK的整个流程。完整代码如下:
package com.example.finogeeks.appletdemo;
import android.os.Process;
import android.support.multidex.MultiDexApplication;
import android.text.TextUtils;
import android.widget.Toast;
import com.example.finogeeks.appletdemo.api.ApiOpenPage;
import com.example.finogeeks.appletdemo.api.ApiOpenPageForResult;
import com.example.finogeeks.appletdemo.api.DrawModule;
import com.example.finogeeks.appletdemo.util.ProcessUtilKt;
import com.finogeeks.lib.applet.client.FinAppClient;
import com.finogeeks.lib.applet.client.FinAppConfig;
import com.finogeeks.lib.applet.interfaces.FinCallback;
/**
* 应用的{@link android.app.Application}
*/
public class AppletApplication extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
if (FinAppClient.INSTANCE.isFinAppProcess(this)) {
// 小程序进程不执行任何初始化操作
return;
}
// 服务器信息集合
List<FinStoreConfig> storeConfigs = new ArrayList<>();
// 服务器1的信息
FinStoreConfig storeConfig1 = new FinStoreConfig(
"SDK Key信息", // SDK Key
"SDK Secret信息", // SDK Secret
"服务器1的地址", // 服务器地址
"服务器1的数据上报服务器地址", // 数据上报服务器地址
"/api/v1/mop/", // 服务器接口请求路由前缀
"",
"加密方式" // 加密方式,国密:SM,md5: MD5
);
storeConfigs.add(storeConfig1);
// 服务器2的信息
FinStoreConfig storeConfig2 = new FinStoreConfig(
"SDK Key信息", // SDK Key
"SDK Secret信息", // SDK Secret
"服务器2的地址", // 服务器地址
"服务器2的数据上报服务器地址", // 数据上报服务器地址
"/api/v1/mop/", // 服务器接口请求路由前缀
"",
"加密方式" // 加密方式,国密:SM,md5: MD5
);
storeConfigs.add(storeConfig2);
FinAppConfig config = new FinAppConfig.Builder()
.setFinStoreConfigs(storeConfigs) // 服务器信息集合
.build();
FinAppClient.INSTANCE.init(this, config, new FinCallback<Object>() {
@Override
public void onSuccess(Object result) {
}
@Override
public void onError(int code, String error) {
Toast.makeText(AppletApplication.this, "SDK初始化失败", Toast.LENGTH_SHORT).show();
}
@Override
public void onProgress(int status, String error) {
}
});
}
}
4. SDK使用示例
4.1 启动小程序
在平台中上架小程序之后,我们就可以通过调用SDK中启动小程序的接口来打开小程序了。启动小程序的代码如下:
FinAppClient.INSTANCE.getAppletApiManager().startApplet(this, "5e0dc1f574193e00010d73c1");
如果启动小程序时需要携带启动参数,则可以调用支持传递启动参数的接口,如下:
Map<String, String> params = new HashMap<>();
// path为小程序页面路径
params.put("path", "/pages/index/index");
// query为启动参数,内容为"key1=value1&key2=value2 ..."的形式
params.put("query", "aaa=\"test\"&bbb=\"123\"");
FinAppClient.INSTANCE.getAppletApiManager().startApplet(this, "5e0dc1f574193e00010d73c1", params);
4.2 自定义小程序接口
实现小程序自定义接口需要创建一个继承自AbsApi
的类,并在创建的子类中重写父类的方法(最主要的是apis()
方法和invoke()
方法)。
下面以在小程序里面打开一个原生页面为例来演示如何实现并注册小程序自定义接口:
- 首先,创建
ApiOpenPage
类,继承自AbsApi
; - 然后,在
ApiOpenPage
中重写apis()
方法用于返回所有可调用的API的名称。我们可以在apis()
返回的数组中添加多个API的名称,当前示例只添加了一个openPage
; - 最后,在
ApiOpenPage
中重写invoke()
方法用于实现对应的逻辑。当前示例实现了一个简单的页面跳转,即如果在小程序中调用了openPage
这个接口,那么就会在原生这边启动一个Activity。
package com.example.finogeeks.appletdemo.api;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.example.finogeeks.appletdemo.SecondActivity;
import com.finogeeks.lib.applet.api.AbsApi;
import com.finogeeks.lib.applet.interfaces.ICallback;
import org.json.JSONObject;
/**
* 自定义小程序接口,实现简单的页面跳转功能
*/
public class ApiOpenPage extends AbsApi {
private Context mContext;
public ApiOpenPage(Context context) {
mContext = context;
}
/**
* 返回可调用的api名称的数组
*
* @return 可调用的api名称的数组
*/
@Override
public String[] apis() {
return new String[]{"openPage"};
}
/**
* 接收到对应的API调用时,会触发此方法,在此方法中实现API的业务逻辑
*
* @param event 事件名称,即API名称
* @param param 事件参数
* @param callback 回调接口,用于执行完业务逻辑之后把结果回调给小程序
*/
@Override
public void invoke(String event, JSONObject param, ICallback callback) {
if ("openPage".equals(event)) {
String url = param.optString("url");
if (!TextUtils.isEmpty(url)) {
Intent intent = new Intent();
intent.setClass(mContext, SecondActivity.class);
mContext.startActivity(intent);
callback.onSuccess(null);
} else {
callback.onFail();
}
}
}
}
实现自定义小程序接口之后,还需要两个步骤才能在小程序调用接口:
- 将自定义的小程序接口注册到SDK中。通过调用
IExtensionApiManager
接口的registerApi
方法实现接口注册:
FinAppClient.INSTANCE.getExtensionApiManager().registerApi(new ApiOpenPage(context));
- 在小程序工程中增加自定义接口配置。在小程序工程的根目录创建
FinClipConf.js
文件,在FinClipConf.js
中配置对应的自定义接口,配置如下:
module.exports = {
extApi:[
{
name: 'openPage', // 扩展接口名
params: { // 扩展接口参数,可以只列必须的参数
url: ''
}
}
]
}
4.3 原生和小程序网页之间进行交互
小程序加载网页时,如果网页要调用原生的功能,则需要将原生代码以自定义JSSDK接口的形式注册到SDK内部。自定义JSSDK接口的实现方式和自定义小程序接口一样,即继承AbsApi
并重写父类方法。
不同的地方在于接口注册时调用的是IExtensionWebApiManager
的registerApi
方法,而不是IExtensionApiManager
中的方法:
FinAppClient.INSTANCE.getExtensionWebApiManager().registerApi(new WebApi(context));
同时,SDK也支持原生调用网页中的JavaScript
代码,调用方式如下:
FinAppClient.INSTANCE.getAppletApiManager().callJS("appId", "funcName", "funParams", 1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
result -> Toast.makeText(context, "callJS successfully : " + result, Toast.LENGTH_SHORT).show(),
throwable -> Toast.makeText(context, "callJS failed : " + throwable.getLocalizedMessage(), Toast.LENGTH_SHORT).show()
);
5. 扩展SDK
以上内容都是围绕finapplet
这个核心SDK来阐述的,其实除了核心SDK之外,我们还提供了扩展SDK,扩展SDK是一个依赖核心SDK的库,里面提供了核心SDK中所没有的各种小程序API。
之所以提供扩展SDK,是因为既要保证核心SDK足够轻量,又要保证小程序API足够丰富。核心SDK负责建立起运行小程序的基础框架并提供一小部分最不可获取的API,在权限方面仅保留了存储、相机、地理位置等基本权限,体积仅1MB多一点,扩展SDK则是作为核心SDK的补充而存在的,里面API将不断丰富和完善。
如果希望使用扩展SDK中的API,在gradle中依赖扩展SDK库即可:
implementation 'com.finogeeks.mop:plugins:+'