一、 分享吐槽
分享真的是做一次就要重新看一次文檔,慢慢整理一份,微信QQ的文檔還好說,微博文檔我都懷疑自己穿越了。所以打算整理一份分享框架,只寫最后一次。
二、 分享到哪
微信/QQ/抖音/微博/快手(快手暫時還不支持)
三、 分享類型
文本分享/圖片分享/視頻分享/網(wǎng)頁分享
四、 一臺沒有感情的分享機(jī)器
就我列舉出來的分享方法,兩兩組合已經(jīng)有20個方法了,還不包括后續(xù)擴(kuò)展的。當(dāng)然可以整理一個下面的工具類:
public class ShareUtils {
public static void shareWxText(Activity activity, String title, String description, String linkUrl, String imageUrl){
}
public static void shareWxImage(){
}
public static void shareQQText(){
}
public static void shareQQImage(){
}
}
但是這種工具類根本就不是最后一次寫啊。
1.首先每個方法的入?yún)⒍己芏啵谋痉窒砭陀?個參數(shù)了,還不加上回調(diào);
2.微博的sdk接入簡直反人類,需要在每個喚起的頁面增加onActivityResult()特殊代碼。
針對問題1,可以利用構(gòu)建者模式,按需構(gòu)建需要的入?yún)ⅲ?針對問題2,可以利用一個透明的activity,在分享的時候作為接收回調(diào)的過渡頁面。
五、 具體實(shí)現(xiàn)
定義分享渠道和分享類型:
public class ShareChannel {
public static final int DOUYIN = 0x100; //抖音
public static final int WECHAT = 0x200; //微信
public static final int WECHAT_FRIENDS = 0x201; //微信朋友圈
public static final int QQ = 0x300; //QQ
public static final int QQ_ZONE = 0x301; //QQ空間
public static final int WEIBO = 0x400; //微博
}
public class ShareType {
public static final int SHARE_TYPE_TEXT = 0x1000; //文本
public static final int SHARE_TYPE_IMAGE = 0x2000; //圖片
public static final int SHARE_TYPE_MUSIC = 0x3000;
public static final int SHARE_TYPE_APP = 0x4000;
public static final int SHARE_TYPE_VIDEO = 0x5000;
public static final int SHARE_TYPE_WEBPAGE = 0x6000;
}
構(gòu)建入?yún)ⅲ?/p>
public class ShareData implements Serializable {
private ArrayList<String> uris;
private String title = "標(biāo)題";
private String description = "描述";
private String linkUrl;
private String imageUrl;
private String musicUrl;
private String videoUrl;
private String defaultText;
private String appName;
private int shareChannel;
private int shareType;
private ShareData() {
}
public static ShareData.Builder builder() {
return new Builder();
}
public static class Builder {
private ArrayList<String> uris;
private String title;
private String description;
private String linkUrl;
private String imageUrl;
private String musicUrl;
private String videoUrl;
private String appName;
private String defaultText;
private int shareChannel;
private int shareType;
public ShareData build() {
ShareData shareData = new ShareData();
return shareData;
}
}
/**
* 分享調(diào)用
*
* @param activity
*/
public void share(Activity activity) {
}
}
定義接口,描述分享的能力:
public interface IShare {
void shareText(ShareData shareData);
void shareImages(ShareData shareData);
void shareMusic(ShareData shareData);
void shareApp(ShareData shareData);
void shareVideos(ShareData shareData);
void shareWebPage(ShareData shareData);
}
微信分享直接定義類實(shí)現(xiàn)IShare方法,接入api就行。重點(diǎn)說微博,一言難盡,微博需要借助中間頁,才能按照接口標(biāo)準(zhǔn)來調(diào)用。新建中間頁:
public class WeiBoEntryActivity extends AppCompatActivity implements WbShareCallback, IShare {
private static final int THUMB_SIZE = 150;
public static final String KEY_SHARE_DATA = "shareData";
public static final String KEY_SHARE_TYPE = "shareType";
private WbShareHandler wbApi;
private ShareData shareData;
public static void start(Context context, ShareData shareData, int shareType) {
Intent starter = new Intent(context, WeiBoEntryActivity.class);
starter.putExtra(KEY_SHARE_DATA, shareData);
starter.putExtra(KEY_SHARE_TYPE, shareType);
context.startActivity(starter);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wei_bo_entry);
wbApi = new WbShareHandler(this);
wbApi.registerApp();
Intent intent = getIntent();
if (intent != null && intent.hasExtra(KEY_SHARE_DATA)) {
shareData = (ShareData) intent.getSerializableExtra(KEY_SHARE_DATA);
int shareType = intent.getIntExtra(KEY_SHARE_TYPE, SHARE_TYPE_TEXT);
switch (shareType){
case SHARE_TYPE_TEXT:
shareText(shareData);
break;
case SHARE_TYPE_IMAGE:
shareImages(shareData);
break;
}
}
}
@Override
public void onWbShareSuccess() {
Toast.makeText(this, "微博分享成功", Toast.LENGTH_SHORT).show();
finish();
}
@Override
public void onWbShareCancel() {
Toast.makeText(this, "微博分享取消", Toast.LENGTH_SHORT).show();
finish();
}
@Override
public void onWbShareFail() {
Toast.makeText(this, "微博分享失敗", Toast.LENGTH_SHORT).show();
finish();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if(wbApi != null){
wbApi.doResultIntent(intent, this);
}else{
finish();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (wbApi != null) {
wbApi.doResultIntent(data, this);
}
}
}
@Override
public void shareText(ShareData shareData) {
WeiboMultiMessage weiboMessage = new WeiboMultiMessage();
TextObject textObject = new TextObject();
textObject.text = shareData.getDescription();
weiboMessage.textObject = textObject;
wbApi.shareMessage(weiboMessage, false);
}
@Override
public void shareImages(ShareData shareData) {
}
public void shareMusic(ShareData shareData) {
}
public void shareApp(ShareData shareData) {
}
@Override
public void shareVideos(ShareData shareData) {
}
@Override
public void shareWebPage(ShareData shareData) {
}
}
這個頁面什么時候喚起呢?新建WeiBoShare類:
public class WeiBoShare implements IShare {
private Activity activity;
public WeiBoShare(Activity activity) {
this.activity = activity;
}
@Override
public void shareText(ShareData shareData) {
WeiBoEntryActivity.start(activity, shareData, SHARE_TYPE_TEXT);
}
@Override
public void shareImages(ShareData shareData) {
WeiBoEntryActivity.start(activity, shareData, SHARE_TYPE_IMAGE);
}
@Override
public void shareMusic(ShareData shareData) {
}
@Override
public void shareApp(ShareData shareData) {
}
@Override
public void shareVideos(ShareData shareData) {
WeiBoEntryActivity.start(activity, shareData, SHARE_TYPE_VIDEO);
}
@Override
public void shareWebPage(ShareData shareData) {
WeiBoEntryActivity.start(activity, shareData, SHARE_TYPE_WEBPAGE);
}
}
這樣我們在構(gòu)建完參數(shù)調(diào)用share()方法的時候會先判斷shareChannel去初始化對應(yīng)的分享實(shí)體,例如微博分享就初始化WeiBoShare對象,然后調(diào)用shareText()方法的時候會開啟中間頁,完成分享。
當(dāng)完成所有渠道的代碼之后,ShareData類的share()可以完善為:
public class ShareData{
//省略上面已有代碼
public void share(Activity activity) {
//根據(jù)shareChannel執(zhí)行對應(yīng)的渠道分享
switch (shareChannel) {
case ShareChannel.DOUYIN:
shareDouYin(activity);
break;
case ShareChannel.WECHAT:
case ShareChannel.WECHAT_FRIENDS:
shareWeChat(activity);
break;
case ShareChannel.QQ:
case ShareChannel.QQ_ZONE:
QqEntryActivity.start(activity, this, shareType);
break;
case ShareChannel.WEIBO:
WeiBoEntryActivity.start(activity, this, shareType);
break;
}
}
private void shareDouYin(Activity activity) {
switch (shareType) {
case ShareType.SHARE_TYPE_IMAGE:
new DouYinShare(activity).shareImages(this);
break;
case ShareType.SHARE_TYPE_VIDEO:
new DouYinShare(activity).shareVideos(this);
break;
}
}
private void shareWeChat(Activity activity) {
//根據(jù)對應(yīng)的shareType執(zhí)行對應(yīng)的類型分享
switch (shareType) {
case ShareType.SHARE_TYPE_TEXT:
new WechatShare(activity).shareText(this);
break;
case ShareType.SHARE_TYPE_IMAGE:
new WechatShare(activity).shareImages(this);
break;
case ShareType.SHARE_TYPE_VIDEO:
new WechatShare(activity).shareVideos(this);
break;
case ShareType.SHARE_TYPE_WEBPAGE:
new WechatShare(activity).shareWebPage(this);
break;
}
}
}
到這我測試了一下,整體沒有問題,但是還是開篇講的,要是再來一個渠道和類型,我還是要case一下啊。如果能改為插件式的那種,盡可能增加少的代碼,入侵更少的類。
六、 插件式
既然是插件式的,就需要一個插件管理類:
public class SharePluginsManager {
private SharePluginsManager() {
}
private static SharePluginsManager instance = null;
public static SharePluginsManager getInstance() {
if (instance == null) {
synchronized (SharePluginsManager.class) {
if (instance == null) {
instance = new SharePluginsManager();
}
}
}
return instance;
}
/**
* 緩存分享實(shí)體
*/
private ArrayMap<String, IShare> shareMap = new ArrayMap<>();
/**
* 初始化分享類集合
*/
private List<String> shareClazzs = new ArrayList<>();
/**
* 添加share插件
* @param shareClazz
*/
public void addSharePlugin(String shareClazz){
if(!shareClazzs.contains(shareClazz)){
shareClazzs.add(shareClazz);
}
}
/**
* 通過渠道獲取插件實(shí)體,過濾方法在這里使用到
* @param shareChannel
* @return
*/
public IShare getShareEntity(int shareChannel) {
try {
int size = shareClazzs.size();
for (int i = 0; i < size; i++) {
String shareClazz = shareClazzs.get(i);
IShare iShare = shareMap.get(shareClazz);
if (iShare == null) {
Class<?> clazz = Class.forName(shareClazz);
iShare = (IShare) clazz.newInstance();
shareMap.put(shareClazz, iShare);
}
if (iShare != null && iShare.filter(shareChannel)) {
return iShare;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
需要增加一個ShareFilter接口,對分享的插件進(jìn)行過濾,因為SharePluginsManager肯定是一個集合,每一次分享需要從集合過濾出需要的插件。為減少代碼量,直接讓插件自身具有過濾的能力了。
更改IShare接口:
public interface IShare {
//過濾
boolean filter(int shareChannel);
//分享派發(fā)
void share(Activity activity, ShareData shareData);
//分享文本
void shareText(Activity activity, ShareData shareData);
//分享圖片
void shareImages(Activity activity, ShareData shareData);
//分享音樂
void shareMusic(Activity activity, ShareData shareData);
//分享應(yīng)用
void shareApp(Activity activity, ShareData shareData);
//分享視頻
void shareVideos(Activity activity, ShareData shareData);
//分享網(wǎng)頁
void shareWebPage(Activity activity, ShareData shareData);
}
還拿上面的WeiBoShare舉例,修改類名為WeiBoSharePlugin,看起來像那么回事:
public class WeiBoSharePlugin implements IShare {
public WeiBoSharePlugin() {
}
@Override
public boolean filter(int shareChannel) {
return shareChannel == ShareChannel.WEIBO;
}
@Override
public void share(Activity activity, ShareData shareData) {
int shareType = shareData.getShareType();
switch (shareType) {
case SHARE_TYPE_TEXT:
shareText(activity, shareData);
break;
case SHARE_TYPE_IMAGE:
shareImages(activity, shareData);
break;
case SHARE_TYPE_VIDEO:
shareVideos(activity, shareData);
break;
case SHARE_TYPE_WEBPAGE:
shareWebPage(activity, shareData);
break;
}
}
@Override
public void shareText(Activity activity, ShareData shareData) {
WeiBoEntryActivity.start(activity, shareData, SHARE_TYPE_TEXT);
}
@Override
public void shareImages(Activity activity, ShareData shareData) {
WeiBoEntryActivity.start(activity, shareData, SHARE_TYPE_IMAGE);
}
@Override
public void shareMusic(Activity activity, ShareData shareData) {
}
@Override
public void shareApp(Activity activity, ShareData shareData) {
}
@Override
public void shareVideos(Activity activity, ShareData shareData) {
WeiBoEntryActivity.start(activity, shareData, SHARE_TYPE_VIDEO);
}
@Override
public void shareWebPage(Activity activity, ShareData shareData) {
WeiBoEntryActivity.start(activity, shareData, SHARE_TYPE_WEBPAGE);
}
}
這樣修改之后,ShareData下的share()方法就變得非常簡單了:
public class ShareData implements Serializable {
//其他代碼省略...
public void share(Activity activity) {
IShare iShare = SharePluginsManager.getInstance().getShareEntity(shareChannel);
if (iShare != null) {
iShare.share(activity, this);
} else {
Toast.makeText(activity, "沒有找到分享渠道", Toast.LENGTH_SHORT).show();
}
}
}
七、 分享示例
最終調(diào)用:
ShareData.builder()
.shareChannel(ShareChannel.WEIBO)
.shareType(ShareType.SHARE_TYPE_TEXT)
.build()
.share((Activity) mContext);
擴(kuò)展的話也很方便,只需要新建一個類實(shí)現(xiàn)IShare接口,然后在App啟動的時候,注冊一下插件:
SharePluginsManager.getInstance().addSharePlugin(QqSharePlugin.class.getName());
SharePluginsManager.getInstance().addSharePlugin(WechatSharePlugin.class.getName());
SharePluginsManager.getInstance().addSharePlugin(WeiBoSharePlugin.class.getName());
SharePluginsManager.getInstance().addSharePlugin(DouYinSharePlugin.class.getName());
唯一不和諧的就是ShareData的share()方法入?yún)ⅲ枰獋魅胍粋€Activity。這個Activity可以在ShareData參數(shù)時構(gòu)建嗎?不可以,主要原因就是微博這種需要傳遞ShareData到中間頁,Activity又沒有繼承自Serializable,傳遞會失敗。微博,西八... ...