初始化
Unity3D
概述
接入向导
MSDKPolicy
TargetAPI 31
MSDK 权限列表
常见问题
Android
概述
接入向导
MSDKPolicy
TargetAPI 31
MSDK Android 权限列表
常见问题
IOS
概述
接入向导
Universal Link
MSDK iOS 权限列表
常见问题
其他
数据结构
系统工具
接入配置 / Android / 概述

概述

MSDK for Android 除了提供登录,好友关系链,分享,加绑群的能力外,还提供了大量的增值拓展能力,包括公告系统、内置浏览器、腾讯移动推送 TPNS、数据上报、LBS、Crash分析。这些增值模块使游戏开发者能够轻松构建游戏额外能力,更专注于游戏本身的体验,为玩家带来更优质的游戏。

接入向导

1 前置条件

1)首先您需要确认是否已获取MSDK Android版本,如果您未获取可点击下面链接进行下载。

MSDK for Android 下载

2)需要已安装Java环境,并配置环境变量。

配置系统变量(参考):
JAVA_HOME : D:/Program Files/java/jdk1.6.0_25
Path : C:\Windows\system32;C:\Windows;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
CLASSPATH : .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
更详细步骤请网上搜索"java 环境变量配置"

3)MSDK for Android 需要JDK1.7及以上,build-tools 22.0.1及以上。

4)适配版本最低要求为9编译目标版本为28

<uses-sdk
        android:minSdkVersion="9"
        android:targetSdkVersion="28" />

2 导入MSDK

MSDK的Android版本包中包含示例工程 MSDKDemo,及版本依赖工程 MSDKLibrary。按以下步骤可快速导入MSDK。

1)在eclipse菜单中依次点击 : Project->Properties->Add...

2)选择MSDK的依赖工程包 : MSDKLibrary

3 添加MSDK配置

打开MSDKDemo工程进入assets目录将msdkconfig.inicert文件夹channel.inicopy到游戏工程目录的assets目录下。
注意:

  • 3.2.6a至3.2.18a版本不需要cert文件夹、channel.ini文件从3.3.5a版本开始已不需要。
  • 3.3.12a-3.3.15a 版本,手Q OpenSDK FileProvider 需要做如下配置:在AndroidManifest.xml 中新增配置,如下
<provider  
    android:authorities="com.example.wegame.QQSDKFileProvider"
    android:name="android.support.v4.content.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true" >
        <meta-data 
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"/>
</provider>

需要在 MSDKLibrary/res/xml 目录中新增 手Q file_paths.xml,如下

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-files-path name="opensdk_external" path="Images/tmp"/>
    <root-path name="opensdk_root" path=""/>
</paths>

注意事项:

  • 需要把上述配置中的 com.example.wegame,替换为业务自己应用包名,否则会导致图片分享时获取图片失败的异常,并有可能和其他 App 产生冲突。由于 FileProvider 影响图片相关分享,建议项目组接入后务必验证所有图片相关的分享,确保功能可用
  • file_paths.xml 中配置的内容,需要与业务调用接口时传入的图片路径位置相匹配,msdk默认带了部分配置,如业务传入的路径配置不在默认配置中,需要补齐对应配置

  • 3.3.16a 版本开始,方便业务接入,手Q、微信 OpenSDK FileProvider 配置合一起处理(如已接入3.3.12-3.3.15的业务升级,需注意修改) , 在AndroidManifest.xml 中新增配置,如下

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.wegame.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"/>
</provider>

需要在 MSDKLibrary/res/xml 目录中新增 file_paths.xml,如下

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-files-path name="opensdk_external" path="Images/tmp"/>
    <external-files-path name="sharedata" path="shareData/"/>
    <external-files-path name="msdk_webview_share" path="MSDK/msdk_webview"/>
    <root-path name="opensdk_root" path=""/>
</paths>

注意事项:

  • 需要把上述配置中的 com.example.wegame,替换为业务自己应用包名,否则会导致图片分享时获取图片失败的异常,并有可能和其他 App 产生冲突。由于 FileProvider 影响图片相关分享,建议项目组接入后务必验证所有图片相关的分享,确保功能可用
  • file_paths.xml 中配置的内容,需要与业务调用接口时传入的图片路径位置相匹配,msdk默认带了部分配置,如业务传入的路径配置不在默认配置中,需要补齐对应配置

4 修改AndroidManifest.xml文件

1)copy MSDK相关权限到游戏AndroidManifest.xml文件的manifest标签下

<!-- TODO SDK接入权限模块 START -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- 如果使用LBS功能,则需要配置位置权限 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- 如果使用LBS功能,则需要配置位置权限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.RESTART_PACKAGES" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <!-- 登录上报时需要带设备名称, 通过蓝牙模块来获取设备名称 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <!-- 可选的权限:异常上报系统log,TPNS也需要 -->
    <uses-permission android:name="android.permission.READ_LOGS" />


    <!-- 腾讯移动推送所需权限 Start-->
    <!-- 【必须】 腾讯移动推送SDK VIP版本所需权限 -->
    <!--注: 其中com.example.wegame修改为应用的包名-->
    <permission
        android:name="com.example.wegame.permission.XGPUSH_RECEIVE"
        android:protectionLevel="signature" />
    <uses-permission android:name="com.example.wegame.permission.XGPUSH_RECEIVE" />
    <!-- 【必须】 腾讯移动推送 SDK 所需权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!-- 魅族推送SDK内部使用的自定义组件权限,不涉及用户个人敏感信息及隐私合规问题 -->
    <uses-permission android:name="com.meizu.flyme.permission.PUSH" />
	
    <!-- 【可选】 腾讯移动推送 SDK 所需权限 -->
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RESTART_PACKAGES" />
    <uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE" />
    <uses-permission android:name="com.vivo.notification.permission.BADGE_ICON" />
    <!-- 腾讯移动推送所需权限 End-->

   <!-- 接入腾讯移动推送厂商通道权限配置 Start -->
   <!--注: 其中${applicationId}修改为应用的包名-->
    <permission
        android:name="${applicationId}.permission.MIPUSH_RECEIVE"
        android:protectionLevel="signature" />
    <uses-permission android:name="${applicationId}.permission.MIPUSH_RECEIVE" />
    <uses-permission android:name="com.meizu.flyme.push.permission.RECEIVE" />
    <permission android:name="${applicationId}.push.permission.MESSAGE"
        android:protectionLevel="signature"/>
    <uses-permission android:name="${applicationId}.push.permission.MESSAGE" />
    <uses-permission android:name="com.meizu.c2dm.permission.RECEIVE" />
    <permission android:name="${applicationId}.permission.C2D_MESSAGE"
        android:protectionLevel="signature"/>
    <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
    <uses-permission android:name="com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE"/>
    <uses-permission android:name="com.heytap.mcs.permission.RECIEVE_MCS_MESSAGE"/>
    <uses-permission android:name="com.hihonor.push.permission.READ_PUSH_NOTIFICATION_INFO"/>
    <!-- 接入腾讯移动推送厂商通道权限配置 End -->

    <!-- 读取sdcard权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 
    <!-- 适配Android 9.0 使用apache http -->
    <uses-library android:name="org.apache.http.legacy" android:required="false"/>
    <!-- TODO SDK接入权限模块 END -->

    <!-- TODO SDK接入 接入支付需要设置屏幕兼容声明 START -->
    <supports-screens
        android:anyDensity="true"
        android:largeScreens="true"
        android:normalScreens="true" />
    <!-- TODO SDK接入 接入支付需要设置屏幕兼容声明 END -->

2)copy MSDK的相关组件到游戏的AndroidManifest.xml的application标签下

<!-- 6.0系统权限配置activity, 2.18.0新增-->
<activity
    android:name="com.tencent.msdk.PermissionActivity"
    android:excludeFromRecents="true"
    android:exported="true"
    android:label="PermissionActivity"
    android:launchMode="singleTop"
    android:theme="@android:style/Theme.Translucent.NoTitleBar"
    android:screenOrientation="behind"
    <!-- 【必须】请修改为 游戏包名.diff -->
    android:taskAffinity="com.example.wegame.diff" />

<activity
    android:name="com.tencent.msdk.NameAuthActivity"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:screenOrientation="sensor"
    android:launchMode="singleTop"
    android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<!-- TODO SDK接入 QQ接入配置 START -->
<activity
    android:name="com.tencent.tauth.AuthActivity"
    android:launchMode="singleTask"
    android:noHistory="true" >
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- 【必须】请修改为 tencent游戏的手Q appid -->
        <data android:scheme="tencent100703379" />
        <!-- 100703379要换成游戏的appid -->
        <!-- 这里请设置tencentminiapp+qqappid-->
        <data android:scheme="tencentminiapp100703379" />
    </intent-filter>
</activity>
<activity
    android:name="com.tencent.connect.common.AssistActivity"
    android:configChanges="orientation|keyboardHidden|screenSize"
    android:screenOrientation="behind"
    android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<!-- 3.3.12-3.3.15 需要QQSDKFileProvider配置,3.3.16 开始不需要,其中 com.example.wegame替换为游戏的包名 -->
<provider 
    android:authorities="com.example.wegame.QQSDKFileProvider"
    android:name="android.support.v4.content.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true" >
    <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
         android:resource="@xml/file_paths"/>
</provider>
<!-- 3.3.12-3.3.15 需要QQSDKFileProvider配置,3.3.16 开始不需要,其中 com.example.wegame替换为游戏的包名 -->

<!-- 3.3.16 开始需要FileProvider配置,其中 com.example.wegame替换为游戏的包名 -->
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.wegame.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"/>
</provider>
<!-- 3.3.16 开始需要FileProvider配置,其中 com.example.wegame替换为游戏的包名 -->

<!-- TODO SDK接入 QQ接入配置 END -->

<!-- TODO SDK接入 微信接入配置 START -->
<activity
<!-- 【必须】此处应改为 游戏包名.wxapi.WXEntryActivity -->
    android:name="com.example.wegame.wxapi.WXEntryActivity"
    android:excludeFromRecents="true"
    android:exported="true"
    android:label="WXEntryActivity"
    android:launchMode="singleTop"
    android:theme="@android:style/Theme.Translucent.NoTitleBar"
    <!-- screenOrientation属性在3.3.0及后续版本移除 -->
    android:screenOrientation="portrait"
    <!-- configChanges属性在3.3.0版本新增 -->
    android:configChanges="orientation|screenSize|keyboardHidden"
    <!-- 【必须】此处应改为 游戏包名.diff -->
    android:taskAffinity="com.example.wegame.diff" >
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <!-- 【必须】此处应改为 游戏的微信 appid -->
        <data android:scheme="wxcde873f99466f74a" />
        <!-- scheme的值要换成游戏的微信appid -->
    </intent-filter>
