【Android】微信登录,一篇文章搞定

11,842 阅读7分钟

申请账号

微信开放平台申请账号
添加应用后在底部填写Android包名应用签名,填写Android的就完了,iOS咱不管
签名使用Gensignature获取 Gensignature下载位置 使用方法是把这个APP安装到手机,然后把要获取签名的APP使用发布版签名安装到同一个手机,然后输入包名点击下图的获取。 获取签名 签名和包名填写到页面底部的这里↓ 填写包名和签名
然后在页面顶部复制这个AppID,这个东西是不会变的,复制一次就行了,下面的AppSecret可用可不用。
因为微信的登录回调写得很智障,用户昵称头像之类的东东,你和后台,谁去管微信要,谁就要用到这个AppSecret AppID

自动给debug包签名

如果不想每次都手动签名,可以使用gradle脚本自动签名。 在APPbuild.gradleandroid中加入

signingConfigs {
        debug {
            keyAlias 'aaa'//库中对应的签名文件
            keyPassword '123456'//签名密码
            storeFile file('D:/AndroidProjects/xxx.jks')//签名库文件路径和名称,强烈建议存在项目目录里
            storePassword '123456'//签名库密码
        }
    }

导入lib

跟我念:Gradle大法好!微信支付和微信登录都在这一个包里,导入就完事了

//微信SDK
implementation 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'

登录代码(写到你的登录界面里)

先实例化一个微信API对象
Java

private IWXAPI api;

//实例化全局微信api对象,可以写到Activity的onCreate里也可以把整个对象放在Application里全局使用
        api = WXAPIFactory.createWXAPI(this, "你的AppID", false);
        api.registerApp("你的AppID");

Kotlin

lateinit var api: IWXAPI

//实例化全局微信api对象,可以写到Activity的onCreate里也可以把整个对象放在Application里全局使用
        api = WXAPIFactory.createWXAPI(this, "你的AppID", false)
        api.registerApp("你的AppID")

然后是登录方法
Java

private void login(IWXAPI api) {
        if (!api.isWXAppInstalled()) {
            ToastUtils.showShort("您还未安装微信客户端!");//这里是一句Toast,可以用你自己的Toast工具类替换
            return;
        }
        SendAuth.Req req =new SendAuth.Req();
        req.scope = "snsapi_userinfo";
        req.state = "wechat_sdk_demo_test";
        api.sendReq(req);
        finish();
    }

Kotlin

private fun login(api: IWXAPI) {
        if (!api.isWXAppInstalled) {
            ToastUtils.showShort("您还未安装微信客户端!")//这里是一句Toast,可以用你自己的Toast工具类替换
            return
        }
        val req = SendAuth.Req()
        req.scope = "snsapi_userinfo"
        req.state = "wechat_sdk_demo_test"
        api.sendReq(req)
        finish()
    }

直接调用这个方法就可以请求微信登录,回调写的下面提到的Activtiy里。
对,没错,回调写到其他Activity里,是不是很扯淡?不知道他微信的SDK开发人员脑子里是不是进了硫酸(╯‵□′)╯︵┻━┻写出这种神仙操作逻辑。

创建wxapi的Activity

你的包名下建一个子package叫wxapi,这个名字不能改,然后在里面新建
WXEntryActivity -> 微信登录/微信分享回调
WXPayEntryActivity -> 微信支付回调
他们都继承Activity实现IWXAPIEventHandler接口

注册回调Activity

<!-- 微信回调STRART -->
        <activity
            android:name=".wxapi.WXEntryActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:exported="true"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />

        <!--/*微信支付*/-->
        <activity
            android:name=".wxapi.WXPayEntryActivity"
            android:exported="true"
            android:launchMode="singleInstance"
            android:theme="@style/AppSplash">
            <!-- 微信支付 -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="在这里填写AppID" />
            </intent-filter>
        </activity>
        

style内容

  <style name="AppSplash" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- 设置启动背景透明 -->
        <item name="android:windowIsTranslucent">true</item>
        <!-- 设置启动不要Title -->
        <item name="android:windowNoTitle">true</item>
    </style>

本篇讲微信登录,支付用的WXPayEntryActivity如果你的项目没用到可以不写也没关系。
WXEntryActivity和刚才一样拿一个WXAPI对象,创建过程是一毛一样的这里就省略不写了,如果你一开始是写在Application里面的现在直接拿来用就好了。
不同的是这次要在onCreateonNewIntent里加上

setIntent(intent);//onNewIntent还要在前面加这句,onCreate不用
api.handleIntent(intent, this);

然后重写onReq(请求发出)和onResp(请求返回)两个方法
发出的地方(onReq)打个日志就好

Toast.makeText(this, "openid = " + req.openId, Toast.LENGTH_SHORT).show();

接收返回的地方(onResp)我们来搞事情

Java

