Android 轻量级分享封装

前言

提到分享,几乎所有应用都会有的功能,虽然说不是难点,但是还是要有。最近公司也在做分享功能,以前也做过很多次分享,基本都是集成第三方分享SDK,然后按照开发文档步骤完成。隔一段时间就会忘记来龙去脉,再次做分享,还要去查看官方文档。因此,本次准备花点时间封装第三方分享SDK,这样做的好处是方便以后使用,另一方面也可以自己学习到一些新的东西。分享有两种方式,一种是第三方分享SDK,第二种是隐士跳转方式,用于隐士跳转方式有某些局限,一方面是QQ空间分享不对外开放,另一方面是第三方分享SDK额分享监听做的比较全面,不需要再次自己去封装。因此本文主要介绍关于第三方分享SDK的封装,,后面也会把原生的封装带上。本文同样采用建造者模式创建,结构简单清晰,本文省略申请账号过程。基本使用如下:

测试分享.png
ShareKeeper.getInstance()
                .builder(this)
                .setPlatform(ShareKeeper.PLATFORM_QZONE)
                .setShareType(ShareKeeper.TYPE_DEFAULT)
                .setTitle(title)
                .setDesc(desc)
                .setImageUrl(imageUrl)
                .setImagePath(localPicPath)
                .setWebUrl(webUrl)
                .setAVdioUrl(videoUrl)
                .setAVdioPath(localVideoPath)
                .setOnShareListener(this)
                .share();

属性介绍

可能看到上面的使用可能会觉得那么多属性,肯定很复杂,其实不然。上面举例是把所有的属性都列举出来了,但是实际用不到那么多属性。下面就来介绍所有的属性。

builder(this) :构建建造者模式
setPlatform(): 设置分享平台,例如:QQ、微信、QQ空间等 (可以不填,默认QQ分享)
setShareType():设置分享的类型,根据形式分为,图文、纯图片、音视频、纯文本(此分类不已第三方分享SDK分类),可以不设置,默认为图文类型
setTitle(title):设置标题,可以不设置(内部已经做了防止空指针,但是建议设置,或者设置appname亦可)
setDesc(desc):设置简介,设置同上
setImageUrl(imageUrl):设置网络图片路径
.setImagePath(localPicPath):设置本地图片路径
setAVdioUrl(videoUrl):设置网络音视频链接
setAVdioPath(localVideoPath):设置本地音视频地址
setWebUrl(webUrl):设置分享的链接(除了分享图片之外,都是必备的属性)
setOnShareListener(this):设置分享监听回调(建议回调)

微信分享

检测客户端
/**
     * 手机是否安装微信客户端
     *
     * @param context
     * @return
     */
    public static boolean isWeixinAvilible(Context context) {
        final PackageManager packageManager = context.getPackageManager();// 获取packagemanager
        List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息
        if (pinfo != null) {
            for (int i = 0; i < pinfo.size(); i++) {
                String pn = pinfo.get(i).packageName;
                if (pn.equals("com.tencent.mm")) {
                    return true;
                }
            }
        }
        return false;
    }
微信属性介绍
WXMediaMessage.IMediaObject :微信分享SDK内部做了分享的封装类,包括文本、图片、视频等
WXMediaMessage :携带IMediaObject 以及标题、描述、缩略图信息
msg.title :标题
msg.description :描述
msg.thumbData :缩略数据(图片内容)
SendMessageToWX.Req:携带WXMediaMessage 和分享数据
transaction    :分享携带数据,相当于类型,回调到WXEntryActivity为BaseResp resp中的参数,可以做过滤作用
message :携带WXMediaMessage 
scene :场景,即分享平台
创建不同的类型

由于微信分享和朋友圈分享没有区别,所以在一起统一封装如下:

