实习周记4(facebook、google、SDK封装)

文章目录

    • 本周知识清单
    • 1. 接入FaceBook SDK
      • 1.1 自定义按钮调用Facebook登录接口
      • 1.2 使用ShareDialog自定义分享框
      • 1.3 选择系统图片
      • 1.4 选择系统视频
    • 2. 封装自己的SDK
      • 2.1 初始化SDK
      • 2.2 回调(Callback)的设计
      • 2.3 使用instanceof减少接口方法
    • 3. 使用构造者模式
    • 4. 接入google SDK
      • 4.1 在开发者平台中创建应用
      • 4.2 接入配置
      • 4.3 google SDK初始化话声明获取权限
      • 4.4登录
      • 4.5获取用户token
      • 问题总结
    • 一周小结

本周知识清单

  • 接入Facebook SDK
  • 封装自己的SDK
  • 构造者模式
  • 接入google SDK

1. 接入FaceBook SDK

Facebook官方文档https://developers.facebook.com/docs/facebook-login/android
Facebook的官方文档也有中文版的,其中步骤大体清晰,这里记录一下容易出错的点

1.1 自定义按钮调用Facebook登录接口

使用Facebook登录使用的是LoginManager这个类,在实现登录之前需要为它实现监听

LoginManager.getInstance().registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {

            }

            @Override
            public void onCancel() {

            }

            @Override
            public void onError(FacebookException error) {

            }
        });

然后调用登录方法

    LoginManager.getInstance().logInWithReadPermissions(mContext, Arrays.asList("email"));

1.2 使用ShareDialog自定义分享框

跟LoginManager一样,需要对ShareDialog注册监听

shareDialog.registerCallback(mCallbackManager, new FacebookCallback<Sharer.Result>() {
            @Override
            public void onSuccess(Sharer.Result result) {
            }

            @Override
            public void onCancel() {
            }

            @Override
            public void onError(FacebookException error) {
            }
        });

封装要分享的内容,使用ShareContent的子类,如分享图片

        Bitmap bitmap = BitmapFactory.decodeFile(uri);
        SharePhoto sharePhoto = new SharePhoto.Builder()
                .setBitmap(bitmap)
                .build();
        SharePhotoContent sharePhotoContent = new SharePhotoContent
                .Builder()
                .addPhoto(sharePhoto)
                .build();
        mShareDialog.show(sharePhotoContent);

1.3 选择系统图片

动态申请访问权限

//权限
private String[] allPermissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
    ...
if (VERSION.SDK_INT>= 23) {
        ActivityCompat.requestPermissions(this,allPermissions,1);
}

打开系统相册

 Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                startActivityForResult(intent,IMAGE);

在onActivityResult()中获取图片路径

  if(resultCode == Activity.RESULT_OK && data != null) {
        Uri selectedImages = data.getData();
        String[] filePathColumns = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(selectedImages, filePathColumns, null, null, null);
        cursor.moveToFirst();
        int columnIndex = cursor.getColumnIndex(filePathColumns[0]);
        String imagePath = cursor.getString(columnIndex);
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
    }

1.4 选择系统视频

Intent intent1 = new Intent(Intent.ACTION_PICK,MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent1,VIDEO);

在onActivityResult()中获取视频Uri

if (resultCode == Activity.RESULT_OK && data != null) {
        showToastUtils(data.getDataString());
        logsUtils(data.getDataString());
        Uri uri = data.getData();
}

2. 封装自己的SDK

组长跟我强调了封装SDK要点有两点

  • a) 最大限度封装
    要让用户尽量少做工作,我们尽可能地封装大部分功能

  • b) 保证通用性
    要保证大部分用户能使用,而不是仅仅针对某一特定用户

2.1 初始化SDK

一般来说,在入口Activity中的onCreate方法中开始初始化SDK

    /**
     * 初始化SDK
     */
    private void initSDK() {
        SDKPoxy.getInstance().init(this);
    }

