Unity接入第三方Android SDK——之微信开放平台

1,294 阅读8分钟

系统环境

本文所涉及到的系统及开发环境 :

操作系统版本: MacOS Catalina 10.15.6 手机Android版本: 9(MIUI 11.0.7) Unity版本: 2019.4.9f1 Android Studio版本: 3.6 微信版本: 7.0.19 微信开放平台SDK版本: 6.6.4

大概思路

微信开放平台提供的SDK有关于Android的,但是没有关于Unity的,所以要想通过Unity实现微信分享,只需要解决Unity和Android的通信问题,剩下的就是根据微信的官方文档实现微信分享文字、图片、链接、小程序等。

微信开放平台创建应用时 关于签名的问题

MD5签名是根据你开发Unity项目时所使用的keystore生成的,keystore文件所在的位置可以通过Unity->Player Settings -> Player -> Publishing Settings 的Keystore Manager找到。

找到keystore文件之后,之后就是生成签名,生成签名的方法可以用微信开发平台提供的签名生成工具,这种方法略显复杂,其实可以直接使用keytool查看对应的MD5签名。

以MacOS上Android Studio默认的debug.keystore为例,在debug.keystore的文件目录下输入:

keytool -list -v -keystore debug.keystore

结果如下图所示,蓝色框框住的内容就是对应的MD5签名,填写到微信开放平台是注意去掉所有的冒号 image.png

一、创建Android Library

image.png

image.png

image.png

image.png

image.png

这个时候你应该能看到新创建的Module了 image.png

二、将Unity Library和微信的openSDK拷贝到项目中

1. 将Unity Library拷贝到项目中

如果你仅仅是想建立从Unity到Android的单向连接,也就是Unity可以访问Android,而不需要Android访问Unity,那么可以跳过这一步。

如果是你想建立Unity到Android的双向通信,那么就需要访问UnityPlayer Java 项目了。 在Unity安装目录下找到 classes.jar 如果Unity编译用的Scripting Backend是Mono就是:

(...\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar)

如果是IL2CPP, 对应的目录就是 (...\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes\classes.jar) 把classes.jar复制粘贴到前面创建的wechatshare moudle的libs文件夹下,最好重名下,比如unityplayer.jar image.png

2. 将微信的openSDK拷贝到项目中

因为最终build出来的插件是放在Unity中使用 ,而不是Android Studio, 所以事实上它并不能够下载远程文件,所以我们需要将微信openSDK对应的jar文件下载下来。

下载地址:微信openSDK

这里笔者下载的6.6.4版本 image.png 和unityplayer.jar一样,将它放置在module的libs目录下 image.png

现在我们已经将unityplayer.jar和微信openSDK相关的jar加入到项目中了,现在我们要让Android Studio知道这件事。

三、设置Gradle File

对应wechatshare的build.gradle现在应该是长这样: image.png

接下来把Dependencies的内容整个换成: dependencies ``**{**``**    ** compileOnly files (``**'libs/unityplayer.jar'**``) implementation files (``**'libs/wechat-sdk-android-without-mta-6.6.4.jar'**``) **}**

注意

  1. **unityplayer.jar**使用的不是implementation files,而是compileOnly files,这样就可以 保证unityplayer.jar不会打包进入最终的Plugin文件中,从而避免和Unity项目中的classes.jar冲突。
  2. 不要使用implementation fileTree(dir: 'libs', include: ['*.jar']),这样写的话又会将unityplayer.jar编译进最终的plugin中造成libarary冲突

上面替换dependencies的步骤可能删除了单元测试的libraries, 检查你的项目文件目录并删除所有和测试相关的classes, 否则后面编译是没法通过的。 image.png

四、代码部分-实现Unity和Android的相互通信

先创建一个空的Activity image.png

image.png

我们让MainAcitivy继承Activity image.png MainAcitivy的onCreate,事实上在通过Unity调用MainActivity里面的方法onCreate并不会被调用,因为第一次接触Android,所以具体并不清楚,所以不要尝试在这里面做一些初始化的信息。如果有Android大神欢迎指教关于onCreate没有被调用的原因。

根据微信官方文档的示例,要使你的程序启动后微信终端能响应你的程序,必须在代码中向微信终端注册你的 id,关键的两行代码:

// 通过WXAPIFactory工厂,获取IWXAPI的实例
api = WXAPIFactory.createWXAPI(this, APP_ID, true);
// 将应用的appId注册到微信
api.registerApp(APP_ID);