/**
     * 创建分享的IMediaObject
     *
     * @param builder
     * @param bitmap
     * @return
     */
    private static WXMediaMessage.IMediaObject createIMediaObject(ShareKeeper.Builder builder, Bitmap bitmap) {

        int mPlatform = builder.mPlatform;
        int mShareType = builder.mShareType;
        WXMediaMessage.IMediaObject mediaObject = null;
        OnShareListener mOnShareListener = builder.mOnShareListener;

        if (mShareType == ShareKeeper.TYPE_DEFAULT) {
            WXWebpageObject webpageObject = new WXWebpageObject();
            String mWebUrl = builder.mWebUrl;
            if (!TextUtils.isEmpty(mWebUrl)) {
                webpageObject.webpageUrl = mWebUrl;
                mediaObject = webpageObject;
            } else {
                if (mOnShareListener != null) {
                    mOnShareListener.onShareFailed(mPlatform, "分享的链接不能为空!");
                }
            }
        } else if (mShareType == ShareKeeper.TYPE_PICTURE) {
            if (bitmap != null) {
                WXImageObject imgObj = new WXImageObject(bitmap);
                mediaObject = imgObj;
            } else {
                if (mOnShareListener != null) {
                    mOnShareListener.onShareFailed(mPlatform, "分享的图片不能为空!");
                }
            }
        } else if (mShareType == ShareKeeper.TYPE_AVDIO) {
            String videoUrl = builder.mAVdioUrl;
            if (!TextUtils.isEmpty(videoUrl)) {
                WXVideoObject videoObject = new WXVideoObject();
                videoObject.videoUrl = videoUrl;
                mediaObject = videoObject;
            } else {
                if (mOnShareListener != null) {
                    mOnShareListener.onShareFailed(mPlatform, "分享的视频链接不能为空!");
                }
            }
        } else if (mShareType == ShareKeeper.TYPE_TXT) {
            String desc = builder.mDesc;
            if (!TextUtils.isEmpty(desc)) {
                WXTextObject textObj = new WXTextObject();
                textObj.text = desc;
                mediaObject = textObj;
            } else {
                if (mOnShareListener != null) {
                    mOnShareListener.onShareFailed(mPlatform, "分享的文本不能为空!");
                }
            }
        }
        return mediaObject;
    }

说明:WXMediaMessage.IMediaObject封装了微信分享不同,所以在此根据分享类型创建不同的IMediaObject,有些必不可缺的属性都做了分享回调失败,当然也可以做抛异常的形式(不建议,程序会崩溃)。

微信分享
/**
     * 执行
     *
     * @param bitmap
     * @param iwxapi
     * @param builder
     */
    private static void performShare(Bitmap bitmap, IWXAPI iwxapi, ShareKeeper.Builder builder) {
        OnShareListener mOnShareListener = builder.mOnShareListener;
        WXMediaMessage.IMediaObject iMediaObject = createIMediaObject(builder, bitmap);

        if (iMediaObject != null) {
            //检测参数可以分享
            boolean checkArgs = iMediaObject.checkArgs();
            if (checkArgs) {
                //共同的部分
                WXMediaMessage msg = new WXMediaMessage(iMediaObject);
                msg.title = builder.mTitle;//标题
                msg.description = builder.mDesc;//描述
               //缩略图
                if (bitmap != null) {
                    byte[] thumbData = BitmpUtils.compressBitmapSpecifySize(bitmap, IMAGE_MAX_SIZE);
                    if (thumbData.length > IMAGE_MAX_SIZE) {
                        if (mOnShareListener != null) {
                            mOnShareListener.onShareFailed(builder.mPlatform, "分享的缩略图过大");
                        }
                        return;
                    }
                    msg.thumbData = thumbData;//缩略数据(图片内容)
                }
                //共同的部分
                SendMessageToWX.Req req = new SendMessageToWX.Req();
                req.transaction = buildDiffTransaction(builder.mShareType);
                req.message = msg;
                int mScene = builder.mPlatform == ShareKeeper.PLATFORM_WECHAT ? WXSceneSession : WXSceneTimeline;
                req.scene = mScene;
                //携带监听
                Bundle bundle = new Bundle();
                bundle.putSerializable("builder", builder);
                req.toBundle(bundle);
                iwxapi.sendReq(req);
            } else {
                if (mOnShareListener != null) {
                    mOnShareListener.onShareFailed(builder.mPlatform, "分享参数异常");
                }
            }
        }
    }

