Android开发丶集成微信原生登录
好久没写博客了,大概是与ReactNative大战半月已经有点疲惫了,今天早上接到一个需求,项目中需要集成微信三方登录,在经历了数个只有手机账号登录的项目,终于等到一个集成三方登录的需求了,还记得上次实现功能还是刚参加工作时,用Mob的ShareSDK来集成实现的,该平台集成了数个主流平台的分享和登录功能,本来想继续抱着Mob大佬的大腿,后来觉得本项目也只集成一个微信登录而已,相比MobShareSDK,不需要再去申请额外的账号,所以最终还是决定入坑原生来将其实现,查阅了官方文档和相关资料,最后总算是将它完成了,不过相比Mob,确实封装不彻底,流程较为繁琐,而且官方文档对新手可能不太友好,所以决定来记录下相关流程和心得,ok,lets go!!!
话不多说,效果图走起来!(不要纠结是两个手机。。。)
第二个界面,我们把返回的字符串提取出昵称nickname和头像headurl,可以根据实际需求抽取字段,完成后续的操作。
下面详述实现流程
1.首先当然是去微信开放平台新建应用获取appId和secret
值得一提的是,因为审核周期过于长,所以等不及的小伙伴可以先把已经注册好的具有微信登录的app的id和secret值拿过来测试,之后审核通过后直接替换正式的id值即可,不过这里要注意:
1)测试demo的包名必须与那个已注册好的app的包名保持一致
2)测试demo的签名文件也必须与那个已注册好的app的签名文件保持一致
这样就没啥问题了,在微信开放平台那里,这就是两个相同的app,不会出什么幺蛾子。
2.接下来当然就是集成环境了
微信原生SDK的集成特别方便,在gradle里添加依赖即可
implementation 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+' implementation 'com.squareup.okhttp3:okhttp:3.4.1'
第一条依赖为微信原生SDK,第二条为Okhttp网络框架
在包名路径下,新建wxapi文件夹,再新建一个WXEntryActivity。(路径文件名必须保持一致)
打开WXEntryActivity
public class WXEntryActivity extends AppCompatActivity implements IWXAPIEventHandler
重写两个方法。
@Override public void onReq(BaseReq baseReq) { } //请求回调结果处理 @Override public void onResp(BaseResp baseResp) { }
点击
等待加载完毕,如无报错环境即搭载成功。
3.接下来就是实现功能了,打开MainActivity,在onCreate里注册应用
private void regToWx() { api = WXAPIFactory.createWXAPI(this, APP_ID, false); api.registerApp(APP_ID); }
我们放一个触发请求登录的按钮
写请求方法。
if (!api.isWXAppInstalled()) { Toast.makeText(MainActivity.this, "您的设备未安装微信客户端", Toast.LENGTH_SHORT).show(); } else { final SendAuth.Req req = new SendAuth.Req(); req.scope = "snsapi_userinfo"; req.state = "wechat_sdk_demo_test"; api.sendReq(req); }
首先判断是否安装微信客户端,没有就文字提示,反之才进行请求。
4.打开WXEntryActivity
在onCreate方法里隐藏状态栏并获取wxapi
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSupportActionBar().hide(); // 隐藏状态栏 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //接收到分享以及登录的intent传递handleIntent方法,处理结果 iwxapi = WXAPIFactory.createWXAPI(this, "wx45ccf8958a0a24c7", false); iwxapi.handleIntent(getIntent(), this); }
5.在onRep()方法里获取回调。
//请求回调结果处理 @Override public void onResp(BaseResp baseResp) { //登录回调 switch (baseResp.errCode) { case BaseResp.ErrCode.ERR_OK: String code = ((SendAuth.Resp) baseResp).code; //获取accesstoken getAccessToken(code); Log.d("fantasychongwxlogin", code.toString()+ ""); break; case BaseResp.ErrCode.ERR_AUTH_DENIED://用户拒绝授权 finish(); break; case BaseResp.ErrCode.ERR_USER_CANCEL://用户取消 finish(); break; default: finish(); break; } }
根据返回码,如果请求成功会返回BaseResp.ErrCode.ERR_OK:相等的值,然后通过code和getAccessToken()方法再获取accessToken。
private void getAccessToken(String code) { createProgressDialog(); //获取授权 StringBuffer loginUrl = new StringBuffer(); loginUrl.append("https://api.weixin.qq.com/sns/oauth2/access_token") .append("?appid=") .append("wx45ccf8958a0a24c7") .append("&secret=") .append("e9c071f3326663856bc6cf02c2d6b657") .append("&code=") .append(code) .append("&grant_type=authorization_code"); Log.d("urlurl", loginUrl.toString()); OkHttpClient okHttpClient = new OkHttpClient(); final Request request = new Request.Builder() .url(loginUrl.toString()) .get()//默认就是GET请求,可以不写 .build(); Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.d("fan12", "onFailure: "); mProgressDialog.dismiss(); } @Override public void onResponse(Call call, Response response) throws IOException { String responseInfo= response.body().string(); Log.d("fan12", "onResponse: " +responseInfo); String access = null; String openId = null; try { JSONObject jsonObject = new JSONObject(responseInfo); access = jsonObject.getString("access_token"); openId = jsonObject.getString("openid"); } catch (JSONException e) { e.printStackTrace(); } getUserInfo(access, openId); } }); }
这里我们在请求之前新建一个progressDialog,避免长时间白屏(因为在进行多次网络请求)造成卡死的假象
private void createProgressDialog() { mContext=this; mProgressDialog=new ProgressDialog(mContext); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);//转盘 mProgressDialog.setCancelable(false); mProgressDialog.setCanceledOnTouchOutside(false); mProgressDialog.setTitle("提示"); mProgressDialog.setMessage("登录中,请稍后"); mProgressDialog.show(); }
如果请求成功,我们通过JSON解析获取access和token值,再通过getUserInfo(access, openId)方法获取用户信息
private void getUserInfo(String access, String openid) { String getUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access + "&openid=" + openid; OkHttpClient okHttpClient = new OkHttpClient(); final Request request = new Request.Builder() .url(getUserInfoUrl) .get()//默认就是GET请求,可以不写 .build(); Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.d("fan12", "onFailure: "); mProgressDialog.dismiss(); } @Override public void onResponse(Call call, Response response) throws IOException { String responseInfo= response.body().string(); Log.d("fan123", "onResponse: " + responseInfo); SharedPreferences.Editor editor= getSharedPreferences("userInfo", MODE_PRIVATE).edit(); editor.putString("responseInfo", responseInfo); editor.commit(); finish(); mProgressDialog.dismiss(); } }); }
请求成功时,后台会返回一串用户信息的字符串,我们把字符串塞到SharedPreference缓存中,finish掉这个界面
6.回到MainActivity,重写onResume()。
@Override protected void onResume() { super.onResume(); SharedPreferences sp= getSharedPreferences("userInfo", MODE_PRIVATE); String responseInfo= sp.getString("responseInfo", ""); if (!responseInfo.isEmpty()){ try { JSONObject jsonObject = new JSONObject(responseInfo); nickname = jsonObject.getString("nickname"); headimgurl = jsonObject.getString("headimgurl"); } catch (JSONException e) { e.printStackTrace(); } tv.setText("昵称:"+ nickname+ "\n"+ "头像:"+ headimgurl); SharedPreferences.Editor editor= getSharedPreferences("userInfo", MODE_PRIVATE).edit(); editor.clear(); editor.commit(); } }
这里我们从缓存中的用户信息字符串中获取有用的信息,根据实际需求完成相应的操作即可。
值得一提的是,完成后续的相应操作后,我们要把该缓存清空,否则每次该界面可见都会自动完成相应的后续操作。
至此全部完成,没有demo的博客都是耍流氓,demo附上