一、首先在Application的onCreate中寫:
1
2
3
4
5
6
7
|
// GeneralAppliction.java
public
static
IWXAPI sApi;
@Override
public
void
onCreate() {
super
.onCreate();
sApi = WXEntryActivity.initWeiXin(
this
, AppConst.WEIXIN_APP_ID);
}
|
二、在需要登錄的地方添加:
1
2
|
// MainActivity.java
WXEntryActivity.loginWeixin(MainActivity.
this
, GeneralAppliction.sApi);
|
三、下面對具體的集成步驟做詳細的描述。
集成步驟:
1、在開放平臺註冊創建應用,申請登錄權限
2、下載sdk,拷貝相關文件到項目工程目錄
3、全局初始化微信組件
4、請求授權登錄,獲取code
5、通過code獲取授權口令access_token
6、在第5步判斷access_token是否存在和過期
7、如果access_token過期無效,就用refresh_token來刷新
8、使用access_token獲取用戶信息
1. 在開放平臺註冊創建應用,申請登錄權限
這一步其實不用怎麼講,無法就是在微信開放平臺上註冊一個賬號,然後創建移動應用。
需要注意的是:應用簽名的部分
此處應用簽名我使用的是線上的key的md5,關於這個需要注意的問題可以看:Android的簽名總結
2. 下載sdk,拷貝相關文件到項目工程目錄
開發工具包(SDK)的下載:可以使用微信分享、登錄、收藏、支付等功能需要的庫以及文件
下載後把libammsdk.jar文件拷貝到AS工程的libs目錄,並把示例Demo裏源文件目錄下的wxapi目錄整個拷貝到,工程目錄的src下的根包下:
如果wxapi這個文件夾放的位置不對,講無法登錄,微信sdk無法找到登錄的Activity授權功能。然後在Manifest.xml裏面加入:
1
2
3
4
5
6
|
<activity
android:name=
".wxapi.WXEntryActivity"
android:theme=
"@android:style/Theme.Translucent.NoTitleBar"
android:configChanges=
"keyboardHidden|orientation|screenSize"
android:exported=
"true"
android:screenOrientation=
"portrait"
/>
|
3. 全局初始化微信組件
全局初始化微信組件,當然是Application的onCreate裏(當然Activity的onCreate也是可以的,爲了全局使用微信api對象方便操作):
1
2
3
4
5
6
7
8
9
10
11
|
@Override
public
void
onCreate() {
super
.onCreate();
// 初始化微信組件
initWeiXin();
}
public
static
IWXAPI sApi;
private
void
initWeiXin() {
sApi = WXEntryActivity.initWeiXin(
this
, AppConst.WEIXIN_APP_ID);
}
|
4. 請求授權登錄,獲取code
爲了同一業務的單一原則我把微信相關的都統一封裝到了wxapi包下和WXEntryActivity中:
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
// 實現IWXAPIEventHandler 接口,以便於微信事件處理的回調
public
class
WXEntryActivity
extends
Activity
implements
IWXAPIEventHandler {
private
static
final
String WEIXIN_ACCESS_TOKEN_KEY =
"wx_access_token_key"
;
private
static
final
String WEIXIN_OPENID_KEY =
"wx_openid_key"
;
private
static
final
String WEIXIN_REFRESH_TOKEN_KEY =
"wx_refresh_token_key"
;
private
Gson mGson;
@Override
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
// 微信事件回調接口註冊
GeneralAppliction.sApi.handleIntent(getIntent(),
this
);
mGson =
new
Gson();
}
/**
* 微信組件註冊初始化
* @param context 上下文
* @param weixin_app_id appid
* @return 微信組件api對象
*
/
public static IWXAPI initWeiXin(Context context, @NonNull String weixin_app_id) {
if (TextUtils.isEmpty(weixin_app_id)) {
Toast.makeText(context.getApplicationContext(), "app_id 不能爲空", Toast.LENGTH_SHORT).show();
}
IWXAPI api = WXAPIFactory.createWXAPI(context, weixin_app_id, true);
api.registerApp(weixin_app_id);
return api;
}
/**
* 登錄微信
*
* @param api 微信服務api
*/
public
static
void
loginWeixin(Context context, IWXAPI api) {
// 判斷是否安裝了微信客戶端
if
(!api.isWXAppInstalled()) {
Toast.makeText(context.getApplicationContext(),
"您還未安裝微信客戶端!"
, Toast.LENGTH_SHORT).show();
return
;
}
// 發送授權登錄信息,來獲取code
SendAuth.Req req =
new
SendAuth.Req();
// 應用的作用域,獲取個人信息
req.scope =
"snsapi_userinfo"
;
/**
* 用於保持請求和回調的狀態,授權請求後原樣帶回給第三方
* 爲了防止csrf攻擊(跨站請求僞造攻擊),後期改爲隨機數加session來校驗
*/
req.state =
"app_wechat"
;
api.sendReq(req);
}
// 微信發送請求到第三方應用時,會回調到該方法
@Override
public
void
onReq(BaseReq req) {
switch
(req.getType()) {
case
ConstantsAPI.COMMAND_GETMESSAGE_FROM_WX:
break
;
case
ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
break
;
default
:
break
;
}
}
// 第三方應用發送到微信的請求處理後的響應結果,會回調到該方法
@Override
public
void
onResp(BaseResp resp) {
switch
(resp.errCode) {
// 發送成功
case
BaseResp.ErrCode.ERR_OK:
// 獲取code
String code = ((SendAuth.Resp) resp).code;
// 通過code獲取授權口令access_token
getAccessToken(code);
break
;
}
}
}
|
小夥伴有疑問code是啥玩意:
第三方通過code進行獲取access_token的時候需要用到,code的超時時間爲10分鐘,一個code只能成功換取一次access_token即失效。code的臨時性和一次保障了微信授權登錄的安全性。第三方可通過使用https和state參數,進一步加強自身授權登錄的安全性。
這樣客戶端使用的地方只要:
1
|
WXEntryActivity.loginWeixin(MainActivity.
this
, GeneralAppliction.sApi);
|
5. 通過code獲取授權口令access_token
我們在onResp的回調方法中獲取了code,然後通過code獲取授權口令access_token:
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
|
/**
* 獲取授權口令
*/
private
void
getAccessToken(String code) {
"appid="
+ AppConst.WEIXIN_APP_ID +
"&secret="
+ AppConst.WEIXIN_APP_SECRET +
"&code="
+ code +
"&grant_type=authorization_code"
;
// 網絡請求獲取access_token
httpRequest(url,
new
ApiCallback<String>() {
@Override
public
void
onSuccess(String response) {
Logger.e(response);
// 判斷是否獲取成功,成功則去獲取用戶信息,否則提示失敗
processGetAccessTokenResult(response);
}
@Override
public
void
onError(
int
errorCode,
final
String errorMsg) {
Logger.e(errorMsg);
showMessage(
"錯誤信息: "
+ errorMsg);
}
@Override
public
void
onFailure(IOException e) {
Logger.e(e.getMessage());
showMessage(
"登錄失敗"
);
}
});
}
/**
* 處理獲取的授權信息結果
* @param response 授權信息結果
*/
private
void
processGetAccessTokenResult(String response) {
// 驗證獲取授權口令返回的信息是否成功
if
(validateSuccess(response)) {
// 使用Gson解析返回的授權口令信息
WXAccessTokenInfo tokenInfo = mGson.fromJson(response, WXAccessTokenInfo.
class
);
Logger.e(tokenInfo.toString());
// 保存信息到手機本地
saveAccessInfotoLocation(tokenInfo);
// 獲取用戶信息
getUserInfo(tokenInfo.getAccess_token(), tokenInfo.getOpenid());
}
else
{
// 授權口令獲取失敗,解析返回錯誤信息
WXErrorInfo wxErrorInfo = mGson.fromJson(response, WXErrorInfo.
class
);
Logger.e(wxErrorInfo.toString());
// 提示錯誤信息
showMessage(
"錯誤信息: "
+ wxErrorInfo.getErrmsg());
}
}
/**
* 驗證是否成功
*
* @param response 返回消息
* @return 是否成功
*/
private
boolean
validateSuccess(String response) {
String errFlag =
"errmsg"
;
return
(errFlag.contains(response) && !
"ok"
.equals(response))
|| (!
"errcode"
.contains(response) && !errFlag.contains(response));
}
|
6. 在第5步判斷access_token是否存在和過期
在回調的onResp方法中獲取code後,處理access_token是否登錄過或者過期的問題:
1
2
3
4
5
6
7
8
9
10
|
// 從手機本地獲取存儲的授權口令信息,判斷是否存在access_token,不存在請求獲取,存在就判斷是否過期
String accessToken = (String) ShareUtils.getValue(
this
, WEIXIN_ACCESS_TOKEN_KEY,
"none"
);
String openid = (String) ShareUtils.getValue(
this
, WEIXIN_OPENID_KEY,
""
);
if
(!
"none"
.equals(accessToken)) {
// 有access_token,判斷是否過期有效
isExpireAccessToken(accessToken, openid);
}
else
{
// 沒有access_token
getAccessToken(code);
}
|
判斷授權口令是否有效:
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
|
/**
* 判斷accesstoken是過期
* @param accessToken token
* @param openid 授權用戶唯一標識
*/
private
void
isExpireAccessToken(
final
String accessToken,
final
String openid) {
"access_token="
+ accessToken +
"&openid="
+ openid;
httpRequest(url,
new
ApiCallback<String>() {
@Override
public
void
onSuccess(String response) {
Logger.e(response);
if
(validateSuccess(response)) {
// accessToken沒有過期,獲取用戶信息
getUserInfo(accessToken, openid);
}
else
{
// 過期了,使用refresh_token來刷新accesstoken
refreshAccessToken();
}
}
@Override
public
void
onError(
int
errorCode,
final
String errorMsg) {
Logger.e(errorMsg);
showMessage(
"錯誤信息: "
+ errorMsg);
}
@Override
public
void
onFailure(IOException e) {
Logger.e(e.getMessage());
showMessage(
"登錄失敗"
);
}
});
}
|
7. 如果access_token過期無效,就用refresh_token來刷新
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
|
/**
* 刷新獲取新的access_token
*
/
private
void
refreshAccessToken() {
// 從本地獲取以存儲的refresh_token
final
String refreshToken = (String) ShareUtils.getValue(
this
, WEIXIN_REFRESH_TOKEN_KEY,
""
);
if
(TextUtils.isEmpty(refreshToken)) {
return
;
}
// 拼裝刷新access_token的url請求地址
"appid="
+ AppConst.WEIXIN_APP_ID +
"&grant_type=refresh_token"
+
"&refresh_token="
+ refreshToken;
// 請求執行
httpRequest(url,
new
ApiCallback<String>() {
@Override
public
void
onSuccess(String response) {
Logger.e(
"refreshAccessToken: "
+ response);
// 判斷是否獲取成功,成功則去獲取用戶信息,否則提示失敗
processGetAccessTokenResult(response);
}
@Override
public
void
onError(
int
errorCode,
final
String errorMsg) {
Logger.e(errorMsg);
showMessage(
"錯誤信息: "
+ errorMsg);
// 重新請求授權
loginWeixin(WXEntryActivity.
this
.getApplicationContext(), GeneralAppliction.sApi);
}
@Override
public
void
onFailure(IOException e) {
Logger.e(e.getMessage());
showMessage(
"登錄失敗"
);
// 重新請求授權
loginWeixin(WXEntryActivity.
this
.getApplicationContext(), GeneralAppliction.sApi);
}
});
}
|
8. 使用access_token獲取用戶信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* 獲取用戶信息
*
/
private
void
getUserInfo(String access_token, String openid) {
"access_token="
+ access_token +
"&openid="
+ openid;
httpRequest(url,
new
ApiCallback<String>() {
@Override
public
void
onSuccess(String response) {
// 解析獲取的用戶信息
WXUserInfo userInfo = mGson.fromJson(response, WXUserInfo.
class
);
Logger.e(
"用戶信息獲取結果:"
+ userInfo.toString()); }
@Override
public
void
onError(
int
errorCode, String errorMsg) {
showMessage(
"錯誤信息: "
+ errorMsg);
}
@Override
public
void
onFailure(IOException e) {
showMessage(
"獲取用戶信息失敗"
);
}
});
}
|
通信部分
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
private
OkHttpClient mHttpClient =
new
OkHttpClient.Builder().build();
private
Handler mCallbackHandler =
new
Handler(Looper.getMainLooper());
/**
* 通過Okhttp與微信通信
* * @param url 請求地址
* @throws Exception
*/
public
void
httpRequest(String url,
final
ApiCallback<String> callback) {
Logger.e(
"url: %s"
, url);
final
Request request =
new
Request.Builder()
.url(url)
.get()
.build();
mHttpClient.newCall(request).enqueue(
new
Callback() {
@Override
public
void
onFailure(Call call,
final
IOException e) {
if
(callback !=
null
) {
mCallbackHandler.post(
new
Runnable() {
@Override
public
void
run() {
// 請求失敗,主線程回調
callback.onFailure(e);
}
});
}
}
@Override
public
void
onResponse(Call call,
final
Response response)
throws
IOException {
if
(callback !=
null
) {
if
(!response.isSuccessful()) {
mCallbackHandler.post(
new
Runnable() {
@Override
public
void
run() {
|