您好,登錄后才能下訂單哦!
Android中怎么實(shí)現(xiàn)微信登錄,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。
一、首先在A(yíng)pplication的onCreate中寫(xiě):
// 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);
三、下面對(duì)具體的集成步驟做詳細(xì)的描述。
集成步驟:
1、在開(kāi)放平臺(tái)注冊(cè)創(chuàng)建應(yīng)用,申請(qǐng)登錄權(quán)限
2、下載sdk,拷貝相關(guān)文件到項(xiàng)目工程目錄
3、全局初始化微信組件
4、請(qǐng)求授權(quán)登錄,獲取code
5、通過(guò)code獲取授權(quán)口令access_token
6、在第5步判斷access_token是否存在和過(guò)期
7、如果access_token過(guò)期無(wú)效,就用refresh_token來(lái)刷新
8、使用access_token獲取用戶(hù)信息
1. 在開(kāi)放平臺(tái)注冊(cè)創(chuàng)建應(yīng)用,申請(qǐng)登錄權(quán)限
這一步其實(shí)不用怎么講,無(wú)法就是在微信開(kāi)放平臺(tái)上注冊(cè)一個(gè)賬號(hào),然后創(chuàng)建移動(dòng)應(yīng)用。
需要注意的是:應(yīng)用簽名的部分
此處應(yīng)用簽名我使用的是線(xiàn)上的key的md5,關(guān)于這個(gè)需要注意的問(wèn)題可以看:Android的簽名總結(jié)
2. 下載sdk,拷貝相關(guān)文件到項(xiàng)目工程目錄
開(kāi)發(fā)工具包(SDK)的下載:可以使用微信分享、登錄、收藏、支付等功能需要的庫(kù)以及文件
示例Demo
下載后把libammsdk.jar文件拷貝到AS工程的libs目錄,并把示例Demo里源文件目錄下的wxapi目錄整個(gè)拷貝到,工程目錄的src下的根包下:
如果wxapi這個(gè)文件夾放的位置不對(duì),講無(wú)法登錄,微信sdk無(wú)法找到登錄的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對(duì)象方便操作):
@Override public void onCreate() { super.onCreate(); // 初始化微信組件 initWeiXin(); } public static IWXAPI sApi; private void initWeiXin() { sApi = WXEntryActivity.initWeiXin(this, AppConst.WEIXIN_APP_ID); }
4. 請(qǐng)求授權(quán)登錄,獲取code
為了同一業(yè)務(wù)的單一原則我把微信相關(guān)的都統(tǒng)一封裝到了wxapi包下和WXEntryActivity中:
// 實(shí)現(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)接口注冊(cè) GeneralAppliction.sApi.handleIntent(getIntent(), this); mGson = new Gson(); } /** * 微信組件注冊(cè)初始化 * @param context 上下文 * @param weixin_app_id appid * @return 微信組件api對(duì)象 * / 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) { // 判斷是否安裝了微信客戶(hù)端 if (!api.isWXAppInstalled()) { Toast.makeText(context.getApplicationContext(), "您還未安裝微信客戶(hù)端!", Toast.LENGTH_SHORT).show(); return; } // 發(fā)送授權(quán)登錄信息,來(lái)獲取code SendAuth.Req req = new SendAuth.Req(); // 應(yīng)用的作用域,獲取個(gè)人信息 req.scope = "snsapi_userinfo"; /** * 用于保持請(qǐng)求和回調(diào)的狀態(tài),授權(quán)請(qǐng)求后原樣帶回給第三方 * 為了防止csrf攻擊(跨站請(qǐng)求偽造攻擊),后期改為隨機(jī)數(shù)加session來(lái)校驗(yàn) */ req.state = "app_wechat"; api.sendReq(req); } // 微信發(fā)送請(qǐng)求到第三方應(yīng)用時(shí),會(huì)回調(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ā)送到微信的請(qǐng)求處理后的響應(yīng)結(jié)果,會(huì)回調(diào)到該方法 @Override public void onResp(BaseResp resp) { switch (resp.errCode) { // 發(fā)送成功 case BaseResp.ErrCode.ERR_OK: // 獲取code String code = ((SendAuth.Resp) resp).code; // 通過(guò)code獲取授權(quán)口令access_token getAccessToken(code); break; } } }
小伙伴有疑問(wèn)code是啥玩意:
第三方通過(guò)code進(jìn)行獲取access_token的時(shí)候需要用到,code的超時(shí)時(shí)間為10分鐘,一個(gè)code只能成功換取一次access_token即失效。code的臨時(shí)性和一次保障了微信授權(quán)登錄的安全性。第三方可通過(guò)使用https和state參數(shù),進(jìn)一步加強(qiáng)自身授權(quán)登錄的安全性。
這樣客戶(hù)端使用的地方只要:
WXEntryActivity.loginWeixin(MainActivity.this, GeneralAppliction.sApi);
5. 通過(guò)code獲取授權(quán)口令access_token
我們?cè)趏nResp的回調(diào)方法中獲取了code,然后通過(guò)code獲取授權(quán)口令access_token:
/** * 獲取授權(quán)口令 */ private void getAccessToken(String code) { String url = "https://api.weixin.qq.com/sns/oauth3/access_token?" + "appid=" + AppConst.WEIXIN_APP_ID + "&secret=" + AppConst.WEIXIN_APP_SECRET + "&code=" + code + "&grant_type=authorization_code"; // 網(wǎng)絡(luò)請(qǐng)求獲取access_token httpRequest(url, new ApiCallback<String>() { @Override public void onSuccess(String response) { Logger.e(response); // 判斷是否獲取成功,成功則去獲取用戶(hù)信息,否則提示失敗 processGetAccessTokenResult(response); } @Override public void onError(int errorCode, final String errorMsg) { Logger.e(errorMsg); showMessage("錯(cuò)誤信息: " + 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) { // 驗(yàn)證獲取授權(quán)口令返回的信息是否成功 if (validateSuccess(response)) { // 使用Gson解析返回的授權(quán)口令信息 WXAccessTokenInfo tokenInfo = mGson.fromJson(response, WXAccessTokenInfo.class); Logger.e(tokenInfo.toString()); // 保存信息到手機(jī)本地 saveAccessInfotoLocation(tokenInfo); // 獲取用戶(hù)信息 getUserInfo(tokenInfo.getAccess_token(), tokenInfo.getOpenid()); } else { // 授權(quán)口令獲取失敗,解析返回錯(cuò)誤信息 WXErrorInfo wxErrorInfo = mGson.fromJson(response, WXErrorInfo.class); Logger.e(wxErrorInfo.toString()); // 提示錯(cuò)誤信息 showMessage("錯(cuò)誤信息: " + wxErrorInfo.getErrmsg()); } } /** * 驗(yàn)證是否成功 * * @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是否存在和過(guò)期
在回調(diào)的onResp方法中獲取code后,處理access_token是否登錄過(guò)或者過(guò)期的問(wèn)題:
// 從手機(jī)本地獲取存儲(chǔ)的授權(quán)口令信息,判斷是否存在access_token,不存在請(qǐng)求獲取,存在就判斷是否過(guò)期 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,判斷是否過(guò)期有效 isExpireAccessToken(accessToken, openid); } else { // 沒(méi)有access_token getAccessToken(code); }
判斷授權(quán)口令是否有效:
/** * 判斷accesstoken是過(guò)期 * @param accessToken token * @param openid 授權(quán)用戶(hù)唯一標(biāo)識(shí) */ 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沒(méi)有過(guò)期,獲取用戶(hù)信息 getUserInfo(accessToken, openid); } else { // 過(guò)期了,使用refresh_token來(lái)刷新accesstoken refreshAccessToken(); } } @Override public void onError(int errorCode, final String errorMsg) { Logger.e(errorMsg); showMessage("錯(cuò)誤信息: " + errorMsg); } @Override public void onFailure(IOException e) { Logger.e(e.getMessage()); showMessage("登錄失敗"); } }); }
7. 如果access_token過(guò)期無(wú)效,就用refresh_token來(lái)刷新
/** * 刷新獲取新的access_token * / private void refreshAccessToken() { // 從本地獲取以存儲(chǔ)的refresh_token final String refreshToken = (String) ShareUtils.getValue(this, WEIXIN_REFRESH_TOKEN_KEY, ""); if (TextUtils.isEmpty(refreshToken)) { return; } // 拼裝刷新access_token的url請(qǐng)求地址 String url = "https://api.weixin.qq.com/sns/oauth3/refresh_token?" + "appid=" + AppConst.WEIXIN_APP_ID + "&grant_type=refresh_token" + "&refresh_token=" + refreshToken; // 請(qǐng)求執(zhí)行 httpRequest(url, new ApiCallback<String>() { @Override public void onSuccess(String response) { Logger.e("refreshAccessToken: " + response); // 判斷是否獲取成功,成功則去獲取用戶(hù)信息,否則提示失敗 processGetAccessTokenResult(response); } @Override public void onError(int errorCode, final String errorMsg) { Logger.e(errorMsg); showMessage("錯(cuò)誤信息: " + errorMsg); // 重新請(qǐng)求授權(quán) loginWeixin(WXEntryActivity.this.getApplicationContext(), GeneralAppliction.sApi); } @Override public void onFailure(IOException e) { Logger.e(e.getMessage()); showMessage("登錄失敗"); // 重新請(qǐng)求授權(quán) loginWeixin(WXEntryActivity.this.getApplicationContext(), GeneralAppliction.sApi); } }); }
8. 使用access_token獲取用戶(hù)信息
/** * 獲取用戶(hù)信息 * / 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) { // 解析獲取的用戶(hù)信息 WXUserInfo userInfo = mGson.fromJson(response, WXUserInfo.class); Logger.e("用戶(hù)信息獲取結(jié)果:" + userInfo.toString()); } @Override public void onError(int errorCode, String errorMsg) { showMessage("錯(cuò)誤信息: " + errorMsg); } @Override public void onFailure(IOException e) { showMessage("獲取用戶(hù)信息失敗"); } }); }
通信部分
private OkHttpClient mHttpClient = new OkHttpClient.Builder().build(); private Handler mCallbackHandler = new Handler(Looper.getMainLooper()); /** * 通過(guò)Okhttp與微信通信 * * @param url 請(qǐng)求地址 * @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() { // 請(qǐng)求失敗,主線(xiàn)程回調(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() { // 請(qǐng)求出錯(cuò),主線(xiàn)程回調(diào) callback.onError(response.code(), response.message()); } }); } else { mCallbackHandler.post(new Runnable() { @Override public void run() { try { // 請(qǐng)求成功,主線(xiàn)程返回請(qǐng)求結(jié)果 callback.onSuccess(response.body().string()); } catch (final IOException e) { // 異常出錯(cuò),主線(xiàn)程回調(diào) mCallbackHandler.post(new Runnable() { @Override public void run() { callback.onFailure(e); } }); } } }); } } } }); } // Api通信回調(diào)接口 public interface ApiCallback<T> { /** * 請(qǐng)求成功 * * @param response 返回結(jié)果 */ void onSuccess(T response); /** * 請(qǐng)求出錯(cuò) * * @param errorCode 錯(cuò)誤碼 * @param errorMsg 錯(cuò)誤信息 */ void onError(int errorCode, String errorMsg); /** * 請(qǐng)求失敗 */ void onFailure(IOException e); }
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。