zoukankan      html  css  js  c++  java
  • Unity 多平台原生SDK接入速览(一):微信开放平台

    该系列将记录我对于五个平台(微信、QQ、Facebook、Twitter、微博)的原生SDK的调研,重点关注登录和分享。P.S. 当前并没有 iOS 设备,因此文章都是以 Android 平台的接入为主,使用的 IDE 为 Android Studio。

    ZeroyiQ:Unity 多平台原生SDK接入速览(二):QQ互联

    ZeroyiQ:Unity 多平台原生SDK接入速览(三):Facebook

    ZeroyiQ:Unity 多平台原生SDK接入速览(四):Twitter

    ZeroyiQ:Unity 多平台原生SDK接入速览(五):微博

    一、前言

    微信开放平台,当前(2020-6-24)注册账户必须要填写企业信息,还需要应用审核。请优先解决账户和审核问题,获取到应用 AppID 和 Secret。

    二、SDK接入

    1. 配置环境

    项目 build.gradle 中添加依赖。

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

    2. 设置权限

    AndroidManifest.xml 中设置,如果使用到扫码登录,或者 mta(腾讯移动分析) 才需要添加以下权限。

    <!-- 扫码登录 需要权限-->
    <uses-permission android:name="android.permission.INTERNET" />
    
    <!-- mta 需要权限-->
    <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"/>
    

    3. 初始化

    要使微信能响应我们的程序,必须向微信注册我们的应用(AppID)。

    private static final String WX_ID = "应用ID(需要替换)";
    // IWXAPI 是第三方app和微信通信的openApi接口
    private static IWXAPI WXAPI;
        
    private void init() {
        // 通过WXAPIFactory工厂,获取IWXAPI的实例
        WXAPI = WXAPIFactory.createWXAPI(activity, WX_ID, true);
    
        // 将应用的appId注册到微信
        WXAPI.registerApp(WX_ID);
    
        //建议动态监听微信启动广播进行注册到微信
        activity.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                WXAPI.registerApp(WX_ID);
                }
            }, new IntentFilter(ConstantsAPI.ACTION_REFRESH_WXAPP));
    }
    

    4. 发送请求

    现在我们就可以通过 通过 IWXAPI 的 sendReq 和 sendResp 两个方法来发送请求了。

    boolean sendReq(BaseReq req);

    sendReq 是第三方 app 主动发送消息给微信,发送完成之后会切回到第三方 app 界面。

    boolean sendResp(BaseResp resp);

    sendResp 是微信向第三方 app 请求数据,第三方 app 回应数据之后会切回到微信界面。

    5. 接收请求

    在与包名相同的路径下新增一个 wxapi 目录,并在该目录下新增一个 WXEntryActivity 类,该类继承自 Activity ,实现 IWXAPIEventHandler 接口。

    img

    WXEntryActivity

    AndroidManifest.xml 中配置该 Activity,需要填入我们自己的包名

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

    WXEntryActivity 中添加 Intent 的传递

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            WeChat.WXAPI.handleIntent(getIntent(), this);
        }
    
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            setIntent(intent);
            WeChat.WXAPI.handleIntent(intent, this);
        }
    

    IWXAPIEventHandler 有 onReq 和 onResp 两个回调方法。要注意的是通过 sendReq 发送的请求,将由 onResp 回调回来;通过 sendResp 发送的请求,将由 onReq 回调回来。

    三、登录

    1. 发起登录请求

    在注册完 OpenSdk 后,发起登录请求。

        /**
         * 登录微信
         * @param context 上下文
         * @param api 微信 OpenAPI
         * @param wechatCode 回调接口
         */
        public static void loginWeChat(Context context, IWXAPI api, WeChatCode wechatCode) {
            //判断是否安装了微信客户端
            if (!api.isWXAppInstalled()) {
                ToastUtils.show(context.getApplicationContext(),R.string.wechat_error_unInstalled);
                return;
            }
            mWeChatCode = wechatCode;
    
            // 发送授权登录信息,来获取code
            SendAuth.Req req = new SendAuth.Req();
    
            // 应用的作用域,获取个人信息
            req.scope = "snsapi_userinfo";
    
            /**
             * 用于保持请求和回调的状态,授权请求后原样带回给第三方
             * 为了防止csrf攻击(跨站请求伪造攻击),后期改为随机数加session来校验
             */
            Random random = new Random();
            WeChat.WXState = WeChat.WX_STATE_ROOT + random.nextInt(1000);
            req.state = WeChat.WXState;
    
            // 发送请求
            api.sendReq(req);
        }
    
        /**
         * 返回code的回调接口
         */
        public interface WeChatCode {
            void getResponse(String code);
        }
    

    2. 接收回调,获得 code

    WXEntryActivity 的 onResp 方法中接收回调。

         /**
         * 第三方应用发送到微信的请求处理后的响应结果,会回调到该方法
         * @param baseResp 回调 response
         */
        @Override
        public void onResp(BaseResp baseResp) {
            int result;
            switch (baseResp.errCode) {
                case BaseResp.ErrCode.ERR_OK:
                    result = R.string.errcode_success;
                    UnityCallApi.unityLogInfo(TAG, "onResp OK");
                    break;
                case BaseResp.ErrCode.ERR_USER_CANCEL:
                    result = R.string.errcode_cancel;
                    UnityCallApi.unityLogInfo(TAG, "onResp ERR_USER_CANCEL ");
                    break;
                case BaseResp.ErrCode.ERR_AUTH_DENIED:
                    result = R.string.errcode_deny;
                    UnityCallApi.unityLogInfo(TAG, "onResp ERR_AUTH_DENIED");
                    break;
                case BaseResp.ErrCode.ERR_UNSUPPORT:
                    result = R.string.errcode_unsupported;
                    UnityCallApi.unityLogInfo(TAG, "onResp ERR_UNSUPPORT " + baseResp.errCode);
                    break;
                default:
                    result = R.string.errcode_unknown;
                    UnityCallApi.unityLogInfo(TAG, "onResp default errCode " + baseResp.errCode);
                    break;
            }
            ToastUtils.show(this,getString(result)+ ", type=" + baseResp.getType());
    
            if (baseResp.getType() == ConstantsAPI.COMMAND_SENDAUTH) {
                // 校验 state
                String state = ((SendAuth.Resp) baseResp).state;
                if (state.equals(WeChat.WXState)) {
                    String code = ((SendAuth.Resp) baseResp).code;
                    // 返回 code 进行下一步
                    mWeChatCode.getResponse(code);
                    UnityCallApi.unityLogInfo(TAG, "Get WeChat scope. code:" + code);
                } else {
                    String errorLog = "onResp: State not match!" + WeChat.WXState + "/" + state;
                    UnityCallApi.unityLogError(TAG, errorLog);
                }
            }
        }
    

    3. 获取 access_token

    优先判断本地是否已经存储 access_token,有则进行有效期检测,没有则通过 code 获取最新 access_token。

        public void login(Activity activity) {
            WXEntryActivity.loginWeChat(this.activity, WXAPI, new WXEntryActivity.WeChatCode() {
                @Override
                public void getResponse(String code) {
                    // 从手机本地获取存储的授权口令信息,判断是否存在access_token,不存在请求获取,存在就判断是否过期
                    String accessToken = (String) ShareUtils.getValue(WeChat.this.activity, WEIXIN_ACCESS_TOKEN_KEY, "none");
                    String openid = (String) ShareUtils.getValue(WeChat.this.activity, WEIXIN_OPENID_KEY, "");
                    if (!"none".equals(accessToken)) {
                        // 有access_token,判断是否过期有效
                        isExpireAccessToken(accessToken, openid);
                    } else {
                        // 没有access_token
                        getAccessToken(code);
                    }
                }
            });
        }
    

    getAccessToken 获取最新 access_token

        /**
         * 微信登录获取授权口令
         */
        private void getAccessToken(String code) {
            String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                    "appid=" + WX_ID +
                    "&secret=" + WX_SECRET +
                    "&code=" + code +
                    "&grant_type=authorization_code";
            // 网络请求 GET 获取access_toke
            //NetClient是对Okhttp3 的封装,见引用3
            NetClient.getNetClient().callNet(url, new NetClient.MyCallBack() {
                @Override
                public void onFailure(int code) {
                    
                }
    
                @Override
                public void onResponse(String response) {
                    // 处理回调
                    processGetAccessTokenResult(response);
                }
            });
       }
    

    isExpireAccessToken 校验 access_token,没有通过则刷新 refreshAccessToken

     /**
         * 微信登录判断accesstoken是过期
         *
         * @param accessToken token
         * @param openid      授权用户唯一标识
         */
        private void isExpireAccessToken(final String accessToken, final String openid) {
            String url = "https://api.weixin.qq.com/sns/auth?" +
                    "access_token=" + accessToken +
                    "&openid=" + openid;
            NetClient.getNetClient().callNet(url, new NetClient.MyCallBack() {
                @Override
                public void onFailure(int code) {
                   
                }
                @Override
                public void onResponse(String response) {
                    WXErrorInfo info = mGson.fromJson(response, WXErrorInfo.class);
                    if (0 == info.getErrcode() && "ok".equals(info.getErrmsg())) {
                        // accessToken没有过期,获取用户信息
                        getUserInfo(accessToken, openid);
                        Toast.makeText(activity.getApplicationContext(), response.toString(), Toast.LENGTH_LONG).show();
                    } else {
                        // 过期了,使用refresh_token来刷新accesstoken
                        refreshAccessToken();
                    }
                }
            });
        }
    
        /**
         * 微信登录刷新获取新的access_token
         */
        private void refreshAccessToken() {
            // 从本地获取以存储的refresh_token
            final String refreshToken = (String) ShareUtils.getValue(activity, WEIXIN_REFRESH_TOKEN_KEY, "");
            if (TextUtils.isEmpty(refreshToken)) {
                return;
            }
            // 拼装刷新access_token的url请求地址
            String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?" +
                    "appid=" + WX_ID +
                    "&grant_type=refresh_token" +
                    "&refresh_token=" + refreshToken;
            // 执行请求
            NetClient.getNetClient().callNet(url, new NetClient.MyCallBack() {
                @Override
                public void onFailure(int code) {
                    // 重新请求授权
                    login(activity);
                }
    
                @Override
                public void onResponse(String response) {
                    WXAccessTokenInfo info = mGson.fromJson(response, WXAccessTokenInfo.class);
                    saveAccessInfoToLocation(info);
                    // 判断是否获取成功,成功则去获取用户信息,否则提示失败
                    processGetAccessTokenResult(response);
                }
            });
    
        }
    

    processGetAccessTokenResult 处理请求返回的结果 response

        /**
         * 微信登录处理获取的授权信息结果
         *
         * @param response 授权信息结果
         */
        private void processGetAccessTokenResult(String response) {
            // 验证获取授权口令返回的信息是否成功
            if (validateSuccess(response)) {
                // 使用Gson解析返回的授权口令信息
                WXAccessTokenInfo tokenInfo = mGson.fromJson(response, WXAccessTokenInfo.class);
                // 保存信息到手机本地
                saveAccessInfoToLocation(tokenInfo);
                // 获取用户信息
                getUserInfo(tokenInfo.getAccess_token(), tokenInfo.getOpenid());
            } else {
                // 授权口令获取失败,解析返回错误信息
                WXErrorInfo wxErrorInfo = mGson.fromJson(response, WXErrorInfo.class);
                String result = String.format(Locale.ENGLISH, "processGetAccessTokenResult: Get Access Token Error. Code:%d msg:%s", wxErrorInfo.getErrcode(), wxErrorInfo.getErrmsg());
                UnityCallApi.unityLogError(TAG, result);
            }
        }
    

    WXAccessTokenInfo 是对应正确返回的类

    img

    WXErrorInfo 是对应错误返回的类

    img

    四、获取用户信息

    在登录后,发送获取用户信息请求。

        /**
         * 微信token验证成功后,联网获取用户信息
         *
         * @param access_token
         * @param openid
         */
        private void getUserInfo(String access_token, String openid) {
            String url = "https://api.weixin.qq.com/sns/userinfo?" +
                    "access_token=" + access_token +
                    "&openid=" + openid;
            NetClient.getNetClient().callNet(url, new NetClient.MyCallBack() {
                @Override
                public void onFailure(int code) {
                    UnityCallApi.unityLogError(TAG, "Get User Info Error.Code:" + code);
                    UnityCallApi.sendLoginInfoToUnity(false, "");
                }
    
                @Override
                public void onResponse(String response) {
                    UnityCallApi.unityLogInfo(TAG, "Get User Info Successful.");
                    // 发送到 Unity 进行解析
                    UnityCallApi.sendLoginInfoToUnity(true, response);
                }
            });
        }
    

    将返回信息传递给 Unity 进行解析。

    正确返回 json

    img

    错误返回 json

    img

    五、分享

    1. 文字

    img

            WXTextObject textObj = new WXTextObject();
            textObj.text = text;
    
            // 多媒体消息对象
            WXMediaMessage msg = new WXMediaMessage();
            msg.mediaObject = textObj;
            // msg.title = "Will be ignored";
            msg.description = text;
            msg.mediaTagName = "我是mediaTagName啊";
    
            SendMessageToWX.Req req = new SendMessageToWX.Req();
            // type + 时间戳
            req.transaction = buildTransaction("text");
            req.message = msg;
            req.scene = mTargetScene;
    
            WXAPI.sendReq(req);
    

    2. 图片

    img

            WXImageObject imgObj = new WXImageObject(bmp);
    
            WXMediaMessage msg = new WXMediaMessage();
            msg.mediaObject = imgObj;
            
            // bitmap 缩放到 150*150
            Bitmap thumbBmp = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
            bmp.recycle();
            // bitmap 转 二进制
            msg.thumbData = ShareUtils.bmpToByteArray(thumbBmp, true);
    
            SendMessageToWX.Req req = new SendMessageToWX.Req();
            req.transaction = buildTransaction("img");
            req.message = msg;
            req.scene = mTargetScene;
            WXAPI.sendReq(req);
    

    3. 网页

    img

        WXWebpageObject webpage = new WXWebpageObject();
        webpage.webpageUrl = "http://www.qq.com";
    
        WXMediaMessage msg = new WXMediaMessage(webpage);
        msg.title = "叮咚,群助手提醒你~";
        msg.description = "离下班还有最后一个小时了!";
    
        Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.send_img);
        Bitmap thumbBmp = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
        bmp.recycle();
        msg.thumbData = Util.bmpToByteArray(thumbBmp, true);
        
        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = buildTransaction("webpage");
        req.message = msg;
        req.scene = mTargetScene;
        api.sendReq(req);
    

    六、总结

    微信开放平台感觉目前还是缺少API文档, 比如登录的 scope 里到底有哪些作用域,就不是很明确。依靠谷歌,还是找到些线索,根据引用4描述有以下几种。

    snsapi_message:帮助你通过该应用向好友发送消息
    snsapi_userinfo:获得你的公开信息(昵称,头像等)
    snsapi_friend:寻找与你共同使用该应用的好友
    snsapi_contact:获得你的好友关系
    

    然而添加后,依旧不能申请到朋友关系,并且也不知道是通过什么接口获取的。当前目测只有和 TX 合作的应用能够申请到相关权限。替代方案为自己手动维护个关系网。分享链接,链接中包含分享用户id。有用户点击,则能判断两人为朋友关系。

    img

    当前分享应用,用户点击分享跳转应用的操作,推测也需要进行合作。替换方案为分享网页,用户点击后,引导右上角打开默认浏览器,之后就是 Android 通过浏览器起调应用了。

    img

    七、引用

    1. 资源中心是微信开放平台开发者所需所有相关资源的汇集地,包括: | 微信开放文档
    2. 如何在Unity中使用官方SDK实现微信、QQ、微博帐号登录(Android) -腾讯游戏学院
    3. android 网络请求okhttp解耦逆天封装,使用简单,扩展性强
    4. 作业部落 Cmd Markdown 编辑阅读器
  • 相关阅读:
    Java:day4
    Java:day3
    Java:day2
    Java学习的第一天
    void指针
    const* 和 *const
    指针数组和数组指针
    c++的const
    C语言基础总结
    C、C++、java中的static区别
  • 原文地址:https://www.cnblogs.com/ZeroyiQ/p/13353804.html
Copyright © 2011-2022 走看看