其中createWXAPI方法中的第一个参数是一个Context, 前面说了因为onCreate不会被调用,所以也就意味着MainActivity这个Activity其实是空的,即便笔者尝试过为MainActivity生成一个Instance传入createWXAPI中 ,依旧告知为空,当初笔者为这个抓破脑袋,实在是没做过Android开发.

后来网上寻寻觅觅,发现可以利用反射机制获取当前UnityPlayer的Activity,并得到当前的context具体代码如下:

public static Activity unityActivity;
private static Context context;
//  利用反射机制获取unity项目的上下文
static Context getContent(){
        if(null == unityActivity) {
            try {
                Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
                Activity activity = (Activity) classtype.getDeclaredField("currentActivity").get(classtype);
                unityActivity = activity;
                context = activity;
            } catch (ClassNotFoundException e) {
                System.out.println(e.getMessage());
            } catch (IllegalAccessException e) {
                System.out.println(e.getMessage());
            } catch (NoSuchFieldException e) {
                System.out.println(e.getMessage());
            }
        }
        return context;
  }

好了,这个关键问题解决了,剩下的就简单了,接下来就是在MainActivity中写下一系列public static方法,共Unity调用,一个简单的示例:

 // 安卓调用Unity的方法示例
public static void SendMessageToUnity(String gameObjectName, String UnityMethodName){
    // 第一个参数:Unity中某个GameObject的gameObjectName
    // 第二个参数:挂在gameObjectName上的脚本的某个方法
    // 第三个参数:UnityMethodName这个方法接受的参数,这里是个String
    UnityPlayer.UnitySendMessage(gameObjectName, UnityMethodName, "Android发来的贺电");
}

在Unity中调用

public Text messageText = null;
private string _className = "com.unityplugins.wechatshare.MainActivity";
void Start() {
	AndroidJavaClass mainActivityClass = new AndroidJavaClass(_className);
	// 第一个参数: MainActivity中的方法名,即SendMessageToUnity,该方法没有返回值
    // 第二、三个参数: 分别是SendMessageToUnity需要接收的第一和第二个参数,对比上面Java的代码
    mainActivityClass.CallStatic("SendMessageToUnity", gameObject.name, "ReceiveFromAndroid");
}

private void ReceiveFromAndroid(string message) {
    messageText.text =  message;
}

完事之后可以进行个简单的测试,具体的编译流程:编译流程

如果都没有问题的话,运行成功后messageText对应的Text文本的内容会显示:Android发来的贺电 image.png

五、代码部分 - Android端微信分享相关代码

新建一个Java Class image.png image.png 笔者这里将之命名为WeChatController 在这个类里实现微信分享有关的各个功能:向微信注册APP、分享场景(朋友圈、对话、收藏)、分享各类媒体内容等。具体请参考微信官方文档,这里不一一讲解,部分代码如下:

public class WeChatController {

    public static IWXAPI api;

    private String _APP_ID = "";
    private static WeChatController Instance = null;

    public static WeChatController GetInstance() {
        if (Instance == null) {
            Instance = new WeChatController();
        }
        return Instance;
    }

    public void Init(Context content, String APP_ID) {
        _APP_ID = APP_ID;
        RegisterAppToWX(content);
    }

    private void RegisterAppToWX(Context content) {
        api = WXAPIFactory.createWXAPI(content, _APP_ID, true);
        api.registerApp(_APP_ID);
    }

    // 判断是否安装了微信
    public boolean IsWeChatInstalled() {

        return api.isWXAppInstalled();
    }

    // 分享链接至微信
    // scene: 分享到哪个场景 0 代表对话、1 代表朋友圈、2 代码收藏
    public void ShareWebpageToWX(int scene, String url, String title, String description) {
        // 设置网址
        WXWebpageObject webpageObject = new WXWebpageObject();
        webpageObject.webpageUrl = url;

        // 设置标题
        WXMediaMessage msg = new WXMediaMessage(webpageObject);
        msg.title = title;
        msg.description = description;

        // 设置缩略图
        msg.thumbData = GetDrawableIconByPackageName(MainActivity.PackageName);

        // 构造一个发信请求
        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = BuildTransaction("webpage");
        req.message = msg;
        req.scene = scene;

        // 发送请求给微信客户端
        api.sendReq(req);
    }