说明:1、boolean checkArgs = iMediaObject.checkArgs(),微信分享SDK内部检测参数,判断参数是否正确
2、byte[] thumbData = BitmpUtils.compressToBitmapLast(bitmap, IMAGE_MAX_SIZE)为缩略图,除了分享图片 必备之外,其他情况都可以没有。

压缩图片

关于微信分享最容易出错的就是分享的图片过大,微信规定分享的缩略图大小不能超过32768K,所以分享之前必须做压缩处理,本文主要采用先质量压缩至10%,如果还超过规定分享缩略图大小再采取尺寸压缩,如果尺寸压缩之后依旧不行,可以采用第三方压缩工具或者替换图片。

 /**
     * 图片压缩到指定大小
     *
     * @param bmp
     * @param maxByteSize
     * @return
     */
    public static byte[] compressBitmapSpecifySize(final Bitmap bmp, final long maxByteSize) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        byte[] bytes = null;
        int options = 100;
        int outSize = baos.size();

        if (outSize <= maxByteSize) {
            bytes = baos.toByteArray();
        } else {
            while (outSize > maxByteSize && options > 0) {
                baos.reset(); //清空baos
                bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
                outSize = baos.size();
                options -= 10;
            }

            if (outSize > maxByteSize) {
                bytes = compressBitmapSize(bmp, options, maxByteSize);
            } else {
                bytes = baos.toByteArray();
            }
        }
        if (!bmp.isRecycled()) bmp.recycle();
        return bytes;
    }

    /**
     * 需要压缩
     *
     * @param bmp
     */
    private static byte[] compressBitmapSize(Bitmap bmp, int options, final long maxByteSize) {
        //继续在当前的质量下压缩
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
        int outSize = baos.size();
        int orginOpi = options;

        while (outSize > maxByteSize && options > 0) {
            baos.reset(); //清空baos
            Bitmap bitmap = BitmpUtils.scaleBitmap(bmp, options * 0.1f);
            bitmap.compress(Bitmap.CompressFormat.JPEG, orginOpi, baos);//这里压缩options%,把压缩后的数据存放到baos中
            options--;
        }

        byte[] bytes = baos.toByteArray();
        return bytes;
    }

微信分享结果处理

微信分享的结果处理是在WXEntryActivity的onResp(BaseResp resp)方法中处理各种分享返回结果

if (shareBuilder != null) {
            OnShareListener onShareListener = shareBuilder.mOnShareListener;
            if (onShareListener != null) {
                int errCode = resp.errCode;
                if (errCode == BaseResp.ErrCode.ERR_OK) {
                    onShareListener.onShareSuccess(shareBuilder.mPlatform);
                } else if (errCode == BaseResp.ErrCode.ERR_SENT_FAILED) {
                    onShareListener.onShareFailed(shareBuilder.mPlatform, "发送失败");
                } else if (errCode == BaseResp.ErrCode.ERR_AUTH_DENIED) {
                    onShareListener.onShareFailed(shareBuilder.mPlatform, "认证被否决");
                } else if (errCode == BaseResp.ErrCode.ERR_UNSUPPORT) {
                    onShareListener.onShareFailed(shareBuilder.mPlatform, "版本不支持");
                } else if (errCode == BaseResp.ErrCode.ERR_COMM) {
                    onShareListener.onShareFailed(shareBuilder.mPlatform, "一般错误");
                } else if (errCode == BaseResp.ErrCode.ERR_USER_CANCEL) {
                    onShareListener.onCancleShare(shareBuilder.mPlatform, "取消分享");
                } else {
                    onShareListener.onShareFailed(shareBuilder.mPlatform, "未知错误");
                }
            }
        }

说明:关于微信分享结果需要注意一点,由于微信分享SDK升级,分享取消和分享成功都会返回分享成功。
新版微信分享取消与成功合并

QQ分享

检测QQ客户端
/**
     * 手机是否安装QQ客户端
     *
     * @param context
     * @return
     */
    public static boolean isQQClientAvailable(Context context) {
        final PackageManager packageManager = context.getPackageManager();
        List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);
        if (pinfo != null) {
            for (int i = 0; i < pinfo.size(); i++) {
                String pn = pinfo.get(i).packageName;
                if (pn.equals("com.tencent.mobileqq")) {
                    return true;
                }
            }
        }
        return false;
    }
