其实现在很多团队都有自己的小程序,只是大部分还运行在微信上。不过新的项目需求出现时,业务团队往往会问:既然页面和功能已经做好了,能不能在自己的 APP 里继续使用?

从技术角度来看,自有APP也能运行小程序,整体的思路就是:在APP内集成小程序容器,由容器提供代码加载、页面渲染、路由、生命周期和端能力调用等运行环境。原有微信小程序项目无需二次开发,只要有代码,就可以作为业务小程序运行在企业自己的APP中。

在这里插入图片描述

小程序代码、运行时与宿主APP的关系

小程序页面能够正常展示和交互,需要小程序代码、运行时与宿主 APP 共同工作。

小程序项目承载页面结构、样式、JavaScript 业务逻辑、组件和网络请求。业务功能如何展示、用户如何操作,大都由小程序项目中的代码决定。

小程序代码无法脱离运行时单独执行。页面创建、路由跳转、存储、网络、权限和原生端通信,都由运行时承接。微信已经把运行时集成在客户端里,因此用户和小程序开发者很少需要关注它的存在。

宿主 APP 提供小程序的运行入口。微信是一种宿主,企业自有 APP 也可以成为宿主。自有 APP 原本没有运行小程序的能力,需要集成小程序容器 SDK。集成完成后,APP 可以根据小程序 AppID 加载管理平台中的可用版本,也能向小程序开放登录、导航、扫码、文件选择等经过授权的原生能力。

小程序能在哪个 APP 中运行,取决于宿主是否提供了相容的运行时和端能力接口。微信小程序使用的开发模型并没有把业务代码永久锁定在微信客户端中。

小程序容器如何运行小程序

把已有小程序迁入自有 APP,不只是端上能不能打开一个页面的问题。实际项目还要管代码包、版本、宿主关联、审核、发布范围和线上状态。

FinClip 小程序容器集成在宿主 APP 内,处理小程序的下载、启动、运行和端内交互。FinClip 小程序管理平台位于服务端,用来管理小程序资产、代码包版本、宿主应用关联、审核与上下架。项目需要时,还可以配置灰度发布策略,将新版本控制在指定范围内。

宿主 APP 继续负责账号体系、原生导航、消息、支付、设备权限和整体用户体验;业务后台继续处理订单、会员、内容等业务数据。小程序容器负责端内运行,管理平台控制代码包与发布状态,三方共同组成“发布、加载、运行、交互”链路。

代码实践:如何在宿主APP打开小程序

Android 宿主可以通过 SDK 的公开接口启动小程序。apiServerappId 由项目实际环境传入,path 指向需要进入的小程序页面,query 用来传递业务参数。openOrderMiniProgram 应在 SDK 初始化成功后调用,代码省略了 Java import 语句。

private void openOrderMiniProgram(
        Activity activity,
        String apiServer,
        String appId,
        String orderId
) {
    Map<String, String> startParams = new HashMap<>();
    startParams.put("path", "/pages/order/detail");
    startParams.put(
            "query",
            "orderId=" + Uri.encode(orderId) + "&source=hostApp"
    );

    RemoteFinAppletRequest request =
            IFinAppletRequest.Companion.fromAppId(apiServer, appId);
    request.setStartParams(startParams);

    FinAppClient.INSTANCE.getAppletApiManager().startApplet(
            activity,
            request,
            new FinSimpleCallback<String>() {
                @Override
                public void onSuccess(String result) {
                    Log.i("MiniProgram", "start success: " + result);
                }

                @Override
                public void onError(int code, String error) {
                    Log.e(
                            "MiniProgram",
                            "start failed: " + code + ", " + error
                    );
                    Toast.makeText(
                            activity,
                            "小程序启动失败:" + code,
                            Toast.LENGTH_SHORT
                    ).show();
                }
            }
    );
}

startApplet 负责发起小程序启动。动态 orderId 在进入 query 前完成 URL 编码,启动失败后则记录错误码。正式项目还应记录失败时间、AppID、SDK 版本和网络状态,再根据错误码区分网络不可用、小程序未上架、宿主未关联或其他异常。

在这里插入图片描述

小程序调用 APP 原生能力

小程序运行在自有 APP 后,账户、支付和设备权限等能力仍由宿主提供。例如小程序中的“查看账户资料”按钮,可以通过自定义 API 打开 APP 原生账户页。自定义 API 只暴露明确授权的原生能力,不将整个 APP 的内部实现开放给小程序。

ApiOpenPage 继承 FinClip SDK 的 AbsApi,并将 openPage 映射到宿主原生页面:

public class ApiOpenPage extends AbsApi {
    private final Context context;

    public ApiOpenPage(Context context) {
        this.context = context;
    }

    @Override
    public String[] apis() {
        return new String[]{"openPage"};
    }

