Skip to content

Latest commit

 

History

History
847 lines (588 loc) · 36.1 KB

微端接入指南.md

File metadata and controls

847 lines (588 loc) · 36.1 KB

1. 整体流程概述

该文档介绍了如何通过下载补丁包然后动态加载(热更新)的方式实现微端包到完整包的转化,并且还实现了登录/支付的穿透。

微端涉及的模块和关系如下。 其中,您只需要实现游戏APK(生成补丁包)、微端APK、云端APK,其他模块由腾讯云提供。

模块 说明
游戏APK 游戏APK,可以是任意安装包,用于生成补丁包。是微端APP热更新升级后的目标APP。
微端APK 微端APK,集成了微端SDK,是用于广告投放的包体,供C端用户玩云游戏的载体。
云端APK 云端APK,集成了云端SDK,运行于云端,是C端用户在微端上玩的云游戏。
补丁包 补丁包,由游戏APK经过工具一键转化生成。微端APK在运行时从网络下载补丁包,动态加载后进行热更新,从而升级为完整本地游戏。
微端SDK 微端SDK,集成了云游戏终端SDK和热更新SDK,实现了微端的应用逻辑,包括云游戏试玩,云端数据通道交互,补丁包的检查、下载、校验、合成,下载智能限速等交互逻辑。
云游戏终端SDK 云游戏终端SDK,是云游戏PAAS平台提供的终端SDK,它实现了云游戏在终端上的通用功能,方便快速构建云游戏终端APP。
热更新SDK 热更新SDK,实现热更新机制,包括补丁包处理合成、加载逻辑。实现微端APP转化为本地游戏。
云端SDK 云端SDK,提供云端与终端数据通道能力,用于实现APP的登录和支付穿透的功能。
微端云游戏后台 微端云游戏试玩的业务后台,调用云游戏PAAS平台的后端API,实现云试玩的业务层逻辑。

 

整体接入和运行流程如下

备注:如果您是游戏发行商角色,没有游戏工程源码,但是游戏APK里有集成了你的登录支付SDK,那么你也可以独立完成接入云端SDK生成云端APK,而无需游戏研发团队的介入。

微端接入优势

  • 一站式便捷接入。

    流程极简,提供完备的SDK/Demo/工具,一次接入多个游戏复用。

  • 投放包体极小。

    10M左右,提升投放效果,降低买量成本。

  • 原生游戏体验。

    依赖于腾讯云渲染技术,云游戏体验接近本地游戏。本地原生的登录支付体验。

  • 边玩边下。

    试玩云游戏的过程中静默下载补丁包,智能限速,不影响云游体验。

  • 免安装静默升级。

    下载完补丁包后,免安装静默转化为本地游戏,用户无感知。

2. 原理解析

2.1 登录/支付穿透

登录穿透的逻辑流程是:

(1)微端APP调用本地的登录SDK完成本地登录过程,将获取到的登录授权信息,通过数据通道发送给云端APP。

(2)云端APP收到微端发送的登录授权信息,完成云端APP的登录交互。

 

支付穿透的逻辑流程是:

(1)云端APP将支付请求,通过数据通道发送给微端APP。

(2)微端APP调用本地的支付SDK完成支付,将支付结果信息再通过数据通道发送给云端APP。

(3)云端APP使用这个信息完成云端APP的支付交互。

 

其中,需要注意以下几点,

(1)由于云端app会提前预加载,微端连接之前数据通道无法使用。

(2)使用数据通道之前,必须由微端先发送消息给云端app,才能正常建立通信,详情参考3.3.3 实现登录/支付穿透逻辑

2.2 云试玩

云试玩的逻辑流程是:

(1)客户部署云端app到腾讯云服务器中,服务器创建多个实例预加载云端app等待微端app连接。

(2)微端app通过微端sdk连接在云端已经预加载成功的云端app实例,并通过数据通道同步两者的状态。

 

其中,需要注意以下几点,

(1)预加载是指云端app已经在服务器中提前被打开,而不是微端app打开时才打开云端的app。

(2)微端app每次连接一个新的云端app实例,所以需要保存的数据需要通过数据通道保存到微端app。

(3)云试玩体验建议使用真机,模拟器系统存在不确定性,会影响云游戏的体验。

