2016-01-22 11:11:09 u010976213 阅读数 10515

第三方SDK接入–微信

说明


由于公司的需求,app需要使用微信登录以及分享到微信等功能,近期对微信的第三方登录、分享功能进行了少许研究,并记录如下。

注意点:
1、 需到“微信开放平台”进行注册;
2、 注册完后如果未进行“开发者资质认证“,微信开放平台帐号下的应用,只能拥有分享给朋友或者发送到朋友圈两个功能的权限。
3、 “开发者资质认证“通过后,微信开放平台帐号下的应用,将获得微信登录、智能接口、公众号第三方平台开发等高级能力。
4、 “开发者资质认证“的有效期为1年,有效期最后两个月可申请年审即可续期,审核费用为300元。


微信开放平台

微信集成

成功注册并登陆微信公众平台后可以看到如上页面,我们目前开发用的基本是“资源中心“,”管理中心“。

  • 资源中心中有许多的开发文档可供我们开发时进行参考。
  • 在管理中心我们就可以创建并浏览我们的应用了。

微信集成

注意在创建应用的时候我们不可以在应用的名字中包含“微信“等字样,我就是因为第一次写名称使用了”微信SDK测试“,结果审核不给通过。

微信集成

这里重要的就是应用的签名,关于签名的获取,我们需要先给自己的应用签名,无论你是使用Eclipse还是Android Studio开发都需要先给你自己的应用签名打包成Apk然后安装到手机上,然后使用微信资源中心提供的签名生成apk,安装到手机上。在手机上打开签名生成工具输入你应用的包名即可得到你应用的签名。

以下地址是微信资源中心签名工具的地址:
https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419319167&token=75dc8457c70c2f4324e9cfd255674d7965d65049&lang=zh_CN

填写完整并提交完毕后就需要耐心等待审核通过了,一般半天就会有结果的。审核通过后可以查看应用的信息如下,这里重要的就是应用的AppIDAppSecret,接下来集成SDK的时候要用到这两个值。

微信集成


APP集成微信SDK

1、 所需文件

1) 申请到的AppID。
2) 微信SDK,在资源中心—资源下载—Andoid资源下载中即可下载。微信SDK连接如下:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419319167&lang=zh_CN

微信集成

下载完毕解压后里面最重要的文件就是libammsdk.jar这个jar包了,其他暂时不需要。

2、 应用中集成

2.1、Eclipse导入Jar包

在工程中新建一个libs目录,将开发工具包中libs目录下的libammsdk.jar复制到该目录中.

微信集成

右键单击工程,选择Build Path中的Configure Build Path…,选中Libraries这个tab,并通过Add Jars…导入工程libs目录下的libammsdk.jar文件。(如下图所示)

微信集成

2.2、Android Studio导入Jar包

将工程切换到Project视图,然后将开发工具包中libs目录下的libammsdk.jar复制到该工程的app→libs目录中。

微信集成

右键刚刚粘贴进来的libammsdk.jar文件,在弹出的菜单上点击Add As Library即可,如下图所示。

微信集成

2.3、在代码中使用Jar包

Jar包在导入的时候针对两种开发工具写了Eclipse和Android Studio两种导入方法,导入完毕后进行代码的开发时则不用区分不同开发工具的用法了。

2.3.1、分享给微信好友、分享到朋友圈或者添加到微信收藏

这一步比较简单不会涉及太多的东西。微信分享及收藏目前支持文字、图片、音乐、视频、网页共五种类型。

但是要使你的程序启动后微信终端能响应你的程序,必须在代码中向微信终端注册你的APPID。这一步的原理就相当于:你想联系一个人,那么你就必须先给他打个电话,电话接通了你们才能通话,否则则无法联系。所以这个“打电话”的过程是需要在你要分享之前执行的,你可以在Application中注册,也可以在一个Activity中注册,注意只需要注册一次就好了。其他时候也需要使用IWXAPI,但是不需要再注册了。

注册代码类似如下:

private static final String WECHAT_APP_ID = "你申请应用得到的APPID";
// IWXAPI是第三方app和微信通信的openapi接口
    private IWXAPI iwxapi;
/**
     * 将当前应用的APP_ID注册到微信
     */
    private void regToWx() {
        iwxapi = WXAPIFactory.createWXAPI(this,WECHAT_APP_ID, true);
        iwxapi.registerApp(WECHAT_APP_ID);
}

分享或收藏的目标场景,通过修改scene场景值实现(下文中红色代码处为修改分享的场景)。
发送到聊天界面——WXSceneSession
发送到朋友圈——WXSceneTimeline
添加到微信收藏——WXSceneFavorite
具体的分享的用法如下,在调用封装好的方法之前别忘了下面的两句代码。

private IWXAPI api;
api = WXAPIFactory.createWXAPI(this,WECHAT_APP_ID, true);
2.3.1.1文字类型分享示例

(以下代码中做了封装,与官网中开发文档中不符的以官方文档为主)