    // 分享文字至微信
    public void ShareTextToWX(int scene, String text) {
        //初始化一个 WXTextObject 对象,填写分享的文本内容
        WXTextObject textObj = new WXTextObject();
        textObj.text = text;

        //用 WXTextObject 对象初始化一个 WXMediaMessage 对象
        WXMediaMessage msg = new WXMediaMessage();
        msg.mediaObject = textObj;
        msg.description = text;

        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = BuildTransaction("text");
        req.message = msg;
        req.scene = scene;
        //调用api接口,发送数据到微信
        api.sendReq(req);
    }

    // 分享图片至微信
    public void ShareImageToWX(int scene, byte[] imgData) {
        WXImageObject imgObj = new WXImageObject(imgData);
        WXMediaMessage msg = new WXMediaMessage();
        msg.mediaObject = imgObj;
        msg.thumbData = GetDrawableIconByPackageName(MainActivity.PackageName);

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

    // 分享音乐至微信
    public void ShareMusicToWX(int scene, String musicUrl, String title, String description){
        //初始化一个WXMusicObject,填写url
        WXMusicObject music = new WXMusicObject();
        music.musicUrl= musicUrl;

        //用 WXMusicObject 对象初始化一个 WXMediaMessage 对象
        WXMediaMessage msg = new WXMediaMessage();
        msg.mediaObject = music;
        msg.title = title;
        msg.description = description;
        msg.thumbData = GetDrawableIconByPackageName(MainActivity.PackageName);

        //构造一个Req
        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = BuildTransaction("music");
        req.message = msg;
        req.scene = scene;
        //调用api接口,发送数据到微信
        api.sendReq(req);
    }

    // 分享视频至微信
    public void ShareVideoToWX(int scene, String videoUrl, String title, String description){
        //初始化一个WXVideoObject,填写url
        WXVideoObject video = new WXVideoObject();
        video.videoUrl =videoUrl;

        //用 WXVideoObject 对象初始化一个 WXMediaMessage 对象
        WXMediaMessage msg = new WXMediaMessage(video);
        msg.title = title;
        msg.description = description;
        msg.thumbData = GetDrawableIconByPackageName(MainActivity.PackageName);

        //构造一个Req
        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = BuildTransaction("video");
        req.message =msg;
        req.scene = scene;

        //调用api接口,发送数据到微信
        api.sendReq(req);
    }

    // 分享小程序至微信
    public void ShareMinniProgramToWX(int scene, String lowVersionUrl, String miniProgramAPPID, String path,
                                      String title, String description, byte[] coverImgData){
        WXMiniProgramObject miniProgramObj = new WXMiniProgramObject();
        miniProgramObj.webpageUrl = lowVersionUrl; // 兼容低版本的网页链接
        miniProgramObj.miniprogramType = WXMiniProgramObject.MINIPTOGRAM_TYPE_RELEASE;// 正式版:0,测试版:1,体验版:2
        miniProgramObj.userName = miniProgramAPPID;     // 小程序原始id
        miniProgramObj.path = path;   //小程序页面路径;对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar"
        WXMediaMessage msg = new WXMediaMessage(miniProgramObj);
        msg.title = title;                    // 小程序消息title
        msg.description = description;        // 小程序消息desc
        msg.thumbData = coverImgData;           // 小程序消息封面图片,小于128k

        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = BuildTransaction("miniProgram");
        req.message = msg;
        req.scene = scene;
        api.sendReq(req);
    }

    private String BuildTransaction(final String type) {
        return (type == null) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis();
    }
}

六、编译流程

Android Studio部分

Android Studio右上角有个“Gradle”, 点开然后找到 wechatshare/Tasks/build, 然后三连,即先后进行三步操作,clean -> build -> assemble, 如下图 image.png 生成的arr文件在 wechatshare/build/outputs/arr这个目录下 image.png

Unity部分

将Android Studio生成的aar文件拷贝到Unity中 Assets/Plugins/Android目录下(此目录默认是没有的,需要自己手动创建) image.png 因为笔者用的Unity Library即unityplayer.jar是il2cpp目录下的,所以需要在Player Settings中将Scripting Backend设为 IL2CPP: image.png 至此Unity的设置就完成了,接下来只需要Build And Run即可。

七、完整源码

github: github.com/JookiTsui/U… gitee: gitee.com/jooki/Unity…