Android 友盟社会化分享的集成与封装
分享是APP中非常高频的操作之一,在Android的开发环境中,第三方分享框架也有很多。比较流行的包括 极光社会化分享(sharesdk)、友盟社会化分享(Ushare)、Mob社会化分享以及在githut上比较流行的 ShareSDK、ShareUtil、BiliShare、ShareLoginLib等等。 本篇文章基于友盟ShareSDK,介绍了其集成过程并对其进行模块化封装,以便在项目中更快捷的集成和使用。
集成前准备
获取友盟Appkey
在【友盟+】官网注册并且添加新应用,获得Appkey。友盟后台的应用名与实际应用名和包名无关,建议命名为应用名+平台(iOS/Android),Android和IOS两个平台不能进行共用,需要进行分开。 建议使用企业邮箱注册,避免使用个人邮箱注册。
三方账号申请
因为涉及到和各个分享平台的交互,所以在集成前需要在各个平台创建应用并提交审核。创建应用后,分享、登录操作时显示的应用icon、名称和对应开放平台设置有关,必须要创建应用的平台为:微信、新浪、QQ、Facebook、Kakao、LinkeIn、Twitter、钉钉。
目前集成的内容只包含微信、新浪、QQ三个平台,申请渠道如下:
| 平台 | 申请地址 | | :----------: | :------------------------: | | 微信开放平台 | http://open.weixin.qq.com/ | | QQ互联平台 | http://connect.qq.com/ | | 微博开放平台 | http://open.weibo.com |
注1:申请QQ登录一定要在QQ互联平台,不是在QQ开放平台(open.qq.com)
注2:申请过程中可能需要企业相关资质,如法人身份证、营业执照、税务登记证等,需要提前准备好。
注3:提交应用申请时,需要提交应用相关的信息(应用名称、介绍、图标、截图、授权回调域等),微信还需要提交《微信开放平台网站信息登记表》。
注4: 建议大家用企业账号申请第三方开放平台,不要使用个人的QQ、微信、微博和邮箱进行申请,这样可以避免申请人在职位变动或者离职后,导致账号管理的风险和交接麻烦。
注5:开放平台申请多数需要审核流程,因此在项目开始时,建议首先申请开放平台账号和创建应用,以免申请时间长影响开发进度。
申请很复杂,所以一般直接找产品经历要就好啦(#笑)。如果是练习用的话,可以直接用官方Demo的包名com.umeng.soexample创建一个示例项目,沿用签名文件、Appkey以及各个三方的Appkey就好了。
集成友盟sdk
在后期模块化后,直接引入封装好的模块即可集成,这里先介绍一下通过添加sdk的方式集成,以及其中我遇到过的问题。
添加SDK
解压下载的sdk压缩包,我们需要用到其中common和share文件夹下的内容。res顾名思义都是资源文件,复制到项目的res目录中,其他所有jar文件复制到项目的app/libs目录下,并确保在app的gradle依赖中包含implementation fileTree(include: ['*.jar'], dir: 'libs') ,将这个文件夹中所有jar包导入到项目里。
添加回调Activity
用到的三个平台中,只有微信需要手动添加Activity。具体做法是,在包名目录下创建wxapi文件夹,新建一个名为
WXEntryActivity
的Activity继承WXCallbackActivity
,内容为空即可。配置Android Manifest XML
在项目的Manifest中添加各个回调Activity:
<activity android:name=".wxapi.WXEntryActivity" android:configChanges="keyboardHidden|orientation|screenSize" android:exported="true" android:theme="@android:style/Theme.Translucent.NoTitleBar" /> <activity android:name="com.umeng.socialize.media.WBShareCallBackActivity" android:configChanges="keyboardHidden|orientation" android:exported="false" android:theme="@android:style/Theme.Translucent.NoTitleBar"></activity> <activity android:name="com.sina.weibo.sdk.web.WeiboSdkWebActivity" android:configChanges="keyboardHidden|orientation" android:exported="false" android:windowSoftInputMode="adjustResize"></activity> <activity android:name="com.sina.weibo.sdk.share.WbShareTransActivity" android:launchMode="singleTask" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"> <intent-filter> <action android:name="com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name="com.tencent.tauth.AuthActivity" android:launchMode="singleTask" android:noHistory="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="tencent100424468" /> </intent-filter> </activity> <activity android:name="com.tencent.connect.common.AssistActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:theme="@android:style/Theme.Translucent.NoTitleBar" />
其中qq的appkey需要替换成自己申请的appkey,这里用的是友盟官方demo的以作测试用。
添加权限
在AndroidManifest中添加以下权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
如果项目的目标sdk版本高于Android6.0,即 targetSdkVersion >= 23 ,还需要在项目中添加权限的动态申请:
if (Build.VERSION.SDK_INT >= 23) { String[] mPermissionList = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CALL_PHONE, Manifest.permission.READ_LOGS, Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.SET_DEBUG_APP, Manifest.permission.SYSTEM_ALERT_WINDOW, Manifest.permission.GET_ACCOUNTS, Manifest.permission.WRITE_APN_SETTINGS}; ActivityCompat.requestPermissions(this, mPermissionList, 123); }
初始化设置
将前面的都集成之后,接下来就可以正式进入到代码层面的操作啦。在Application中调用友盟的初始化接口,以及设置各个平台的appkey,这里全部用的是官方demo测试用的appkey。
public class App extends Application { @Override public void onCreate() { super.onCreate(); //初始化组件化基础库, 统计SDK/推送SDK/分享SDK都必须调用此初始化接口 UMConfigure.init(this, "5bed13e1f1f5564655000404", "Umeng", UMConfigure.DEVICE_TYPE_PHONE, "d9352161be267fb40fb12ad5eb04edf9"); UMShareAPI.get(this); } //各个平台的配置 { //微信 PlatformConfig.setWeixin("wxdc1e388c3822c80b", "3baf1193c85774b3fd9d18447d76cab0"); //新浪微博(第三个参数为回调地址) PlatformConfig.setSinaWeibo("3921700954", "04b48b094faeb16683c32669824ebdad", "http://sns.whalecloud.com"); //QQ PlatformConfig.setQQZone("100424468", "c7394704798a158208a74ab60104f0ba"); PlatformConfig.setYixin("yxc0614e80c9304c11b0391514d09f13bf"); PlatformConfig.setTwitter("3aIN7fuF685MuZ7jtXkQxalyi", "MK6FEYG63eWcpDFgRYw4w9puJhzDl0tyuqWjZ3M7XJuuG7mMbO"); PlatformConfig.setAlipay("2015111700822536"); PlatformConfig.setLaiwang("laiwangd497e70d4", "d497e70d4c3e4efeab1381476bac4c5e"); PlatformConfig.setPinterest("1439206"); PlatformConfig.setKakao("e4f60e065048eb031e235c806b31c70f"); PlatformConfig.setDing("dingoalmlnohc0wggfedpk"); PlatformConfig.setVKontakte("5764965", "5My6SNliAaLxEm3Lyd9J"); PlatformConfig.setDropbox("oz8v5apet3arcdy", "h7p2pjbzkkxt02a"); } }
添加签名文件
部分平台在申请appkey的时候就需要项目的签名文件,如果没有的话会影响授权。常规项目只要正常申请好签名就可以了,这里讲一下作为测试用例如何添加签名。
将官方demo中app目录下的debug.keystore复制到自己的练习项目app目录下
在项目的app.gradle依赖中android条目下添加如下代码:
buildTypes { release { // 是否进行混淆 minifyEnabled false // 签名文件 signingConfig signingConfigs.debug // 混淆文件的位置 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { minifyEnabled false signingConfig signingConfigs.debug proguardFiles 'proguard-rules.pro' } } signingConfigs { debug { storeFile file('debug.keystore') storePassword "android" keyAlias "androiddebugkey" keyPassword "android" } }
开始分享
终于到了分享的步骤,是骡子是马要牵出来溜溜看看能不能走了,先介绍一下友盟分享的两种形式:
使用分享面板的分享,用户可以调用我们的打开分享面板的方法,点击分享面板的对应平台进行分享。
不使用分享面板的分享,用户可以自己写分享按钮,或者触发事件,然后调用我们的分享方法,进行分享。简而言之,直接分享就是在用户自己的界面组件中插入分享行为,分享面板是打开我们写好的一个界面组件,根据点击事件进行分享。
可分享的内容包括:
平台 | 授权 | 可分享内容 | 用户信息 |
---|---|---|---|
是 | 文本 图片 链接 视频 音乐 | 是 | |
qq空间 | 同qq | 文字(说说) 图片(说说) 链接 视频 音乐 | 同qq |
微信 | 是 | 文本 图片 链接 视频 音乐 | 是 |
微信朋友圈 | 同微信 | 文本 图片 链接 视频 音乐 (分享链接不显示描述) | 同微信 |
微信收藏 | 同微信 | 文本 图片 链接 视频 音乐 文件 | 同微信 |
新浪微博 | 是 | 文本 图片 链接 视频 音乐 文件 | 是 |
下面以直接分享一个网站到微信平台为例,看一下具体的代码实现:
UMWeb web = new UMWeb("https://gank.io/");//创建要分享的Web对象,传入分享的url地址
web.setTitle("测试分享标题");//设置标题
web.setThumb(new UMImage(this, R.drawable.thumb));//设置传入显示的缩略图
web.setDescription("测试分享内容测试分享内容测试分享内容测试分享内容测试分享内容");//设置描述
new ShareAction(ShareDetailActivity.this)//开启分享
.withMedia(web) //填入创建好的分享内容
.setPlatform(SHARE_MEDIA.WEIXIN)//选择分享平台
.setCallback(shareListener)//设置对分享返回结果的监听
.share();//启动分享操作
实现效果如下:
以面板的形式分享这个网站:
ShareBoardlistener boardListener = new ShareBoardlistener() { //创建面板的监听器
@Override
public void onclick(SnsPlatform snsPlatform, SHARE_MEDIA platform) {
new ShareAction(mActivity)//开启分享
.withMedia(web) //填入创建好的分享内容
.setPlatform(platform)//填入选择的平台
.setCallback(shareListener)//设置对分享返回结果的监听
.share();//启动分享操作
}
};
new ShareAction(mActivity)
.setDisplayList(SHARE_MEDIA.WEIXIN, SHARE_MEDIA.WEIXIN_CIRCLE, SHARE_MEDIA.WEIXIN_FAVORITE,
SHARE_MEDIA.SINA, SHARE_MEDIA.QQ, SHARE_MEDIA.QZONE)//设置分享面板显示的平台
.setShareboardclickCallback(boardListener)//添加之前创建的面板监听器
.open(config);//开启分享面板 其中可传入能对面板样式进行自定义操作的config对象
//比如设置面板在底部还是中部显示、是否有取消按钮、图标形状、字体大小和背景颜色等等
默认居中的面板效果如下:
最后不要忘记在调用分享的Activity中,添加如下代码,关闭监听,防止内存泄露等问题。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
UMShareAPI.get(this).onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onDestroy() {
super.onDestroy();
UMShareAPI.get(this).release();
}
对友盟分享的模块化和代码封装
在前面的内容里,我们完整的体验了友盟社会化分享的准备、集成和使用,虽然看起来比较简单明了,但对于初次接触的人还是比较容易碰到问题的,在项目中多次集成也比较繁琐。所以接下来到了本文的重点内容,对友盟的分享进行模块化,在需要时直接导入模块即可使用,并将主要方法进行封装处理,在开发时提高效率,减少代码量。
下面记录一下封装过程中的思路和过程,以及封装后的使用方法。
封装过程
在前面的使用介绍中,可以看到每一次分享,都需要提前创建好待分享的内容,需要传入的数据越多,代码行数越多,所以首先对待创建的对象进行封装。
根据分享的类型,分了8个类:
| 分享类型 | 枚举参数 | | :--------: | :-------------------: | | 仅文本 | SHARE_TYPE_TEXT | | 仅图片 | SHARE_TYPE_IMAGE | | 图文 | SHARE_TYPE_TEXTANDIMG | | 网址 | SHARE_TYPE_WEB | | 网络视频 | SHARE_TYPE_VIDEO | | 网络音乐 | SHARE_TYPE_MUSIC | | GIF表情 | SHARE_TYPE_EMOJI | | 微信小程序 | SHARE_TYPE_MINAPP |
其中图片可以通过资源文件id、File文件、网络地址、Bitmap对象、还有byte[]这些方式来创建,其他很多类型虽然不需要图片,但很多需要缩略图,而缩略图的本质也是图片,所以可以将缩略图独立出来:
private static UMImage mThumb; //创建待分享所用的缩略图 public static void createThumbImage(int thumbResId) { mThumb = new UMImage(mActivity, thumbResId); } public static void createThumbImage(File thumbFile) { mThumb = new UMImage(mActivity, thumbFile); } public static void createThumbImage(String thumbImageUrl) { mThumb = new UMImage(mActivity, thumbImageUrl); } public static void createThumbImage(Bitmap thumbBitmap) { mThumb = new UMImage(mActivity, thumbBitmap); } public static void createThumbImage(byte[] thumbBytes) { mThumb = new UMImage(mActivity, thumbBytes); }
接下来以创建待分享的网址为例,需要传入的数据包括视频地址、标题、描述、缩略图最少4个内容,前三个都是String对象,最复杂的缩略图已经通过上面的方法创建好了,所以只需要添加前三个内容就够了:
private static UMWeb mWeb; //创建待分享的链接 需要缩略图 public static void createUrl(String url, String title, String description) { mWeb = new UMWeb(url); mWeb.setTitle(title); mWeb.setDescription(description); mWeb.setThumb(mThumb);//传入创建好的缩略图 }
到此我们已经创建好了需要分享的网址对象,接下来自然而然就是分享了。实现非常简单粗暴,直接传入需要分享的平台和类型对应的枚举名即可:
public static void share(SHARE_MEDIA platform, SHARE_TYPE shareType) { ShareAction action = new ShareAction(mActivity) .setPlatform(platform).setCallback(mListener); switch (shareType) { case SHARE_TYPE_TEXT: action.withText(mText).share(); break; case SHARE_TYPE_IMAGE: action.withMedia(mImage).share(); break; case SHARE_TYPE_TEXTANDIMG: action.withText(mText).withMedia(mImage).share(); break; case SHARE_TYPE_WEB: action.withMedia(mWeb).share(); break; case SHARE_TYPE_VIDEO: action.withMedia(mVideo).share(); break; case SHARE_TYPE_MUSIC: action.withMedia(mMusic).share(); break; case SHARE_TYPE_EMOJI: action.withMedia(mEmoji).share(); break; case SHARE_TYPE_MINAPP: action.withMedia(mMinAPP).share(); break; } }
到这里基本将直接分享的内容进行了封装,那么如果用户不需要我们已经设好的分享callback,想要自定义呢?所以需要构建一个方法让用户传入自定义的监听器:
//设置分享监听 传入自定义的监听方式 public static void setShareListener(UMShareListener listener) { mListener = listener; }
除了直接分享,还有面板形式的分享,在这里提供了三种形式的封装:
直接在中间显示分享面板
public static void shareBoardAtCenter(SHARE_TYPE shareType) { ShareBoardConfig config = new ShareBoardConfig(); config.setShareboardPostion(ShareBoardConfig.SHAREBOARD_POSITION_CENTER); config.setCancelButtonVisibility(true); shareBoard(config, shareType); }
在底部显示分享面板
public static void shareBoardAtBottom(SHARE_TYPE shareType) { ShareBoardConfig config = new ShareBoardConfig(); config.setShareboardPostion(ShareBoardConfig.SHAREBOARD_POSITION_BOTTOM); config.setCancelButtonVisibility(true); shareBoard(config, shareType); }
用户自定义分享面板
public static void setBoardWithConfig(ShareBoardConfig config, SHARE_TYPE shareType) { shareBoard(config, shareType); }
三种方式,最终都是将面板定义 config 以及要分享的类型 shareType 传入 shareBoard() 方法中,config的具体自定义方法见官方文档,下面贴出 shareBoard() 方法:
private static void shareBoard(ShareBoardConfig config, final SHARE_TYPE shareType) { ShareBoardlistener boardListener = new ShareBoardlistener() { @Override public void onclick(SnsPlatform snsPlatform, SHARE_MEDIA platform) { share(platform, shareType); } }; new ShareAction(mActivity) .setDisplayList(SHARE_MEDIA.WEIXIN, SHARE_MEDIA.WEIXIN_CIRCLE, SHARE_MEDIA.WEIXIN_FAVORITE, SHARE_MEDIA.SINA, SHARE_MEDIA.QQ, SHARE_MEDIA.QZONE) .setShareboardclickCallback(boardListener) .open(config); }
可以看出在选择平台后,具体分享的实现方法又跳转到了share()中,实现了代码的复用,对于用户需要自定义面板分享哪些平台,同样可以通过setPlatforms()方法实现:
//设置分享面板要分享的内容 public static void setBoardPlatforms(SHARE_MEDIA[] platforms) { mPlatforms = platforms; }
使用方法
整体的使用比较简单明了,不过需要注意的是,除了分享文本图片,其他类型都要记得先创建缩略图。
下面以分享一个网址或者视频为例:
//首先需要初始化 UShareUtils.init(this); //创建缩略图 UShareUtils.createThumbImage(R.drawable.cat80); //创建待分享的网址,传入网址、标题和描述 UShareUtils.createUrl("https://gank.io/", "分享标题", "分享描述"); //创建待分享的视频,传入视频地址、标题和描述 UShareUtils.createVideo("http://vfx.mtime.cn/Video/2018/10/26/mp4/181026140242572417.mp4", "《驯龙高手3》中文预告", "《驯龙高手3 》由梦工场动画出品,驯龙高手是影史上最受欢迎的动画系列之一,而这部备受期待的新片是该系列的第三部。 "); //点击分享 @OnClick({R.id.tv_share_web, R.id.tv_share_video, R.id.tv_share_board, R.id.tv_share_center}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.tv_share_web: //分享WEB到QQ UShareUtils.share(SHARE_MEDIA.QQ, SHARE_TYPE.SHARE_TYPE_WEB); break; case R.id.tv_share_video: //分享视频到微信 UShareUtils.share(SHARE_MEDIA.WEIXIN, SHARE_TYPE.SHARE_TYPE_VIDEO); break; case R.id.tv_board_board: //打开底部分享面板 分享WEB UShareUtils.shareBoardAtBottom(SHARE_TYPE.SHARE_TYPE_WEB); break; case R.id.tv_board_center: //打开中部分享面板 分享视频 UShareUtils.shareBoardAtBottom(SHARE_TYPE.SHARE_TYPE_VIDEO); break; } }