接入 Android

更新时间:
复制 MD 格式

本文介绍了接入小程序小游戏的流程和部分使用小程序小游戏的内容。

前置条件

添加 SDK

基线版本

小程序小游戏支持自定义基线版本 cp_change_114400936。请参照 选择合适的基线 指南,添加 cp_change_114400936 基线以完成配置。

image.png

安装组件和依赖

修改项目根目录 build.gradle

  1. 增加 mvn 仓库。

    maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
    maven {
        credentials {
            username "mvn_read_ws"
            password "mrk8929"
        }
        url "http://mvn.cloud.alipay.com/nexus/content/repositories/releases/"
    }
    maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
    maven {
        url 'http://maven.aliyun.com/nexus/content/repositories/google/'
        name 'aliyun-google'
    }
    maven {
        credentials {
            username '617696fc6970513e6e677c83'
            password '2qAa=hHdfLOp'
        }
        url 'https://packages.aliyun.com/63dc774a9dee9309492b993a/maven/repo-jpkdy'
    }
  2. 在 buildscript 中增加配置。

    buildscript {
        ext.mpaas_artifact = "mpaas-baseline-cp_change_56100909"
        ext.mpaas_baseline = "23"
        repositories {
            mavenLocal()
            maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
            maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
            maven {
                credentials {
                    username "mvn_read_ws"
                    password "mrk8929"
                }
                url "http://mvn.cloud.alipay.com/nexus/content/repositories/releases/"
            }
            google()
            maven {
                url 'http://maven.aliyun.com/nexus/content/repositories/google/'
                name 'aliyun-google'
            }
        }
        dependencies {
            classpath 'com.android.boost.easyconfig:easyconfig:2.6.7' // 根据项目实际gradle版本修改版本号
            classpath 'com.android.tools.build:gradle:4.0.1' // 使用项目实际的gradle版本
        }
    }

申请游戏依赖的 mPaaS 内核 key

申请游戏依赖的 mPaaS 内核 key 请参考 接入 mPaaS 内核。

修改 app 下的 build.gradle

增加依赖和 configuration。

android {
    ...


    configurations {
        all*.exclude group: 'com.mpaas.mriver', module: 'mriveruc-build' // 移除
        all*.exclude module: 'nebulaucsdk-build'
    }

}

dependencies {
    ...
    implementation platform("com.mpaas.android:$mpaas_artifact:$mpaas_baseline")
    implementation 'com.mpaas.android:minigame' // 小游戏
}

初始化 mPaaS

在使用 mPaaS 框架前需要进行一些初始化操作对 Application 对象进行改造。由于是否使用热修复功能会采取不同的初始化内容,因此本文将根据是否使用热修复功能分别向您介绍相应的初始化操作。

不使用热修复功能

不使用 热修复 功能时,只需在 Application 中添加如下代码:

@Override
public void onCreate() {
    super.onCreate();
    
    MP.init(
      this,
      MPInitParam.obtain().setCallback(new MPInitParam.MPCallback() {
        @Override
        public void onInit() {
            Log.d("TAG", "mPaaS Init finish");
        }
      })
    );
}
说明
  • 如果您集成使用 Kotlin 的话,可以使用 mPaaS 提供的 mPaaS KTX,详情参考 mPaaS Kotlin 扩展。

  • 如果您需要继续使用 QuinoxlessFramework 初始化方式,您的调用不会受到任何影响,代码和业务不需要做任何更改。

重要

请不要在使用 MP.init 方法前过滤进程,除主进程外,在 tools 和 push 子进程中也需要执行初始化代码。

使用热修复功能

使用 热修复 功能时,需要完成以下操作。

操作步骤

  1. 使 Application 对象重新继承 QuinoxlessApplicationLike,并注意将此类防混淆。此处以 “MyApplication”为例。

     @Keep
     public class MyApplication extends QuinoxlessApplicationLike implements Application.ActivityLifecycleCallbacks {
    
      private static final String TAG = "MyApplication";
    
      @Override
      protected void attachBaseContext(Context base) {
          super.attachBaseContext(base);
    
          Log.i(TAG, "attacheBaseContext");
    
      }
    
      @Override
      public void onCreate() {
          super.onCreate();
          Log.i(TAG, "onCreate");
          registerActivityLifecycleCallbacks(this);
      }
    
      @Override
      public void onMPaaSFrameworkInitFinished() {
          LoggerFactory.getTraceLogger().info(TAG, getProcessName());
      }
    
      @Override
      public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
          Log.i(TAG, "onActivityCreated");
      }
    
      @Override
      public void onActivityStarted(Activity activity) {
    
      }
    
      @Override
      public void onActivityResumed(Activity activity) {
    
      }
    
      @Override
      public void onActivityPaused(Activity activity) {
    
      }
    
      @Override
      public void onActivityStopped(Activity activity) {
    
      }
    
      @Override
      public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    
      }
    
      @Override
      public void onActivityDestroyed(Activity activity) {
    
      }}
  2. 在 AndroidManifest.xml 文件中将 Application 对象指向 mPaaS 提供的 Application 对象。 将生成的 “MyApplication” 类添加到 key 为 mpaas.quinoxless.extern.application 的 meta-data 中。示例如下:

     <application
     android:name="com.alipay.mobile.framework.quinoxless.QuinoxlessApplication" >
     <meta-data
              android:name="mpaas.quinoxless.extern.application"
              android:value="com.mpaas.demo.MyApplication"
              />
     </application>
  3. 引入 Apache HTTP 客户端。

    在使用 RPC 或者热修复功能的时候,需要调用到 Apache HTTP 客户端相关的功能。只需在 AndroidManifest.xml 加入如下代码。更多信息,请参见 使用 Apache HTTP 客户端。

    <uses-library android:name="org.apache.http.legacy" android:required="false"/>