</activity>


<!-- TODO SDK接入 微信接入配置 END -->

<!-- TODO SDK接入 微信扫码登录配置 START -->
<activity
    android:name="com.tencent.msdk.weixin.qrcode.WXQrCodeActivity"
    android:excludeFromRecents="true"
    android:exported="true"
    android:label="WXQrCodeActivity"
    android:launchMode="singleTask"
    <!-- 【必须】此处应改为 游戏包名.diff -->
    android:taskAffinity="com.example.wegame.diff" 
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:theme="@android:style/Theme.Light.NoTitleBar"
    android:screenOrientation="portrait">
    <!-- taskAffinity的值要换成:游戏的包名.diff -->
</activity>
<!-- TODO SDK接入  微信扫码登录配置 END -->

<!-- TODO Notice 公告 配置 START -->
<activity
    android:name="com.tencent.msdk.notice.AlertMsgActivity"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:screenOrientation="sensor"
    android:theme="@style/NoticeAlertTheme" >
</activity>

<service android:name="com.tencent.msdk.notice.RollFloatService" >
</service>
<!-- TODO Notice 公告 配置  END -->

<!-- TODO 浏览器相关 START -->
<activity
    android:name="com.tencent.msdk.webview.JumpShareActivity"
    android:theme="@android:style/Theme.Translucent.NoTitleBar">
</activity>

<activity
    android:name="com.tencent.msdk.webview.WebViewActivity"
    android:process=":msdk_inner_webview" 
    android:hardwareAccelerated="true"
    android:configChanges="orientation|screenSize|keyboardHidden|navigation|fontScale|locale"
    android:screenOrientation="unspecified"
    android:theme="@android:style/Theme.NoTitleBar"
    android:windowSoftInputMode="stateHidden|adjustResize" >

    <meta-data android:name="titlebar_hideable" android:value="false"/>
    <meta-data android:name="toolbar_portrait_hideable" android:value="false"/>
    <meta-data android:name="toolbar_landscape_hideable" android:value="false"/>

</activity>
<!-- TODO 浏览器相关 END -->

<!-- TODO 应用宝省流量更新相关 START -->
<service
    android:name="com.tencent.tmdownloader.TMAssistantDownloadService"
    android:exported="false"
    android:process=":TMAssistantDownloadSDKService" >
</service>
<!-- TODO 应用宝省流量更新 END -->

<!-- msdk httpdns网络监听,该配置已废弃 -->
<receiver
    android:name="com.tencent.special.httpdns.Cache$ConnectReceiver"
    android:label="NetworkConnection" >
	<intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>


<!-- 腾讯移动推送组件配置START -->

<!-- 如果接入点为上海集群,需要额外新增配置 START -->
<!-- TPNS 1.1.6.3 及之前的版本 XG_GUID_SERVER、XG_STAT_SERVER 和 XG_LOG_SERVER 配置生效, 1.1.6.3 以后的版本 XG_SERVER_SUFFIX 生效,建议都加上防止后面升级出现不兼容的问题。XG_GUID_SERVER、XG_STAT_SERVER 和 XG_LOG_SERVER  需要带 https 头,XG_SERVER_SUFFIX 不带 https 头-->

    <meta-data
        android:name="XG_GUID_SERVER"
        android:value="https://guid.tpns.sh.tencent.com/guid/api/GetGuidAndMqttServer" />
    <meta-data
        android:name="XG_STAT_SERVER"
        android:value="https://stat.tpns.sh.tencent.com/log/statistics/push" />
    <meta-data
        android:name="XG_LOG_SERVER"
        android:value="https://log.tpns.sh.tencent.com/v3/mobile/log/upload" />
    <meta-data
        android:name="XG_SERVER_SUFFIX"
        android:value="tpns.sh.tencent.com" />
<!-- 如果接入点为上海集群,需要额外新增配置 END -->

<!-- 用于延迟 TPNS service 启动,直到注册时才启动service,以满足内部策略-->
        <meta-data
            android:name="XG_SERVICE_PULL_UP_OFF"
            android:value="true" />

<!-- 【必须】 old_access_id填写升级前免费版本XG的AccessId,如果不知道,可以在飞鹰系统SDK参数处查询 -->
        <meta-data
            android:name="XG_OLD_ACCESS_ID"
            android:value="xxx" />
        <!-- 【必须】 请修改为APP的AccessId,不同集群AccessId前缀不一样,上海集群158开头,广州集群150开头,以注册后生成的为准,中间没空格 -->
        <meta-data
            android:name="XG_V2_ACCESS_ID"
            android:value="xxx" />
        <!-- 【必须】 请修改为APP的AccessKey,“A”开头的12位字符串,中间没空格 -->
        <meta-data
            android:name="XG_V2_ACCESS_KEY"
            android:value="xxx" />
        <!-- 【必须】 腾讯移动推送 TPNS 默认通知 -->
        <!-- 【注意】 XGPushActivity 配置于 3.3.16 版本开始废弃 -->
        <activity
            android:name="com.tencent.android.tpush.XGPushActivity">
            <intent-filter>
                <action android:name="android.intent.action" />
            </intent-filter>
        </activity>
        <!-- 【注意】 XGPushActivity 配置于 3.3.16 版本开始废弃 -->

        <!-- 【注意】 TpnsActivity 配置于 3.3.16 版本开始新增,其中 host 替换成 “应用包名” -->
        <activity android:name="com.tencent.android.tpush.TpnsActivity"
             android:exported="true"
             android:theme="@android:style/Theme.Translucent.NoTitleBar">
             <intent-filter>
                 <data
                     android:scheme="tpns"
                     android:host="com.example.wegame"/>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
        <!-- 【注意】 TpnsActivity 配置于 3.3.16 版本开始新增,其中 host 替换成 “应用包名” -->

        <!-- 【注意】 InnerTpnsActivity 配置于 3.3.23 版本开始新增,其中 ${applicationId} 替换成 “应用包名” -->
        <activity
            android:name="com.tencent.android.tpush.InnerTpnsActivity"
            android:exported="false"
            android:launchMode="singleInstance"
            android:theme="@android:style/Theme.Translucent.NoTitleBar">
            <intent-filter>
                <!-- 【必须】 其中${applicationId}修改为应用的包名-->
                <action android:name="${applicationId}.OPEN_TPNS_ACTIVITY_V2" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <data
                    <!-- 【必须】 其中${applicationId}修改为应用的包名-->
                    android:host="${applicationId}"
                    android:scheme="stpns" />

                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.BROWSABLE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action" />
            </intent-filter>
        </activity>
        <!-- 【注意】 InnerTpnsActivity 配置于 3.3.23 版本开始新增,其中 ${applicationId} 替换成 “应用包名” -->

        <!-- 【必须】 腾讯移动推送 TPNS receiver广播接收 -->
        <receiver
            android:name="com.tencent.android.tpush.XGPushReceiver"
            <!-- 【注意】 android:exported="false" 于 3.3.23 版本开始新增 -->
            android:exported="false"
            <!-- 【注意】 android:exported="false" 于 3.3.23 版本开始新增 -->
            android:process=":xg_vip_service">
            <intent-filter android:priority="0x7fffffff">
                <!-- 【必须】 腾讯移动推送 TPNS SDK的内部广播 -->
                <action android:name="com.tencent.android.xg.vip.action.SDK" />
                <action android:name="com.tencent.android.xg.vip.action.INTERNAL_PUSH_MESSAGE" />
                <action android:name="com.tencent.android.xg.vip.action.ACTION_SDK_KEEPALIVE" />

                <!-- 【注意】 以下可选配置于 3.3.22 版本开始去除 -->

                <!-- 【可选】 系统广播:网络切换 -->
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
                <!-- 【可选】 系统广播:开屏 -->
                <action android:name="android.intent.action.USER_PRESENT" />
                <!-- 【可选】 一些常用的系统广播,增强腾讯移动推送 TPNS service的复活机会,请根据需要选择。当然,你也可以添加APP自定义的一些广播让启动service -->
                <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />

                <!-- 【注意】 以上可选配置于 3.3.22 版本开始去除 -->
            </intent-filter>
        </receiver>

        <!-- 【必须】 腾讯移动推送 TPNS service -->
        <!-- XGVipPushService 节点的 persistent 属性于 3.3.281 版本开始去除 -->
        <service
            android:name="com.tencent.android.tpush.service.XGVipPushService"
            android:process=":xg_vip_service"></service>

        <service android:name="com.tencent.android.tpush.rpc.XGRemoteService">
            <intent-filter>
                <!-- 【必须】 请修改为当前APP名包.XGVIP_PUSH_ACTION -->
                <action android:name="com.example.wegame.XGVIP_PUSH_ACTION" />
            </intent-filter>
        </service>

        <!-- 【必须】 【注意】authorities修改为 包名.XGVIP_PUSH_AUTH -->
        <provider
            android:name="com.tencent.android.tpush.XGPushProvider"
            android:authorities="com.example.wegame.XGVIP_PUSH_AUTH" />

        <!-- 如需关闭与 TPNS 应用的联合保活功能,可配置自身应用不被其他应用拉起,可添加以下配置,如不需要可忽略以下配置。其中authorities修改为 包名.xxx.XGVIP_PUSH_AUTH   xxx 为任意自定义名称: --> 
        <!-- 【注意】该配置与上一个配置节点一致,二选一即可,这里为了便于表述,分别来写 -->
        <provider
            android:name="com.tencent.android.tpush.XGPushProvider"
            tools:replace="android:authorities"
            android:authorities="com.example.wegame.xxx.XGVIP_PUSH_AUTH"
            android:exported="false"
            android:enabled="false" />


        <!-- 【必须】 【注意】authorities修改为 包名.TPUSH_PROVIDER -->
        <provider
            android:name="com.tencent.android.tpush.SettingsContentProvider"
            android:authorities="com.example.wegame.TPUSH_PROVIDER" />

        <!-- 【可选】用于增强保活能力 -->
        <!-- 【注意】authorities修改为 包名.AUTH_XGPUSH_KEEPALIVE -->
        <provider
            android:name="com.tencent.android.tpush.XGVipPushKAProvider"
            android:authorities="com.example.wegame.AUTH_XGPUSH_KEEPALIVE"
            android:exported="true" />

        <!-- MQTT START-->

        <service android:exported="false"
            android:process=":xg_vip_service"
            <!-- 【注意】 name 于3.3.16 版本开始变更为 com.tencent.tpns.mqttchannel.services.MqttService-->
            android:name="com.tencent.bigdata.mqttchannel.services.MqttService" />
            <!-- 【注意】 name 于3.3.16 版本开始变更为 com.tencent.tpns.mqttchannel.services.MqttService-->

        <!--【注意】authorities修改为 包名.XG_SETTINGS_PROVIDER -->
        <provider
            android:exported="false"
            <!-- 【注意】 name 于3.3.16 版本开始变更为 com.tencent.tpns.baseapi.base.SettingsContentProvider-->
            android:name="com.tencent.bigdata.baseapi.base.SettingsContentProvider"
            <!-- 【注意】 name 于3.3.16 版本开始变更为 com.tencent.tpns.baseapi.base.SettingsContentProvider-->
            android:authorities="com.example.wegame.XG_SETTINGS_PROVIDER" />
        <!-- MQTT END-->


