IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 【Unity3D】Unity3D项目接入微信和QQ的SDK分享功能总结(安卓端) -> 正文阅读

[游戏开发]【Unity3D】Unity3D项目接入微信和QQ的SDK分享功能总结(安卓端)




前言


第一次进行 Unity 项目的 SDK 接入,中途踩了很多坑,也学到了很多东西。

在此记录系统性的总结一下自己接入过程,避免以后踩同样的坑。

因为目前需求只要进行纯图片的分享,所以本文只记录了如何分享图片的相关方法。



正文


一、Unity 与 Android 的交互


目前有三种方式可以进行交互:

① AndroidStudio 导出 jar 包,Unity 接入 Jar 包,使用 Jar 包封装好的功能

② AndroidStudio 导出 Arr 包,Unity 接入 Arr 包,使用 Arr 包封装好的功能

③ Unity 导出安卓工程,在 AndroidStudio 中对导出工程进一步改造

由于游戏包体过大,为了节省打包时间,我在这里选择了方式 ③ 进行接入。

1. Unity 调用 Android 的方法


在 Unity 导出 Android 工程的时候勾选 Export Project
在这里插入图片描述

该操作会自动生成一个 UnityPlayerActivity.java,其路径为:

xxx\src\main\java\com\xxx\xxx\UnityPlayerActivity.java

首先要获取 Android 下的 currentActivity,代码如下

private AndroidJavaObject _current_activity = null;
private AndroidJavaObject CurrentActivity
{
	get
	{
		if (_current_activity == null)
		{
			AndroidJavaClass unity_player = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
			_current_activity = unity_player.GetStatic<AndroidJavaObject>("currentActivity");
		}
		return _current_activity;
	}
}

获取到之后,就可以使用CurrentActivity.Call<T>(string func,string parms)调用Android端的方法

注:调用的方法要声明在MainActivity中。

可以在 Android 端将所有功能封装成一个 “sdkCommand” 方法

在 Unity 中通过传入方法名,来调用不同方法

private string SendSdkCommand(string strCommand, string P2 = "", string P3 = "", string P4 = "", string P5 = "")
{
	return CurrentActivity.Call<string>("sdkCommand", strCommand, P2, P3, P4, P5);
}

2. Android 调用 Unity 的方法


使用 UnitySendMessage 方法

UnityPlayer.UnitySendMessage(String var0, String var1, String var2)
  • var0:场景中的 GameObject 名,要调用的方法所在的脚本要挂载在上面
  • var1:要调用的方法名,要调用的方法必须是void类型
  • var2:该方法一定有一个String的参数,该参数一定不能为 null !!!

注:我当时在微信SDK的回调中调用 UnitySendMessage 方法,var2 传入的是 resp.errorMsg。
调试半天都卡在 UnitySendMessage 的执行,
最后发现当回调成功的时候 resp.errorMsg 是 null,所以导致调用失败。

这里可以通过通用单例类[1]来创建该 GameObject

并将其放到 DontDestroyOnLoad 当中,保证在游戏运行过程中不会被销毁

创建平台管理类 PlatformHelper.cs

public class PlatformHelper : SingletonMonoBehaviour<PlatformHelper>
{
	// 获取currentActivity
	private AndroidJavaObject _current_activity = null;
	private AndroidJavaObject CurrentActivity
	{
		get
		{
			if (_current_activity == null)
			{
				AndroidJavaClass unity_player = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
				_current_activity = unity_player.GetStatic<AndroidJavaObject>("currentActivity");
			}
			return _current_activity;
		}
	}
	
	// 测试方法
	public void AndroidCallUnity(string str)
	{
		Debug.Log(str);
	}
}

这样就可以这样调用

UnityPlayer.UnitySendMessage("PlatformHelper", "AndroidCallUnity", str) // 注意str不能为null

3. 编写 Unity 端接口


在导出Android工程前,需要现在Unity端写好未来要调用的一些方法

所有的平台交互方法都可以写在统一的平台管理类[2]当中,便于管理


二、Android 端接入微信SDK


1. 申请 AppID

微信开放平台 进行登记

登记并选择移动应用进行设置后将该应用提交审核