(4)热更新的静默转化并不是将部署到云端的app转化到本地,而是加载正常游戏包生成的补丁包来实现完整游戏的转化。

2.3 热更新

热更新的逻辑流程是:

(1)微端app在运行过程中去客户配置的补丁包链接中下载该微端包可用的补丁。

(2)下载成功后通过微端sdk的能力加载该补丁包完成到完整游戏的转化。

(3)热更新完成后,重启微端app进入完整游戏逻辑。

3. 详细接入步骤

3.1 构建云端包

如果不需要实现登录支付穿透,可以直接把游戏apk给到对接群的同事,部署到云端获取gameId即可完成游戏体验。

如果需要实现登录支付穿透,请按照以下步骤修改游戏APP,将其转化为云端包,

demo中接入了微信登录的功能,以下步骤展示了如何改造游戏包的微信登录部分,使其能够配合在本地运行的微端包完成登录支付穿透,

微端包登录/支付穿透的修改参考3.3.3 实现登录/支付穿透逻辑

第一步,集成云端SDK。打开游戏原始包工程,在app/build.gradle文件中添加云端SDK依赖。

dependencies {
    implementation "com.tencent.tcr:micro-cloud-sdk:2.0.1"
}

云端sdk的具体使用方法请参考云端SDK的API文档,同时云端包的适配可以参考云端APP示例工程.

第二步,建立数据通道,实现登录/支付穿透的具体逻辑。

1)建立数据通道,开始监听微端发送的数据。监听的代码如下:

   DataChannel.Listener listener = new DataChannel.Listener() {
        public void onReceive(byte[] buffer, int len) {
            // 从监听的端口接收到数据
            String dataFromRemote = new String(buffer, 0, len);
            // 微端传递的是json格式的数据,所以转为JSONObject易于操作,不以json格式传输也可以
            JSONObject receiveData = new JSONObject(dataFromRemote);
            // 收到微端发送的消息证明数据通道已经连接成功
            isConnectSuccess = true;

            // 只有在收到微端发送的{"connected":true}消息之后,才能向微端发送消息
            // 因为云端包对应的实例会提前在云服务器中启动,等待微端的连接,微端未连接之前都是无法通信的。
            if (!receiveData.isNull("connected") && receiveData.getBoolean("connected")) {
                // 微端和云端的数据通道已经连接成功了,此时可以向微端发送登录请求。
                // 登录穿透修改:将游戏中原先触发登录弹窗的逻辑删除,并向微端发送登录消息,让弹窗在微端收到login消息之后再弹出
                // 云端包示例工程只是为了演示才在收到{"connected":true}消息之后向微端包发送“login”消息,实际开发过程中发送“login”消息只要保证isConnectSuccess为true即可,具体的交互逻辑可以自定义。
                sendData("login");
            }

                // 收到微端login之后的结果,获取到登录用户的信息即可执行之后的逻辑。
            if (!receiveData.isNull("authCode")) {
                // 登录穿透修改:继续执行之前在游戏工程中获取authCode之后的逻辑
                // 不同的登录sdk在此处的处理逻辑有所不同,可以参考微信登录的流程
                WXBiz.getInstance().getToken(receiveData.getString("authCode"));
            }
        }

        public void onException(Exception e) {
            // 数据接收或发送过程中出现异常
        }
    };
  
    // 建立并打开数据通道,开始监听
    mDataChannel = new DataChannel(listener, 6666);
    mDataChannel.open();

这部分的详细代码在云端APP示例工程的TcrMicroCloud/src/main/java/com/DefaultCompany/Unity2018Empty/DataChannelService.java类中。

登录穿透是将“直接拉起本地登录软件进行登录”改为“通过数据通道拉起微端所在设备的登录软件进行登录”。

主要演示了云端APP如何与微端APP通过数据通道建立联系,可以对比游戏APP示例工程的代码,在适配云端登录穿透时,需要删除游戏工程中弹出登录弹窗部分的逻辑,

删除之后,通过数据通道发送“login”消息给到微端APP,微端APP收到该消息之后在本地再弹出登录弹窗。

微端APP在完成微信登录的授权获取到authCode时,再通过微端sdk提供的发送消息接口将authCode发送给云端包,云端包收到authCode后继续执行之后的逻辑来获取用户信息。