<!-- 腾讯移动推送组件配置END -->

以下为腾讯移动推送厂商通道组件的相关配置,其中,${HW_APPID}替换为自己申请的 HW_APPID,${applicationId}替换为自己的包名

<!-- 腾讯移动推送厂商通道组件配置 Start -->
        <meta-data
            android:name="com.huawei.hms.client.appid"
            android:value="${HW_APPID}" >
        </meta-data>

        <!-- 3.3.18 版本开始这里不需要声明 BridgeActivity、UpdateProvider,已在 HWPushSDK 中声明了 -->
        <activity
            android:name="com.huawei.hms.activity.BridgeActivity"
            android:configChanges="orientation|locale|screenSize|layoutDirection|fontScale"
            android:excludeFromRecents="true"
            android:exported="false"
            android:hardwareAccelerated="true"
            android:theme="@android:style/Theme.Translucent" >
            <meta-data
                android:name="hwc-theme"
                android:value="androidhwext:style/Theme.Emui.Translucent" />
        </activity>
        <provider
            android:name="com.huawei.hms.update.provider.UpdateProvider"
            android:authorities="${applicationId}.hms.update.provider"
            android:exported="false"
            android:grantUriPermissions="true" >
        </provider>
        <!-- 3.3.18 版本开始这里不需要声明 BridgeActivity、UpdateProvider,已在 HWPushSDK 中声明了 -->

        <!-- PushEventReceiver、HWPushMessageReceiver 已于 3.3.18 版本开始去除 -->
        <receiver android:name="com.huawei.hms.support.api.push.PushEventReceiver" >
            <intent-filter>
                <!-- 接收通道发来的通知栏消息,兼容老版本PUSH -->
                <action android:name="com.huawei.intent.action.PUSH" />
            </intent-filter>
        </receiver>

        <receiver android:name="com.tencent.android.hwpush.HWPushMessageReceiver" >
            <intent-filter>
                <!-- 必须,用于接收TOKEN -->
                <action android:name="com.huawei.android.push.intent.REGISTRATION" />
                <!-- 必须,用于接收消息 -->
                <action android:name="com.huawei.android.push.intent.RECEIVE" />
                <!-- 可选,用于点击通知栏或通知栏上的按钮后触发onEvent回调 -->
                <action android:name="com.huawei.android.push.intent.CLICK" />
                <!-- 可选,查看PUSH通道是否连接,不查看则不需要 -->
                <action android:name="com.huawei.intent.action.PUSH_STATE" />
            </intent-filter>
        </receiver>
        <!-- PushEventReceiver、HWPushMessageReceiver 已于 3.3.18 版本开始去除 -->

         <!--小米-->
        <service
            android:name="com.xiaomi.push.service.XMPushService"
            android:enabled="true"
            android:process=":pushservice" />
        <service
            android:name="com.xiaomi.push.service.XMJobService"
            android:enabled="true"
            android:exported="false"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:process=":pushservice" />
        <!-- 注:此service必须在3.0.1版本以后(包括3.0.1版本)加入 -->
        <service
            android:name="com.xiaomi.mipush.sdk.PushMessageHandler"
            android:enabled="true"
            android:exported="true" />
        <service
            android:name="com.xiaomi.mipush.sdk.MessageHandleService"
            android:enabled="true" />
        <!-- 注:此service必须在2.2.5版本以后(包括2.2.5版本)加入 -->
        <!-- NetworkStatusReceiver对应的intent-filter需要删除 -->
        <receiver
            android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="com.xiaomi.push.service.receivers.PingReceiver"
            android:exported="false"
            android:process=":pushservice" >
            <intent-filter>
                <action android:name="com.xiaomi.push.PING_TIMER" />
            </intent-filter>
        </receiver>
        <receiver
            android:exported="true"
            android:name="com.tencent.android.mipush.XMPushMessageReceiver">
            <intent-filter>
                <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.xiaomi.mipush.ERROR" />
            </intent-filter>
        </receiver>

        <!--魅族-->
        <service
            android:name="com.meizu.cloud.pushsdk.NotificationService"
            android:exported="true"/>
	<!--魅族渠道于 3.3.256a 版本开始新增 MzPushSystemReceiver 节点,并移除 com.meizu.cloud.pushsdk.SystemReceiver 节点-->	
	<receiver 
	    android:name="com.meizu.cloud.pushsdk.MzPushSystemReceiver"
            android:exported="false"
            android:permission="com.meizu.flyme.permission.PUSH">
            <intent-filter>
                <action android:name="com.meizu.flyme.push.intent.PUSH_SYSTEM" />
            </intent-filter>
        </receiver>
		
        <receiver 
	    android:name="com.tencent.android.mzpush.MZPushMessageReceiver"
	    android:exported="true">
            <intent-filter>
                <!-- 接收push消息 -->
                <action android:name="com.meizu.flyme.push.intent.MESSAGE" />
                <!-- 接收register消息-->
                <action android:name="com.meizu.flyme.push.intent.REGISTER.FEEDBACK"/>
                <!-- 接收unregister消息-->
                <action android:name="com.meizu.flyme.push.intent.UNREGISTER.FEEDBACK"/>

                <action android:name="com.meizu.c2dm.intent.REGISTRATION" />
                <action android:name="com.meizu.c2dm.intent.RECEIVE" />

                <category android:name="${applicationId}"></category>
            </intent-filter>
        </receiver>

        <!--OPPO-->
	<!--OPPO 渠道 PushService、AppPushService 已于 3.3.281 版本开始去除-->
        <service
            android:name="com.heytap.mcssdk.PushService"
            android:permission="com.coloros.mcs.permission.SEND_MCS_MESSAGE">
            <intent-filter>
                <action android:name="com.coloros.mcs.action.RECEIVE_MCS_MESSAGE"/>
            </intent-filter>
        </service>

        <service
            android:name="com.heytap.mcssdk.AppPushService"
            android:permission="com.heytap.mcs.permission.SEND_MCS_MESSAGE">
            <intent-filter>
                <action android:name="com.heytap.mcs.action.RECEIVE_MCS_MESSAGE"/>
            </intent-filter>
        </service>
	<!--OPPO 渠道 PushService、AppPushService 已于 3.3.281 版本开始去除-->
		
	<!--OPPO 渠道于 3.3.281 版本开始新增 CompatibleDataMessageCallbackService、DataMessageCallbackService 配置-->
	<service
            android:name="com.heytap.msp.push.service.CompatibleDataMessageCallbackService"
            android:permission="com.coloros.mcs.permission.SEND_MCS_MESSAGE"
            android:exported="true">
            <intent-filter>
                <action android:name="com.coloros.mcs.action.RECEIVE_MCS_MESSAGE"/>
            </intent-filter>
        </service>
        <service
            android:name="com.heytap.msp.push.service.DataMessageCallbackService"
            android:permission="com.heytap.mcs.permission.SEND_PUSH_MESSAGE"
            android:exported="true">
            <intent-filter>
                <action android:name="com.heytap.mcs.action.RECEIVE_MCS_MESSAGE"/>
                <action android:name="com.heytap.msp.push.RECEIVE_MCS_MESSAGE"/>
            </intent-filter>
        </service>
	<!--OPPO 渠道于 3.3.281 版本开始新增 CompatibleDataMessageCallbackService、DataMessageCallbackService 配置-->
	
	<!--荣耀渠道配置,于 3.3.32 版本开始支持-->
	<service
            android:name="com.tencent.android.tpush.honor.HonorMessageService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.hihonor.push.action.MESSAGING_EVENT"/>
            </intent-filter>
        </service>
		
	<meta-data
            android:name="com.hihonor.push.sdk_version"
            android:value="7.0.41.301" >
        </meta-data>
			
	<!--荣耀推送 appId-->
	<meta-data
            android:name="com.hihonor.push.app_id"
            android:value="your_honor_appid" >
        </meta-data>
	<!--荣耀渠道配置,于 3.3.32 版本开始支持-->

        <!-- 腾讯移动推送厂商通道组件配置 End -->

3)修改游戏主Activity的启动方式launchmode为singleTask 示例配置如下:

<activity 
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="sensor"
            android:launchMode="singleTask"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
...
</activity>

5 编辑游戏配置信息

打开游戏项目assets目录下msdkconfig.ini文件即可编辑MSDK的配置信息

MSDK配置项说明