只有审核通过的应用才能进行开发

2. 下载 SDK 及 API 文档

我使用的是 Android Studio 进行接入,所以在 build.gradle 文件中,添加微信的SDK依赖即可

dependencies {
    api 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'
}

3. 代码中使用开发工具包

[1] 在 AndroidManifest.xml 中添加权限

<uses-permission android:name="android.permission.INTERNET" />
<!-- for mta statistics, not necessary-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

[2] 注册到微信

这里为了区分多个 SDK,我将微信用到的方法都封装到 WeChatSDK.java [3]

并且将方法和相关 API 声明为静态,便于在 MainActivity 当中调用

在 UnityPlayerActivity.java 的中合适的地方(我这里选择的是在生命周期 onCreate )初始化

WeChatSDK.Init(activity);

[3] 发送请求或响应到微信

现在,你的程序要发送请求或发送响应到微信终端

可以通过 IWXAPI 的 sendReq 和 sendResp 两个方法来实现

对于这两个方法的实现和使用可以参考微信的官方文档

[4] 注册响应回调

在你的包名相应目录下新建一个 wxapi 目录

在该 wxapi 目录下新增一个 WXEntryActivity 类
在这里插入图片描述
该类继承自 Activity,实现 IWXAPIEventHandler 接口

public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
    // Setup activity layout
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (WeChatSDK.GetWXAPI() != null){
            WeChatSDK.GetWXAPI().handleIntent(getIntent(), this);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        WeChatSDK.GetWXAPI().handleIntent(getIntent(), this);
    }

    @Override
    public void onReq(BaseReq baseReq) {

    }

    @Override
    public void onResp(BaseResp resp) {
        // 返回结果
        if (resp != null) {
        	// 调用回调方法
            WeChatSDK.ShareCallback(resp);
        }
        finish();
    }
}

主要有几个注意点:

  1. 要重写 onNewIntent 方法,调用 WXAPI.handleIntent()
  2. 如果 onResp 执行之后,黑屏停留在微信的 Activity 不回到游戏,记得检查一下在 onResp 执行完逻辑后调用 finish() 方法结束该 Activity

并在 AndroidManifest.xml 添加如下属性

<activity
	android:name=".wxapi.WXEntryActivity"
	android:label="@string/app_name"
	android:theme="@android:style/Theme.Translucent.NoTitleBar"
	android:exported="true"
	android:taskAffinity="com.xxx.xxx"
	android:launchMode="singleTask">
</activity>

4. 微信分享图片

官方文档的方法是提供文件路径,将文件路径处理成二进制数据

我这里由于是分享 Unity StreamingAssets 下的资源

所以可以直接由 Unity 加载然后处理成二进制数据直接传入

/**
* 分享图片
* @param data 图片二进制数据
* @param type  分享类型 0:会话 1:朋友圈
*/
public static void ShareImg(byte[] data, int type)
{
	mActivity.runOnUiThread(new Runnable()
	{
    	@Override
        public void run()
        {
        	if (data == null)
        	{
            	Log.d(TAG, "Image not exist");
                return;
            }
            Bitmap shareBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
            WXImageObject imgObj = new WXImageObject(shareBitmap);

            WXMediaMessage msg = new WXMediaMessage();
            msg.mediaObject = imgObj;

            Bitmap thumbBmp = Bitmap.createScaledBitmap(shareBitmap, THUMB_SIZE, THUMB_SIZE, true);
            msg.thumbData = bmpToByteArray(thumbBmp);  // 设置缩略图

            SendMessageToWX.Req req = new SendMessageToWX.Req();
            req.transaction = buildTransaction("img");
            req.message = msg;
            req.scene = type;
            sendReq(req);
       	}
    });
}

/**
* 得到Bitmap的byte
*
* @param bmp 图片
* @return 返回压缩的图片
*/
private static byte[] bmpToByteArray(Bitmap bmp)
{
	ByteArrayOutputStream output = new ByteArrayOutputStream();
	bmp.compress(Bitmap.CompressFormat.PNG, 100, output);
	
	byte[] result = output.toByteArray();
	try
	{
		output.close();
	}
	catch (Exception e)
	{
		e.printStackTrace();
	}
	return result;
}