/**
     * 分享到微信
     *
     * @param text              文字内容
     * @param isShareToTimeline false表示分享给朋友,true表示分享到朋友圈
     */
    public static void sendReqText(String text, boolean isShareToTimeline) {
        // 初始化一个WXTextObject对象
        WXTextObject textObj = new WXTextObject();
        textObj.text = text;

        // 用WXTextObject对象初始化一个WXMediaMessage对象
        WXMediaMessage msg = new WXMediaMessage();
        msg.mediaObject = textObj;
        // 发送文本类型的消息时,title字段不起作用
        // msg.title = "Will be ignored";
        msg.description = text;

        // 构造一个Req
        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = buildTransaction("text"); // transaction字段用于唯一标识一个请求
        req.message = msg;
//要分享给好友还是分享到朋友圈
        req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;

        // 调用api接口发送数据到微信
        api.sendReq(req);
    }
2.3.1.2图片类型分享示例

(以下代码中做了封装,与官网中开发文档中不符的以官方文档为主)

/**
     * 发送图片到微信
     *
     * @param context           上下文
     * @param index             索引。0:表示Bitmap文件;1:表示手机本地图片;2:表示网络图片(图片的URL)
     * @param isShareToTimeline false表示分享给朋友,true表示分享到朋友圈
     */
    public static void sendReqImg(Context context, int index, boolean isShareToTimeline) {
        switch (index) {
            case 0: {
                Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), R.mipmap.welcome);
                WXImageObject imgObj = new WXImageObject(bmp);

                WXMediaMessage msg = new WXMediaMessage();
                msg.mediaObject = imgObj;

                Bitmap thumbBmp = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
                bmp.recycle();
                msg.thumbData = Util.bmpToByteArray(thumbBmp, true);  // 设置缩略图

                SendMessageToWX.Req req = new SendMessageToWX.Req();
                req.transaction = buildTransaction("img");
                req.message = msg;
                //要分享给好友还是分享到朋友圈
                req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
                api.sendReq(req);

                break;
            }
            case 1: {
                String path = SDCARD_ROOT + "/test.png";
                File file = new File(path);
                if (!file.exists()) {
                    String tip = "图片文件不存在";
                    Toast.makeText(context, tip + " path = " + path, Toast.LENGTH_LONG).show();
                    break;
                }

                WXImageObject imgObj = new WXImageObject();
                imgObj.setImagePath(path);

                WXMediaMessage msg = new WXMediaMessage();
                msg.mediaObject = imgObj;

                Bitmap bmp = BitmapFactory.decodeFile(path);
                Bitmap thumbBmp = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
                bmp.recycle();
                msg.thumbData = Util.bmpToByteArray(thumbBmp, true);

                SendMessageToWX.Req req = new SendMessageToWX.Req();
                req.transaction = buildTransaction("img");
                req.message = msg;
                //要分享给好友还是分享到朋友圈
                req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
                api.sendReq(req);
                break;
            }
            case 2: {
                String url = "http://weixin.qq.com/zh_CN/htmledition/images/weixin/weixin_logo0d1938.png";

                try {
                    WXImageObject imgObj = new WXImageObject();
                    imgObj.imageUrl = url;

                    WXMediaMessage msg = new WXMediaMessage();
                    msg.mediaObject = imgObj;

                    Bitmap bmp = BitmapFactory.decodeStream(new URL(url).openStream());
                    Bitmap thumbBmp = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
                    bmp.recycle();
                    msg.thumbData = Util.bmpToByteArray(thumbBmp, true);

                    SendMessageToWX.Req req = new SendMessageToWX.Req();
                    req.transaction = buildTransaction("img");
                    req.message = msg;
                    //要分享给好友还是分享到朋友圈
                    req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
                    api.sendReq(req);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                break;
            }
            default:
                break;
        }
    }
2.3.1.3音乐类型分享示例

(以下代码中做了封装,与官网中开发文档中不符的以官方文档为主)

/**
     * 分享音乐到微信
     *
     * @param context           上下文
     * @param index             索引。0:表示发送音乐url;1表示发送低带宽音乐url
     * @param musicUrl          音乐的URL
     * @param title             标题
     * @param description       描述
     * @param isShareToTimeline false表示分享给朋友,true表示分享到朋友圈
     */
    public static void sendReqMusic(Context context, int index, String musicUrl, String title, String description, boolean isShareToTimeline) {
        switch (index) {
            case 0: {
                WXMusicObject music = new WXMusicObject();
                music.musicUrl = musicUrl;

                WXMediaMessage msg = new WXMediaMessage();
                msg.mediaObject = music;
                msg.title = title;
                msg.description = description;

                Bitmap thumb = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
                msg.thumbData = Util.bmpToByteArray(thumb, true);

                SendMessageToWX.Req req = new SendMessageToWX.Req();
                req.transaction = buildTransaction("music");
                req.message = msg;
                //要分享给好友还是分享到朋友圈
                req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
                api.sendReq(req);
                break;
            }
            case 1: {
                WXMusicObject music = new WXMusicObject();
                music.musicLowBandUrl = musicUrl;

                WXMediaMessage msg = new WXMediaMessage();
                msg.mediaObject = music;
                msg.title = title;
                msg.description = description;

                Bitmap thumb = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
                msg.thumbData = Util.bmpToByteArray(thumb, true);

                SendMessageToWX.Req req = new SendMessageToWX.Req();
                req.transaction = buildTransaction("music");
                req.message = msg;
//要分享给好友还是分享到朋友圈
                req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
                api.sendReq(req);
                break;
            }
            default:
                break;
        }
    }