    @Override
    public void invoke(String event, JSONObject param, ICallback callback) {
        if (!"openPage".equals(event)) {
            callback.onFail();
            return;
        }

        String url = param.optString("url");
        if ("account".equals(url)) {
            Intent intent = new Intent(context, AccountActivity.class);
            if (!(context instanceof Activity)) {
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            context.startActivity(intent);
            callback.onSuccess(null);
        } else {
            callback.onFail();
        }
    }
}

FinAppClient.init 成功后完成 API 注册:

FinAppClient.INSTANCE.getExtensionApiManager()
        .registerApi(new ApiOpenPage(getApplicationContext()));

小程序工程根目录的 FinClipConf.js 还需要声明同名能力:

module.exports = {
  extApi: [
    {
      name: 'openPage',
      sync: false,
      params: { url: '' }
    }
  ]
}

声明完成后,小程序页面通过 ft.openPage 发起调用:

ft.openPage({
  url: 'account',
  success: () => {
    console.log('原生页面已打开')
  },
  fail: () => {
    console.error('原生页面打开失败')
  }
})

原生端不应该直接接受任意 Activity 类名或外部链接。ApiOpenPage 只接受 account 业务路由,再由宿主映射到项目自己的 AccountActivity。涉及用户资料、支付、文件或设备能力时,宿主还要校验登录态、业务权限和小程序可调用范围。

APP 主动向小程序发送事件

宿主 APP 也可以主动向运行中的小程序发送事件。用户在 APP 内重新登录、切换账号或完成原生支付后,业务状态可以及时同步给小程序。

Android 宿主发送一个账号状态变更事件:

private void notifyAccountChanged(String appId, boolean loggedIn) {
    try {
        JSONObject data = new JSONObject();
        data.put("loggedIn", loggedIn);

        JSONObject event = new JSONObject();
        event.put("name", "accountChanged");
        event.put("data", data);

        FinAppClient.INSTANCE.getAppletApiManager()
                .sendCustomEvent(appId, event.toString());
    } catch (JSONException exception) {
        Log.e("MiniProgram", "build event failed", exception);
    }
}

小程序侧按事件名监听,并在页面销毁时移除监听:

Page({
  data: {
    loggedIn: false
  },

  onLoad() {
    this.accountChangedHandler = (res) => {
      const loggedIn = Boolean(res.data && res.data.loggedIn)
      this.setData({ loggedIn })
    }

    ft.addCustomEventListener(
      'accountChanged',
      this.accountChangedHandler
    )
  },

  onUnload() {
    ft.removeCustomEventListener(
      'accountChanged',
      this.accountChangedHandler
    )
  }
})

sendCustomEvent 发送的数据体需要包含 name 字段,小程序侧才能用 addCustomEventListener 按名称收到对应事件。addCustomEventListener 在 FinClip 基础库 3.0.51 中加入,项目若需要兼容更早的基础库,应使用 ft.onCustomEvent 接收事件并自行按 name 分发。参数内容应该尽量小且稳定,不要在端间直接传递密码、长期凭证或完整的敏感用户资料。
在这里插入图片描述

已有微信小程序能够复用多少

迁移评估需要分开检查通用代码和微信平台专属能力。

页面结构、样式、普通 JavaScript 业务逻辑、自研组件、网络请求封装和多数通用小程序 API,通常具备复用基础。能否原样运行,还要结合容器兼容清单、小程序基础库版本和实际代码进行验证。

与微信平台强绑定的能力需要单独处理,常见的有微信登录、微信支付、微信分享、微信插件、云开发以及依赖微信帐号或社交关系的接口。离开微信后,微信登录、支付和分享能力不会自动转换成企业 APP 的对应能力,需要通过宿主自定义 API 或项目自身的业务接口重新对接。

因此,“无需重写整个小程序”是合理的项目目标,“不做任何适配就能百分之百运行”则不应该写进方案。

容器接入之后的技术与管理边界

小程序容器接入 APP 后,业务功能有机会从 APP 主工程中拆出来,按小程序维护独立版本。CIO 和 APP 负责人关注的也不只是增加一种页面形式,还包括能否减少业务调整对 APP 整包发布的依赖。

开发团队可以为原有微信小程序资产增加自有 APP 运行入口。业务代码尽量保持通用,登录、支付、导航和设备权限等平台差异由宿主能力层承接,代码责任也会更清晰。

运营和管理团队通过 FinClip 小程序管理平台管理代码包、版本、审核、宿主关联和上下架。容器负责小程序在 APP 内的运行,管理平台决定运行哪个版本、发布范围以及异常版本如何处理。容器与管理平台配合运行,自有 APP 才能持续管理和运营小程序业务。

Logo

一站式 AI 云服务平台

更多推荐