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

1,809

ZeroyiQ:Unity 多平台原生SDK接入速览(一):微信开放平台

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

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

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

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

一、前言

由于该系列为了能适应更多的引擎,因此主要还是选择了对于原生SDK的研究。这里接入的就是 Facebook-Android-SDK。

如果只是 Unity 平台接入 Facebook 的话,还是推荐直接使用官方提供的 Unity特供版本,使用起来简单易上手。

Facebook 开发平台可以直接通过 Facebook 账户直接登录,当前(2020-7-1)由于新冠疫情,个人开发者验证已暂停,只能进行企业认证。但是依旧能够创建应用,获取应用编号。

在创建应用并且添加 Facebook 登录的产品后,能够选快速启动的教程,官方提供了很完善的引导。官方项目中也包含较完善的 Demo。

img

二、SDK 接入

1. 添加包管理仓库

在工程的 build.gradle (Project) 文件中增加 mavenCentral。

buildscript {
    repositories {
        mavenCentral()
    }
}

img

2. 添加依赖

在需要接入SDK的模块的 build.gradle (Module: app) 文件中增加依赖,之后编译即可使用。

dependencies {
    implementation 'com.facebook.android:facebook-android-sdk:[4,5)'
}

img

3. 后台设置秘钥散列

Facebook 为了验证应用的请求的正确性,保证之间的安全,还需要提供开发环境或者发布后的秘钥散列。

生成秘钥依赖如下:

开发环境

如果环境没有配置好,keytool 和 openssl 都可以使用绝对路径,xxx\openssl-0.9.8e_X64\bin\openssl.exe。

keytool -exportcert -alias androiddebugkey -keystore "C:\Users\USERNAME\.android\debug.keystore" | "PATH_TO_OPENSSL_LIBRARY\bin\openssl" sha1 -binary | "PATH_TO_OPENSSL_LIBRARY\bin\openssl" base64

发布后

YOUR_RELEASE_KEY_ALIAS 密钥别名

YOUR_RELEASE_KEY_PATH 密钥路径

keytool -exportcert -alias YOUR_RELEASE_KEY_ALIAS -keystore YOUR_RELEASE_KEY_PATH | openssl sha1 -binary | openssl base64

4. 设置 Android 相关配置

AndroidManifest.xml 中申请网络权限。

res\values\strings.xml (没有该文件,自己创建一个)中添加 AppId 和登录 scheme。

<string name="facebook_app_id">561412404767402</string> 
<!--scheme 格式为 fb + appid-->
<string name="fb_login_protocol_scheme">fb561412404767402</string>

AndroidManifest.xml 中添加 meta-data 和 activity 的配置。

        <meta-data
            android:name="com.facebook.sdk.ApplicationId"
            android:value="@string/facebook_app_id" />

        <activity
            android:name="com.facebook.FacebookActivity"
            android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
            android:label="@string/app_name" />
        <activity
            android:name="com.facebook.CustomTabActivity"
            android:exported="true">
            <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="@string/fb_login_protocol_scheme" />
            </intent-filter>
        </activity>

5. 实现回调

创建 callbackManager,以便处理回调,可以在 OnCreate 的时候调用如下代码进行初始化。

callbackManager = CallbackManager.Factory.create();

在登录和分享中,都可以调用各自的 registerCallback ,传入初始化后的 callbackManager 和 FacebookCallback 接口,并实现 FacebookCallback 中的回调方法。

三、登录

1. 添加依赖

build.gradle 中添加。

implementation 'com.facebook.android:facebook-login:[5,6)'

2. 发起请求

Facebook 提供一个 LoginButton 的登录元素,直接添加到界面。通过点击按钮就能直接发起登录请求。还有一种就是自定义按钮,通过登录 Manager,主动发起登录请求,这里演示两种方式登录。

2.a Facebook 原生按钮

布局 xml 中添加。

<com.facebook.login.widget.LoginButton
    android:id="@+id/login_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="30dp"
    android:layout_marginBottom="30dp" /> 

直接通过 LoginButton 绑定回调,也能通过登录 Manager,两种方式二选一,具体见下文自定义按钮。

    loginButton = (LoginButton) findViewById(R.id.login_button);
    // 设置登录用户域
    loginButton.setReadPermissions("email");
    // 如果 loginbutton 放在一个 fragment 中需要添加如下
    loginButton.setFragment(this);    

    // LoginButton 注册 callback
    loginButton.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
        @Override
        public void onSuccess(LoginResult loginResult) {
            // App code
        }

        @Override
        public void onCancel() {
            // App code
        }

        @Override
        public void onError(FacebookException exception) {
            // App code
        }
    });