支付穿透的逻辑和登录一致,也是需要将“直接拉起本地支付软件进行支付”改为“通过数据通道拉起微端所在设备的支付软件进行支付”。

2)在需要数据交换时通过数据通道向微端发起对应消息。

云端发送的限制是65536的字符,微端发送数据的限制是1200字符。如果发送数据大于发送限制,需要考虑将数据分段进行发送,所以尽量保证数据通道不要传输大型数据。

    /**
     * 通过此方法向发送方回复数据
     * 必须在数据通道连接成功后使用
     * 该方法必须在非UI线程中调用。
     *
     * @param data 要发送的数据
     */
    public void sendData(String data) {
        if (mDataChannel != null) {
            mDataChannel.send(data.getBytes(Charset.forName("UTF-8")));
        }
    }

注意:云端包发送数据必须在云端包收到微端发送的消息之后,这样云端才能确认是和哪个微端进行通信,同时发送数据的操作不能在子线程中执行。

第三步,云端APK的本地调试。

云端包需要部署到腾讯云服务器上才可以接收到微端包发送的数据,部署游戏相对比较复杂。因此需要在部署之前在本地测试云端APK的数据通道的功能是否符合预期,测试方法如下:

下载数据通道测试APP,解压之后通过AndroidStudio打开。模拟微端包发送数据,代码如下:

    private void sendDataToCloud(String testData) {
        Log.d(TAG, "sendDataToCloud: " + testData);
        try {
            InetAddress address = InetAddress.getByName("localhost");
            byte[] buf = testData.getBytes(StandardCharsets.UTF_8);
            DatagramPacket sendPacket = new DatagramPacket(buf, buf.length, address, 6666);
            socket.send(sendPacket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

云端包安装到手机上会开启一个udp端口,测试APP的逻辑就是模拟云端容器向云端app的数据通道端口发送数据。

先在手机上打开云端包,然后按home键回到桌面,再打开测试APP,点击test按钮向安装在本地手机上的云端APP发送测试数据,过滤云端APP的相关log标签查看是否成功收到测试APP发送的数据。

第四步,将云端APK部署到腾讯云服务器。

本地调试成功之后,将该apk给到腾讯云对接的工作人员,待游戏部署成功后向工作人员获取游戏ID。

第一次部署apk需要工作人员协助,之后调试过程中需要更新云端apk,可以申请自助替换云端apk。

3.2 构建补丁包

安装java11环境。下载微端APP示例工程,解压后,在Terminal中进入到hotUpdate目录下,执行以下脚本:

./generate_patch.sh 游戏APK路径

该脚本命令将根据游戏APK生成对应的补丁包,存放在:hotUpdate/out/patch.apk。

此步骤会根据游戏apk自动生成的壳子四大组件,并把自动生成的壳子组件拷贝到微端工程的app/src/main/java中。

3.3 构建微端包

3.3.1 准备工作

微端APP下载完补丁包后,免安装静默转化为本地游戏,是通过热更新技术实现的。您可以下载微端体验apk体验静默转化的效果。

为了实现热更新,微端APK需要依赖游戏APK的R.txt文件和AndroidManifest.xml文件。AndroidManifest.xml文件是为了保证游戏APK里的Application、四大组件和权限声明在微端APK里有向系统注册。R.txt文件是为了保证AndroidManifest.xml文件里引用到的资源ID在微端APK和游戏APK里是一样的,避免热更新后发生资源引用的错误。

1、下载微端APP示例工程,解压并导入到AndroidStudio。

2、通过游戏APK获取R.txt文件。

在Terminal中进入到微端工程的hotUpdate目录下,使用以下命令生成R.txt:

./generate_resource_id.sh 游戏APK路径

该脚本主要通过aapt2命令提取游戏原始apk中的资源名称和ID,最终生成R.txt。

R.txt文件生成路径为:hotUpdate/out/R.txt。

注意:在windows上使用需要修改为windows的调用方式,如何修改请查看脚本。

3、通过游戏APK获取AndroidManifest.xml文件。

在Terminal中进入到工程的hotUpdate目录下,使用以下命令生成AndroidManifest.xml:

./generate_manifest.sh 游戏APK路径

该脚本主要通过apktools反编译获取游戏原始apk的AndroidManifest.xml文件。

AndroidManifest.xml文件生成路径为hotUpdate/out/AndroidManifest.xml。

同时还会生成存放游戏apk资源的game目录,方便之后使用。

3.3.2 实现热更新

第一步,修改构建脚本。

打开微端工程app模块下的build.gradle文件,修改applicationId、minSdkVersion、targetSdkVersion与您的游戏工程一致。

defaultConfig {
        // 应用名称和您的游戏包名一致
        applicationId "com.DefaultCompany.Unity2018Empty"
        // 与你的游戏版本保持一致
        minSdkVersion 21 // 微端SDK最低支持到16
        //noinspection ExpiredTargetSdkVersion,OldTargetApi
        targetSdkVersion 29 // 与你的游戏版本保持一致
        // 与你的游戏abi保持一致
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a'
        }
        ...
    }

修改签名文件与您的更新包工程一致。

signingConfigs {
        release {
            keyAlias "testres"
            keyPassword "testres"
            storePassword "testres"
            storeFile file("keystore/micro.keystore")
        }
    }

第二步,修复清单文件中的编译错误。

在准备工作中会自动把游戏的清单文件文件拷贝到微端工程的app/src/main目录下。

在3.2构建补丁包中会自动生成游戏中的四大组件,并把生成的壳子四大组件自动拷贝到微端工程的app/src/main/java目录下,

自动生成的组件中的内容都是空的,作为壳子在安装微端apk时注册给系统,这样热更新之后就可以找到对应的组件了。

之后打开app/src/main/AndroidManifest.xm文件,会发现文件中会有编译错误,

请把缺失的style、string、xml等从hotupdate/game中拷贝到微端工程的对应目录中,

修复清单文件中的编译错误,拷贝时为了减少最终的微端apk体积,建议只将缺少的资源拷贝到微端app模块。

第三步,修改工程代码。

将UnityPlayerActivity里的代码拷贝到新AndroidManifest.xml文件里带有"android.intent.action.MAIN"的主Activity类里,并把UnityPlayerActivity类删掉。

第四步,修改工程配置。

打开工程assets下的microSettings.xml,修改对应参数:

<?xml version="1.0" encoding="utf-8"?>
<settings>
    ......
    <!--    是否使用热更新的方式进行升级转化。 -->
    <setting key="hot_update" value="true" />
    <!--    补丁包的地址。仅在hot_update为true的情况下才有意义。 -->
    <setting key="patch_url"
            value="https://cg-sdk-1258344699.cos.ap-nanjing.myqcloud.com/micro/patch/TcrMicroAppForUnity2018Empty/patch.apk" />
    <!--    游戏包的地址。配置之后,热更新失败会跳到浏览器去下载完整游戏apk -->
    <setting key="apk_url" value="https://cg-sdk-1258344699.file.myqcloud.com/test/test.apk" />
</settings>

1、在配置文件patch_url中填写patch文件的下载链接,可以选择自己维护这个链接。

2、在配置文件apk_url中填写游戏官方包的下载链接,热更新失败会跳到浏览器去下载完整游戏apk。

第五步,构建生成微端apk后,在Terminal中进入到hotUpdate目录下,使用以下命令校验微端包:

./check_old_config.sh 微端apk路径 游戏APK路径

该脚本检查微端apk的清单文件是否和游戏原始apk一致,并输出检查结果。

可以正常校验通过没有ERROR信息即为正常。校验结果将生成到hotUpdate/check_result目录下。

校验失败请检查第二步操作是否正确。

第六步,本地调试验证。

注意:热更新调试验证建议使用真机进行测试。

在Terminal中进入到hotUpdate目录下,使用以下命令进行本地调试:

./local_debug.sh 微端apk路径 补丁包路径

该脚本将生成的补丁包通过adb命令push到手机的Download目录下,并通过adb卸载之前的微端包,安装新的微端包。

注意:需要先adb连接要调试的设备,修改脚本中要卸载微端的包名为当前微端的包名。

在手机上点击刚安装的apk,运行之后会提示“检测到本地补丁包“的弹窗,点击“加载补丁”选取刚才push到sd卡Download中的补丁文件,选择完毕后悬浮球显示更新中,更新完成弹出“重启弹窗”,点击“立即重启“按钮,查看是否可以正常重启。

第七步,本地调试验证成功后,删除sd卡Download中的补丁文件,并将补丁包上传到第四步的下载地址中开始验证。

3.3.3 实现登录/支付穿透逻辑

第一步,通知云端APP,数据通道已连接成功。

public class UnityPlayerActivity extends MicroBaseActivity {

    private boolean isConnectSuccess = false;

    ......

    @Override
    protected void onDataChannelConnectSuccess() {
        // 这个回调是告诉微端APP,当前数据通道是可用状态
        // 必须向云端游戏发送一条信息,告诉云端微端此时已经连上了,云端在收到这条消息之后才能进行数据传输
        // 这条消息可以是自定义的任何消息
        isConnectSuccess = true;
        setDebugMode(true);
        sendConnectMsg();
    }

    //向云端发送连接成功消息
    private void sendConnectMsg() {
        Log.d(TAG, "sendConnectMsg");
        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("connected", true);
            sendDataToCloudGame(jsonObject.toString());
        } catch (Exception e) {
            Log.e(TAG, "sendConnectMsg exception, " + e.getMessage());
        }
    }

    private void setDebugMode(boolean isDebug) {
        Log.d(TAG, "setDebugMode" + isDebug);
        try {
            JSONObject jsonObject = new JSONObject();
            // 测试的云端apk中收到该字段为true,则会在云端将收到的信息通过Toast显示出来,方便调试
            // 客户也可以在自己的云端包中新增一个类似的开关,把收到的微端包信息show出来
        
            jsonObject.put("debug", isDebug);
            sendDataToCloudGame(jsonObject.toString());
        } catch (Exception e) {
            Log.e(TAG, "sendConnectMsg exception, " + e.getMessage());
        }
    }
}

微端APP在收到微端SDK的onDataChannelConnectSuccess()回调之后,必须发送一条确认消息给云端APP(消息可以自定义),云端APP只有在收到这条确认消息之后才可以向微端APP发送消息。

使用setDebugMode设置调试模式,可以通过Toast的方式看到云端apk收到的微端消息,方便调试。由于云端游戏会提前预加载运行等待微端连接,所以在微端连接之前弹出的Toast都是无法被看到的,

这时候如果想要查看云端的运行情况,需要在对接群中联系后台开发同学帮忙抓取云端日志。

第二步,实现具体的登录支付逻辑。

微端APP示例工程中实现了微信登录穿透的逻辑,接下来介绍如果将微信登录获取到的授权信息传递给云端app,

public class UnityPlayerActivity extends MicroBaseActivity {
    
    ......

    // 由于微信的授权信息在微信提供的WXEntryActivity中获取到的,因此这里使用了Activity之间的intent传递数据
    // 具体可以参考app/src/main/java/com/DefaultCompany/Unity2018Empty/wxapi/WXEntryActivity.java中传递authCode的代码逻辑
    @Override
    protected void onNewIntent(Intent intent) {
        Log.d(TAG, "onNewIntent: " + intent.getStringExtra("authCode"));
        super.onNewIntent(intent);
        // 在这里接收WXEntryActivity传递过来的authCode
        if (intent.getStringExtra("authCode") != null) {
            sendLoginCall(intent.getStringExtra("authCode"));
        }
    }

    @Override
    protected void onReceiveCloudGameMessage(String data) {
        // 接收云端游戏的返回信息
        if ("login".equals(data)) {
            login();
        } else if ("pay".equals(data)) {
            pay();
        }
    }

    // 处理支付操作
    private void pay() {
        Log.d(TAG, "pay: ");
        // 支付部分逻辑与登录类似,可以参考登录部分传输逻辑。
    }

    // 处理登录操作
    private void login() {
        Log.d(TAG, "login: ");
        // 本地弹出登录窗口
        // 登录支付修改:弹窗的逻辑默认在游戏apk中,微端适配登录穿透之后需要在收到云端发送的“login”消息之后弹出
        WXBiz.getInstance().showLoginDialog(this, new DialogClickListener() {
            @Override
            public void onPositiveClick() {
                // 调用微信登录
                WXBiz.getInstance().login();
            }

            @Override
            public void onNegativeClick() {
                finish();
            }
        });
    }

    // 获取到微信返回的authCode,通过数据通道传递给云端APP
    // 登录原始逻辑:打开游戏弹出登录窗口->用户点击登录->拉起本地微信->授权成功,获取authCode->继续执行之后逻辑
    // 登录穿透后:打开微端应用->进入云游戏->数据通道连接成功(onDataChannelConnectSuccess)->向云端发送{"connected", true}消息
    // ->云端收到{"connected", true}->云端发送login消息->微端收到login消息弹出登录窗口->用户点击登录->拉起本地微信
    // ->授权成功获取authCode->发送authCode到云端APP->云端APP收到authCode后,执行之后逻辑
    public void sendLoginCall(String authCode) {
        if (!isConnectSuccess) {
            Log.d(TAG, "未连接成功,不能向云端发送登录回调");
            return;
        }
        Log.d(TAG, "向云端发送登录回调");
        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("authCode", authCode);
            sendDataToCloudGame(jsonObject.toString());
        } catch (JSONException e) {
            Log.e(TAG, "sendLoginCall exception, " + e.getMessage());
        }
    }
}