2.3.1.4视频类型分享示例

(以下代码中做了封装,与官网中开发文档中不符的以官方文档为主)

/**
     * 分享视频到微信
     *
     * @param context           上下文
     * @param index             索引。0:表示发送视频url;1:表示发送低带宽视频url
     * @param title             标题
     * @param description       描述
     * @param isShareToTimeline false表示分享给朋友,true表示分享到朋友圈
     */
    public static void sendReqVideo(Context context, int index, String VideoUrl, String title, String description, boolean isShareToTimeline) {
        switch (index) {
            case 0: {
                WXVideoObject video = new WXVideoObject();
                video.videoUrl = VideoUrl;

                WXMediaMessage msg = new WXMediaMessage(video);
                msg.title = title;
                msg.description = description;
                Bitmap thumb = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
                msg.thumbData = Util.bmpToByteArray(thumb, true);

                SendMessageToWX.Req req = new SendMessageToWX.Req();
                req.transaction = buildTransaction("video");
                req.message = msg;
//要分享给好友还是分享到朋友圈
                req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
                api.sendReq(req);
                break;
            }
            case 1: {
                WXVideoObject video = new WXVideoObject();
                video.videoLowBandUrl = VideoUrl;

                WXMediaMessage msg = new WXMediaMessage(video);
                msg.title = title;
                msg.description = description;

                SendMessageToWX.Req req = new SendMessageToWX.Req();
                req.transaction = buildTransaction("video");
                req.message = msg;
                //要分享给好友还是分享到朋友圈
                req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
                api.sendReq(req);
                break;
            }
            default:
                break;
        }
    }
2.3.1.5网页类型分享示例

(以下代码中做了封装,与官网中开发文档中不符的以官方文档为主)

/**
     * 分享网址到微信
     *
     * @param context           上下文
     * @param webUrl            要分享的网址
     * @param title             标题
     * @param description       描述
     * @param isShareToTimeline false表示分享给朋友,true表示分享到朋友圈
     */
    public static void sendReqWeb(Context context, String webUrl, String title, String description, boolean isShareToTimeline) {
        WXWebpageObject webpage = new WXWebpageObject();
        webpage.webpageUrl = webUrl;
        WXMediaMessage msg = new WXMediaMessage(webpage);
        msg.title = title;
        msg.description = description;
        Bitmap thumb = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
        msg.thumbData = Util.bmpToByteArray(thumb, true);

        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = buildTransaction("webpage");
        req.message = msg;
//要分享给好友还是分享到朋友圈
        req.scene = isShareToTimeline ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
        api.sendReq(req);
    }

这样只要正确调用了上面的方法基本也就分享成功了,但是这里并不完善,因为分享完毕返回我们的app时微信还会返回给我们的app一些数据,来表示分享成功、失败或者取消的状态。但是这并不影响app的使用,具体的回调函数我们在下文中再做详解。


2.3.2、使用微信登录

使用微信登录会有三种情况:

1、 用户手机上没有微信,那么就提示没有微信,并引导用户下载(官方说法);
2、 用户手机上有微信,但是没有登录,那么需要用户使用账号密码登录;
3、 用户手机上有微信,并且已登录,那么只需要用户点击授权按钮即可;

这里会涉及到许多东西,我们先大概看一下整个请求微信登陆的过程。(这里也是我根据官方的文档然后自己的理解来的,如有错误纰漏之处还请指出)

微信集成

注: 实线代表客户端请求客户端或者服务端
虚线代表服务端相应客户端的请求

0、 首先,用户偷懒不想注册想使用微信的账号密码登录(额,好吧);
1、 用户在app中点击“微信登录”按钮,我们的app这时需要请求微信;
2、 集成好的app去调用微信,微信给出相应(开始提到的三种情况);
3、 用户下载好后,登录后者直接授权后,提交结果给微信服务器;
4、 微信服务器根据结果返回临时code(新版的jar包中返回的code名称改为了token,但实际仍为code的值,使用方法一致);
5、 App根据临时的code、AppID以及AppSecret换取微信的access_token。一同返回的还有refresh_token,因为access_token是有有效期的,过期后可以使用refresh_token进行刷新。但是refresh_token也是有有效期的,一般是30天,这个过期了就需要用户重新进行授权了;
6、 微信服务器响应,返回access_token等信息;
7、 根据得到的access_token可以拿到用户的基本信息,如下:

  • openid 普通用户的标识,对当前开发者帐号唯一
  • nickname 普通用户昵称
  • sex 普通用户性别,1为男性,2为女性
  • province 普通用户个人资料填写的省份
  • city 普通用户个人资料填写的城市
  • country 国家,如中国为CN
  • headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
  • privilege 用户特权信息,json数组,如微信沃卡用户为(chinaunicom)
  • unionid 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。;

到此,微信登录也就明朗了,代码就不必贴了吧,请参考微信的官方文档,剩下文章稍后完善。

2016-03-01 16:26:16 violetjack0808 阅读数 3263

首先说一句,微信SDK是我见过最坑的sdk,文档写的很简略,导致开发者走了好多弯路。

好了,说下实现微信开发接入的步骤

0. 附上demo

  为了便于理解,附上测试demo和生成的APK。

  DEMO下载地址