2.b 自定义按钮(Unity 中触发)

直接通过 logInWithReadPermissions 方法触发登录请求。

    public void login(Activity activity) {
        // 判断当前 accessToken 是否已经存在(已登录)并且 accessToken 未过期
        if (!AccessToken.isCurrentAccessTokenActive()) {
            LoginManager.getInstance().registerCallback(callbackManager,
                    new FacebookCallback<LoginResult>() {
                        @Override
                        public void onSuccess(LoginResult loginResult) {
                            UnityCallApi.unityLogInfo(TAG, "Login successful.");
                            getUserInfo();
                        }

                        @Override
                        public void onCancel() {
                            UnityCallApi.unityLogInfo(TAG, "Login cancel.");

                        }

                        @Override
                        public void onError(FacebookException error) {
                            error.printStackTrace();
                            UnityCallApi.unityLogError(TAG, "Login Error" + error.toString());
                        }
                    });
            // 主动发起请求,public_profile 为基本用户域
            LoginManager.getInstance().logInWithReadPermissions(activity, Arrays.asList("public_profile"));
        }
    }

3. onActivityResult 中传递结果

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        callbackManager.onActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }

四、获取用户信息

    private void getUserInfo() {
        // 个人 me 请求,注册请求回调
        GraphRequest request = GraphRequest.newMeRequest(
                AccessToken.getCurrentAccessToken(),
                new GraphRequest.GraphJSONObjectCallback() {
                    @Override
                    public void onCompleted(JSONObject object, GraphResponse response) {
                        FacebookRequestError error = response.getError();
                        if (null != object && TextUtils.isEmpty(error.getErrorMessage())) {
                            UnityCallApi.unityLogInfo(TAG, "Get User Info Successful.");
                            UnityCallApi.sendLoginInfoToUnity(true, object.toString());
                        } else {
                            UnityCallApi.unityLogError(TAG, String.format("Get User Info Failed.Code: %s Msg: %s", error.getErrorCode(), error.getErrorMessage()));
                            UnityCallApi.sendLoginInfoToUnity(false, "");
                        }
                    }
                }
        );
        Bundle parameters = new Bundle();
        // 设置请求字段 
        parameters.putString("fields", "id,name,gender");
        request.setParameters(parameters);
        request.executeAsync();
    }

五、分享

Build.gradle 中添加依赖

implementation 'com.facebook.android:facebook-share:[5,6)'

设置分享回调,可以和 2.5 设置回调一起初始化。

            shareDialog = new ShareDialog(activity);
            shareDialog.registerCallback(callbackManager, new FacebookCallback<Sharer.Result>() {
                @Override
                public void onSuccess(Sharer.Result result) {
                    UnityCallApi.unityLogInfo(TAG, "Share Successful.");
                }

                @Override
                public void onCancel() {
                    UnityCallApi.unityLogInfo(TAG, "Share Cancel.");
                }

                @Override
                public void onError(FacebookException error) {
                    UnityCallApi.unityLogError(TAG, String.format("Share Error.Msg:%s", error.toString()));
                }
            });

1. 网页

    public void shareWebLink(Bundle params) {
        if (ShareDialog.canShow(ShareLinkContent.class)) {
            ShareLinkContent content = new ShareLinkContent.Builder()
                    .setContentUrl(Uri.parse("网页"))
                    .build();
            shareDialog.show(content);
        }
    }

img

2. 图片

分享图片或者视频的时候,必须要在 AndroidManifest.xml 中配置 provider, authorities 格式为 com.facebook.app.FacebookContentProvider+appid。

        <provider
            android:name="com.facebook.FacebookContentProvider"
            android:authorities="com.facebook.app.FacebookContentProvider{APPID}"
            android:exported="true" />

分享代码

    public void shareImage(Bundle params) {
        if (ShareDialog.canShow(SharePhotoContent.class)) {
            SharePhoto photo = new SharePhoto.Builder()
                    .setBitmap(bitmap)  // bitmap 图片
                    .build();
            SharePhotoContent content = new SharePhotoContent.Builder()
                    .addPhoto(photo)
                    .build();
            shareDialog.show(content);
        }
    }

img

六、总结

Facebook 对于 accessToken 封装的很好,可以直接通过静态方法直接进行判断,使用起来比较规整。对于申请用户域,Facebook 有套很完整的规则文档

七、引用

  1. Facebook 官方快速入门
  2. Android开发-FaceBook 登录集成
  3. facebook/facebook-android-sdk