第三步,微端包与云端包联调。

打开工程assets下的microSettings.xml,填入云端包的对应参数

<?xml version="1.0" encoding="utf-8"?>
<settings>
    <!--云端包部署之后获取的游戏ID-->
    <setting key="game_id" value="game-xxxxx" />
    
    ......

    <!--与云端通信的端口号-->
    <setting key="port" value="6666" />

    ......
</settings>

云端包部署之后会获取到对应的gameId,将该gameId填入到微端配置参数中,端口号为云端包建立数据通道时开启的端口号。

修改之后构建微端包,安装到手机并运行,验证登录/支付穿透流程是否正常。

适配完成后可以删掉用于演示用的登录穿透逻辑。

4. 其他功能接入说明

4.1 微端工程UI定制化

微端工程有一套默认的UI模块(micro-ui),其中包含各种提示弹窗、悬浮球、启动加载界面,如果您需要定制化自己的UI,可以根据自己需求进行更改。

4.1.1 启动界面定制

背景图片替换:

替换micro-ui/src/main/res/drawable-xxhdpi/micro_loading.jpg该图片为您想要展示的图片。

启动界面显示效果:

除此之外,如果您想要修改进度条的显示效果,可以修改micro-ui/src/main/res/layout/micro_loading_view.xml来更改进度条的显示效果。