字段 平台 描述
MSDK_ENV Android MSDK环境选择,联调时可选择测试环境test,正式发布时务必选择正式环境release
WXTOKEN_REFRESH Android MSDK微信自动刷新票据开关,详细了解参考登录模块
PUSH Android 腾讯移动推送功能开关
needNotice Android MSDK公告功能开关
noticeTime Android MSDK公告系统公告数据更新时间(连网请求公告数据), 单位为分钟(最小值为5分钟)
MSDK_REAL_NAME_AUTH_SWITCH Android 实名认证配置(3.2.14及其以上版本必须配置为1);
0:使用MSDK实名认证UI,认证后回到登录页;
1:使用MSDK实名认证UI,认证后登录进入游戏;
2:使用游戏自定义认证UI,认证后回调到游戏;
BETA Android 应用宝抢号功能开关
STAT_LOG Android 灯塔、Bugly、手Q OpenSDK日志开关,设为true时会打印详细日志,正式上线时需设为false;3.3.271版本开始支持手Q OpenSDK
CLOSE_BUGLY_REPORT Android 关闭bugly上报开关,设为true即关闭了crash上报功能
SAVE_UPDATE Android 应用宝省流量下载更新功能开关
GRAY_TEST_SWITCH Android 白名单限号开关,预约抢号需向协同规划组申请
localLog Android 3.x版本该配置已移除
SDKLog开关 0:不打印;1:logcat打印;2:打印到本地文件;3:logcat和本地文件同时打印;本地打印的保存在:
2.15.1以前的版本:SDCard/MSDK/msdk.log
2.15.1以上的版本:/sdcard/Android/data/游戏包名/files/MSDKLog.log.0
SCOPE Android 拓展微信scope字段,多个scope用“,”隔开;2.18.0版本开始支持
V2SIGNING_ENABLED Android V2签名开关,启用V2签名的包一定要打开这个开关(不开会导致渠道号读取错误),true为开启,不填默认关闭;适配Target 30必须开启,如果没有支持到Target 30或者不需要V2签名,则可以保持为false
V3SIGNING_ENABLED Android V3签名开关,启用V3签名的包一定要打开这个开关(不开会导致渠道号读取错误),true为开启,不填默认关闭;3.3.15版本开始支持
IPV6_SUPPORT Android 是否支持IPV6,设为true即支持,false即不支持;3.3.0版本开始支持
BEACON_SENSOR_ENABLE Android 是否开启BeaconID获取;设为true即开启,false即不开启;3.3.9版本开始支持,3.3.18版本开始默认开启,3.3.21版本开始该配置已移除
BEACON_CC_ENABLE Android 是否开启反作弊,默认不开启;设为true即开启,false即不开启;3.3.9版本开始支持
BEACON_PAGE_PATH_ENABLE Android 是否开启页面追踪,默认不开启;设为true即开启,false即不开启;3.3.9版本开始支持
XG_OTHER_PUSH_ENABLE Android 是否开启腾讯移动推送厂商通道,默认不开启;设为true即开启,false即不开启;3.3.11版本开始支持
XG_OTHER_PUSH_XIAOMI_APPID Android 填写小米通道的appId;3.3.11版本开始支持
XG_OTHER_PUSH_XIAOMI_APPKEY Android 填写小米通道的appKey;3.3.11版本开始支持
XG_OTHER_PUSH_MEIZU_APPID Android 填写魅族通道的appId;3.3.11版本开始支持
XG_OTHER_PUSH_MEIZU_APPKEY Android 填写魅族通道的appKey;3.3.11版本开始支持
XG_OTHER_PUSH_OPPO_APPID Android 填写OPPO通道的appKey;3.3.11版本开始支持
XG_OTHER_PUSH_OPPO_APPSECRET Android 填写OPPO通道的appSecret;3.3.11版本开始支持
MSDK_WEBVIEW_LOADING_BACKGROUND_COLOR Android 打开webview时加载loading页面颜色的开关,不配置或者配置为""时不开启加载的loading页面;如配置,会开启携带该背景颜色的loading页面且必须填写格式为不包含透明度的16进制颜色值,如:#000000;3.3.12版本开始支持
XG_PULL_UP_OTHER_APP_ENABLE Android 腾讯移动推送联合保活能力开关,设为true即开启,false即关闭,默认关闭,可防止自身应用拉起其他应用;3.3.13版本开始支持
MSDK_CENTER_CONTROL_WEBVIEW_LOADING_BACKGROUND_COLOR Android 打开中控webview时加载loading页背景色开关,不配置默认为:#010C0F;3.3.16版本开始支持
CLOSE_BEACON_REPORT Android 灯塔上报开关,默认应将其设为false,设为true即关闭灯塔上报功能,不配置则默认开启;3.3.17版本开始支持
CLOSE_HTTPDNS Android HttpDns开关,默认应将其设为false,设为true即关闭HttpDns功能,不配置则默认开启;3.3.17版本开始支持
BUGLY_REPORT_URL_ANDROID Android Bugly自定义Server Url配置,用于Bugly到CrashSight的迁移,迁移前,请确保appId已经在CrashSight后台管理端配置(添加对应项目)。触发崩溃或者Bugly日志后,验证请到CrashSight管理端进行验证。示例:BUGLY_REPORT_URL_ANDROID = https://test-astat.bugly.qcloud.com/rqd/async;3.3.19版本开始支持
MSDK_DENIED_COLLECT_LIST Android Apn&AndroidID&QImei&QImei36采集配置开关,可将不允许采集的字段配置在这个配置项中(多个字段使用英文逗号连接),不配置默认允许采集;3.3.25版本开始支持
CLOSE_BUGLY_CALLBACK Android Crash上报回调及error上报回调开关,设为false即为开启回调,true即为关闭回调,不配置则默认开启;3.3.26版本开始支持
DELETE_ACCOUNT_URL_TEST Android 注销账号页面测试环境url,当前配置为:https://gacc-account-web-test.odp.qq.com/writeoff.html;3.3.271版本开始支持
DELETE_ACCOUNT_URL_RELEASE Android 注销账号页面正式环境url,当前配置为:https://gacc-account-web.odp.qq.com/writeoff.html;3.3.271版本开始支持
IGNORE_ILINK_AUTH_BUFFER Android 如需要微信视频号自动授权能力,需配置为 true;3.3.32版本开始支持
CLOSE_WEBVIEW_X5 Android TBS X5 内核开关,默认关闭 true,false 打开;3.3.32版本开始支持

2.16.0及其以上版本注意事项:

从2.16.0之前的版本升级到2.16.0配置文件msdkconfig.ini需要进行如下修改

1.增加了MSDK域名配置(游戏不需要修改直接copy到配置文件里面)

;以下配置为msdk内部使用,游戏无需关注
MSDK_ENV_TEST_URL=http://msdktest.qq.com
MSDK_ENV_RELEASE_URL=http://msdk.qq.com

2.增加域名选择配置(游戏根据情况选择正确的环境)

;正式环境与测试环境选择,test为测试环境, release为正式环境
MSDK_ENV=test

3.删除原来的域名配置方案(删除MSDK_URL=xxxxx)

;正式环境与测试环境选择,msdktest.qq.com为测试环境域名,msdk.qq.com为正式环境域名
MSDK_URL=http://msdktest.qq.com (删除)

可以参考MSDKDemo的配置文件

2.18.0及其以上版本注意事项:

从2.16.0之前的版本升级到2.18.0配置文件msdkconfig.ini需要进行如下修改

1.增加域名选择配置(游戏根据情况选择正确的环境)

;正式环境与测试环境选择,test为测试环境, release为正式环境
MSDK_ENV=test

2.删除原来的域名配置方案(删除MSDK_URL=xxxxx)

;正式环境与测试环境选择,msdktest.qq.com为测试环境域名,msdk.qq.com为正式环境域名
MSDK_URL=http://msdktest.qq.com (删除)

3.MSDKLibrary工程res/values/msdk_properties.xml增加了文件注意更新合入

从2.16.0升级到2.18.0只执行2、3两步就行

可以参考MSDKDemo的配置文件

6 初始化代码

1) 引入MSDK包

在需要使用MSDK接口的java代码中引入MSDK包

import com.tencent.msdk.api.WGPlatform;

2) 注册登录回调、平台拉起回调

因登录回调、平台拉起回调可在游戏未调用相关接口时触发, 所以需要在游戏初始化MSDK前注册。 - 登录回调:登录事件结果的通知,游戏在手Q/微信的登录态以登录回调为准,详细设置参考登录模块回调设置。 - 平台拉起回调:手Q/微信中拉起游戏后会调用此回调,包含拉起账号,平台透传数据等信息,详细设置参考登录模块异账号处理