在初始化的时候把自身Activity传给SDK,方便接下来需要Context参数的操作
在获取SDKPoxy的实例时使用单例模式,防止参数混乱

2.2 回调(Callback)的设计

定义一个回调口,可以让用户访问完某一个SDK接口后能接受到SDK的反馈

  • 在onCreate新建一个实例
 mSDKCallBack = new SDKCallBack() {
            @Override
            public void onResult(MySDKResult mySDKResult) {
                switch (mySDKResult.getCode()) {
                    case StatusCode.SDK_INIT_SUCCESS:
                        showToastUtils("初始化成功");
                        break;
                    case StatusCode.SDK_INTT_FAIL:
                        showToastUtils("初始化失败");
                        break;
                    case StatusCode.SDK_LOGIN_SUCCESS:
                        showToastUtils("登录成功");
                        break;
                    case StatusCode.SDK_LOGIN_FAIL:
                        showToastUtils("登录失败");
                        break;
                    case StatusCode.SDK_SHARE_SUCCESS:
                        showToastUtils("分享成功");
                        break;
                    case StatusCode.SDK_SHARE_FAIL:
                        showToastUtils("分享失败");
                        break;
                    default:
                        break;
                }
            }
        };
  • 初始化
SDKPoxy.getInstance().init(this,mSDKCallBack);

接下来使用SDK的功能的时候就可以实现数据的回调了,如登录完成后,在SDK中执行

MySDKResult result = new MySDKResult();
result.setCode(StatusCode.SDK_LOGIN_SUCCESS);
mSDKCallBack.onResult(result);

2.3 使用instanceof减少接口方法

像facebook中图片分享和视频分享是类似的,调用同样的接口,只是用了不同的参数,这样也就避免了使用多个接口,而其中辨识不同的类就需要用到instanceof

  • 第一步,定义一个父类或者接口,我这里定义接口
public interface SDKShareContent {
}
  • 第二步,定义图片和视频的实体类
public class SDKSharePhotoContent implements SDKShareContent {
    private Bitmap bitmap;

    public SDKSharePhotoContent(Builder builder){
        this.bitmap = builder.bitmap;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public static class Builder {
        private Bitmap bitmap;
        public Builder() {

        }
        public Builder addBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
            return this;
        }
        public SDKSharePhotoContent build() {
            return new SDKSharePhotoContent(this);
        }
    }
}



public class SDKShareVideoContent implements SDKShareContent {
    private Uri uri;

    public SDKShareVideoContent(Builder builder){
        this.uri = builder.uri;
    }

    public Uri getUri() {
        return uri;
    }

    public static class Builder {
        private Uri uri;
        public Builder() {

        }
        public Builder addUri(Uri uri) {
            this.uri = uri;
            return this;
        }
        public SDKShareVideoContent build() {
            return new SDKShareVideoContent(this);
        }
    }
}

  • 第三步,对接口接收到的不同子类进行分类
        if(sdkShareContent instanceof SDKSharePhotoContent) {
            SDKSharePhotoContent sdkSharePhotoContent = (SDKSharePhotoContent) sdkShareContent;
            SharePhoto sharePhoto = new SharePhoto.Builder()
                    .setBitmap(sdkSharePhotoContent.getBitmap())
                    .build();
            SharePhotoContent sharePhotoContent = new SharePhotoContent
                    .Builder()
                    .addPhoto(sharePhoto)
                    .build();
            mShareDialog.show(sharePhotoContent);
        } else if(sdkShareContent instanceof SDKShareVideoContent) {
            SDKShareVideoContent sdkShareVideoContent = (SDKShareVideoContent) sdkShareContent;
            ShareVideo shareVideo = new ShareVideo.Builder()
                    .setLocalUrl(sdkShareVideoContent.getUri())
                    .build();
            ShareVideoContent shareVideoContent = new ShareVideoContent.Builder()
                    .setVideo(shareVideo)
                    .build();
            mShareDialog.show(shareVideoContent);
        }

3. 使用构造者模式