QQ分享属性介绍

qq分享属性全部封装在一个Bundle里面

QQShare.SHARE_TO_QQ_KEY_TYPE:分享类型
QQShare.SHARE_TO_QQ_TITLE:分享标题(默认不可以为空,内部做了防止空指针)
QQShare.SHARE_TO_QQ_SUMMARY:分享简介(设置同上)
QQShare.SHARE_TO_QQ_TARGET_URL:weburl(除了分享图片以外必须参数)
QQShare.SHARE_TO_QQ_IMAGE_URL:网络图片(缩略图,但不是单纯分享图片的地址,没有也不影响)
QQShare.SHARE_TO_QQ_IMAGE_LOCAL_URL:本地图片路径(单纯的本地图片,必要参数,一定要本地路径)
QQShare.SHARE_TO_QQ_EXT_INT, QQShare.SHARE_TO_QQ_FLAG_QZONE_AUTO_OPEN):分享qq空间特有(权限)

注意:1、qq空间的属性需要把QQ替换成QZONE
2、网络图片链接在qq分享和qq空间分享的数据装载形式不一样,qq分享直接存在Bundle中,qq空间在先存放在集合中,然后添加集合方式,具体看下面代码。

QQ分享

由于QQ不能单独分享文本,因此需要单独处理,另外QQ空间不对外开放且QQ空间不能单独分享图片和视频,因此QQ空间分享只做默认的图文分享。因此下面仅介绍QQ分享。

文本分享
//纯文本分享
Intent intent = new Intent("android.intent.action.SEND");
intent.setType(NetiveShareTask.TYPE_TXT);
intent.putExtra(Intent.EXTRA_SUBJECT,"分享");
intent.putExtra(Intent.EXTRA_TEXT,builder.mDesc+"");//不能为空
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(QQ_PACKAGE_NAME,QQ_SHARE_COMPONENT_NAME));
activity.startActivityForResult(intent,TEXT_REQUEST_CODE);

图片分享

QQ分享SDK默认只支持本地图片路径分享,因此如果是网络图片,需要先下载到本地然后再去分享,本地图片分享跟其他分享区别不大,只有一点就是纯图片分享,图片不能为空,但是链接是唯一可以为空的类型,因此这里主要讲解网络图片的分享。

/**
     * 分享网络图片
     * 需要先下载到本地
     * 然后设置到本地路径去分享
     *
     * @param activity
     * @param builder
     */
    private static void performShareNetPic(final Activity activity, final Tencent mTencent, final ShareKeeper.Builder builder) {
        new Thread() {
            public void run() {
                OnShareListener mOnShareListener = builder.mOnShareListener;
                try {
                    String mImageUrl = builder.mImageUrl;
                    //先获取图片然后保存到本地分享
                    Bitmap urlBitmap = BitmpUtils.getUrlBitmap(mImageUrl);
                    //检测生成的图片是否存在
                    if (urlBitmap == null && mOnShareListener != null) {
                        mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享图片为空!");
                        return;
                    }
                    //SDK分享网络图片
                    final String saveBitmapToLocal = BitmpUtils.saveBitmapToLocal(activity, urlBitmap);
                    builder.setImagePath(saveBitmapToLocal);
                    //最后转为本地分享
                    performShareLocalPic(activity, mTencent, builder);
                } catch (IOException e) {
                    e.printStackTrace();
                    if (mOnShareListener != null) {
                        mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享异常:" + e.getMessage());
                    }
                }
            }
        }.start();
    }

音视频分享

