Android集成微信登錄的步驟詳解
一、首先在Application的onCreate中寫:
// GeneralAppliction.java
public static IWXAPI sApi;
@Override
public void onCreate() {
super.onCreate();
sApi = WXEntryActivity.initWeiXin(this, AppConst.WEIXIN_APP_ID);
}
二、在需要登錄的地方添加:
// MainActivity.java WXEntryActivity.loginWeixin(MainActivity.this, GeneralAppliction.sApi);
三、下面對具體的集成步驟做詳細(xì)的描述。
集成步驟:
1、在開放平臺注冊創(chuàng)建應(yīng)用,申請登錄權(quán)限
2、下載sdk,拷貝相關(guān)文件到項目工程目錄
3、全局初始化微信組件
4、請求授權(quán)登錄,獲取code
5、通過code獲取授權(quán)口令access_token
6、在第5步判斷access_token是否存在和過期
7、如果access_token過期無效,就用refresh_token來刷新
8、使用access_token獲取用戶信息
1. 在開放平臺注冊創(chuàng)建應(yīng)用,申請登錄權(quán)限
這一步其實不用怎么講,無法就是在微信開放平臺上注冊一個賬號,然后創(chuàng)建移動應(yīng)用。

需要注意的是:應(yīng)用簽名的部分

此處應(yīng)用簽名我使用的是線上的key的md5,關(guān)于這個需要注意的問題可以看:Android的簽名總結(jié)
2. 下載sdk,拷貝相關(guān)文件到項目工程目錄
開發(fā)工具包(SDK)的下載:可以使用微信分享、登錄、收藏、支付等功能需要的庫以及文件
下載后把libammsdk.jar文件拷貝到AS工程的libs目錄,并把示例Demo里源文件目錄下的wxapi目錄整個拷貝到,工程目錄的src下的根包下:

