Facebook官方文档https://developers.facebook.com/docs/facebook-login/android
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"));
跟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);
动态申请访问权限
//权限
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);
}
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();
}
组长跟我强调了封装SDK要点有两点
a) 最大限度封装
要让用户尽量少做工作,我们尽可能地封装大部分功能
b) 保证通用性
要保证大部分用户能使用,而不是仅仅针对某一特定用户
一般来说,在入口Activity中的onCreate方法中开始初始化SDK
/**
* 初始化SDK
*/
private void initSDK() {
SDKPoxy.getInstance().init(this);
}
在初始化的时候把自身Activity传给SDK,方便接下来需要Context参数的操作
在获取SDKPoxy的实例时使用单例模式,防止参数混乱
定义一个回调口,可以让用户访问完某一个SDK接口后能接受到SDK的反馈
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);
像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);
}
由上面可以看出,实体类是使用了构造者模式,为什么要使用构造者模式
内部结构
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();
google SDK还是和facebook SDK的接入机制还是有点区别的,如:facebook中登录成功的话,它会直接发起成功的回调。但是google无论成功与否,它都是在onActivityResult中获取task,然后通过捕获异常判断是否登录成功。
需要注册开发者账号,创建应用配置appid
在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'
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可用于向自己服务器注册用户
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();
}
}
mGoogleSignInClient.silentSignIn()
.addOnCompleteListener(mContext, new OnCompleteListener<GoogleSignInAccount>() {
@Override
public void onComplete(@NonNull Task<GoogleSignInAccount> task) {
handleSignInResult(task);
}
});
}
这一周主要是对facebook登录、分享接口的熟悉和实现,同时做了一些把facebook的接口封装到自己SDK上的工作,对如何封装SDK功能和流程有了初步的了解。除此之外,还熟悉了一下google的第三方登录的实现,接下来还了解google pay的流程,但google pay的流程比想象中复杂程度还要高一点,需要花点心思继续专研。