微信和 QQ 的文档烂到一种境界,这几乎已经是所有人的共识了,如果希望像友盟那样,按照文档一步步配置集成 SDK 就能实现所想要的功能,基本算是痴心妄想。微信的文档上次更新是在 2012 年,QQ 的文档更新实在 2013 年。其实真正代码实现部分很简单,只是文档实在太烂…这也是我写这篇文章的原因。愿天下没有难读的文档。阿门…
###微信登录
我觉着有必要吐槽一下网易,微信的文档已经烂的突破天际了,易信居然还要抄过去,还抄的几乎一模一样,真是醉了。
微信安卓资源下载,在这里可以下载到官方提供的有关微信所有的资源了。在集成 SDK 之前,一定要先申请开通登录权限和支付权限,而且提交到后台的包名和签名 md5 值一定要应用的包名和签名 md5 值一样,同时,还得注意,应用本身的签名 md5 值是带有冒号的,而微信后台索要填写的签名 md5 值是没有冒号的,如果你多加了冒号,多出了位数,那么抱歉,是不会有任何错误提示的。
非开发部分的工作完了之后,微信官方会提供一个 appid 和 appsecret ,就可以开始正式地集成 SDK 了。
- 要导入 libammsdk.jar 这个包;
在 Application 里注册;
1
2
3
4public static IWXAPI wechatApi;
public static Tencent mTencent;
wechatApi = WXAPIFactory.createWXAPI(this, Constants.WECHAT_APP_ID, true);
wechatApi.registerApp(Constants.WECHAT_APP_ID);//填写上自己的 appid组织获取微信 openid 的请求参数并发起请求,只要发起这个请求成功,就满足拉起登录界面的充分条件了;
1
2
3
4
5
6private static final String WX_REQ_SCOPE = "snsapi_userinfo";
private static final String WX_REQ_STATE = "kuaimai_wx_login";
final SendAuth.Req req = new SendAuth.Req();
req.scope = WX_REQ_SCOPE;
req.state = WX_REQ_STATE;
BaseApplication.wechatApi.sendReq(req);在项目的包名同一级目录下创建一个包,包名必须是 wxapi ,然后再在这个包里创建一个名为 WXEntryActivity 的 Activity ,这是作为拉起微信登录的一个登录界面而存在的。后面要说的微信支付也必须遵循这样的步骤,唯一不同的是 Activity 的名字换了。包结构如下:
在 AndroidManifest 里注册这个 Activity,同时添加必要的权限;
1
2
3
4
5
6<!-- 微信登录 -->
<activity
android:name="com.lykuaimai.kuaimai.wxapi.WXEntryActivity"
android:exported="true"
android:label="@string/wechat_login_title"
android:theme="@android:style/Theme.NoDisplay" />在 WXEntryActivity 这个类里处理登录的操作,首先要在 onCreate() 和 onNewIntent() 方法里做如下的操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent paramIntent) {
BaseApplication.wechatApi.handleIntent(paramIntent, this);
}接下来就是处理微信的返回了,如果在 onResp() 方法里 baseResp.errCode 的值为 BaseResp.ErrCode.ERR_OK 了,就说明微信登录已经成功了,这时候会返回一些参数,如 openid, access_token 等,我们可以根据返回的这些东西继续其他的操作,如果需要长期的授权,就需要进一步调用其他的接口,去获取 refresh_token 。要注意的是,这里获取 openid/access_token ,refresh_token 的请求都是普通的 http 请求,使用 Volley 或者 Okhttp 都可以。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18@Override
public void onResp(BaseResp baseResp) {
switch (baseResp.errCode) {
case BaseResp.ErrCode.ERR_OK:
String code = ((SendAuth.Resp) baseResp).code; //微信确认登录返回的code
break;
case BaseResp.ErrCode.ERR_AUTH_DENIED:
ToastUtil.showShort(getString(R.string.wechat_error_auth_denied));
break;
case BaseResp.ErrCode.ERR_USER_CANCEL:
ToastUtil.showShort(getString(R.string.wechat_error_auth_cancel));
finish();//此处有巨坑啊,我加一个 finish 算是一个投机取巧的办法,因为项目里只是用微信登录获取 openid 这一步,而微信 SDK 提供的没有提供取消请求的方法,导致第一步请求结束,仍然占用某些资源不释放,等待下一步的请求(这两句是我脑补的),如果用户点了返回,就会 ANR ,只好采取这种简单的粗暴的方式帮它释放资源了。
break;
default:
ToastUtil.showShort(getString(R.string.wechat_errcode_unknown));
break;
}
}
这就是微信登录的整个流程,它是如此的简单以至于我已经不敢相信文档里居然是描述的这些东西。
###QQ 登录
QQ登录更加简单一些,但是文档更加脑残,很难想象文档说明居然不在 QQ 开放平台的链接里,而是在 QQ 互联里,而要下载 SDK ,又必须跳转到 QQ 开放平台。更加让人无法忍受的是,这么大一个公司的网站,那网页打开的速度简直比蜗牛还慢。由于 QQ SDK 的网页打开实在太慢,就不贴地址了。不扯其他了,还是直接说步骤吧。
- 导入 mta-sdk 和 open_sdk 这两个 jar 包;
注册 QQ 的 SDK;
1
Tencent mTencent = Tencent.createInstance(Constants.QQ_APP_ID, LoginActivity.this.getApplicationContext());
在 AndroidManifest 里注册登录用到的 Activity,需要注意的是 AssistActivity 一定要加上,坑爹的文档又给漏掉了;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!-- QQ 登录 -->
<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" />
<data android:scheme="你申请到的有授权登录权限的 QQ 号码" />
</intent-filter>
</activity>
<activity
android:name="com.tencent.connect.common.AssistActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />发起登录请求;
1
2
3
4private static final String QQ_REQ_STATE = "all";
if (!mTencent.isSessionValid()) {
mTencent.login(LoginActivity.this, QQ_REQ_STATE, createIUiListener());
}在 createIUiListener() 方法里处理登录回调,返回值一定要是 IUiListener ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17private IUiListener createIUiListener() {
return new IUiListener() {
@Override
public void onComplete(Object o) {
//在这里处理返回的数据,返回的字段主要ret, pay_token, pf, openid, expires_in, pfkey, msg, access_token;接下来的请求同样只是普通的 http 请求,可以使用 Volley 或者 Okhttp 来进行进一步的操作。
}
@Override
public void onError(UiError uiError) {
LogerUtil.i(TAG, uiError.errorMessage);
LogerUtil.i(TAG, uiError.errorDetail);
}
@Override
public void onCancel() {
LogerUtil.i(TAG, CANCEL);
}
};
}
到这里,QQ 登录也写完了。
###微信支付
微信支付所用的 jar 包跟微信登录是一样的,只不过支付的文档官网上连一个字都没有。这里需要跟服务端配合来做,先有服务端来获取预支付订单的 pre_payid 和签名 nonceStr ,按照官方 Demo 的意思,貌似客户端也可以调用接口获取,可实际上我调用了也没任何卵用,最后仍然是服务端掉了微信的服务器获取的 pre_payid 和 nonceStr ,然后再加上其他几个参数在客户端去调微信服务器即可。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
51final IWXAPI msgApi = WXAPIFactory.createWXAPI(this, null);
private PayReq req;
private StringBuilder stringBuilder;
{
stringBuilder = new StringBuilder();
}
private void sendPayReq(String prePayId, String nonceStr) {
genPayReq(prePayId, nonceStr);
msgApi.registerApp(Constants.WECHAT_APP_ID);
msgApi.sendReq(req);
}
@SuppressWarnings("deprecation")
private void genPayReq(String prePayId, String nonceStr) {
req.appId = Constants.WECHAT_APP_ID;
req.partnerId = Constants.MCH_ID;
req.prepayId = prePayId;
req.packageValue = "Sign=WXPay";
req.nonceStr = nonceStr;
req.timeStamp = String.valueOf(genTimeStamp());
List<NameValuePair> signParams = new LinkedList<>();
signParams.add(new BasicNameValuePair("appid", req.appId));
signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
signParams.add(new BasicNameValuePair("package", req.packageValue));
signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));
req.sign = genAppSign(signParams);
stringBuilder.append("sign\n").append(req.sign).append("\n\n");
Log.e("orion", signParams.toString());
}
private long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
@SuppressWarnings({"deprecation", "ConstantConditions"})
private String genAppSign(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < params.size(); i++) {
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append("key=");
sb.append(Constants.WECHAT_PAY_APP_KEY);//这里的 appkey 是申请微信支付权限时让填写的 32 位字符
this.stringBuilder.append("sign str\n").append(sb.toString()).append("\n\n");
String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
Log.e("orion", appSign);
return appSign;
}
官方给的 Demo 里请求用的是 HttpClient ,在 Android5.0 已被弃用,如果有兴趣的话,可以换成 HttpUrlConnection ,或者用 Volley ,Okhttp 之类的三方框架。
然后需要创建一个 AppRegister ,要放在 wxapi 这个包下,官方 Demo 里给的,看代码意思是注册微信 API 用的,不过我怀疑没有什么用处;1
2
3
4
5
6
7
8public class AppRegister extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final IWXAPI api = WXAPIFactory.createWXAPI(context, null);
api.registerApp(Constants.WECHAT_APP_ID);
}
}
然后就是配置 AndroidManifest 了;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<!-- 微信支付 -->
<activity
android:name=".ui.activity.WxPayActivity"
android:exported="true"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="微信的 appid" />
</intent-filter>
</activity>
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop" />
<receiver
android:name=".wxapi.AppRegister"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP" />
</intent-filter>
</receiver>
最后就是处理回调了,要在 wxapi 这个包里创建一个名为 WXPayEntryActivity 这个 Activity ,具体的处理也比较简单;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20private IWXAPI api;
api = WXAPIFactory.createWXAPI(this, Constants.WECHAT_APP_ID);
api.handleIntent(getIntent(), this);//onCreate() 方法里调用
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
@Override
public void onResp(BaseResp resp) {
LogerUtil.i(TAG, resp.errCode);
if (resp.errCode == 0) {
//TODO 成功
} else {
//TODO 失败
}
}
至此,关于微信授权登录,QQ 授权登录和微信支付所有相关的内容就全部写完了。可以看到内容非常简单,只希望以后相关的文档能够更加容易读懂一点。愿天下没有难读的文档。