API

初始化

import android.app.Application;
import android.util.Log;
import com.mpaas.core.MP;
import com.mpaas.core.MPInitParam;
import com.mpaas.mas.adapter.api.MPLogger;
import com.mpaas.mriver.api.init.MriverInitParam;
import com.mpaas.mriver.nebula.api.uc.MRUCInitCallback;
import com.mpaas.mriver.nebula.api.uc.MRUCService;
import com.mpaas.mriver.xriver.XRiver;
import com.mpaas.uc.dynamic.api.MPUCDynamicInit;

public class MRiverApp extends Application implements MPInitParam.MPCallback{

    private static final String TAG = "MRiverApp";

    @Override
    public void onCreate() {
        super.onCreate();
        MP.init(this, createInitParams());
    }

    @Override
    public void onInit() {
        MPLogger.setUserId("xxxx"); // 设置userId,用来白名单灰度、IDE扫码预览使用
    }

    MPInitParam createInitParams() {
        final MriverInitParam mriverInitParam = MriverInitParam.getDefault();
        mriverInitParam.setMriverInitCallback(new MriverInitParam.MriverInitCallback() {
            @Override
            public void onInit() {
                XRiver.init();
            }

            @Override
            public void onError(Exception e) {
            }
        });
        return MPInitParam.obtain().setCallback(this).addComponentInitParam(mriverInitParam);
    }


}

配置拦截 RPC(广告依赖)

//初始化完成时
Mriver.setProxy(MRGameRpcProxy.class, new MRGameRpcProxy() {
            @Override
            public boolean rpc(String s, Object o, JSONObject jsonObject, App app, Page page, int i, String s1, boolean b, BridgeCallback bridgeCallback) {
                if (!TextUtils.isEmpty(s) && s.contains("querygamedefaultad")) {
                    JSONObject data = new JSONObject();
                    data.put("success", false);
                    bridgeCallback.sendJSONResponse(data);
                }

                return true;
            }
        });

启动游戏

XRiver.startApp("appId"); // appId表示后台发布的游戏id

账号授权

客户端注入 my.getAuthCode() 能力

初始化完成后设置 proxy。在客户端注入 my.getAuthCode() 能力,直接返回的 openId 即为游戏所需要的用户 ID。

Mriver.setProxy(RVOpenAuthProxy.class, new RVOpenAuthProxy() {
            @Override
            public void getAuthAccountInfo(Context context, List<String> list, String s, boolean b, Page page, IAuthCallback iAuthCallback) {
                // ignore
                iAuthCallback.onSuccess(BridgeResponse.SUCCESS.get());
            }

            @Override
            public void auth(Context context, Page page, String sessionId, List<String> scope, Map<String, String> map, boolean b, Bundle bundle, IAuthCallback iAuthCallback) {
                // handle authCode request
                // auth_base表示静默授权,不会弹申请窗;auth_user会弹出申请弹窗
                if (scope != null && (scope.contains("auth_user") || scope.contains("auth_base")) && page.getApp() != null) {
                    String appId = page.getApp().getAppId();
                    
                    // 从App服务端获取authCode/openId
                    String authCode = requestAuthCodeFromSuperAppServer(appId);
                    String openId = requestOpenIdFromSuperAppServer(appId);
                    
                    if (!TextUtils.isEmpty(authCode)) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("authCode", authCode);
                        jsonObject.put("openId", openId); // 返回openId
                        iAuthCallback.onSuccess(jsonObject); // callback to miniprogram
                    } else {
                        iAuthCallback.onFail(-1, "auth code request err");
                    }
                }
            }

            @Override
            public boolean appxRpc(JSONObject jsonObject, BridgeCallback bridgeCallback) {
                // ignore
                bridgeCallback.sendJSONResponse(BridgeResponse.SUCCESS.get());
                return true;
            }
        });

广告

方式一

内置穿山甲模式,只需配置广告位。注册获取广告信息的接口,注入广告数据。

Mriver.setProxy(MRGameAdProxy.class, new MRGameAdProxy() {
    /**
    * appId: 小游戏id
    * adType: 广告类型: 0 激励视频广告; 1 banner广告; 2 插屏/全屏广告
    **/
    @Override
    public void getGameAdInfo(String appId, App app, int adType, GameAdInfoCallback callback) {
        Map<String, String> adInfo = new HashMap();
        if (adType == 1) {
          adInfo.put("code", "xxx"); // 添加广告位信息
        } else {
        }
        if (callback != null) {
          callback.onCallback(adInfo);
        }
    }
});