@Override
private void onResp(BaseResp resp) {
    String result = "";
    switch (resp.errCode) {
            case BaseResp.ErrCode.ERR_USER_CANCEL:
                result = "操作取消";
                break;
            case BaseResp.ErrCode.ERR_AUTH_DENIED:
                result = "请求被拒绝";
                break;
            default:
                result = "未知错误";
                break;
            }
        }
        switch (resp.type) {
            case 1:{
                if (resp.errCode == BaseResp.ErrCode.ERR_OK) {
                    SendAuth.Resp resp2 = (SendAuth.Resp) resp;
                    val code = resp2.code;
                    result = "登录成功";
                    getAccessToken(code);//如果你家后台要昵称头像啥的用户信息你还要用这个code去请求微信的接口,否则在这里直接返回code给后台即可
                }
                break;
            }
            case 2: {
                if (resp.errCode == BaseResp.ErrCode.ERR_OK) {
                    result = "分享成功";
                }
            }
        }
        ToastUtil.showShort(result);
        Logger.d(result);
}

Kotlin

override fun onResp(resp: BaseResp) {
        when (resp.errCode) {
            BaseResp.ErrCode.ERR_USER_CANCEL -> result = "操作取消"
            BaseResp.ErrCode.ERR_AUTH_DENIED -> result = "请求被拒绝"
            else -> {
                result = "未知错误"
            }
        }
        when (resp.type) {
            1 -> {
                if (resp.errCode == BaseResp.ErrCode.ERR_OK) {
                    val code = (resp as SendAuth.Resp).code
                    result = "登录成功"
                    getAccessToken(code)//如果你家后台要昵称头像啥的用户信息你还要用这个code去请求微信的接口,否则在这里直接返回code给后台即可
                }
            }
            2 -> {
                if (resp.errCode == BaseResp.ErrCode.ERR_OK) {
                    result = "分享成功"
                }
            }
        }
        ToastUtil.showShort(result)
        Logger.d(result)
    }

如果你家后台只要code的话,到这里为止就结束了,把getAccessToken那个方法换成http请求发给你家后台就行了。
如果你还想拿微信的昵称和头像的话,请继续往下看。

用code调微信接口拿token

Java+OkHttp3

private void getAccessToken(String code) {
        //        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token";
        OkHttpClient mOkHttpClient = new OkHttpClient();
        ///< Post方式也可以...
        //        RequestBody body = new FormBody.Builder()
        //                .add("appid", "替换为你的appid")
        //                .add("secret", "替换为你的app密钥")
        //                .add("code", code)
        //                .add("grant_type", "authorization_code")
        //                .build();
        url += "?appid=" + "替换为你的appid" + "&secret=xxxxxxxx"
                + "&code=" + code + "&grant_type=authorization_code";
        final Request request = new Request.Builder()
                .url(url)
                //.post(body)
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                finish();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json = response.body().string();
                WXTokenModel accessToken = JSONObject.parseObject(json, new TypeReference<WXTokenModel>() {
                });
                getUserInfo(accessToken.getAccess_token(), accessToken.getOpenid());
            }
        });
    }

Kotlin+Kolley

/**
     * @param code 根据code再去获取AccessToken
     */
    private fun getAccessToken(code: String) {
        //        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        ///< Post方式也可以...
        //        RequestBody body = new FormBody.Builder()
        //                .add("appid", "替换为你的appid")
        //                .add("secret", "替换为你的app密钥")
        //                .add("code", code)
        //                .add("grant_type", "authorization_code")
        //                .build();
        Http.post {
            //            url += ("?appid=" + "替换为你的appid" + "&secret=xxxxxxxx"
//                    + "&code=" + code + "&grant_type=authorization_code")
            url = "https://api.weixin.qq.com/sns/oauth2/access_token"
            params {
                "appid" - "你的AppID"
                "secret" - "你的Secret"
                "code" - code//你拿到的code
                "grant_type" - "authorization_code"
            }
            onSuccess { byts ->
                var result = byts.toString(Charset.defaultCharset())
                if (TextUtils.isNotEmpty(result)) {
                    val model: WXTokenModel = StringNullAdapter.gson.fromJson(result)
                    getUserInfo(model.access_token, model.openid)
                }
            }
            onFail { error ->
                var message = error.message
                ToastUtil.showShort(message)
            }
        }
    }

其中用到的
kolley是一个仅支持Kotlin的轻量级网络请求框架
StringNullAdapter是一个解析json时即使遇到空也不会崩溃的第三方库仅支持Kotlin

implementation 'com.ohmerhe.kolley:kolley:0.3.1'
implementation 'com.github.mfangtao:FTLibary:2.0.2'

WXTokenModel

JavaBean

public class WXTokenModel{
    private String access_token = "";
    private int expires_in = 0;
    private openid: String = "";
    private String refresh_token  = "";
    private String scope: = "";
    
