集成过程中踩了不少坑,我会尽量写的详细一点。
关于开发平台申请appID,appSecret,下载SDK等步骤不表。
环境配置 libammsdk.jar导入lib,androidManifest中添加基本权限 <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>接收微信的回调消息如分享是否成功,登录授权等信息是否拿到,需要注册一个Activity,这个Activity必须建立在包名下的wxapi文件夹中。 比如项目包名为com.android.test,需要在包名下新建一个wxapi的文件夹,并在其中新建WXEntryActivity类,实现IWXAPIEventHandler接口。 注意类名必须为WXEntryActivity。androidManifest中:
<activity android:name=".wxapi.WXEntryActivity" android:exported="true"> </activity>WXEntryActivity 中:
public class WXEntryActivity extends Activity implements IWXAPIEventHandler { private IWXAPI api; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //初始化api并向微信注册应用。 api = WXAPIFactory.createWXAPI(this, APP_ID); api.registerApp(APP_ID); api.handleIntent(getIntent(), this); } // 微信发送请求到第三方应用时,会回调到该方法 @Override public void onReq(BaseReq baseReq) { } // 第三方应用发送到微信的请求处理后的响应结果,会回调到该方法 @Override public void onResp(BaseResp baseResp) { switch (baseResp.errCode) { case BaseResp.ErrCode.ERR_OK: //如果集成了分享和登录功能,那么可以通过baseResp.getType()来判断是哪个回调信息,1为登录授权,2为分享 //如果是登录授权,那么调用了登录api以后,这里会返回获取accessToken需要的code SendAuth.Resp sendResp = (SendAuth.Resp) baseResp; String code = sendResp.code; break; case BaseResp.ErrCode.ERR_SENT_FAILED: break; case BaseResp.ErrCode.ERR_USER_CANCEL: break; } } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); //如果分享的时候,该已经开启,那么微信开始这个activity时,会调用onNewIntent,所以这里要处理微信的返回结果 setIntent(intent); api.handleIntent(intent, this); } public void getAccessToken(String code, String secret) { //微信登录时,需要通过code请求url获取accesstoken。 String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + "appid=" + APP_ID + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code"; //以下是网络请求拿到accesstoken以及openid. ... } public void getUserInfo(String accessToken, String openId) { //如果获取到了AccessToken和openid,请求url获取用户信息 String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId; ... } } 微信分享及登录 我新建了WeChatManager类,在其中做了不同类型的分享及登录。 public class WeChatManager { private Context mContext; private IWXAPI wxapi; private static WeChatManager mInstance; private static final int THUMB_SIZE = 150; public static final int WECHAT_SHARE_WAY_TEXT = 1; //文字 public static final int WECHAT_SHARE_WAY_PICTURE = 2; //图片 public static final int WECHAT_SHARE_WAY_WEBPAGE = 3; //链接 public static final int WECHAT_SHARE_WAY_VIDEO = 4; //视频 public static final int WECHAT_SHARE_TYPE_TALK = SendMessageToWX.Req.WXSceneSession; //会话 public static final int WECHAT_SHARE_TYPE_FRIENDS = SendMessageToWX.Req.WXSceneTimeline; //朋友圈 private ShareContent mShareContentText, mShareContentPicture, mShareContentWebpag, mShareContentVideo; public WeChatManager(Context context) { this.mContext = context; initWxApi(); } private void initWxApi() { wxapi = WXAPIFactory.createWXAPI(mContext, APP_ID, true); wxapi.registerApp(APP_ID); } /** * 通过微信分享 * * @param shareContent 分享的方式(文本、图片、链接) * @param shareType 分享的类型(朋友圈,会话) */ public void shareByWebchat(ShareContent shareContent, int shareType) { switch (shareContent.getShareWay()) { case WECHAT_SHARE_WAY_TEXT: shareText(shareContent, shareType); break; case WECHAT_SHARE_WAY_PICTURE: sharePicture(shareContent, shareType); break; case WECHAT_SHARE_WAY_WEBPAGE: shareWebPage(shareContent, shareType); break; case WECHAT_SHARE_WAY_VIDEO: shareVideo(shareContent, shareType); break; } } private abstract class ShareContent { protected abstract int getShareWay(); protected abstract String getContent(); protected abstract String getTitle(); protected abstract String getURL(); protected abstract byte[] getPictureResource(); } /** * 设置分享链接的内容 * * @author chengcj1 */ public class ShareContentWebpage extends ShareContent { private String title; private String content; private String url; private byte[] pictureResource; public ShareContentWebpage(String title, String content, String url, byte[] pictureResource) { this.title = title; this.content = content; this.url = url; this.pictureResource = pictureResource; } @Override protected int getShareWay() { return WECHAT_SHARE_WAY_WEBPAGE; } @Override protected String getContent() { return content; } @Override protected String getTitle() { return title; } @Override protected String getURL() { return url; } @Override protected byte[] getPictureResource() { return pictureResource; } } /* * 获取网页分享对象 */ public ShareContent getShareContentWebpag(String title, String content, String url, byte[] pictureResource) { if (mShareContentWebpag == null) { mShareContentWebpag = new ShareContentWebpage(title, content, url, pictureResource); } return (ShareContentWebpage) mShareContentWebpag; } /* * 分享链接 */ private void shareWebPage(ShareContent shareContent, int shareType) { WXWebpageObject webpage = new WXWebpageObject(); webpage.webpageUrl = shareContent.getURL(); WXMediaMessage msg = new WXMediaMessage(webpage); msg.title = shareContent.getTitle(); msg.description = shareContent.getTitle(); if(shareContent.getPictureResource() != null) { msg.thumbData = shareContent.getPictureResource(); } else { Bitmap thumb = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.ic_launcher); Bitmap thumbBitmap = Bitmap.createScaledBitmap(thumb, THUMB_SIZE, THUMB_SIZE, true); thumb.recycle(); msg.thumbData = bitmapToByteArray(thumbBitmap); } SendMessageToWX.Req req = new SendMessageToWX.Req(); req.transaction = buildTransaction("webpage"); req.message = msg; req.scene = shareType; wxapi.sendReq(req); } }这里我建议分享的图片类型,如果是工程中的图片,就用int,如果需要是网上下载下来的,或者本地图册获取到的,就用byte[],这里我在下面说明原因。 关于不同类型的分享,应该传递什么必要参数,我建议在文档中找一找。在做链接分享的时候,我要上传的图片是从服务器下载下载的,之前是将图片类型设置为bitmap传入的。 但是常会出现图片大小超过32K的情况(规定链接分享上传的图片大小不能超过32K),如果需要用bitmap传入,这里可以用采样率压缩图片。 压缩图片有质量压缩和采样率压缩,质量压缩即:
public byte[] compressBmp(Bitmap bitmap){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); int options = 100; bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); while (baos.toByteArray().length > 32768 && options!= 10) { baos.reset(); options -= 10; bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos); } return baos.toByteArray(); }质量压缩能改变File或者stream流的大小,方便上传服务器等操作,但是像素是没有改变的,也就是说转变成bitmap以后的大小还是和以前一样。 如有需要可以去查查更详细的质量压缩的资料。 那么bitmap改变不了大小,传给wxapi的图片还是超过限制,就需要采样率压缩,或者叫做尺寸压缩。我们将流中的图片利用BitmapFactory转成bitmap时,设置 options.inJustDecodeBounds = true,这样将只会读取图片边框,不会将数据读入内存,然后计算长宽比,设置options.inSampleSize = be 采样率压缩会使图片失真。
/** 以上是质量压缩,不会减少图片的像素,但是BitmapFactory.decode到内存中像素和大小仍然没有变 **/ ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; Bitmap newBitmap = BitmapFactory.decodeStream(bis, null, options); options.inJustDecodeBounds = false ; int w = options.outWidth; int h = options.outHeight; //我们设置为微信的占位图的分辨率 float ww = THUMB_SIZE; float hh = THUMB_SIZE; int be = 1; //缩放比,1表示不缩放 if(w > h && w > ww){ be = (int)(options.outWidth*1.0f / ww) + 1; } else if(h > w && h > hh){ be = (int)(options.outHeight*1.0f / hh) + 1; } if(be <= 0) be = 1; options.inSampleSize = be; bis = new ByteArrayInputStream(bos.toByteArray()); newBitmap = BitmapFactory.decodeStream(bis, null, options);登录就相对简单了:
public void loginWX(){ SendAuth.Req req = new SendAuth.Req(); req.scope = "snsapi_userinfo"; //授权域,snsapi_userinfo 表示获取用户个人信息 req.state = "wechat_sdk_demo_test"; wxapi.sendReq(req); }剩下的步骤在WXEntryActivity中实现。
如果客户端没有QQ,那么Android是不能直接进行QQ网页授权登录的!
如果客户端没有QQ,那么Android是不能直接进行QQ网页授权登录的!
如果客户端没有QQ,那么Android是不能直接进行QQ网页授权登录的!
说三遍,因为官方文档上写了可以!(坑),而且你用腾讯提供的测试appid也是能网页授权的,但是换到自己的就不行,因为腾讯就已经不提供网页授权的方式了。 当然,如有需要,可以写前端代码,用js桥获取到前端拿到的用户数据。
我从官方下载的jar包为mta-sdk-1.6.2.jar,open_sdk_r5756.jar。 androidmanifest中配置基本权限,声明类:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- SDK2.1新增获取用户位置信息 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.GET_TASKS"/> <activity android:name="com.tencent.tauth.AuthActivity" android:launchMode="singleTask" android:noHistory="true" > <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 这地方的222222需要用你在开放平台申请的appid替换 --> <data android:scheme="tencent222222" /> </intent-filter> </activity> <activity android:name="com.tencent.connect.common.AssistActivity" android:configChanges="orientation|keyboardHidden" android:screenOrientation="behind" android:theme="@android:style/Theme.Translucent.NoTitleBar" />在分享类里面,我们先初始化Tencent类
private void initQQApi() { mTencent = Tencent.createInstance(QQ_APP_ID, getApplicationContext()); mListener = new ShareQQListener(); initDialog(); }其中mListener实现了IUiListener,主要接受QQ的回调消息,包括分享的和登录的。
/** * @desc QQ分享回调类 */ class ShareQQListener implements IUiListener { @Override public void onComplete(Object o) { //TODO 同样都有QQ登录和分享的回调,这个可以分开写。 //QQ登录先初始化openId 和 Token initOpenidAndToken((JSONObject) o); //再获取用户信息 getUserInfo(); } @Override public void onError(UiError uiError) { finish(); } @Override public void onCancel() { hideDialog(); finish(); } }当然要接收到QQ的回调消息,还需要在onActivityResult添加一些代码:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (mListener == null) mListener = new ShareQQListener(); if (requestCode == Constants.REQUEST_LOGIN || requestCode == Constants.REQUEST_APPBAR) { Tencent.onActivityResultData(requestCode, resultCode, data, mListener); } }分享分好友和空间,默认好友,params.putInt(QQShare.SHARE_TO_QQ_EXT_INT, QQShare.SHARE_TO_QQ_FLAG_QZONE_AUTO_OPEN)就可以分享空间。
public void shareToQQFriends() { final 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, "要分享的标题"); params.putString(QQShare.SHARE_TO_QQ_SUMMARY, "要分享的摘要"); params.putString(QQShare.SHARE_TO_QQ_TARGET_URL, "http://www.qq.com/news/1.html"); params.putString(QQShare.SHARE_TO_QQ_IMAGE_URL, "http://imgcache.qq.com/qzone/space_item/pre/0/66768.gif"); mTencent.shareToQQ(QQShareActivity.this, params, mListener); }登录授权更简单:
public void loginQQ() { String SCOPE = "all"; //授权域 if (!mTencent.isSessionValid()) { mTencent.login(this, SCOPE, mListener); } }同样的,我们也要在回调消息里面拿到openid和accessToken,才能获取到用户信息
private void initOpenidAndToken(JSONObject jsonObject) { try { String token = jsonObject.getString(Constants.PARAM_ACCESS_TOKEN); String expires = jsonObject.getString(Constants.PARAM_EXPIRES_IN); String openid = jsonObject.getString(Constants.PARAM_OPEN_ID); if (token != null && expires != null && openid != null) if (!token.isEmpty() && !expires.isEmpty() && !openid.isEmpty()) { mTencent.setAccessToken(token, expires); mTencent.setOpenId(openid); userId = openid; } } catch (JSONException e) { e.printStackTrace(); } } public void getUserInfo() { if (mTencent != null && mTencent.isSessionValid()) { UserInfo userInfo = new UserInfo(QQShareActivity.this, mTencent.getQQToken()); userInfo.getUserInfo(new IUiListener() { @Override public void onComplete(Object o) { Log.v(TAG, o.toString()); //TODO hideDialog(); finish(); } @Override public void onError(UiError uiError) { hideDialog(); finish(); } @Override public void onCancel() { hideDialog(); finish(); } }); } }我下载的新浪微博的SDK是weiboSDKCore_3.1.4.jar 注意微博的SDK包里面有个lib文件夹,里面所有文件夹和.so库文件都要拷到工程中去
配置androidmanifest
<activity android:name="com.sina.weibo.sdk.component.WeiboSdkBrowser" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="adjustResize" android:exported="false" > </activity> <service android:name="com.sina.weibo.sdk.net.DownloadService" android:exported="false"> </service>添加DownloadService这个service的时候会报红,提示找不到这个类。是不影响运行的。
做分享的类中要加过滤器:
<activity android:name="xxx.xxx.xxx"> <intent-filter> <action android:name="com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> 初始化微博api: public void initSinaApi(Bundle savedInstanceState){ mWeiboShareAPI = WeiboShareSDK.createWeiboAPI(this, WEIBO_APP_KEY); mWeiboShareAPI.registerApp(); // 当 Activity 被重新初始化时(该 Activity 处于后台时,可能会由于内存不足被杀掉了), // 需要调用 {@link IWeiboShareAPI#handleWeiboResponse} 来接收微博客户端返回的数据。 // 执行成功,返回 true,并调用 {@link IWeiboHandler.Response#onResponse}; // 失败返回 false,不调用上述回调 if (savedInstanceState != null) { mWeiboShareAPI.handleWeiboResponse(getIntent(), this); } } 分享的类实现IWeiboHandler.Response接口,接收回调信息 @Override public void onResponse(BaseResponse baseResponse) { if(baseResponse != null){ switch (baseResponse.errCode){ case WBConstants.ErrorCode.ERR_OK : finish(); break; case WBConstants.ErrorCode.ERR_CANCEL : finish(); break; case WBConstants.ErrorCode.ERR_FAIL : finish(); break; } } } 同样需要在onNewIntent中添加一些代码 @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); mWeiboShareAPI.handleWeiboResponse(intent,this); }对于微博分享有mWeiShareAPI这种SSO授权的方式,分享的时候调用非常简单:
WeiboMultiMessage weiboMessage = new WeiboMultiMessage(); //文字 TextObject textObject = new TextObject(); textObject.text = content; weiboMessage.textObject = textObject; //图片 ImageObject imageObject = new ImageObject(); imageObject.setImageObject(bm); weiboMessage.mediaObject = imageObject; //发送 SendMultiMessageToWeiboRequest request = new SendMultiMessageToWeiboRequest(); request.transaction = String.valueOf(System.currentTimeMillis()); request.multiMessage = weiboMessage; mWeiboShareAPI.sendRequest(this, request);但是如果手机上没有微博客户端,是没法进行SSO授权并分享的,所以我用all in one的模式。
public void sendShareRequest(String content,Bitmap bm){ WeiboMultiMessage weiboMessage = new WeiboMultiMessage(); //文字 TextObject textObject = new TextObject(); textObject.text = content; weiboMessage.textObject = textObject; //图片 ImageObject imageObject = new ImageObject(); imageObject.setImageObject(bm); weiboMessage.mediaObject = imageObject; // 2. 初始化从第三方到微博的消息请求 SendMultiMessageToWeiboRequest request = new SendMultiMessageToWeiboRequest(); // 用transaction唯一标识一个请求 request.transaction = String.valueOf(System.currentTimeMillis()); request.multiMessage = weiboMessage; AuthInfo authInfo = new AuthInfo(this, Config.WEIBO_APPKEY, Config.WEIBO_REDIRECT_URL, Config.WEIBO_SCOPE); Oauth2AccessToken accessToken = AccessTokenKeeper.readAccessToken(getApplicationContext()); String token = ""; if (accessToken != null) { token = accessToken.getToken(); } mWeiboShareAPI.sendRequest(this, request, authInfo, token, new WeiboAuthListener() { @Override public void onWeiboException( WeiboException arg0 ) { } @Override public void onComplete( Bundle bundle ) { // TODO Auto-generated method stub Oauth2AccessToken newToken = Oauth2AccessToken.parseAccessToken(bundle); AccessTokenKeeper.writeAccessToken(getApplicationContext(), newToken); Toast.makeText(getApplicationContext(), "onAuthorizeComplete token = " + newToken.getToken(), Toast.LENGTH_SHORT).show(); } @Override public void onCancel() { } }); }