class GlobalCallback : public WGPlatformObserver
{
   public:
    virtual void OnLoginNotify(LoginRet &loginRet)
    {
        LOGD("OnLoginNotify: flag:%d platform:%d OpenId:%s, Token Size: %d", loginRet.flag, loginRet.platform, loginRet.open_id.c_str(), loginRet.token.size());
        // 下面是MSDKSample使用的逻辑, 游戏忽略此部分内容
        if(gActivity == NULL){
            LOGD("OnLoginNotify:Activity is null %d", loginRet.flag);
            return;
        }
        jclass activitycls = mEnv->GetObjectClass(gActivity);
        jmethodID method = mEnv->GetMethodID(activitycls, "stopWaiting", "()V");
        mEnv->CallVoidMethod(gActivity, method);
        if (loginRet.flag == eFlag_NeedRealNameAuth)
        {  // 需要实名认证
            LOGD("OnLoginNotify:Need_Realname_Auth %d", loginRet.flag);

            //     TODO 游戏如果配置MSDK_REAL_NAME_AUTH_SWITCH=0或者1(3.2.14及其以上版本必须配置为1)在这里取消对msdk的超时处理

            //     TODO 游戏如果配置MSDK_REAL_NAME_AUTH_SWITCH=2(3.2.14及其以上版本必须配置为1)在这里调用自定义的实名认证界面

            // 这里模拟用户在游戏自定义界面填好信息准备提交注册

            jmethodID method = mEnv->GetMethodID(activitycls, "isCustomUI", "()Z");
            bool isCustom = mEnv->CallBooleanMethod(gActivity, method);
            if (isCustom)
            {
                RealNameAuthInfo authinfo;
                authinfo.name = "zhangsan";
                authinfo.identityType = eIDType_IDCards;
                authinfo.identityNum = "430455198411262142";
                authinfo.province = 11;
                WGPlatform::GetInstance()->WGRealNameAuth(authinfo);
            }

            return;
        }
        if (loginRet.platform == ePlatform_QQ)
        {
            // 读取QQ的登陆票据
            switch (loginRet.flag)
            {
                case eFlag_Succ:
                {
                    std::string accessToken = "";
                    std::string payToken = "";
                    for (int i = 0; i < loginRet.token.size(); i++)
                    {
                        if (loginRet.token.at(i).type == eToken_QQ_Access)
                        {
                            accessToken.assign(loginRet.token.at(i).value);
                        }
                        else if (loginRet.token.at(i).type == eToken_QQ_Pay)
                        {
                            payToken.assign(loginRet.token.at(i).value);
                        }
                    }
                    LOGD("accessToken : %s", accessToken.c_str());
                    LOGD("payToken : %s", payToken.c_str());

                    // 下面是MSDKSample使用的逻辑, 游戏忽略此部分内容
                    jmethodID method = mEnv->GetMethodID(activitycls, "letUserLogin", "()V");
                    mEnv->CallVoidMethod(gActivity, method);
                    LOGD("OnLoginNotify call letUserLogin end%s", "");
                    break;
                }
                case eFlag_NotInWhiteList:
                case eFlag_NeedRealNameAuth:
                case eFlag_QQ_NotInstall:
                case eFlag_QQ_NotSupportApi:
                case eFlag_QQ_UserCancel:
                case eFlag_QQ_NoAcessToken:
                case eFlag_QQ_LoginFail:
                case eFlag_QQ_NetworkErr:
                default:
                {
                    std::stringstream ss;
                    ss << "OnLoginNotify";
                    ss << "flag:" << loginRet.flag << " platform:" << loginRet.platform << " desc:" << loginRet.desc;
                    jstring j_result = mEnv->NewStringUTF(ss.str().c_str());
                    // 显示登陆界面
                    jmethodID method2 = mEnv->GetMethodID(activitycls, "letUserLogout", "(Ljava/lang/String;)V");
                    mEnv->CallVoidMethod(gActivity, method2,j_result);

                }
            }
        }
        else if (loginRet.platform == ePlatform_Weixin)
        {
            switch (loginRet.flag)
            {
                case eFlag_Succ:
                {
                    std::string accessToken = "";
                    std::string refreshToken = "";
                    for (int i = 0; i < loginRet.token.size(); i++)
                    {
                        if (loginRet.token.at(i).type == eToken_WX_Access)
                        {
                            accessToken.assign(loginRet.token.at(i).value);
                        }
                        else if (loginRet.token.at(i).type == eToken_WX_Refresh)
                        {
                            refreshToken.assign(loginRet.token.at(i).value);
                        }
                    }
                    LOGD("accessToken : %s", accessToken.c_str());
                    LOGD("payToken : %s", refreshToken.c_str());

                    // 下面是MSDKSample使用的逻辑, 游戏忽略此部分内容
                    jmethodID method = mEnv->GetMethodID(activitycls, "letUserLogin", "()V");
                    mEnv->CallVoidMethod(gActivity, method);
                    LOGD("OnLoginNotify call letUserLogin end%s", "");

                    break;
                }
                case eFlag_WX_RefreshTokenSucc:
                {
                    // WGRefreshWXToken调用成功, 成功用当前的refreshToken换到新的accessToken
                    std::string accessToken = "";
                    std::string refreshToken = "";
                    for (int i = 0; i < loginRet.token.size(); i++)
                    {
                        if (loginRet.token.at(i).type == eToken_WX_Access)
                        {
                            accessToken.assign(loginRet.token.at(i).value);
                        }
                        else if (loginRet.token.at(i).type == eToken_WX_Refresh)
                        {
                            refreshToken.assign(loginRet.token.at(i).value);
                        }
                    }
                    LOGD("accessToken : %s", accessToken.c_str());
                    LOGD("payToken : %s", refreshToken.c_str());

                    // 下面是MSDKSample使用的逻辑, 游戏忽略此部分内容
                    jmethodID method = mEnv->GetMethodID(activitycls, "letUserLogin", "()V");
                    mEnv->CallVoidMethod(gActivity, method);
                    mEnv->DeleteLocalRef(activitycls);
                    LOGD("OnLoginNotify call letUserLogin end%s", "");
                    break;
                }
                case eFlag_NotInWhiteList:
                case eFlag_NeedRealNameAuth:
                case eFlag_WX_NotSupportApi:
                case eFlag_WX_UserCancel:
                case eFlag_WX_UserDeny:
                case eFlag_WX_AccessTokenExpired:
                case eFlag_WX_RefreshTokenExpired:
                case eFlag_WX_LoginFail:
                case eFlag_Error:
                case eFlag_WX_RefreshTokenFail:
                default:
                {
                    std::stringstream ss;
                    ss << "OnLoginNotify";
                    ss << "flag:" << loginRet.flag << " platform:" << loginRet.platform << " desc:" << loginRet.desc;
                    jstring j_result = mEnv->NewStringUTF(ss.str().c_str());
                    // 显示登陆界面
                    jmethodID method2 = mEnv->GetMethodID(activitycls, "letUserLogout", "(Ljava/lang/String;)V");
                    mEnv->CallVoidMethod(gActivity, method2,j_result);
                    /*std::stringstream ss;
                    ss << "OnShareNotify";
                    ss << "flag:" << loginRet.flag << " platform:" << loginRet.platform << " desc:" << loginRet.desc;
                    displayResult(ss.str());*/
                }
            }
        }
        else
        {
            // 显示登陆界面
            jmethodID method2 = mEnv->GetMethodID(activitycls, "letUserLogout", "()V");
            mEnv->CallVoidMethod(gActivity, method2);
        }
        // 下面是MSDKSample使用的逻辑, 游戏忽略此部分内容
        jmethodID methodResetLoginOpt = mEnv->GetMethodID(activitycls, "resetLoginOpt", "()V");
        mEnv->CallVoidMethod(gActivity, methodResetLoginOpt);
        mEnv->DeleteLocalRef(activitycls);
    }

    virtual void OnShareNotify(ShareRet &shareRet)
    {
        LOGD("OnShareNotify: platform:%d flag:%d", shareRet.platform, shareRet.flag);
        // 处理分享回调
        if (shareRet.platform == ePlatform_QQ)
        {
            switch (shareRet.flag)
            {
                case eFlag_Succ:
                    // 分享成功
                    break;
                case eFlag_Error:
                    // 分享失败
                    break;
            }
        }
        else if (shareRet.platform == ePlatform_Weixin)
        {
            switch (shareRet.flag)
            {
                case eFlag_Succ:
                    // 分享成功
                    break;
                case eFlag_Error:
                    // 分享失败
                    break;
            }
        }
        std::stringstream ss;
        ss << "OnShareNotify";
        ss << "flag:" << shareRet.flag << " platform:" << shareRet.platform << " desc:" << shareRet.desc;
        displayResult(ss.str());
    }

    virtual void OnWakeupNotify(WakeupRet &wakeupRet)
    {
        LOGD("OnWakeupNotify: platform:%d flag:%d openid:%s", wakeupRet.platform, wakeupRet.flag,
            wakeupRet.open_id.c_str());
        // TODO GAME 这里要处理异账号的逻辑
        switch (wakeupRet.flag)
        {
            case eFlag_Succ:
                if(wakeupRet.platform == eWakeupPlatform_TencentMsdk){
                    jstring j_msg_title = mEnv->NewStringUTF("由MSDK Scheme启动");
                    std::string c_extinfo;
                    for(int i =0;i<wakeupRet.extInfo.size();i++){
                        c_extinfo = c_extinfo + wakeupRet.extInfo[i].key + ":" + wakeupRet.extInfo[i].value + "\n";
                    }
                    jstring j_msg_msg = mEnv->NewStringUTF(c_extinfo.c_str());
                    jclass cls = mEnv->GetObjectClass(gActivity);
                    jmethodID methodShowTips = mEnv->GetMethodID(cls, "showTipsDialog", "(Ljava/lang/String;Ljava/lang/String;)V");
                    mEnv->CallVoidMethod(gActivity, methodShowTips, j_msg_title, j_msg_msg);
                    mEnv->DeleteLocalRef(j_msg_title);
                    mEnv->DeleteLocalRef(j_msg_msg);
                }
            case eFlag_UrlLogin:
            case eFlag_AccountRefresh:
            {
                // 下面是MSDKSample使用的逻辑, 游戏忽略此部分内容
                jclass cls = mEnv->GetObjectClass(gActivity);
                jmethodID methodletUserLogin = mEnv->GetMethodID(cls, "letUserLogin", "()V");
                mEnv->CallVoidMethod(gActivity, methodletUserLogin);
                LOGD("OnWakeupNotify call letUserLogin end%s", "");
                break;
            }
            case eFlag_NeedSelectAccount:
            {
                LOGD("diff account%s", "");
                jclass cls = mEnv->GetObjectClass(gActivity);
                jmethodID methodshowDiffLogin = mEnv->GetMethodID(cls, "showDiffLogin", "()V");
                mEnv->CallVoidMethod(gActivity, methodshowDiffLogin);
                LOGD("OnWakeupNotify call showDiffLogin end%s", "");
                break;
            }
            case eFlag_NeedLogin:
            {
                LOGD("login%s", "");
                jclass cls = mEnv->GetObjectClass(gActivity);
                jmethodID methodletUserLogout = mEnv->GetMethodID(cls, "letUserLogout", "()V");
                if(wakeupRet.platform == eWakeupPlatform_TencentMsdk){
                    jstring j_msg_title = mEnv->NewStringUTF("由MSDK Scheme启动");
                    std::string c_extinfo = "";
                    for(int i =0;i<wakeupRet.extInfo.size();i++){
                        c_extinfo = c_extinfo + wakeupRet.extInfo[i].key + ":" + wakeupRet.extInfo[i].value + "\n";
                    }
                    jstring j_msg_msg = mEnv->NewStringUTF(c_extinfo.c_str());
                    jclass cls = mEnv->GetObjectClass(gActivity);
                    jmethodID methodShowTips = mEnv->GetMethodID(cls, "showTipsDialog", "(Ljava/lang/String;Ljava/lang/String;)V");
                    mEnv->CallVoidMethod(gActivity, methodShowTips, j_msg_title, j_msg_msg);
                    mEnv->DeleteLocalRef(j_msg_title);
                    mEnv->DeleteLocalRef(j_msg_msg);
                }
                LOGD("OnWakeupNotify call letUserLogout end%s", "");
                break;
            }
            default:
            {
                // default 不处理
                LOGD("login%s", "");
                jclass cls = mEnv->GetObjectClass(gActivity);
                jmethodID methodletUserLogout = mEnv->GetMethodID(cls, "letUserLogout", "()V");
                mEnv->CallVoidMethod(gActivity, methodletUserLogout);
                LOGD("OnWakeupNotify call letUserLogout end%s", "");
                break;
            }
        }
    }

    virtual void OnRelationNotify(RelationRet &relationRet)
    {
        LOGD("OnRelationCallBack flag:%d ", relationRet.flag);
        LOGD("OnRelationCallBack desc:%s ", relationRet.desc.c_str());
        LOGD("OnRelationCallBack friends total:%d ", relationRet.persons.size());
        // TODO GAME 这里应该也要返回平台
        std::stringstream ss;
        ss << "OnRelationNotify";
        ss << "flag:" << relationRet.flag << " friends total:" << relationRet.persons.size();
        ss << " type:" << (int)relationRet.type;
        switch (relationRet.flag)
        {
            case eFlag_Succ:
            {
                // relationRet.persons.at(0) 中保存的即是个人信息
                for (int i = 0; i < relationRet.persons.size(); i++)
                {
                    std::string city = relationRet.persons.at(i).city;
                    std::string gender = relationRet.persons.at(i).gender;
                    std::string nickName = relationRet.persons.at(i).nickName;
                    std::string openId = relationRet.persons.at(i).openId;
                    std::string pictureLarge = relationRet.persons.at(i).pictureLarge;
                    std::string pictureMiddle = relationRet.persons.at(i).pictureMiddle;
                    std::string pictureSmall = relationRet.persons.at(i).pictureSmall;
                    std::string provice = relationRet.persons.at(i).provice;

                    ss << "\ncity=" << city << "&";
                    ss << "gender=" << gender << "&";
                    ss << "nickName=" << nickName << "&";
                    ss << "openId=" << openId << "&";
                    ss << "pictureLarge=" << pictureLarge << "&";
                    ss << "pictureMiddle=" << pictureMiddle << "&";
                    ss << "pictureSmall=" << pictureSmall << "&";
                    ss << "provice=" << provice << "&";
                }
                break;
            }
            default:
                break;
        }

        displayResult(ss.str());
    }