1. 申请微信开发平台账号、申请appID

  在微信开放平台申请账号,登录后到管理中心创建项目,我这里是Android项目,所以这里是创建移动项目。

  第一步:填应用信息上传应用缩略图,这个随意传。

  第二步:官网随便填(当时填了百度都没事),然后应用平台选择Android,特别注意包名要和需要开发的包名是完全一致的(如果不知道在Manifest中找),而签名则需要通过你KeyStone来决定(请看第二点)。

  第三步:提交审核,只有通过审核后你才能拿到AppID和AppSecret来开发,审核时间挺久的,耐心等待。

2. 签名生成与使用

  2.1 签名生成工具

    到微信开放平台的资源中心去下载签名生成工具并安装到测试手机上,(链接:签名生成工具

  2.2 对测试应用进行签名

    必须始终使用同一个KeyStore来签名。因为签名生成工具是通过应用的包名找到应用的KeyStore,以此来生成签名的。假如更改了KeyStore必须在重新生成签名并更新到微信开放平台。

    具体签名方法可以看blog(构建类型要选择release):Android Studio系列-签名打包

  2.3 安装签名过的APK,并使用签名生成工具生成签名。

  2.4 将生成的签名填在微信开放平台的应用签名中。

3. 实现过程

  3.1 下载jar包并导入

    下载地址:微信开放平台资源中心

  3.2 WXEntryActivity

    在包名下面创建wxapi的包,并在wxapi的包中创建WXEntryActivity继承Activity,实现IWXAPIEventHandler接口,最后在Manifest中设置Activity的exported属性为true。这条很重要,如果不是"包名.wxapi.WXEntryActivity"的路径,就获取不到返回值。下面的这个Activity的简单实现。

public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
    // IWXAPI 是第三方app和微信通信的openapi接口
    private IWXAPI api;
    private static final String APP_ID = "wx5f30e72504c5d0c2";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        api = WXAPIFactory.createWXAPI(this, APP_ID, false);
        api.handleIntent(getIntent(), this);
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onResp(BaseResp resp) {
        Toast.makeText(this, "errCode = " + resp.errCode, Toast.LENGTH_LONG).show();
        switch (resp.errCode) {
            case BaseResp.ErrCode.ERR_OK:
                //分享成功
                break;
            case BaseResp.ErrCode.ERR_USER_CANCEL:
                //分享取消
                break;
            case BaseResp.ErrCode.ERR_AUTH_DENIED:
                //分享拒绝
                break;
        }
        finish();
    }

    @Override
    public void onReq(BaseReq arg0) {

    }
}
     别忘了Manifest的exported属性

<activity
    android:name=".wxapi.WXEntryActivity"
    android:exported="true" />
    这个Activity出现的逻辑是:MainActivity --发送请求--> 微信分享界面 --退出该界面--> WXEntryActivity

    只有跳转到微信进行过操作后才会跳转到这个Activity。

  3.3 MainActivity

    我在这个类中发送了分享图片和文本到朋友圈的请求。

public class MainActivity extends Activity {

    private IWXAPI wxApi;
    private static final String APP_ID = "wx5f30e72504c5d0c2";
    private static final int THUMB_SIZE = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 实例化
        wxApi = WXAPIFactory.createWXAPI(this, APP_ID);
        wxApi.registerApp(APP_ID);
        findViewById(R.id.btnImage).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new AsyncTask<Void, Void, Void>() {
                    @Override
                    protected Void doInBackground(Void... params) {
                        sendImageToWX();
                        return null;
                    }
                }.execute();
            }
        });
        findViewById(R.id.btnText).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendTextToWX();
            }
        });
    }

    /**
     * 分享图片到微信朋友圈
     */
    private void sendImageToWX() {
        Bitmap bmp = getUrlBitmap();
        if (bmp != null) {
            WXImageObject imgObj = new WXImageObject(bmp);
            Bitmap thumbBmp = Bitmap.createScaledBitmap(bmp, THUMB_SIZE, THUMB_SIZE, true);
            bmp.recycle();

            WXMediaMessage msg = new WXMediaMessage();
            msg.mediaObject = imgObj;
            msg.thumbData = bmpToByteArray(thumbBmp, true);  // 设置缩略图

            SendMessageToWX.Req req = new SendMessageToWX.Req();
            req.transaction = buildTransaction("img");
            req.message = msg;
            req.scene = SendMessageToWX.Req.WXSceneTimeline;//发送到朋友圈
//            req.scene = SendMessageToWX.Req.WXSceneSession;//发送给微信好友
            wxApi.sendReq(req);
        }
    }

    /**
     * 获取图片
     *
     * @return
     */
    private Bitmap getUrlBitmap() {
        try {
            URL url = new URL("http://b.hiphotos.baidu.com/image/pic/item/fd039245d688d43f76b17dd4781ed21b0ef43bf8.jpg");
            Bitmap bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
            if (bitmap.getByteCount() > 4096000) {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = bitmap.getByteCount() / 4096000;//缩放比例
                options.inJustDecodeBounds = false;
                bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream(), null, options);
            }
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 分享文本到朋友圈
     */
    private void sendTextToWX() {
        String text = "这是一段测试文本";
        // 初始化一个WXTextObject对象
        WXTextObject textObj = new WXTextObject();
        textObj.text = text;

        // 用WXTextObject对象初始化一个WXMediaMessage对象
        WXMediaMessage msg = new WXMediaMessage();
        msg.mediaObject = textObj;
        msg.description = text;

        // 构造一个Req
        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = buildTransaction("text"); // transaction字段用于唯一标识一个请求
        req.message = msg;
        req.scene = SendMessageToWX.Req.WXSceneTimeline;
//        req.scene = SendMessageToWX.Req.WXSceneSession;//发送给微信好友

        // 调用api接口发送数据到微信
        wxApi.sendReq(req);
    }

    private String buildTransaction(final String type) {
        return (type == null) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis();
    }

    private byte[] bmpToByteArray(Bitmap bmp, boolean needRecycle) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG, 100, output);
        if (needRecycle) {
            bmp.recycle();
        }
        byte[] result = output.toByteArray();
        try {
            output.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}
    到此就可以实现微信的分享功能。