4.1.2 弹窗效果定制

修改弹窗文字:

打开micro-ui/src/main/java/com/tencent/tcr/micro/MicroUISettings.java,

修改对应弹窗的文字提示和对应的按钮文字。

修改显示效果:

如果您需要定义自己的弹窗,可以根据需要修改弹窗的UI配置代码。

4.1.3 悬浮球定制

打开micro-ui/src/main/res/layout/micro_float_window.xml,修改悬浮球的界面效果。

4.2 数据通道连接之前传递参数

数据通道需要在微端APP云游戏连接成功之后才可以正常使用,如果需要在数据通道连接之前向云端游戏传递数据,可以通过以下方法:

第一步,修改微端APP,定义要传递的参数gameParas,并通过base64加密。

public abstract class MicroBaseActivity extends Activity {
    ......

    private void init() {
        Log.i(TAG, "init: ");
        // 注意:云游戏启动时带的参数必须经过base64编码,否则会抛出crash(IllegalArgumentException)
        // gameParas为可选参数,不需要携带直接可以使用initConfig(String appVersion)进行初始化。
        // "dGVzdCBkYXRhIGZvciBjbG91ZCBnYW1l"为测试用的gameParas,客户可以传自己需要的。
        MicroContext.getInstance().initConfig(MicroUtils.getVersionName(this),
                "dGVzdCBkYXRhIGZvciBjbG91ZCBnYW1l");
        ......
    }
}