方式二

自定义广告显示。

 Mriver.setProxy(MiniAdShowProxy.class, new MiniAdShowProxy() {
            @Override
            public void showAd(String s, Page page, int i, MiniGameAdShowCallback miniGameAdShowCallback) {
                
            }
        });

数据埋点监听

初始化时设置数据埋点监听。

Mriver.setProxy(PaladinGameTracker.class, new PaladinGameTracker() {

            @Override
            public void gamePageAppear(Activity activity, String appId, String subPageToken, String channelInfo) {
                // 游戏第一次打开或者页面从后台切换到前台
                Log.i(TAG, "gamePageAppear: " + appId);
            }

            @Override
            public void gamePageDisappear(Activity activity, String appId, String subPageToken, String channelInfo) {
                // 游戏退出或者切换到后台
                Log.i(TAG, "gamePageDisappear: " + appId);
            }
        });

关卡上报

本功能专为闯关类游戏设计,适用于具有明确关卡规则与进度体系的场景。

注册自定义 Bridge

MriverEngine.registerBridge(ReportGameStatusExtension.class);

实现 ReportGameStatusExtension

package com.mpaas.demo.tinyapp;

import android.util.Log;

import com.alibaba.ariver.app.api.App;
import com.alibaba.ariver.app.api.Page;
import com.alibaba.ariver.engine.api.bridge.extension.BridgeCallback;
import com.alibaba.ariver.engine.api.bridge.extension.BridgeResponse;
import com.alibaba.ariver.engine.api.bridge.extension.annotation.BindingApiContext;
import com.alibaba.ariver.engine.api.bridge.extension.annotation.BindingCallback;
import com.alibaba.ariver.engine.api.bridge.extension.annotation.BindingNode;
import com.alibaba.ariver.engine.api.bridge.extension.annotation.BindingRequest;
import com.alibaba.ariver.engine.api.bridge.model.ApiContext;
import com.alibaba.ariver.kernel.api.annotation.ActionFilter;
import com.alibaba.ariver.kernel.api.annotation.NativeActionFilter;
import com.alibaba.ariver.kernel.api.extension.bridge.BridgeExtension;
import com.alibaba.ariver.kernel.api.security.Permission;
import com.alibaba.ariver.kernel.common.utils.JSONUtils;
import com.alibaba.fastjson.JSONObject;

public class ReportGameStatusExtension implements BridgeExtension {
    private static final String TAG = "ReportGameExtension";

    public ReportGameStatusExtension() {
    }

    @ActionFilter
    @NativeActionFilter
    public void reportGameStatus(@BindingApiContext ApiContext apiContext, @BindingNode(App.class) App app, @BindingNode(Page.class) Page page, @BindingRequest JSONObject params, @BindingCallback final BridgeCallback bridgeCallback) {
        JSONObject reportData = JSONUtils.getJSONObject(params,"data", null);
        if (reportData == null) {
            bridgeCallback.sendBridgeResponse(BridgeResponse.INVALID_PARAM);
            return;
        }


        // 从启动参数读取 ad schema
        Log.i(TAG, "reportData: " + reportData.toJSONString());
        bridgeCallback.sendBridgeResponse(BridgeResponse.SUCCESS);
    }

    public void onInitialized() {
    }

    public void onFinalized() {
    }

    public Permission permit() {
        return null;
    }
}

以下是 ReportGameStatusExtension 上报时打印的 reportData 典型 JSON 结构。

{
  "currentLevel": 4,      // 当前正在挑战的关卡编号
  "duration": 23,         // 本局游戏持续时长 (单位:秒/毫秒,需根据协议确认)
  "passedLevels": 3       // 已成功通关的关卡总数
}

游戏行为上报

新增小游戏进度上报功能:基于 gameMessage 统一通道,action 字段实现事件路由,payload 字段透传行为ID与累计值。

上报 API

my.call('gameMessage', {
  data: {
    action: 'action_task_report', //行为任务上报固定为 action_task_report
    payload: {
      actionTaskEvent: 'catch_pigs', //行为任务标识
      value: 10 //当前累计行为量
    }
  }
});

数据字段说明

字段名

中文名称

说明

action

消息路由类型

行为任务上报固定为 action_task_report,用于通道路由。

payload

上报数据

参数格式约束:必须直接传递原生 JSON 对象,
严禁使用 JSON.stringify() 。

payload.actionTaskEvent

行为任务标识

具体行为的唯一标识,需与平台协商约定,并与任务配置保持一致。

payload.value

当前累计值

当前统计周期内该行为的累计值,必须为非负安全整数。

说明
  • 外层 action 必须固定为 action_task_report,严禁用于区分具体游戏行为。

  • 系统将自动忽略非法、重复或数值倒退的上报数据;跨会话累计值需由 CP 自行维护并上报最新状态。

  • 对接前请务必与平台对齐以下要素:actionTaskEvent 标识、行为定义、触发时机、累计口径及重置周期。

  • payload.value 须传入当前累计总值(非增量)。例如完成第3次时,应依次上报 1、2、3。