4. 微信SDK Sample的使用

  肯定会有同学和我遇到同样的问题,为什么微信提供的demo无法分享成功。事实是微信开发文档忘了告诉我们需要在运行demo之前将开发工具的Debug.keyStore换成它提供的KeyStore。KeyStore文件可以在SDK的demo中找到。

  由于是Eclipse项目,附上Eclipse修改debug.keystore的路径:Windows->Perferences->Android:Build->Custom debug keystore

  PS:发现SDK提供的demo中有许多分享失败的,暂时还不明真相。另外吐槽下,微信的SDK文档水平简直醉了。




2016-10-11 15:19:29 L18270919464 阅读数 19011

微信公众平台开发:接入JS-SDK和实现分享功能

一、本文是实现微信公众号自定义的分享功能开发,也是亲自实践实现该功的一些总结体会。
首先贴上微信JS-SDK说明文档地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
其次,实现该功能分为四个步骤:
1. 绑定域名,先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”;
2. 引入JS文件,在需要调用JS接口的页面引入JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js
3. 通过config接口注入权限验证配置,所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用);
4. 通过ready接口处理成功验证。

       ![这里写图片描述](http://img.blog.csdn.net/20161011143315576)
 其中最重要的就是第三步,获取config配置的签名signature ,这里着重描述,如下:
 wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '', // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名,见附录1
    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});

贴上微信 JS 接口签名校验工具

> http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign

二、实例说明
1. 前端页面share.html

<!DOCTYPE html>
<head>
    <title>微信js分享接口</title>
    <meta charset="UTF-8">
    <script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
    <script src="http://libs.baidu.com/jquery/1.2.3/jquery.min.js"></script>
</head>
<body>
测试测试测试
<script>
    $(document).ready(function(){
        initPage();
    });
    function initPage() {
        alert(window.location.href);/***用于获得当前连接url用**/
        /***用户点击分享到微信圈后加载接口接口*******/
        $.post("http://xxxxxxx.xxxxx.xxx/demo/wm/share.do",{"url":window.location.href},function(data,status){
            data=eval("("+data+")");
            console.log(data.appid+" "+data.timestamp+" "+data.nonceStr+" "+data.signature);
            wx.config({
                debug: true,
                appId: data.appid,
                timestamp:data.timestamp,
                nonceStr:data.nonceStr,
                signature:data.signature,
                jsApiList: [
                    'checkJsApi',
                    'onMenuShareTimeline',
                    'hideOptionMenu',
                ]
            });
            var shareTitle = "一起分享吧!";
            var shareImg = "http://imgsrc.baidu.com/baike/pic/item/509b9fcb7bf335ab52664fdb.jpg";
            wx.ready(function(){
                alert("准备分享");
                wx.onMenuShareTimeline({
                    title : shareTitle, // 分享标题
                    link : '', // 分享链接
                    imgUrl : shareImg, // 分享图标
                    success : function() {
                        // 用户确认分享后执行的回调函数
                        alert("分享成功");
                    },
                    cancel : function() {
                        // 用户取消分享后执行的回调函数
                        alert("分享取消");
                    }
                });
                //wx.hideOptionMenu();/***隐藏分享菜单****/
            });
        });
    }
</script>
</body>
</html>
  1. 后台处理请求
    WeixinController.java
    @RequestMapping(value = "/share", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> share(String url) throws Exception {
        Map<String, String> ret = new HashMap<String, String>();
        String jsapi_ticket = WeixinUtil.getJsapiTicket();
        String timestamp = Long.toString(System.currentTimeMillis() / 1000);
        String nonceStr = UUID.randomUUID().toString();
        String signature = SignUtil.getSignature(
            jsapi_ticket, nonceStr, timestamp,
            url);
        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonceStr);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
        ret.put("appid", "wxc6599d2c37b5e478");
        return ret;
    }

3.获取accessToken

    /**
     * 获取accessToken
     * 
     * @return
     * @throws ParseException
     * @throws IOException
     */
    public static AccessToken getAccessToken() throws ParseException, IOException {
        AccessToken token = new AccessToken();
        String url = ACCESS_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
        JSONObject jsonObject = doGetStr(url);
        if (jsonObject != null) {
            token.setToken(jsonObject.getString("access_token"));
            token.setExpiresIn(jsonObject.getInt("expires_in"));
        }
        return token;
    }