/**
* 构建一个唯一标志
*
* @param type 分享的类型分字符串
* @return 返回唯一字符串
*/
private static String buildTransaction(String type)
{
	return (type == null) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis();
}

注:如果发送消息时报错,可以使用runOnUiThread进行尝试

5. 总结

总的来说,接入微信的时候没有遇到什么比较大的问题

只要规规矩矩按照文档上的来就可以调通


三、Android 端接入QQSDK


1. 申请 AppID

第一步还是去 QQ互联官网 登记

2. 搭建SDK环境

在项目的 build.gradle 中添加互联sdk maven依赖

dependencies {
	implementation 'com.tencent.tauth:qqopensdk:3.52.0'
	implementation 'com.squareup.okhttp3:okhttp:3.12.12'
}

注:此处还需要引入 androidx 包,因为 QQ 的文件操作会使用到 FileProvider[4]

配置 AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
 <activity
       android:name="com.tencent.tauth.AuthActivity"
       android:noHistory="true"
       android:launchMode="singleTask" >
    <intent-filter>
           <action android:name="android.intent.action.VIEW" />
           <category android:name="android.intent.category.DEFAULT" />
           <category android:name="android.intent.category.BROWSABLE" />
           <data android:scheme="tencent你的AppId" />
    </intent-filter>
 </activity>
<activity
       android:name="com.tencent.connect.common.AssistActivity"
       android:configChanges="orientation|keyboardHidden"
       android:screenOrientation="behind" 
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<application>

注:“tencent你的AppId” ,如果你的appId是1234567,那么此处应该填写 “tencent1234567”。

3. 在代码中使用

[1] 创建实例

首先还是要创建 QQAPI 的实例,同样将代码都封装在 QQSDK.java[5]

mTencent = Tencent.createInstance(APP_ID, mActivity.getApplicationContext(), APP_AUTHORITIES);

在实例化的时候,还需传入一个 APP_AUTHORITIES 参数

该参数为 Manifest 文件中注册 FileProvider 时设置的 authorities 属性值

即 com.xxx.xxx.fileprovider

[2] 初始化 QQAPI

同样的,在 UnityPlayerActivity.java 的中合适的地方调用封装好的方法进行初始化

QQSDK.Init(activity);

[3] 注册回调函数

需要在 onActivityResult 下注册监听(要在 QQAPI 运行的 Activity 中改写才可以)

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	Tencent.onActivityResultData(requestCode, resultCode, data, QQSDK.GetListener());
}

[4] 分享图片

通过shareToQQ(Activity var1, Bundle var2, IUiListener var3)方法发送分享请求

QQ 分享纯图片只能通过图片的路径

所以要在 Unity 端将要分享的图片存入手机中

我选择的是存入 persistentDataPath 下,并将保存的路径传入

string savePath = Application.persistentDataPath + "/" + id.ToString() + "moments.png";
using (FileStream fs = File.Create(savePath))
{
	fs.Write(imageData, 0, webRequest.downloadHandler.data.Length);
}
CurrentActivity.Call("QQShareImage", savePath);

注:各个路径的读写权限和路径位置可以看【Unity3D】数据持久化及方法

5. 总结

总体接入过程中,遇到两个问题

  1. 关于 FileProvider 的配置:原先的 android.support.v4 在高版本已经弃用,转而使用 androidX
    在更换这个包的时候有一个兼容问题需要在 gradle.properties 中做配置。
  2. 如果分享图片的路径传入的是 streamingAssetsPath 下的,很可能 QQ 因为权限问题而访问不到,建议存入persistentDataPath 下。





附录


通用单例类 SingletonMonoBehaviour.cs

using UnityEngine;

namespace Utilities
{
    public class SingletonMonoBehaviour<T> : MonoBehaviour where T : SingletonMonoBehaviour<T>
    {
        private static T _instance;

        public static T instance
        {
            get
            {
                CreateInstance();
                return _instance;
            }
        }