QQ分享SDK是支持音视频分享的,不过是分享的链接,因此如果要分享本地音视频,要特殊处理。因此此处单独做QQ本地音视频的分享讲解。此处采用系统的分享方式,通过隐士跳转分享。

 /**
     * QQ分享本地视频
     *
     * @param activity
     * @param builder
     */
    private static void performQQShareAVideoNative(Activity activity, ShareKeeper.Builder builder) {
        String mAVdioPath = builder.mAVdioPath;
        OnShareListener mOnShareListener = builder.mOnShareListener;
        Uri uri = UriUtils.getUri(activity, mAVdioPath);
        if (uri == null) {
            if (mOnShareListener != null) {
                mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享本地视频地址异常!");
            }
            return;
        }
        Intent intent = new Intent("android.intent.action.SEND");
        intent.setType(NetiveShareTask.TYPE_VIDEO);
        intent.putExtra(Intent.EXTRA_TEXT, builder.mTitle + "");//不能为空
        intent.putExtra(Intent.EXTRA_SUBJECT, builder.mDesc + "");
        intent.putExtra(Intent.EXTRA_STREAM, uri);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setComponent(new ComponentName(QQ_PACKAGE_NAME, QQ_SHARE_COMPONENT_NAME));
        activity.startActivityForResult(intent, VIDEO_REQUEST_CODE);
    }
其他类型分享
/**
     * 分享网页  默认类型
     *
     * @param activity
     * @param mTencent
     * @param builder
     */
    private static void performQQShareWeb(Activity activity, Tencent mTencent, ShareKeeper.Builder builder) {
        Bundle params = new Bundle();
        params.putInt(QQShare.SHARE_TO_QQ_KEY_TYPE, QQShare.SHARE_TO_QQ_TYPE_DEFAULT);//默认分享类型
        params.putString(QQShare.SHARE_TO_QQ_TITLE, builder.mTitle + "");//分享标题(默认不可以为空,防止空指针)
        params.putString(QQShare.SHARE_TO_QQ_SUMMARY, builder.mDesc + "");//分享简介(默认可以为空防止空指针)
        params.putString(QQShare.SHARE_TO_QQ_TARGET_URL, builder.mWebUrl);//weburl(除了图片以外都需要)
        params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, builder.mImageUrl);//网络图片(缩略图,没有也不影响)
        params.putString(QQShare.SHARE_TO_QQ_IMAGE_LOCAL_URL, builder.mImagePath);//本地图片路径(单纯的本地图片分享使用)
        //创建监听器
        mQQShareListener = new QQShareListener(builder);
        mTencent.shareToQQ(activity, params, mQQShareListener);
    }

说明:
1、由于文本分享没用SDK,所以监听只以跳转成功为分享成功,无取消分享监听
2、QQ空间无法进行图片分享(建议QQ空间只做图文分享)
3、QQ分享如果单独分享图片,只能分享本地图片
4、由于SDK做了判断,因此performShare()不再做一些基本的参数判断

QQ分享结果处理

QQ分享一定要处理当前activity的onActivityResult(),否则无法监听到QQ内部的分享回调。

/**
     * 处理QQ分享
     *
     * @param requestCode
     * @param resultCode
     * @param data
     */
    public void performQQShareResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != Activity.RESULT_OK) {
            return;
        }

        if (mShareBuilder != null) {
            OnShareListener mOnShareListener = mShareBuilder.mOnShareListener;
            //需要判断是否是QQ分享
            if (requestCode == Constants.REQUEST_QQ_SHARE
                    || requestCode == Constants.REQUEST_QZONE_SHARE
                    || requestCode == Constants.REQUEST_OLD_SHARE) {
                IUiListener mIUiListener = QQShareTask.getIUiListener();
                if (mOnShareListener != null && mIUiListener != null) {
                    Tencent.onActivityResultData(requestCode, resultCode, data, mIUiListener);
                }
            } else if (requestCode == QQShareTask.TEXT_REQUEST_CODE
                    || requestCode == QQShareTask.PIC_REQUEST_CODE
                    || requestCode == QQShareTask.VIDEO_REQUEST_CODE) {
                //说明是QQ原生方式分享
                if (mOnShareListener != null) {
                    mOnShareListener.onShareSuccess(mShareBuilder.mPlatform, mShareBuilder.mShareType);
                }
            }
        }
    }

原生分享

利用原生的分享也是可以的,但是无法监听分享的结果,暂且以跳转到目标页面为分享成功,异常情况为分享失败。