4.获取jsapi_ticket

    /**
     * 
     * 获取jsapi_ticket
     * 
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static String getJsapiTicket() throws ParseException, IOException {
        AccessToken token = WeixinUtil.getAccessToken();
        String url = JSAPI_TICKET.replace("ACCESS_TOKEN", token.getToken());
        JSONObject jsonObject = doGetStr(url);
        String jsapi_ticket = null;
        if (jsonObject != null) {
            jsapi_ticket = jsonObject.getString("ticket");
        }
        return jsapi_ticket;
    }

5.获取签名
SignUtil .java

import java.security.MessageDigest;

public class SignUtil {

    /**
     * 获得分享链接的签名。
     * @param ticket
     * @param nonceStr
     * @param timeStamp
     * @param url
     * @return
     * @throws Exception
     */
    public static String getSignature(String ticket, String nonceStr, String timeStamp, String url) throws Exception {
        String sKey = "jsapi_ticket=" + ticket
                + "&noncestr=" + nonceStr + "&timestamp=" + timeStamp
                + "&url=" + url;
        System.out.println(sKey);
        return getSignature(sKey);
    }


 /**
     * 验证签名。
     * 
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static String getSignature(String sKey) throws Exception {
        String ciphertext = null;
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        byte[] digest = md.digest(sKey.toString().getBytes());
        ciphertext = byteToStr(digest);
        return ciphertext.toLowerCase();
    }

 /** 
     * 将字节数组转换为十六进制字符串 
     *  
     * @param byteArray 
     * @return 
     */ 
    private static String byteToStr(byte[] byteArray) {  
        String strDigest = "";  
        for (int i = 0; i < byteArray.length; i++) {  
            strDigest += byteToHexStr(byteArray[i]);  
        }  
        return strDigest;  
    }  
  /** 
     * 将字节转换为十六进制字符串 
     *  
     * @param mByte 
     * @return 
     */ 
    private static String byteToHexStr(byte mByte) {  
        char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };  
        char[] tempArr = new char[2];  
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];  
        tempArr[1] = Digit[mByte & 0X0F];  

        String s = new String(tempArr);  
        return s;  
    }
}

以上是大部分代码,稍加修改就可实现
三、注意要点
1.invalid signature签名错误问题
首先需确认签名算法正确,可用 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。也就是你自己后台生成签名要和微信校验算法生成的签名一致才可以(可能大小写不同)。

1.1是否获取到需要填充字段的值
`console.log(data.appid+" "+data.timestamp+" "+data.nonceStr+" "+data.signature);`
1.2确认展示页面config接口注入权限验证配置文件对应字段是否一致
```
wx.config({
               debug: true,
               appId: data.appid,
               **timestamp:data**.**timestamp**,
               **nonceStr:data**.**nonceStr**,
               **signature:data**.**signature**,
               jsApiList: [
                   'checkJsApi',
                   'onMenuShareTimeline',
                   'hideOptionMenu',
               ]
           });
```
1.3确认url是页面完整的url(请在当前页面alert(location.href.split('#')[0])确认),包括'http(s)://'部分,以及'?'后面的参数部分,但不包括'#'hash后面的部分。记住签名用的url必须是调用JS接口页面的完整URL。

1.4确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。

2.虽然是自定义的分享接口,但是只有当你用微信客户端打开,在最上面右边的 “分享到朋友圈”按钮按的时候效果才会出来。

wx.ready(function(){

    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});

3.根据自己的需求修改下面的内容,就可实现分享功能
获取“分享到朋友圈”按钮点击状态及自定义分享内容接口

wx.onMenuShareTimeline({
    title: '', // 分享标题
    link: '', // 分享链接
    imgUrl: '', // 分享图标
    success: function () { 
        // 用户确认分享后执行的回调函数
    },
    cancel: function () { 
        // 用户取消分享后执行的回调函数
    }
});

获取“分享给朋友”按钮点击状态及自定义分享内容接口

wx.onMenuShareAppMessage({
    title: '', // 分享标题
    desc: '', // 分享描述
    link: '', // 分享链接
    imgUrl: '', // 分享图标
    type: '', // 分享类型,music、video或link,不填默认为link
    dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
    success: function () { 
        // 用户确认分享后执行的回调函数
    },
    cancel: function () { 
        // 用户取消分享后执行的回调函数
    }
});

4.确保一定缓存access_token和jsapi_ticket。由于使用的是微信测试账号所以没有做这方面的优化

以上是全部内容,本人只是实现基本功能,如有问题请指正

2019-01-25 11:34:12 Z875983491 阅读数 593

手机微信群控系统独立出来的微信二次开发SDK,可开发群控云控引擎,微信客服系统,快速研发微信营销工具软件、微信营销手机、微信群控云控系统、微信SCRM客服系统、微信淘宝客导购返利机器人,微信群管理机器人等!接入本SDK即可快速实现微信的二次开发,轻松开发出上述软件功能,SDK接入简单、兼容性好、拓展性强,始终支持微信官方最新版本,并定时维护升级!

微信二次开发SDK的部分API功能:

微信加好友功能API

1、导入手机通讯录

2、手机通讯录加好友

3、加指定微信群好友

4、自动通过微信好友请求

5、手机模拟定位

6、附近人加好友

7、各种账号加好友(QQ号、手机号、微信号)