        public static void CreateInstance()
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<T>();
                if (_instance == null)
                {
                    var go = new GameObject(typeof(T).Name);
                    _instance = go.AddComponent<T>();
                }
                if (!_instance.initialized)
                {
                    _instance.Initialize();
                    _instance.initialized = true;
                }
            }
        }

        public virtual void Awake ()
        {
            if (Application.isPlaying)
            {
                DontDestroyOnLoad(this);
            }
            
            if (_instance != null)
            {
                DestroyImmediate (gameObject);
            }
        }

        protected bool initialized;

        protected virtual void Initialize() { }
    }
}

微信SDK类 WeChatSDK.java

public class WeChatSDK 
{
    private static final String TAG = "WeiXinSDK";
    private static final String APP_ID = "*************";
    private static IWXAPI m_wxApi;
    private static Activity mActivity;

    /**
     * 初始化
     * @param activity MainActivity
     */
    public static void Init(Activity activity)
    {
        mActivity = activity;
        m_wxApi = WXAPIFactory.createWXAPI(mActivity, APP_ID);
        m_wxApi.registerApp(APP_ID);

        //建议动态监听微信启动广播进行注册到微信
        mActivity.registerReceiver(new BroadcastReceiver()
        {
            @Override
            public void onReceive(Context context, Intent intent)
            {
                // 将该app注册到微信
                m_wxApi.registerApp(APP_ID);
            }
        }, new IntentFilter(ConstantsAPI.ACTION_REFRESH_WXAPP));
    }

    /**
     * 获取微信api实例
     * @return IWXAPI
     */
    public static IWXAPI GetWXAPI() 
    {
        return m_wxApi;
    }

    /**
     * 发送消息
     * @param req SendMessageToWX
     */
    public static void sendReq(SendMessageToWX.Req req)
    {
        m_wxApi.sendReq(req);
    }

    /**
     * 分享图片
     */
    public static void ShareImg(...)
    {
      	......
    }

    /**
     * 分享回调
     */
    public static void ShareCallback(BaseResp resp)
    {
        ......
    }
}

配置 FileProvider

A. 添加 androidx 依赖

dependencies {
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.documentfile:documentfile:1.0.1'
}

B. 在 AndroidManifest.xml 中配置

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

C. 在 res/xml 路径下创建 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>

注:如果运行发现找不到 android.support.v4 包,在 gradle.properties 中配置

android.enableJetifier=true
android.useAndroidX=true 

QQSDK类 QQSDK.java

public class QQSDK
{
    private static final String TAG = "QQSDK";
    private static final String APP_ID = "1234567";
    private static final String APP_AUTHORITIES = "com.xxx.xxx.fileprovider";
    private static Tencent mTencent;
    private static Activity mActivity;
    private static IUiListener mShareListener;

    /**
     * 初始化
     * @param activity
     */
    public static void Init(Activity activity)
    {
        mActivity = activity;
        // Tencent类是SDK的主要实现类,开发者可通过Tencent类访问腾讯开放的OpenAPI。
        // 其中APP_ID是分配给第三方应用的appid,类型为String。
        // 其中Authorities为 Manifest文件中注册FileProvider时设置的authorities属性值
        mTencent = Tencent.createInstance(APP_ID, mActivity.getApplicationContext(), APP_AUTHORITIES);
        if (mTencent == null)
        {
            Log.e(TAG, "Tencent instance create fail!");
        }

        // 注册监听回调
        mShareListener = new DefaultUiListener()
        {
            @Override
            public void onComplete(Object o)
            {
                Log.d(TAG, "onComplete");
                UnityPlayer.UnitySendMessage("PlatformHelper", "QQShareCallBack", "onComplete");
            }

            @Override
            public void onError(UiError uiError)
            {
                Log.d(TAG, "onError");
            }

            @Override
            public void onCancel()
            {
                Log.d(TAG, "onCancel");
            }
        };
    }

    /**
     * 获取QQApi
     * @return
     */
    public static IUiListener GetListener()
    {
        return mShareListener;
    }

    /**
     * 分享图片
     * @param url
     */
    public static void ShareImg(String url)
    {
    	......
    }
}

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-07-13 17:49:43  更:2021-07-13 17:49:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 20:25:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码