    //getter/setter略
}

Kotlin data class

data class WXTokenModel(
    var access_token: String = "",
    var expires_in: Int = 0,
    var openid: String = "",
    var refresh_token: String = "",
    var scope: String = ""
)

用token调微信接口拿用户信息

Java+OkHttp3

private void getUserInfo(String access_token, String openid) {
        //        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
        String url = "https://api.weixin.qq.com/sns/userinfo";
        OkHttpClient mOkHttpClient = new OkHttpClient();
        RequestBody body = new FormBody.Builder()
                .add("access_token", access_token)
                .add("openid", openid)
                .build();
        final Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Call call = mOkHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                finish();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String json = response.body().string();
                WXUserInfo wxUserInfo = JSONObject.parseObject(json, new TypeReference<WXUserInfo>() {
                });//至此昵称头像全部到手,传给你家后台吧
                finish();
            }
        });
    }

Kotlin+kolley

private fun getUserInfo(access_token: String, openid: String) {
        //        String url = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
        Http.post {
            url = "https://api.weixin.qq.com/sns/userinfo"
            params {
                "access_token" - access_token
                "openid" - openid
            }
            onSuccess { byts ->
                var result = byts.toString(Charset.defaultCharset())
                if (TextUtils.isNotEmpty(result)) {
                    val model: WXUserInfo = StringNullAdapter.gson.fromJson(result)
                    sendThird(openid, model.headimgurl, model.nickname)//至此昵称头像全部到手,传给你家后台吧
                }
            }
            onFail { error ->
                var message = error.message
                ToastUtil.showShort(message)
            }
        }
    }

WXUserInfo

JavaBean

public class WXUserInfo(
    private String city = "";
    private String country = "";
    private String headimgurl = "";
    private String nickname = "";
    private String openid= "";
    private List<String> privilege = ArrayList();
    private String province "";
    private int sex 0;
    private String unionid "";
    
    //getter/setter略
)

Kotlin data class

data class WXUserInfo(
    var city: String = "",
    var country: String = "",
    var headimgurl: String = "",
    var nickname: String = "",
    var openid: String = "",
    var privilege:  MutableList<String> = ArrayList(),
    var province: String = "",
    var sex: Int = 0,
    var unionid: String = ""
)

微信登录奇怪的调用链导致的问题

这里的调用链是你的登录界面→微信的登录界面→微信的回调界面,你会发现在这个跳转中由于微信的登录界面这个东东屁都不给你返回,你的登录界面不能通过onActivtiyResult之类的方法去拿登录结果,登录结果被传给了微信的回调界面。
你的登录界面完完全全就是个请求发送器,发送请求之后就没用了。
WXEntryActivity通常是一个空的界面,没有layout,因为绝大多数APP微信登录之后都是直接进主页的,而不是有一个专门的登录成功界面,所以WXEntryActivity在很多APP中都是透明的。
于是在api.sendReq(req)这行下面通常都会加一句finish()让登录界面出栈,避免用户在APP玩了一圈想退出的时候退回到了栈底的登录界面。
当然这样一来会导致一个问题,那就是用户在微信授权的界面点退出直接就没有栈底元素了直接回桌面了(其实是走到了WXEntryActivity,但是由于它是透明的,所以看起来像是APP退出了一样),所以解决办法就是在WXEntryActivity里当resp.errCode != BaseResp.ErrCode.ERR_OK的时候手动去启动一下我们自己的登录界面。

关于集成友盟的登录方案

如果你的项目使用了友盟分享的话,就不用导入一开始说的那个库了,取而代之的是下面这个库

implementation 'com.umeng.umsdk:share-wx:6.9.4'

然后在ApplicationonCreate中加上

UMConfigure.init(this, "友盟AppKey", "Umeng", UMConfigure.DEVICE_TYPE_PHONE, "友盟MessageSecret");
PlatformConfig.setWeixin("微信AppID", "微信AppSecret");

调用改为 Java

private UMShareAPI umShareAPI;
private UMAuthListener umAuthListener;//这东西的方法直接空实现即可,因为它的回调微信不会走。是方法空实现,不是对象留空


//onCreate中
umShareAPI = UMShareAPI.get(this);

//调用登录
umShareAPI.getPlatformInfo(this, SHARE_MEDIA.WEIXIN, umAuthListener);//微信登录

Kotlin

lateinit var umShareAPI: UMShareAPI
lateinit var umAuthListener: UMAuthListener//这东西的方法直接空实现即可,因为它的回调微信不会走。是方法空实现,不是对象留空


//onCreate中
umShareAPI = UMShareAPI.get(this)

//调用登录
umShareAPI.getPlatformInfo(this, SHARE_MEDIA.WEIXIN, umAuthListener)//微信登录

其他部分和官方SDK相同

参考文献

部分代码来自Android-微信登录坑(+友盟微信登录)