如果wxapi這個文件夾放的位置不對,講無法登錄,微信sdk無法找到登錄的Activity授權(quán)功能。然后在Manifest.xml里面加入:
<activity android:name=".wxapi.WXEntryActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:configChanges="keyboardHidden|orientation|screenSize" android:exported="true" android:screenOrientation="portrait" />
3. 全局初始化微信組件
全局初始化微信組件,當(dāng)然是Application的onCreate里(當(dāng)然Activity的onCreate也是可以的,為了全局使用微信api對象方便操作):
@Override
public void onCreate() {
super.onCreate();
// 初始化微信組件
initWeiXin();
}
public static IWXAPI sApi;
private void initWeiXin() {
sApi = WXEntryActivity.initWeiXin(this, AppConst.WEIXIN_APP_ID);
}
4. 請求授權(quán)登錄,獲取code
為了同一業(yè)務(wù)的單一原則我把微信相關(guān)的都統(tǒng)一封裝到了wxapi包下和WXEntryActivity中:
// 實現(xiàn)IWXAPIEventHandler 接口,以便于微信事件處理的回調(diào)
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);
// 微信事件回調(diào)接口注冊
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 微信服務(wù)api
*/
public static void loginWeixin(Context context, IWXAPI api) {
// 判斷是否安裝了微信客戶端
if (!api.isWXAppInstalled()) {
Toast.makeText(context.getApplicationContext(), "您還未安裝微信客戶端!", Toast.LENGTH_SHORT).show();
return;
}
// 發(fā)送授權(quán)登錄信息,來獲取code
SendAuth.Req req = new SendAuth.Req();
// 應(yīng)用的作用域,獲取個人信息
req.scope = "snsapi_userinfo";
/**
* 用于保持請求和回調(diào)的狀態(tài),授權(quán)請求后原樣帶回給第三方
* 為了防止csrf攻擊(跨站請求偽造攻擊),后期改為隨機數(shù)加session來校驗
*/
req.state = "app_wechat";
api.sendReq(req);
}
// 微信發(fā)送請求到第三方應(yīng)用時,會回調(diào)到該方法
@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;
}
}
// 第三方應(yīng)用發(fā)送到微信的請求處理后的響應(yīng)結(jié)果,會回調(diào)到該方法
@Override
public void onResp(BaseResp resp) {
switch (resp.errCode) {
// 發(fā)送成功
case BaseResp.ErrCode.ERR_OK:
// 獲取code
String code = ((SendAuth.Resp) resp).code;
// 通過code獲取授權(quán)口令access_token
getAccessToken(code);
break;
}
}
}
小伙伴有疑問code是啥玩意:
第三方通過code進(jìn)行獲取access_token的時候需要用到,code的超時時間為10分鐘,一個code只能成功換取一次access_token即失效。code的臨時性和一次保障了微信授權(quán)登錄的安全性。第三方可通過使用https和state參數(shù),進(jìn)一步加強自身授權(quán)登錄的安全性。
這樣客戶端使用的地方只要:
WXEntryActivity.loginWeixin(MainActivity.this, GeneralAppliction.sApi);
5. 通過code獲取授權(quán)口令access_token
我們在onResp的回調(diào)方法中獲取了code,然后通過code獲取授權(quán)口令access_token:
/**
* 獲取授權(quán)口令
*/
private void getAccessToken(String code) {
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
"appid=" + AppConst.WEIXIN_APP_ID +
"&secret=" + AppConst.WEIXIN_APP_SECRET +
"&code=" + code +
"&grant_type=authorization_code";
// 網(wǎng)絡(luò)請求獲取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("登錄失敗");
}
});
}
/**
* 處理獲取的授權(quán)信息結(jié)果
* @param response 授權(quán)信息結(jié)果
*/
private void processGetAccessTokenResult(String response) {
// 驗證獲取授權(quán)口令返回的信息是否成功
if (validateSuccess(response)) {
// 使用Gson解析返回的授權(quán)口令信息
WXAccessTokenInfo tokenInfo = mGson.fromJson(response, WXAccessTokenInfo.class);
Logger.e(tokenInfo.toString());
// 保存信息到手機本地
saveAccessInfotoLocation(tokenInfo);
// 獲取用戶信息
getUserInfo(tokenInfo.getAccess_token(), tokenInfo.getOpenid());
} else {
// 授權(quán)口令獲取失敗,解析返回錯誤信息
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是否存在和過期
在回調(diào)的onResp方法中獲取code后,處理access_token是否登錄過或者過期的問題:
// 從手機本地獲取存儲的授權(quán)口令信息,判斷是否存在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);
}
判斷授權(quán)口令是否有效:
/**
* 判斷accesstoken是過期
* @param accessToken token
* @param openid 授權(quán)用戶唯一標(biāo)識
*/
private void isExpireAccessToken(final String accessToken, final String openid) {
String url = "https://api.weixin.qq.com/sns/auth?" +
"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來刷新
/**
* 刷新獲取新的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請求地址
String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?" +
"appid=" + AppConst.WEIXIN_APP_ID +
"&grant_type=refresh_token" +
"&refresh_token=" + refreshToken;
// 請求執(zhí)行
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);
// 重新請求授權(quán)
loginWeixin(WXEntryActivity.this.getApplicationContext(), GeneralAppliction.sApi);
}
@Override
public void onFailure(IOException e) {
Logger.e(e.getMessage());
showMessage("登錄失敗");
// 重新請求授權(quán)
loginWeixin(WXEntryActivity.this.getApplicationContext(), GeneralAppliction.sApi);
}
});
}
8. 使用access_token獲取用戶信息
/**
* 獲取用戶信息
*
/
private void getUserInfo(String access_token, String openid) {
String url = "https://api.weixin.qq.com/sns/userinfo?" +
"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("用戶信息獲取結(jié)果:" + userInfo.toString()); }
@Override
public void onError(int errorCode, String errorMsg) {
showMessage("錯誤信息: " + errorMsg);
}
@Override
public void onFailure(IOException e) {
showMessage("獲取用戶信息失敗");
}
});
}
通信部分
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() {
// 請求失敗,主線程回調(diào)
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() {
// 請求出錯,主線程回調(diào)
callback.onError(response.code(), response.message());
}
});
} else {
mCallbackHandler.post(new Runnable() {
@Override
public void run() {
try {
// 請求成功,主線程返回請求結(jié)果
callback.onSuccess(response.body().string());
} catch (final IOException e) {
// 異常出錯,主線程回調(diào)
mCallbackHandler.post(new Runnable() {
@Override
public void run() {
callback.onFailure(e);
}
});
}
}
});
}
}
}
});
}
// Api通信回調(diào)接口
public interface ApiCallback<T> {
/**
* 請求成功
*
* @param response 返回結(jié)果
*/
void onSuccess(T response);
/**
* 請求出錯
*
* @param errorCode 錯誤碼
* @param errorMsg 錯誤信息
*/
void onError(int errorCode, String errorMsg);
/**
* 請求失敗
*/
void onFailure(IOException e);
}
總結(jié)
集成的詳細(xì)描述就這樣,至于獲取的用戶信息,小伙伴們應(yīng)該知道后續(xù)自己業(yè)務(wù)的需求,該怎么處理了。以上就是本文的全部內(nèi)容了,希望能對大家的學(xué)習(xí)或者工作帶來一定的幫助,如果有疑問大家可以留言交流。
相關(guān)文章
Android自定義View實現(xiàn)仿網(wǎng)易音樂唱片播放效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實現(xiàn)仿網(wǎng)易音樂唱片播放效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04
Android中使用AsyncTask實現(xiàn)下載文件動態(tài)更新進(jìn)度條功能
這篇文章主要介紹了AsyncTask用法解析-下載文件動態(tài)更新進(jìn)度條,需要的朋友可以參考下2017-08-08
Android UI實現(xiàn)SlidingMenu側(cè)滑菜單效果
這篇文章主要為大家詳細(xì)介紹了Android UI實現(xiàn)SlidingMenu側(cè)滑菜單效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12
Android M(6.x)使用OkHttp包解析和發(fā)送JSON請求的教程
Android 6.0采用的SPDY支持HTTP上GZIP壓縮的傳輸,這使得OkHttp包的功能能夠進(jìn)一步被利用,本文我們來總結(jié)一下Android M(6.0)使用OkHttp包解析和發(fā)送JSON請求的教程2016-07-07
Android使用android-wheel實現(xiàn)省市縣三級聯(lián)動
這篇文章主要為大家詳細(xì)介紹了Android使用android-wheel實現(xiàn)省市縣三級聯(lián)動,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-08-08
Android啟動頁優(yōu)化之實現(xiàn)應(yīng)用秒開
現(xiàn)在很多應(yīng)用都會在進(jìn)入主界面之前,添加一個啟動頁,然后加入幾秒鐘的廣告,我覺得這個不能算是 “真正意義上的 “ 啟動頁,應(yīng)該叫廣告頁。2021-05-05
Android改變ExpandableListView的indicator圖標(biāo)實現(xiàn)方法
這篇文章主要介紹了Android改變ExpandableListView的indicator圖標(biāo)實現(xiàn)方法,結(jié)合實例形式分析了改變ExpandableListView的indicator圖標(biāo)相關(guān)步驟與實現(xiàn)技巧,涉及Android配置文件的修改,需要的朋友可以參考下2016-03-03