第二步,由于云端APP会提前预加载,微端在连接云游戏的时候云端游戏已经处于启动的状态了,所以无法之前传递启动参数来启动云端APP。

因此,需要修改云端APP,在云端APP中新增一个空的Activity作为云端APP的MAIN Activity,

并且在该Activity中新增广播监听,监听微端传递过来的gameParas,然后将gameParas作为intent参数启动游戏之前的主Activity。

public class MainActivity extends AppCompatActivity {
    public static class CloudGamingReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i("CloudGamingReceiver", "GameParas:" + intent.getStringExtra("GameParas"));
            // 收到需要的参数后再启动游戏的第一个activity
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.intent.action.CLOUD_GAMING_STARTED");
        registerReceiver(new CloudGamingReceiver(), intentFilter);
    }
}

4.3 热更新后so中访问游戏资源失败问题处理

微端包在热更新完成后,有可能会遇到游戏包里的个别so访问资源找不到的问题,原因是该游戏so通过未知方式获取到base.apk的文件路径并访问其中资源,而预期是访问补丁包apk里的资源。这种情况可以将该游戏so的文件名添加到工程配置项里解决,见 /assets/microSettings.xml 的配置项 update_so_list 。

如果您不确定哪些so存在这种问题,可以进行如下测试。

第一步,将microSettings.xml里的debug项值改为true,并将游戏apk中lib下的所有可疑so文件名都填写到update_so_list字段的value值中(用英文逗号分隔)。例如