原生分享文本
/**
     * 分享文本
     *
     * @param activity
     * @param builder
     */
    private static void performNativeShareTxt(Activity activity, ShareKeeper.Builder builder) {
        OnShareListener mOnShareListener = builder.mOnShareListener;
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType(NetiveShareTask.TYPE_TXT);
        intent.putExtra(Intent.EXTRA_TEXT, builder.mDesc + "");//不能为空
        Intent chooser = Intent.createChooser(intent, builder.mTitle);
        if (intent.resolveActivity(activity.getPackageManager()) != null) {
            activity.startActivityForResult(chooser, SHARE_REQUEST_CODE);
        } else {
            if (mOnShareListener != null) {
                mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享所在Activity异常!");
            }
        }
    }
原生分享图片

原生分享还是分享本地图片,网络图片要下载到本地再做分享。

/**
     * 原生本地图片分享
     *
     * @param activity
     * @param builder
     */
    private static void performNativeShareLocalPic(Activity activity, ShareKeeper.Builder builder) {
        String mImagePath = builder.mImagePath;
        OnShareListener mOnShareListener = builder.mOnShareListener;
        Uri uri = UriUtils.getUri(activity, mImagePath);
        if (uri == null) {
            if (mOnShareListener != null) {
                mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享本地视频地址异常!");
            }
            return;
        }
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType(NetiveShareTask.TYPE_IMAGE);
        intent.putExtra(Intent.EXTRA_TEXT, builder.mTitle + "");//不能为空
        intent.putExtra(Intent.EXTRA_SUBJECT, builder.mDesc + "");
        intent.putExtra(Intent.EXTRA_STREAM, uri);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Intent chooser = Intent.createChooser(intent, builder.mTitle);
        if (intent.resolveActivity(activity.getPackageManager()) != null) {
            activity.startActivityForResult(chooser, SHARE_REQUEST_CODE);
        } else {
            if (mOnShareListener != null) {
                mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享所在Activity异常!");
            }
        }
    }
原生分享视频
/**
     * 原生分享本地视频
     *
     * @param activity
     * @param builder
     */
    private static void performNativeShareVideo(Activity activity, ShareKeeper.Builder builder) {
        String mAVdioPath = builder.mAVdioPath;
        OnShareListener mOnShareListener = builder.mOnShareListener;
        Uri uri = UriUtils.getUri(activity, mAVdioPath);
        if (uri == null) {
            if (mOnShareListener != null) {
                mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享本地视频地址异常!");
            }
            return;
        }
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType(NetiveShareTask.TYPE_VIDEO);
        intent.putExtra(Intent.EXTRA_TEXT, builder.mTitle + "");//不能为空
        intent.putExtra(Intent.EXTRA_SUBJECT, builder.mDesc + "");
        intent.putExtra(Intent.EXTRA_STREAM, uri);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Intent chooser = Intent.createChooser(intent, builder.mTitle);
        if (intent.resolveActivity(activity.getPackageManager()) != null) {
            activity.startActivityForResult(chooser, SHARE_REQUEST_CODE);
        } else {
            if (mOnShareListener != null) {
                mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享所在Activity异常!");
            }
        }
    }

微博分享

微博分享跟微信分享是非常相似的,所以很多细节,具体细节可以参考代码。

检测微博客户端
/**
     * 判断 用户是否安装微博客户端
     */
    public static boolean isWeiboClientAvailable(Context context) {
        final PackageManager packageManager = context.getPackageManager();
        List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);
        if (pinfo != null) {
            for (int i = 0; i < pinfo.size(); i++) {
                String pn = pinfo.get(i).packageName;
                if (pn.equalsIgnoreCase("com.sina.weibo")) {
                    return true;
                }
            }
        }
        return false;
    }

检测参数是否异常

微博分享每个类型的都有特有的检测参数是否正常的方法,所以还是要单独检测的。具体如下:

 /**
     * 检测参数是否可以分享
     *
     * @param mShareType
     * @param baseMediaObject
     * @return
     */
    private static boolean checkShareArgs(int mShareType, BaseMediaObject baseMediaObject) {
        if (mShareType == ShareKeeper.TYPE_TXT) {
            TextObject textObject = (TextObject) baseMediaObject;
            return textObject.checkArgs();
        } else if (mShareType == ShareKeeper.TYPE_PICTURE) {
            ImageObject imageObject = (ImageObject) baseMediaObject;
            return imageObject.checkArgs();
        } else if (mShareType == ShareKeeper.TYPE_AVDIO || mShareType == ShareKeeper.TYPE_DEFAULT) {
            WebpageObject webpageObject = (WebpageObject) baseMediaObject;
            return webpageObject.checkArgs();
        }
        return false;
    }