    virtual void OnLocationNotify(RelationRet &relationRet)
    {
        LOGD("OnLocationNotify flag %d", relationRet.flag);
        std::stringstream ss;
        ss << "OnLocationNotify";
        ss << " flag:" << relationRet.flag << " friends total:" << relationRet.persons.size();

        switch (relationRet.flag)
        {
            case eFlag_Succ:
            {
                // relationRet.persons.at(0) 中保存的即是个人信息
                for (int i = 0; i < relationRet.persons.size(); i++)
                {
                    std::string city = relationRet.persons.at(i).city;
                    std::string gender = relationRet.persons.at(i).gender;
                    std::string nickName = relationRet.persons.at(i).nickName;
                    std::string openId = relationRet.persons.at(i).openId;
                    std::string pictureLarge = relationRet.persons.at(i).pictureLarge;
                    std::string pictureMiddle = relationRet.persons.at(i).pictureMiddle;
                    std::string pictureSmall = relationRet.persons.at(i).pictureSmall;
                    std::string provice = relationRet.persons.at(i).provice;

                    ss << "\ncity=" << city << "&";
                    ss << "gender=" << gender << "&";
                    ss << "nickName=" << nickName << "&";
                    ss << "openId=" << openId << "&";
                    ss << "pictureLarge=" << pictureLarge << "&";
                    ss << "pictureMiddle=" << pictureMiddle << "&";
                    ss << "pictureSmall=" << pictureSmall << "&";
                    ss << "provice=" << provice << "&";
                }
                break;
            }
            default:
                break;
        }

        displayResult(ss.str());
    }

    virtual void OnLocationGotNotify(LocationRet &locationRet)
    {
        LOGD("OnLocationGotNotify longitude %f", locationRet.longitude);
        LOGD("OnLocationGotNotify latitude %f", locationRet.latitude);
        std::stringstream ss;
        ss << "OnLocationGotNotify";
        // 解决浮点数0.000在demo上显示不完善问题
        if (locationRet.flag == eFlag_Succ)
        {
            ss << " longitude:" << locationRet.longitude << " latitude:" << locationRet.latitude;
        }
        else
        {
            ss << " longitude:" << 0 << " latitude:" << 0;
        }
        displayResult(ss.str());
    }

    virtual void OnFeedbackNotify(int flag, std::string desc)
    {
        LOGD("OnFeedbackNotify %d; %s", flag, desc.c_str());
        std::stringstream ss;
        ss << "OnFeedbackNotify";
        ss << "flag:" << flag << " desc:" << desc;
        displayResult(ss.str());
    }

    virtual void OnAddWXCardNotify(CardRet &cardRet)
    {
        LOGD("OnAddWXCardNotify: platform:%d flag:%d openid:%s", cardRet.platform, cardRet.flag,
            cardRet.open_id.c_str());
        std::stringstream ss;
        ss << "OnAddWXCardNotify";
        ss << "flag:" << cardRet.flag << " desc:" << cardRet.desc;
        displayResult(ss.str());
    }

    virtual std::string OnCrashExtMessageNotify()
    {
        // 此处游戏补充crash时上报的额外信息
        std::string str = "new jni update extra jni crash log now!";
        LOGD("OnCrashExtMessageNotify %s", str.c_str());
        return str;
    }
    // TODO 异账号逻辑先注释
    /*virtual bool OnDiffAccountAlert()
    {
        return false;
    }*/
    virtual std::vector<char> OnCrashExtDataNotify()
    {
        // 此处游戏补充crash时上报的额外信息
        LOGD("OnCrashExtDataNotify %s", "extra binary data");
        //最大值为30k
        char *uploadData = new char[30 * 1024];
        //在此处添加要上报的二进制数据
        memset(uploadData, 6, sizeof(char) * 30 * 1024);
        std::vector<char> extData(uploadData, uploadData + (30 * 1024));

        delete[] uploadData;
        uploadData = NULL;

        return extData;
    }

    virtual ~GlobalCallback()
    {
    }
};

4) 初始化MSDK的JAVA层并设置手Q授权项权限

初始化MSDK的C#层并设置手Q授权项权限后即完成Unity版本的初始化操作,之后可根据各个模块说明调用MSDK的接口,并根据Step5打包游戏包验证真实环境结果。

/***********************************************************
 *  TODO GAME 接入必须要看, baseInfo值因游戏而异,填写请注意以下说明:      
 *      baseInfo值游戏填写错误将导致 QQ、微信的分享,登录失败 ,切记 !!!     
 *      只接单一平台的游戏请勿随意填写其余平台的信息,否则会导致公告获取失败  
 *      offerId 为必填,直接在米大师官网midas.qq.com注册应用生成offerid;如未注册米大师,可暂时填写手Q的appid,待注册米大师后使用米大师生成的offerid,如果offerid没填会导致登录失败
 ***********************************************************/
MsdkBaseInfo baseInfo = new MsdkBaseInfo();
baseInfo.qqAppId = "100703379";
baseInfo.qqAppKey = "4578e54f**********c734514e"; 
//2.18.0版本已经删除qAppKey字段
baseInfo.wxAppId = "wxcde873f99466f74a";
baseInfo.msdkKey = "5d1467a4d**********289965db335f4";
baseInfo.offerId = "100703379";
// TODO GAME 自2.7.1a开始游戏可在初始化msdk时动态设置版本号,灯塔和bugly的版本号由msdk统一设置
// 1、版本号组成 = versionName + versionCode
// 2、游戏如果不赋值给appVersionName(或者填为"")和appVersionCode(或者填为-1),
// msdk默认读取AndroidManifest.xml中android:versionCode="51"及android:versionName="2.7.1"
// 3、游戏如果在此传入了appVersionName(非空)和appVersionCode(正整数)如下,则灯塔和bugly上获取的版本号为2.7.1.271
baseInfo.appVersionName = "2.15.0";
baseInfo.appVersionCode = 68903;

// 注意:传入Initialized的activity即this,在游戏运行期间不能被销毁,否则会产生Crash
WGPlatform.Initialized(this, baseInfo);     
// 设置拉起QQ时候需要用户授权的项
WGPlatform.WGSetPermission(WGQZonePermissions.eOPEN_ALL);

注意:

  • 若未调用 WGSetPermission,可能会导致拉取手Q好友时出现 100030 错误,详细参考登录模块概述部分的授权页说明

  • 如果游戏使用C++接口,设置C++回调需要在调用java的初始化(WGPlatform.Initialized)之后。

5) 设置Android生命周期函数调用

public void onCreate(Bundle savedInstanceState) {
      ...

      WGPlatform.Initialized(this, baseInfo);
      // 设置拉起QQ时候需要用户授权的项
      WGPlatform.WGSetPermission(WGQZonePermissions.eOPEN_ALL);

      // 必须保证handleCallback在Initialized之后
      // launchActivity的onCreat()和onNewIntent()中必须调用
      // WGPlatform.handleCallback()。否则会造成微信登录无回调
      WGPlatform.handleCallback(getIntent());
      ...
  }


@Override
protected void onRestart() {
    super.onRestart();
    WGPlatform.onRestart();
}

@Override
protected void onResume() {
    super.onResume();
    WGPlatform.onResume();

    // TODO GAME 模拟游戏自动登录,这里需要游戏添加加载动画
    // WGLogin是一个异步接口, 传入ePlatform_None则调用本地票据验证票据是否有效
    // 如果从未登录过,则会立即在onLoginNotify中返回flag为eFlag_Local_Invalid,此时应该拉起授权界面
    // 建议在此时机调用WGLogin,它应该在handlecallback之后进行调用。
    if(isFirstLogin) {
        isFirstLogin = false;
        startWaiting();
        WGPlatform.WGLogin(EPlatform.ePlatform_None);
    }

}

// TODO GAME 游戏需要集成此方法并调用WGPlatform.onPause()
@Override
protected void onPause() {
    super.onPause();
    WGPlatform.onPause();

}

// TODO GAME 游戏需要集成此方法并调用WGPlatform.onStop()
@Override
protected void onStop() {
    super.onStop();
    WGPlatform.onStop();
}

// TODO GAME 游戏需要集成此方法并调用WGPlatform.onDestory()
@Override
protected void onDestroy() {
    super.onDestroy();
    WGPlatform.onDestory(this);
    Logger.d("onDestroy");
}

 // TODO GAME 在onActivityResult中需要调用WGPlatform.onActivityResult
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    WGPlatform.onActivityResult(requestCode, resultCode, data);
    Logger.d("onActivityResult");
}

// TODO GAME 在onNewIntent中需要调用handleCallback将平台带来的数据交给MSDK处理
@Override
protected void onNewIntent(Intent intent) {
    Logger.d("onNewIntent");
    super.onNewIntent(intent);

    // TODO GAME 处理游戏被拉起的情况
    // launchActivity的onCreat()和onNewIntent()中必须调用
    // WGPlatform.handleCallback()。否则会造成微信登录无回调
    if (WGPlatform.wakeUpFromHall(intent)) {
        Logger.d("LoginPlatform is Hall");
        Logger.d(intent);
    } else {
        Logger.d("LoginPlatform is not Hall");
        Logger.d(intent);
        WGPlatform.handleCallback(intent);
    }
}

注意:

  1. 新建的Activity不要忘记在AndroidManifest.xml中声明
  2. Override Activity生命周期时,请调用父类的生命周期,例如:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ……
}

MSDKPolicy

1 MSDKPolicy插件

3.3.15a 版本开始新增 MSDKPolicy 插件。MSDKPolicy 是一套通用的 Android 权限流程实现方案,整合了用户协议和权限授权流程,通过配置的形式供业务快速接入。业务可根据自身情况按需合入该插件。用户协议更新参考文档:https://docs.qq.com/doc/DSnlkYU5SZWhPYmNR

重要:如果项目中有接入 TDM 组件,则需要使用 TDM 符合检测标准的版本:具体版本可咨询 TDM 助手。

MSDKPolicy 的流程如下:

1.1 导入插件

添加MSDKPolicy插件,可直接将MSDKPolicy完整的copy到跟其他插件同级目录中,并引入MSDKPolicy目录及添加对MSDKPolicy的依赖

1.2 修改AndroidManifest.xml