<settings>
    ......
    <setting key="debug" value="true" />
    <setting key="update_so_list" value="libunity.so,libxxx1.so,libxxx2.so" />
</settings>

第二步,编译生成微端包并运行到手机,执行热更新流程。

第三步,热更新完成点击重启,同时log中过滤“redirect so”字段,查看过滤的结果中包含了哪些so,将这些so保留,其他so从update_so_list 中移除,并把debug字段改为false,重新生成微端包并验证热更新流程。

第四步,构建生成微端apk,并校验微端apk生成是否正常。

4.4 下载限速配置

补丁包在下载过程中会根据云游戏的状态来自动调节下载速度,默认是三档(256、512、1024)。

如果想要根据需求自定义修改,请按照以下步骤,

1、打开工程assets下的microSettings.xml,

<?xml version="1.0" encoding="utf-8"?>
<settings>
    <!--设置三档下载速度,微端会根据当前云游戏状态进行调节,默认是(256、512、1024)-->
    <setting key="download_speed_low" value="512" />
    <setting key="download_speed_middle" value="1024" />
    <setting key="download_speed_high" value="2048" />
</settings>

4.5 自定义下载时机

1、打开micro-ui/src/main/java/com/tencent/tcr/micro/MicroBaseActivity.java

private void init() {
        ......
        // 本地补丁包存在时加载本地补丁包,不存在则直接开始下载
        if (MicroConstant.HOT_UPDATE && FileUtils.fileIsExists(MicroConstant.LOCAL_DEBUG_PATCH)) {
            showFilePickUpDialog();
        } else {
            // 当前下载时机处于Activity启动阶段,客户可以根据自己需要选择合适时机下载
            MicroHotUpdateBiz.getInstance().startDownload();
        }
    }

将MicroHotUpdateBiz.getInstance().startDownload()放到您需要开始下载的地方。

4.6 APK覆盖安装

1、打开工程assets下的microSettings.xml,

<?xml version="1.0" encoding="utf-8"?>
<settings>
    ...
    <!--    是否使用热更新的方式进行升级转化。 -->
    <setting key="hot_update" value="false" />

    <!--    游戏包的地址。仅在hot_update为false的情况下才有意义。覆盖安装时使用。-->
    <setting key="apk_url" value="https://cg-sdk-1258344699.file.myqcloud.com/test/test.apk" />
    ...
</settings>

2、打开micro-ui/src/main/AndroidManifest.xml,

<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.tencent.tcr.micro.ui"
        xmlns:android="http://schemas.android.com/apk/res/android">
    <!--    微端覆盖安装版本,需要新增安装权限和对应的provider-->
        <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
        <application>
            <provider
                    android:name="androidx.core.content.FileProvider"
                    android:authorities="${applicationId}.provider"
                    android:exported="false"
                    android:grantUriPermissions="true">
                <meta-data
                        android:name="android.support.FILE_PROVIDER_PATHS"
                        android:resource="@xml/micro_filepaths" />
            </provider>
        </application>
</manifest>

在该文件中新增安装权限和对应的provider用于覆盖安装。

4.7 加固兼容