微博分享特殊处

1、既有web分享也有客户端分享,暂时项目中只支持客户端分享,如想支持web分享,需要在manifest添加

<!--sina-->
<activity android:name="com.sina.weibo.sdk.component.WeiboSdkBrowser"
          android:configChanges="keyboardHidden|orientation"
          android:windowSoftInputMode="adjustResize"
          android:exported="false" >
</activity>

2、结果回调需要用到当前分享activity的onNewIntent(Intent intent)方法

@Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        ShareKeeper.getInstance().performWBShareResult(intent);
    }

wbShareHandler.doResultIntent即是微博分享的回调,处理如下:

/**
     * 处理微博的分享监听
     *
     * @param intent
     */
    public void performWBShareResult(Intent intent) {
        WbShareHandler wbShareHandler = WBShareTask.getWbShareHandler();
        if (wbShareHandler != null) {
            if (mShareBuilder != null) {
                final int mPlatform = mShareBuilder.mPlatform;
                final int mShareType = mShareBuilder.mShareType;
                final OnShareListener mOnShareListener = mShareBuilder.mOnShareListener;
                if (mOnShareListener != null) {
                    wbShareHandler.doResultIntent(intent, new WbShareCallback() {
                        @Override
                        public void onWbShareSuccess() {
                            mOnShareListener.onShareSuccess(mPlatform, mShareType);
                        }

                        @Override
                        public void onWbShareCancel() {
                            mOnShareListener.onCancleShare(mPlatform, mShareType, "取消分享!");
                        }

                        @Override
                        public void onWbShareFail() {
                            mOnShareListener.onShareFailed(mPlatform, mShareType, "分享失败!");
                        }
                    });
                }
            }
        }
    }

3、微博SDK内部需要加载libweibosdkcore.so的文件,原因可以参考 Android7.x找不到libsqlite.so 问题,项目中已经添加,具体请参考代码

微博分享完整
public class WBShareTask {


    private static WbShareHandler mShareHandler;
    public static final int IMAGE_MAX_SIZE = 32768;//分享图片大小限制