将启动Activity调整为MSDKPolicyActivity,示例如下:

    <!-- MSDKPolicy 插件配置 -->
    <activity android:name="com.tencent.gcloud.msdk.core.policy.MSDKPolicyActivity"
        android:configChanges = "keyboard|keyboardHidden|screenLayout|screenSize"
        android:launchMode="singleTask"
        android:theme="@style/MSDKPolicyTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
            <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
        </intent-filter>
        <!--  MSDK_POLICY_TARGET_ACTIVITY 用于指定在流程结束时,需要跳转到的应用或者游戏的首个 Activity-->
        <meta-data
            android:name="MSDK_POLICY_TARGET_ACTIVITY"
            android:value="com.example.wegame.MainActivity" />
        <meta-data android:name="MSDK_POLICY_DEBUG" android:value="true"/>
        <!-- 为了符合最新的流程要求,MSDK_RESULT_FILE_NAME 配置从 3.3.19 版本开始默认关闭 -->
        <!-- 去掉此配置后,如果用户之前未同意过协议,覆盖安装的情况下也会弹协议,无论用户覆盖安装前是否登录过 -->
        <!-- 如果您是新接入MSDKPolicy的业务,也建议注释掉下面的 MSDK_RESULT_FILE_NAME 配置以满足最新要求 -->
        <!--<meta-data android:name="MSDK_RESULT_FILE_NAME" android:value="WEGAMEDB2"/> -->
        <meta-data android:name="IS_MSDK_V5" android:value="false"/>
        <!-- 如果业务不想升级到 TDM 的合规版本,需要将下面的 MSDK_POLICY_TDM_REPORT_DISABLE 开关设置为 true,注意,打开后,MSDKPolicy 流程不会做 TDM 上报,流程无法被追踪-->
        <meta-data android:name="MSDK_POLICY_TDM_REPORT_DISABLE" android:value="false" />
        <!-- 用于标记协议版本,如果协议有更新,则往上累加,请填写整数值 -->
        <meta-data android:name="MSDK_POLICY_VERSION" android:value="1" />
    </activity>

注意事项:

  • MSDK_POLICY_TARGET_ACTIVITY 用于指定在流程结束时,需要跳转到的应用或者游戏的首个 Activity,MSDK_POLICY_TARGET_ACTIVITY参数 value 值需调整为游戏的启动 Activity。
  • 其余参数请保持与示例一致。
  • 请通过 android:screenOrientationMSDKPolicyActivity 的屏幕方向配置为与您的游戏或者应用同向,以获得更好的体验。
  • 为了符合最新的流程要求,MSDK_RESULT_FILE_NAME 配置从 3.3.19 版本开始默认关闭;去掉此配置后,如果用户之前未同意过协议,覆盖安装的情况下也会弹协议,无论用户覆盖安装前是否登录过。如果您是新接入 MSDKPolicy 的业务,也建议注释掉 MSDK_RESULT_FILE_NAME 配置以满足最新要求。

1.3 涉及文件

  • msdk_policy_content.html
    用于配置协议内容,已提供一套经法务审阅的文案

  • msdk_permission_content.html
    用于配置 App 所需的权限列表和用途的说明,当前内容为 Demo 展示所用,业务需要根据自己 App 的实际情况做修改,必要权限需要靠前,并给出(必要) 高亮提示

  • values.xml中 msdk_permission_list
    用于配置必要权限,以供权限申请流程轮询,在轮询过程中会向用户申请必要权限

1.4 MSDKPolicy分为两部分

用户协议:

在用户点击 App 图标启动游戏的时候,会拉起用户协议(如上图)。图中的协议内容部分可以配置,当前为业务提供了一套默认协议,如有修改麻烦联系法务确认风险评估。协议内容可以在资源文件 MSDKPolicy/assets/msdk_policy_content.html 中配置,仅支持基础的 html 标签如果是棋牌类游戏接入,请自行联系法务确认棋牌类游戏的协议和链接地址,并做相应调整。

<!doctype html>
<head>
    <meta charset='UTF-8'>
    <meta name='viewport' content='width=device-width initial-scale=1'>
    <title></title>
</head>

<body>
<p>在您使用我们(腾讯)服务前,请您务必审慎阅读、充分理解 <a href='http://game.qq.com/contract.shtml'>腾讯游戏许可及服务协议</a>、<a
        href='http://game.qq.com/privacy_guide.shtml'>腾讯游戏隐私保护指引</a>、<a
        href='https://game.qq.com/privacy_guide_children.shtml'>腾讯游戏儿童隐私保护指引</a>和<a href="https://game.qq.com/zlkdatasys/privacy_SDK.html">第三方信息共享清单</a>的各条款。<strong>同时,您应特别注意前述协议中免除或者限制我们责任的条款、对您权利进行限制的条款、约定争议解决方式和司法管辖的条款。</strong>如您已详细阅读并同意 <a
        href='http://game.qq.com/contract.shtml'>腾讯游戏许可及服务协议</a>、<a
        href='http://game.qq.com/privacy_guide.shtml'>腾讯游戏隐私保护指引</a>、<a
        href='https://game.qq.com/privacy_guide_children.shtml'>腾讯游戏儿童隐私保护指引</a>和<a href="https://game.qq.com/zlkdatasys/privacy_SDK.html">第三方信息共享清单</a> ,请点击 “同意”
    开始使用我们的服务。</p>
</body>

</html>

必要权限申请流程:

当用户点击同意后,进入权限请求页面,开始必要权限申请流程,如下图:

权限说明页面中的内容可以配置,业务需要梳理好自己 App 的所有权限,将其罗列到配置中,其中必要权限需要如上图内容标注清楚。配置文件为 MSDKPolicy/assets/msdk_permission_content.html,仅支持基本的 html 标签。以下仅为示例,业务需要梳理好自己 App 的所有权限。

<p>为确保您的游戏体验,我们将在您使用我们的服务过程中申请以下权限,届时您可以选择同意或者拒绝开启相关权限,若是拒绝则会影响部分功能:</p>
<br/>
<p><strong>存储权限</strong></p>
<p>缓存图片/视频,降低流量消耗</p>
<br/>
<p><strong>⼿机/电话权限</strong></p>
<p>校验 IMSI 码,防止账号被盗</p>
<br/>
<p><strong>位置权限</strong></p>
<p>获取您的位置信息,可以和附近的玩家一起游戏</p>
<br/>
</ul>

当用户点击权限请求页面中的确定按钮,开启 必要权限 的申请流程。

此过程中,会轮询询问用户,以获取 必要权限。业务需要将自己 App 中所需要的必要权限配置到 MSDKPolicy/res/values/values.xml 的msdk_permission_list 中。以下仅为示例,业务需要梳理好自己 App 中所需要的必要权限,例如:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="msdk_permission_list">
        <item>android.permission.ACCESS_FINE_LOCATION</item>
    </array>
</resources>

此示例中配置了 地理位置 必要权限。则在上图中会弹框来申请这个权限(如未授权)。

V3.3.25 版本开始,msdk_permission_content.html 支持配置化。

msdk_permission_content.html “置空” 或者 “被删除”,则不弹 “权限说明” 页面。用户在协议页面点击同意后,会直接进入游戏。

1.5 测试验证

  • 用户在协议页面点击同意后,会开启 TDM 上报打点数据和设备信息。可以联系 MSDK助手 协助验证;
  • values.xml 中的必要权限需要和 msdk_permission_content.html 中带高亮(必要)的权限一致;
  • 关键路径测试:接入完成后,一定要做好关键路径测试,登录&关系链&分享&游戏中心启动&异账号,必须覆盖这些路径。

2 开关配置接口

3.3.21 版本 新增该接口,在用户同意用户协议条款之后,业务可调用该接口开启MSDK以及MSDK所包含的第三方组件(目前支持该能力的有灯塔和TBS组件)对相关信息的获取;在开关开启之前,MSDK不会获取相关信息。接口说明如下:

java接口:
MSDKSensitive.setCouldCollectSensitiveInfo(true);

c++接口:
/*
 * 设置是否允许获取相关信息
 */
void WGSetCouldCollectSensitiveInfo(bool couldCollect);

注意事项

  • 在MSDK初始化之前,可调用java接口提前打开开关,避免个别上报缺失相关信息字段
  • 若在MSDK初始化之前,未调用java接口时,则MSDK初始化之后,必须调用c++接口
  • 不支持多进程场景,建议对内部策略优化相关接口调用都放主进程,或者子进程自行调用组件的相关接口
  • 【重点】手Q OpenSDK 3.5.7版本开始更新了权限相关功能,接入方未调用SetCouldCollectSensitiveInfo授权前无法使用手Q OpenSDK的各项功能

3 设置信息字段接口

3.3.21 版本 新增设置信息字段接口,在用户同意用户协议条款之后,业务可自行获取相关信息字段,调用该接口设置到MSDK以及MSDK所包含的第三方组件。接口说明如下:

java接口:
MSDKSensitive.setSensitiveInfo("{\"AndroidID\":\"xxx\", \"WiFiMacAddress\":\"xxx\",\"Model\":\"xxx\", \"Oaid\":\"xxx\", \"Imsi\":\"xxx\", \"Cid\":\"xxx\"}");

c++接口:
/*
 * 以json形式设置信息字段至各个组件SDK,目前支持{"AndroidID":"xxx","WiFiMacAddress":"xxx","Model":"xxx","Oaid":"xxx","Imsi":"xxx","Cid":"xxx"}
 */
void WGSetSensitiveInfo(const char *jsonInfo);

注意事项

  • 在MSDK初始化之前,可调用java接口提前设置相关信息,避免个别上报缺失相关信息字段
  • 若在MSDK初始化之前,未调用java接口时,则MSDK初始化之后,必须调用c++接口
  • AndroidID字段请务必设置(AndroidID 需业务自行获取),否则影响灯塔数据统计
  • 灯塔、手Q互联OpenSDK、Bugly均需传入Model;Model非空(手Q互联OpenSDK从3.3.281版本开始支持,Bugly从3.3.29版本开始支持)

4 Apn & AndroidID & QImei & QImei36 采集配置接口开关

3.3.25 版本 新增 Apn & AndroidID & QImei & QImei36 采集配置接口开关,业务可按需自行配置这些字段是否允许采集(该接口功能与msdkconfig.ini文件中的MSDK_DENIED_COLLECT_LIST配置项为一个功能,但优先级高于后者)。接口说明如下:

    /**
    * 以json形式设置特别信息单字段开关,优先级小于总开关,目前支持设置AndroidID、Apn、QImei、QImei36
    * 参数示例{"AndroidID":true,"Apn":true,"QImei":true,"QImei36":true}
    *
    */
    void WGSetCollectSensitiveInfo(const char *jsonInfo);

Target API 31 升级指南

1. 背景