如果您的游戏apk使用了加固方案,那就需要在配置文件中设置对应参数,不同的加固厂商对应的flag值不同。微端目前已兼容了腾讯ACE加固和网易易顿加固方案。

对应配置文件 /assets/microSettings.xml 的update_flag字段。

值0表示没有使用加固或者使用了腾讯ACE加固,值12表示使用了爱加密加固,值17表示使用了网易易顿加固。例如

<settings>
    <setting key="update_flag" value="17" />
</settings>

4.8 bugly使用

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "onCreate: ");
        super.onCreate(savedInstanceState);
        // 初始化bugly
        // 应用启动后防止多次初始化bugly
        if (!sBuglyInited) {
            CrashReport.initCrashReport(getApplicationContext(), "xxxxx"/** AppId **/, true);
            sBuglyInited = true;
        }
    }

我们为微端工程接入了bugly用于崩溃监测,如果您想要观测微端工程的crash情况,您可以申请自己的AppId.

申请地址https://bugly.qq.com/v2/index,

申请之后替换为您的AppId,打开bugly后台即可观测微端的崩溃数据。

4.9 异形屏配置

    <!--    是否适配异形屏(摄像头区域)。 如果刘海屏适配遮挡了游戏的内容,可以关闭此开关-->
    <setting key="support_display_cutouts" value="true" />

是否为微端应用进行异形屏配置,默认打开,如果打开后影响游戏显示,将此开关设为false。

4.10 Tinker id 配置

为了保证多个版本的微端包的兼容问题,使用 tinker id 来保证微端包和补丁包的对应关系。

1、打开app/micro-update-support.gradle 文件,将文件中的tinkerId自动填写上自定义字符串,可以是 git 版本号或者 app 版本号。

2、打开hotUpdate/tools/tinker_config.xml,将文件中的TINKER_ID字段配置为tinker_id_加上上一步自定义的字符串。

4.11 客户端空闲等待时间配置

    <!--    客户端空闲时间(可选)。 -->
    <setting key="idle_threshold" value="60000" />

当客户端经过idle_threshold时间没有操作,会触发回调给到com.tencent.tcr.micro.sdk.biz.MicroCloudGameBiz.MicroCloudGameListener#onClientIdle接口, 您可以在该接口中做相关操作。该阈值默认为60000ms(1分钟),可以根据需要进行配置。

4.12 客户端码率修改

云游戏启动时,云端会设置一个固定码率上限。客户端会设置三个档位,分别对应标清,超清,原画,这三个档位的码率可以在/assets/microSettings.xml文件中新增配置,

    <!--   标清 -->
    <setting key="bitrate_upper_limit_low" value="2000" />
    <!--   超清 -->
    <setting key="bitrate_upper_limit_middle" value="5000" />
    <!--   原画 -->
    <setting key="bitrate_upper_limit_high" value="8000" />

5. FAQ

5.1 微端APK重打包

如果您要对微端APK进行重打包操作,需要保证AndroidManifest.xml文件里的资源ID在重打包前后不会发生改变。否则在热更新后(在补丁包里)会找不到对应的资源。

5.2 使用Unity 2020版本

微端包需要将游戏包中lib下的libmain.so拷贝到微端工程的libs,并打入到微端apk中。

5.3 使用oaid sdk

请使用oaid的1.1.0或以上的版本,否则热更新后oaid会出现以下crash。

java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Integer.intValue()' on a null object reference 
at com.bun.miitmdid.core.MdidSdkHelper.InitSdk()

5.4 apache仓库处理

如果热更新后发现运行崩溃,并出现以下堆栈,这个是因为游戏apk中的apache仓库是stub,插件化场景下无法正常运行。

Caused by: java.lang.RuntimeException: Stub!
    at org.apache.http.****

请在生成补丁包时使用以下命令去除游戏apk中的apache仓库。

./generate_patch.sh 游戏APK路径 true

5.5 存储权限处理

由于sdk内部在api小于19时使用到了存储权限,所以对存储权限加了android:maxSdkVersion="18"的限制。会导致app在申请存储权限的时候会被这个限制覆盖掉,无法正常弹出权限申请框。

解决方法:申请android.permission.WRITE_EXTERNAL_STORAGE时加上tools:node="replace"。

<use-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE tools:node="replace"">