    /**
     * 子线程中执行操作
     *
     * @param activity
     * @param builder
     */
    public static void executeShare(Activity activity, final ShareKeeper.Builder builder) {
        boolean weiboClientAvailable = isWeiboClientAvailable(activity);
        OnShareListener mOnShareListener = builder.mOnShareListener;
        //监测是否有客户端
        if (!weiboClientAvailable) {
            mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "监测不到客户端");
        } else {
            WbSdk.install(activity, new AuthInfo(activity, Config.WEIBO_APP_ID, Config.WEIBO_WEB_SITE, Config.SCOPE));
            if (mShareHandler == null) mShareHandler = new WbShareHandler(activity);
            mShareHandler.registerApp();
            final Bitmap mLocalImage = BitmapFactory.decodeFile(builder.mImagePath);
            final String mImageUrl = builder.mImageUrl;
            //根据有没有图片分为三种情况
            new Thread() {
                public void run() {
                    if (mLocalImage != null) {
                        performShare(mLocalImage, mShareHandler, builder);
                    } else if (!TextUtils.isEmpty(mImageUrl)) {
                        Bitmap urlBitmap = BitmpUtils.getUrlBitmap(builder.mImageUrl);
                        performShare(urlBitmap, mShareHandler, builder);
                    } else {
                        performShare(null, mShareHandler, builder);
                    }
                }
            }.start();
        }
    }

    /**
     * 开始分享
     *
     * @param bitmap
     * @param mShareHandler
     * @param builder
     */
    private static void performShare(Bitmap bitmap, WbShareHandler mShareHandler, ShareKeeper.Builder builder) {
        OnShareListener mOnShareListener = builder.mOnShareListener;
        BaseMediaObject iMediaObject = createIMediaObject(builder, bitmap);

        if (iMediaObject != null) {
            //检测参数可以分享
            boolean checkArgs = checkShareArgs(builder.mShareType, iMediaObject);
            if (checkArgs) {
                WeiboMultiMessage message = new WeiboMultiMessage();
                message.mediaObject = iMediaObject;
                mShareHandler.shareMessage(message, true);
            } else {
                if (mOnShareListener != null) {
                    mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享参数异常");
                }
            }
        }
    }


    /**
     * 创建不同的 BaseMediaObject
     *
     * @param builder
     * @param bitmap
     * @return
     */
    private static BaseMediaObject createIMediaObject(ShareKeeper.Builder builder, Bitmap bitmap) {
        int mShareType = builder.mShareType;
        OnShareListener mOnShareListener = builder.mOnShareListener;
        BaseMediaObject baseMediaObject = null;

        if (mShareType == ShareKeeper.TYPE_TXT) {
            TextObject textObject = new TextObject();
            textObject.text = builder.mDesc;
            textObject.title = builder.mTitle;
            baseMediaObject = textObject;
        } else if (mShareType == ShareKeeper.TYPE_PICTURE) {
            ImageObject imageObject = new ImageObject();
            //缩略图
            if (bitmap != null) {
                byte[] thumbData = BitmpUtils.compressBitmapSpecifySize(bitmap, IMAGE_MAX_SIZE);
                if (thumbData.length > IMAGE_MAX_SIZE) {
                    if (mOnShareListener != null) {
                        mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享的缩略图过大");
                    }
                } else {
                    Bitmap byteToBitmap = BitmpUtils.byteToBitmap(thumbData);
                    if (byteToBitmap != null) {
                        imageObject.setImageObject(bitmap);
                    } else {
                        if (mOnShareListener != null) {
                            mOnShareListener.onShareFailed(builder.mPlatform, builder.mShareType, "分享的缩略图异常");
                        }
                    }
                }
            }

            baseMediaObject = imageObject;
        } else if (mShareType == ShareKeeper.TYPE_AVDIO || mShareType == ShareKeeper.TYPE_DEFAULT) {
            WebpageObject webpageObject = new WebpageObject();
            webpageObject.actionUrl = builder.mWebUrl;
            webpageObject.title = builder.mTitle;
            webpageObject.description = builder.mDesc;
            baseMediaObject = webpageObject;
        }

        return baseMediaObject;
    }

    /**
     * 检测参数是否可以分享
     *
     * @param mShareType
     * @param baseMediaObject
     * @return
     */
    private static boolean checkShareArgs(int mShareType, BaseMediaObject baseMediaObject) {
        if (mShareType == ShareKeeper.TYPE_TXT) {
            TextObject textObject = (TextObject) baseMediaObject;
            return textObject.checkArgs();
        } else if (mShareType == ShareKeeper.TYPE_PICTURE) {
            ImageObject imageObject = (ImageObject) baseMediaObject;
            return imageObject.checkArgs();
        } else if (mShareType == ShareKeeper.TYPE_AVDIO || mShareType == ShareKeeper.TYPE_DEFAULT) {
            WebpageObject webpageObject = (WebpageObject) baseMediaObject;
            return webpageObject.checkArgs();
        }
        return false;
    }


    /**
     * 判断 用户是否安装微博客户端
     */
    public static boolean isWeiboClientAvailable(Context context) {
        final PackageManager packageManager = context.getPackageManager();
        List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);
        if (pinfo != null) {
            for (int i = 0; i < pinfo.size(); i++) {
                String pn = pinfo.get(i).packageName;
                if (pn.equalsIgnoreCase("com.sina.weibo")) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取分享的handler
     *
     * @return
     */
    public static WbShareHandler getWbShareHandler() {
        return mShareHandler;
    }

    /**
     * 销毁
     */
    public static void onDestory() {
        if (mShareHandler != null) {
            mShareHandler = null;
        }
    }
}

分享总结

本文既有原生分享又有第三方分享SDK分享,后续会把微博分享补上。由于时间匆忙,也未做过多的测试,如果使用过程中遇到什么问题,请在评论区留言。代码已经上传至GitHub,需要主要替换自己的APPID。
分享的轻量级封装传送门

推荐阅读更多精彩内容