Google Play Store 要求 2022 年 8 月之后上线、11 月之后更新的 App 必须升级到 Target API 31。

官方参考资料:

Starting in August 2022, new apps must target API level 31 (Android 12) or above and adjust for behavioral changes. Wear OS apps must target API level 28 or higher.Starting in November 2022, app updates must target API level 31 or above and adjust for behavioral changes in Android 12.

2. 如何升级到 Target API 31

简单说,将编译的 targetSdkVersion 的值修改为 31 即可

2.1 Android Studio 及 Gradle 编译工程

一般在 App 目录中的 build.gradle 文件中,修改 android -> defaultConfig ->targetSdkVersion 的值为 31 即可,如:

2.2 Unity 项目

在 Unity 菜单 File -> Build Settings ... 弹窗中,Platform 选中 Android,在 Player Settings ...

中打开 Inspector 面板中,找到 Other Settings -> Target API Level 中进行设定

2.3 UnrealEngine 项目

在 UnrealEngine 菜单 File -> Package Project -> Packaging Settings... 中打开弹窗,

并在找到 Platforms -> Android 配置项,修改对应的配置

3. 配置及处理

3.1 安全和隐私设置变更——位置信息

在 Android 12(API 级别 31)或更高版本中,用户可以请求该应用只检索大致位置信息(即仅给予应用ACCESS_COARSE_LOCATION权限),即使该应用请求 ACCESS_FINE_LOCATION 运行时权限也是如此。

关于请求位置权限的官方文档:https://developer.android.com/training/location/permissions#approximate-request

注意:必须在单个运行时请求中同时包含 ACCESS_COARSE_LOCATION 权限及 ACCESS_FINE_LOCATION 权限

3.1.1 权限声明

在 AndroidManifest.xml 文件中声明 ACCESS_COARSE_LOCATION 权限及 ACCESS_FINE_LOCATION 权限。


//权限声明
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3.1.2 处理定位低精度的情况

当应用的 Target API 为31时,对于 Android12 的用户,若用户仅给予获取大致位置的权限,受 GPS 信号强度影响,可能导致定位的精度过低,甚至可能出现定位超时失败的情况,为提高定位成功率及精确度,业务可适当引导用户开启精确位置信息。

3.1.3 关于相关策略说明

为了提高定位的成功率,采用了两个系统 API 来获取附近的基站信息进行辅助定位,具体方式是:优先使用更高版本的 getAllCellInfo,若失败则使用 getCellLocation。此处需要对相关合规进行说明:若 getAllCellInfo 失败则会增加一次获取基站信息的接口调用。

3.2 组件导出的变更

当应用以 Android 12 或更高版本为目标平台,且包含使用 intent 过滤器的 activity、服务或广播接收器,则必须为上述组件显式地声明 `android:exported` 属性。MSDK 已经对内部各个组件进行了相应的处理,无需应用进行相关配置。

3.3 WebView 中的现代 SameSite Cookie 变更

当应用以 Android 12(API 级别 31)或更高版本为目标平台时,WebView 中会包含关于第三方 Cookie 处理方式的变更,应用需关注在需要时使用适当的值对 Cookie 中的 SameSite 属性进行显式的设置,详细的变更说明可参见下方链接。

关于WebView变更的官方文档:https://developer.android.com/about/versions/12/behavior-changes-12#samesite

3.4 关于包可见性的说明

若应用以 Android 11 或更高版本为目标平台,且有拉起其他应用进行登录、分享等操作的应用场景,则应用需要在自身的 AndroidManifest.xml 中添加‘queries‘元素,并声明相应的应用包名。


<manifest package="com.example.game">
    <queries>
        <!-- 指定QQ的包名,可拉起QQ相关的应用进行相应的操作 -->
        <package android:name="com.tencent.mobileqq" />
        <package android:name="com.tencent.tim" />
        <package android:name="com.tencent.minihd.qq" />
        <package android:name="com.tencent.qqlite" />
        
        
    	<!-- 指定微信的包名,可拉起微信进行相应的操作 -->
     	<package android:name="com.tencent.mm" />
		
      	...
      
    </queries>
    ...
</manifest>

关于包可见性的官方文档:https://developer.android.com/training/package-visibility/declaring

4. 升级注意事项

4.1 Gradle版本要求

Target API 升级到 30 及以上时,使用‘queries’标签时需要 Gradle 插件版本至少为 3.3.3,Gradle 版本至少为 4.10.1,若不符合条件,则编译时报 xml 文件合并的错误:


> Task :app:processDemoDebugManifest FAILED
> Error: Missing 'package' key attribute on element package at AndroidManifest.xml:...

解决方案:

升级 Android Gradle 插件版本到 3.3.3 或以上,Gradle 版本到 4.10.1 或以上。

MSDK Android 权限列表

渠道 必选权限 可选权限 使用目的
MSDK 1、android.permission.INTERNET(网络) 4、android.permission.WRITE_EXTERNAL_STORAGE(外部存储) 1、网络通讯
4、向外部存储写入数据,用于程序信息的持久化,日志文件、分享图片等存储; 无该权限,将影响外部存储图片/视频的分享功能
2、android.permission.ACCESS_NETWORK_STATE 5、android.permission.READ_EXTERNAL_STORAGE 2、用于内置浏览器 (WebView) 获取网络状态以感知网络变化
5、从外部存储读取数据,用于程序信息的持久化,日志文件、分享图片等存储; 无该权限,将影响外部存储图片/视频的分享功能
3、android.permission.CHANGE_NETWORK_STATE 6、android.permission.ACCESS_COARSE_LOCATION(粗略位置) 3、用于内置浏览器 (WebView) 的运营商免密登录认证(运营商取号协议
6、向游戏提供 LBS 位置信息服务,游戏通过接口可以获取经纬度,附近玩家的信息,让玩家可以和自己周边的玩家进行互动。如果游戏不需要 LBS 功能,可不配置此权限
7、android.permission.ACCESS_FINE_LOCATION(精确位置) 7、向游戏提供 LBS 位置信息服务,游戏通过接口可以获取经纬度,附近玩家的信息,让玩家可以和自己周边的玩家进行互动。如果游戏不需要 LBS 功能,可不配置此权限
8、android.permission.CAMERA(相机) 8、可以在网页中访问相机,如果游戏不需要该功能,可不配置此权限
QQ 1、android.permission.INTERNET(网络) 1、网络通信
2、android.permission.ACCESS_NETWORK_STATE(网络状态) 2、感知网络变化
WeChat - - -
Bugly 1、android.permission.INTERNET(网络) 1、网络通讯
2、android.permission.ACCESS_NETWORK_STATE(网络状态) 2、感知网络变化
QIMEI 1、android.permission.INTERNET(网络) 1、网络通信
2、android.permission.ACCESS_NETWORK_STATE(网络状态) 2、感知网络变化
Beacon 1、android.permission.INTERNET(网络) 1、网络通信,用于数据上报
2、android.permission.ACCESS_NETWORK_STATE 2、感知网络变化,用于调整数据上报策略
3、android.permission.ACCESS_WIFI_STATE 3、获取Wifi状态和信息,用于调整数据上报策略
TBS 1、查看网络链接 2、剪贴板 1、通过SDK打开网页内容时需要网络保持连接
2、用户主动操作网页或文件内容复制粘贴
3、查看地理位置 3、用户主动打开第三方网页,且网页自身需要定位位置
TPNS 1、应用包名.permission.XGPUSH_RECEIVE(自定义) 7、com.huawei.android.launcher.permission.CHANGE_BADGE(华为自定义权限) 1、使用TPNS推送的权限
7、使用华为设备角标功能权限
2、android.permission.INTERNET 8、com.vivo.notification.permission.BADGE_ICON(Vivo 自定义权限) 2、允许程序访问网络连接,可能产生 GPRS 流量
8、使用vivo设备角标功能权限
3、android.permission.ACCESS_WIFI_STATE 9、android.permission.VIBRATE 3、允许程序获取当前Wi-Fi接入的状态以及WLAN热点的信息
9、允许应用震动
4、android.permission.ACCESS_NETWORK_STATE 10、android.permission.RECEIVE_USER_PRESENT 4、允许程序获取网络信息状态
10、允许应用可以接收点亮屏幕或解锁广播
5、android.permission.SCHEDULE_EXACT_ALARM 11、android.permission.WRITE_EXTERNAL_STORAGE 5、允许定时广播
11、允许程序写入外部存储
6、android.permission.WAKE_LOCK 12、android.permission.RESTART_PACKAGES 6、允许程序在手机屏幕关闭后,后台进程仍然运行
12、允许程序结束任务
13、android.permission.GET_TASKS 13、允许程序获取任务信息

常见问题

1 Android微信登录不了检查步骤

第一步: 检查Log中是否有
lauchWXPlatForm wx SendReqRet: true

有这一句表示已经成功发送请求到微信

如果微信客户端被不能被拉起来,请查看 第二步, 如果微信客户端被拉起了,但是没有回调,请查看 第三步

第二步: 检查签名和包名

下载https://res.wx.qq.com/open/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android2.apk, 将此apk安装到手机上, 在输入框中输入游戏的包名,点击按钮读取游戏包的签名。

检查签名

检查上述工具获取到的签名是否和微信后台配置的签名一致(微信后台配置的签名信息查询请RTX联系MSDK)

第三步: 检查WXEntryActivity.java放置的位置(此文件在MSDKSample中)

此文件一定要放在 游戏+.wxapi 下面,例如游戏的包名为:com.tencent.msdkgame, 则WXEntryActivity.java 应该放在com.tencent.msdkgame.wxapi下。同时查看WXEntryActivity里面的内容是否和下面的一致

/**
 * !!此文件的代码逻辑部分使用者不要修改,MSDK从1.7开始,父类名称由WXEntryActivity改为BaseWXEntryActivity,如果此文件出错请优先检查此项
 */
public class WXEntryActivity extends com.tencent.msdk.weixin.BaseWXEntryActivity { }

此步骤没问题请查看 第四步

第四步:检查handleCallback

游戏的Launch Activity的onCreate和onNewIntent里面是否调用了WGPlatform.handleCallback。

第五步:检查游戏的全局Observer是否设置

检查游戏有没有正确调用WGSetObserver(C++层和Java层)。

2 接入微信注意事项

  • 为了使从微信授权回来不出现白色的框影响体验,可配置主题为"Theme.Translucent.NoTitleBar"如果使用这个透明主题必须同时配置 android:screenOrientation="portrait"否则微信登录会出现异常。
  • 应用包名+.wxapi下面放置WXEntryActivity.java文件。
  • 微信接入的Activity中有三处需要游戏自行修改(在上面示例有标注)。