8、陌陌自动回复引流加好友

9、探探自动回复引流加好友

10、摇一摇打招呼加好友

...上述这些API可开发手机微信群控系统、云控系统、微信营销手机等系列爆粉营销软件!

微信群发功能API

1、微信群发好友(文字、图片、长短视频、链接、名片)

2、微信群发群(文字、图片、长短视频、链接、名片)

3、微信群发收藏(文字、图片、长短视频、链接、名片)

...上述这些API可开发群控、云控等群发系列营销软件!

微信群管理功能API

1、入群欢迎语

2、指定群管理员

3、群管理统计(活跃度、发言数、总人数、邀请数、退出数等)

4、群管理员踢人

5、群关键词回复,群智能客服

6、建群、拉人功能接口

...上述这些API可开发微信群管家、群营销大师等系列微信群管理营销软件!

微信营销功能API

1、发朋友圈(图文、链接、长短视频)

2、发长视频到微信朋友圈

3、朋友圈点赞,按比例点赞,按数量点赞

4、发漂流瓶信息

5、智能自动回复

6、同步指定微信好友朋友圈

...这些API可开发群控、云控、营销手机等系列微信营销软件!

微信聊天对话功能API

1、微信上线通知

2、微信下线通知

3、微信新增好友通知

4、微信删除好友通知

5、微信好友发来信息通知

6、回复微信好友聊天消息

7、聊天执行结果通知

8、获取微信通讯录好友列表

9、获取群列表

10、好友请求添加好友的通知

11、获取手机客户端上微信的二维码

...这些API可开发群控、云控、微信客服系统等系列微信营销软件!

微信营销辅助工具API

1、打开微信

2、定时执行任务

3、任务执行结果通知

4、清理僵尸粉

5、清理微信空间

6、智能随机养号

7、智能抢红包服务

8、微信运动智能点赞

9、提取指定朋友圈内容

10、关注指定公众号

...这些API可辅助开发群控、云控、营销手机、微信客服系统等系列微信营销软件!

其他营销功能API

1、QQ发布说说(暂时未上线)

2、QQ群发好友、QQ群发群、QQ群发群好友(暂时未上线)

3、智能头条评论营销(暂时未上线)

...这些API可辅助开发全网营销系列软件!

没有将API功能全部写出,调用API接口有时候再写出来!SDK可按需定制!咨询请加微信tangjinjinwx

详细接口请参考http://www.yunlauncher.com/Blog/articles/119.html

2018-07-26 21:47:04 Happy_zailing 阅读数 1052

Untiy端做的准备

unity调用java方法一共有四种形式,分别是:

没有返回值的普通方法
AndroidJavaObject jo = new AndroidJavaObject("android.content.res.Configuration");
jo.Call("setToDefaults");

带有返回值的普通方法
AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some string");
int hash = jo.Call<int>("hashCode");

没有返回值的静态方法
AndroidJavaObject jo = new AndroidJavaObject("android.os.Binder");
jo.CallStatic("flushPendingCommands");

带有返回值的静态方法
AndroidJavaObject jo = new AndroidJavaObject("java.lang.String");
string valueString = jo.CallStatic<string>("valueOf", 42.0);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

我用的方法如下,第一个是获取java的类,参数就是类的路径(我的类名:Interface)。第二个方法就是调用类的静态方法,第一个参数是方法名,第二个参数的需要传递的参数,自己定义吧。

AndroidJavaObject jo = new AndroidJavaObject("com.sdk.my.Interface");
jo.CallStatic("InterFaceUnity","msg");
  • 1
  • 2

在unity端需要调用的接口的地方定义好方法,然后在Building Settings中导出Google Android Project,Export导出。

Android端做的准备

打开AndroidStudio导入刚才从unity导出的Android工程。 
在AndroidStudio开始界面,或者在已打开的工程中选择File new 中选择 
等待导入完成。导入完成之后在把下载的微信jar包导入到AndroidStudio中,首先拷贝到libs目录下,然后右击该文件选择addAsLibrary。 
下面的函数是unity调用的静态函数,通过函数传来的参数可以去判断需要调起什么函数。