由上面可以看出,实体类是使用了构造者模式,为什么要使用构造者模式

  • 方便用户创建复杂的对象
  • 代码复用性和封装性

内部结构

public class SDKSharePhotoContent implements SDKShareContent {
    private Bitmap bitmap;

    public SDKSharePhotoContent(Builder builder){
        this.bitmap = builder.bitmap;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public static class Builder {
        private Bitmap bitmap;
        public Builder() {

        }
        public Builder addBitmap(Bitmap bitmap) {
            this.bitmap = bitmap;
            return this;
        }
        public SDKSharePhotoContent build() {
            return new SDKSharePhotoContent(this);
        }
    }
}

实例的创建

                  SDKShareVideoContent sdkShareVideoContent = new SDKShareVideoContent.Builder()
                            .addUri(uri)
                            .build();

4. 接入google SDK

google SDK还是和facebook SDK的接入机制还是有点区别的,如:facebook中登录成功的话,它会直接发起成功的回调。但是google无论成功与否,它都是在onActivityResult中获取task,然后通过捕获异常判断是否登录成功。

4.1 在开发者平台中创建应用

需要注册开发者账号,创建应用配置appid

4.2 接入配置

在AndroidManifest中添加

        
        <meta-data
            android:name="com.google.android.gms.games.APP_ID"
            android:value="@string/google_app_id" />

在value->strings.xml中添加

    
    
    <string name="google_app_id">xxxxstring>
    <string name="server_client_id">xxxxxstring>

在gradle中导入

    //google接入
    implementation 'com.google.android.gms:play-services-auth:15.0.1'

4.3 google SDK初始化话声明获取权限

 GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestEmail()
                .requestIdToken(appId)
                .build();
        mGoogleSignInClient = GoogleSignIn.getClient(mContext,gso);

如GoogleSignInOptions.DEFAULT_SIGN_IN表示使用google账号进行登录,requestEmail声明获取邮箱,requestIdToken获取用户token,该token可用于向自己服务器注册用户

4.4登录

        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        mContext.startActivityForResult(signInIntent,RC_SIGN_IN);

跳转到账户选择并授权页面,该操作完成后在onActivityResult中获取数据

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        logUtil(data.toString());
        if (requestCode == RC_SIGN_IN) {
            Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
            handleSignInResult(task);
        }
    }

在handleSignInResult()中对异常的捕获和用户信息的获取

    private void handleSignInResult(Task task) {
        try {
            GoogleSignInAccount account = task.getResult(ApiException.class);
            showToast("登录成功 账号:"+account.getEmail());
            if (account.getIdToken() != null) {
                showToast(account.getIdToken());
            }
//            showToast(account.getEmail());
        } catch (ApiException e) {
            Log.w(TAG,"------------------------------------------------------ signInResult:fail code = "+e.getStatusCode()+" -------------------------------------------------------");
            showToast("登录失败 错误信息:"+e.getMessage());
            Dialog dialog = GoogleApiAvailability.getInstance().getErrorDialog(mContext,e.getStatusCode(),RC_SIGN_IN);
            dialog.show();
        }
    }

4.5获取用户token

            mGoogleSignInClient.silentSignIn()
                    .addOnCompleteListener(mContext, new OnCompleteListener<GoogleSignInAccount>() {
                        @Override
                        public void onComplete(@NonNull Task<GoogleSignInAccount> task) {
                            handleSignInResult(task);
                        }
                    });
            }

问题总结

  • [1] google 错误码 https://developers.google.com/android/reference/com/google/android/gms/common/api/CommonStatusCodes
    signInResult:fail code = 4 重新登录

一周小结

这一周主要是对facebook登录、分享接口的熟悉和实现,同时做了一些把facebook的接口封装到自己SDK上的工作,对如何封装SDK功能和流程有了初步的了解。除此之外,还熟悉了一下google的第三方登录的实现,接下来还了解google pay的流程,但google pay的流程比想象中复杂程度还要高一点,需要花点心思继续专研。

你可能感兴趣的:(实习随笔)