最后一次寫Android分享

一、 分享吐槽

分享真的是做一次就要重新看一次文檔,慢慢整理一份,微信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,傳遞會失敗。微博,西八... ...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,643評論 2 380