public class Interface {
    static public void InterFaceUnity(String msg)
    {
        Log.i("sv111",msg);
//        WeChatController.GetInstance().SendToWei("111");
//        WeChatController.GetInstance().ShareLinkUrl();
          WeChatController.GetInstance().WeChatLogin();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

新建一个WeChatController类

public class WeChatController {
    static private IWXAPI api; 
    private static WeChatController _instance;

    private  WeChatController(){};
    public static WeChatController GetInstance(){
        if(_instance == null)
        {
            _instance = new WeChatController();
        }
        return _instance;
    }
    //此方法在应用程序启动时需要调用把APP_ID注册到微信中 可以在主Activity中的OnCreate()中调用此函数。
    public void RegToWx(Context context){ 
        api = WXAPIFactory.createWXAPI(context,Constants.APP_ID);
        api.registerApp(Constants.APP_ID);
    }
    //分享文字
    public void SendToWei(String name) {
        WXTextObject textObj = new WXTextObject();
        textObj.text = "textObj.text";
        // 用WXTextObject对象初始化一个WXMediaMessage对象
        WXMediaMessage msg = new WXMediaMessage();
        msg.mediaObject = textObj;
        // 发送文本类型的消息时,title字段不起作用
        // msg.title = "Will be ignored";
        msg.description = "msg.description";
        // 构造一个Req
        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = "sharetext"; // transaction字段用于唯一标识一个请求
        req.message = msg;
        req.scene = true ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
        // 调用api接口发送数据到微信
        api.sendReq(req);
    }
    public void ShareLinkUrl() {
        WXWebpageObject webpage = new WXWebpageObject();
        webpage.webpageUrl = "http://www.baidu.com";
        //用WXMebpageObject 对象初始化一个WXMediaMessage对象,填写标题,描述
        WXMediaMessage msg = new WXMediaMessage(webpage);
        msg.title = "百度大法好";
        msg.description = "网页描述:我是饭太黏";  //描述只在发送给朋友时显示,发送到朋友圈不显示

        //链接图片
        Resources re = UnityPlayerActivity._instance.getResources();  //通过一个活动的Activity  (UnityPlayerActivity._instance)提换为可用的Activity
        Bitmap thumb = BitmapFactory.decodeResource(re,R.drawable.app_icon); //图片小于32k,如果drawable下没有图片可以把图片的代码注释掉;
        msg.thumbData = Util.bmpToByteArray(thumb, true);

        SendMessageToWX.Req req = new SendMessageToWX.Req();
        req.transaction = "shareurl";
        req.message = msg;
        req.scene = true ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
        api.sendReq(req);
    }
    public void WeChatLogin()
    {
        Log.i("sv111", "WeChatLogin"+R.drawable.app_icon);
        SendAuth.Req req = new SendAuth.Req();
        req.transaction = "Login";
        req.scope = "snsapi_userinfo";   // 应用授权作用域,如获取用户个人信息则填写snsapi_userinfo
        req.state = "wechat_sdk_demo_test";
        api.sendReq(req);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

新建一个Util类,此类是直接从微信Demo中直接拷贝过来的,分享链接会用到 具体我也不是很懂。可以直接从demo中拷贝。在此不再贴出来了。 
新建一个Constants类,此类中存放一些id之类的信息。

public class Constants {
    public static final String APP_ID = "写自己的Id"; //从微信申请的id
    public static class ShowMsgActivity {
        public static final String STitle = "showmsg_title";
        public static final String SMessage = "showmsg_message";
        public static final String BAThumbData = "showmsg_thumb_data";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

微信回调函数,在你的包名相应目录下新建一个wxapi目录,并在该wxapi目录下新增一个WXEntryActivity类,该类继承自Activityb. 实现IWXAPIEventHandler接口,微信发送的请求将回调到onReq方法,发送到微信请求的响应结果将回调到onResp方法,在WXEntryActivity中将接收到的intent及实现了IWXAPIEventHandler接口的对象传递给IWXAPI接口的handleIntent方法,例如

public class WXEntryActivity extends Activity implements IWXAPIEventHandler{

    private static final int TIMELINE_SUPPORTED_VERSION = 0x21020001;

    private IWXAPI api;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
      //  setContentView(R.layout.entry);
        Log.i("sv111","onCreate");
        api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);

        api.handleIntent(getIntent(), this);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        Log.i("sv111","onNewIntent");
        super.onNewIntent(intent);

        setIntent(intent);
        api.handleIntent(intent, this);
    }

    @Override
    public void onReq(BaseReq req) {
        Log.i("sv111","onReq");
        switch (req.getType()) {
            case ConstantsAPI.COMMAND_GETMESSAGE_FROM_WX:
                goToGetMsg();
                break;
            case ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
                goToShowMsg((ShowMessageFromWX.Req) req);
                break;
            default:
                break;
        }
    }

    @Override
    //请求微信后的回调
    public void onResp(BaseResp resp) {
        Log.i("sv111","onresp");
        int result = 0;
        String str = "";
        int type = 0;
        if(resp.transaction.equals("sharetext") )  //通过唯一标示去识别
        {
            str = "分享文字";
            type = 1;
        }else if (resp.transaction.equals("shareurl") )
        {
            str = "分享链接";
            type = 2;
        }else if (resp.transaction.equals("Login") )
        {
            str = "登陆";
            type = 3;
        }
        switch (resp.errCode) {
            case BaseResp.ErrCode.ERR_OK:  //正确返回
                str += "成功";
                if (type == 3)
                {
                    OnRespAuth((SendAuth.Resp)resp);  //强转为SendAuth.Resp对象
                }
                break;
            case BaseResp.ErrCode.ERR_USER_CANCEL: //用户取消
                str += "取消";
                break;
            case BaseResp.ErrCode.ERR_AUTH_DENIED: //认证否决  (用户拒绝授权)
                str += "失败";
                break;
            case BaseResp.ErrCode.ERR_SENT_FAILED: //发送失败
                str += "发送失败";
                break;
            default:
                str += "未知错误";
                break;
        }

        Toast.makeText(this, str, Toast.LENGTH_LONG).show();
        finish();///
    }

    public void OnRespAuth(SendAuth.Resp resp)
    {
        Log.i("sv111","OnRespAuth");
        Log.i("sv111",resp.code);
        Log.i("sv111",resp.url);
    }
}
也可以拿微信demo里的WXEntryActivity类进行修改。
没有更多推荐了,返回首页