轉(zhuǎn)載需要著名出處:
http://blog.csdn.net/lowprofile_coding/article/details/78004224
之前寫過(guò)微信登錄分享支付第一版:
http://blog.csdn.net/lowprofile_coding/article/details/48086381
前言
大部分的app都有接入第三方sdk的需求咖杂。例如第三方登錄需要接入微信庆寺、QQ、微博诉字。第三方支付需要接入微信懦尝、支付寶、銀聯(lián)壤圃。
這些我都有使用過(guò)陵霉,都有使用過(guò)他們的sdk,感覺(jué)最麻煩的就是微信伍绳,不能直接調(diào)試踊挠,得用正式的簽名進(jìn)行簽名才能調(diào)試。還有他們官方的demo也是跑不起來(lái)的冲杀,因?yàn)闆](méi)有簽名文件效床。需要注意的地方也很多。
代碼實(shí)現(xiàn)
微信sdk現(xiàn)在支持Android Studio在線引用了漠趁,之前都是添加jar的方法扁凛。需要訪問(wèn)微信的接口獲取用戶信息忍疾,所以把我們之前封裝的okhttp也一起在線引用闯传。okhttp需要在自定義的Application中初始化這個(gè)我就不貼代碼了。之前已經(jīng)講過(guò)很多次卤妒。在app/build.gradle文件dependencies標(biāo)簽中加入以下兩行代碼:
compile 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'
compile 'com.ansen.http:okhttpencapsulation:1.0.1'
需要用到網(wǎng)絡(luò)甥绿,所以在AndroidManifest.xml文件中加入網(wǎng)絡(luò)權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登錄之后信息在這里顯示"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="昵稱:"/>
<TextView
android:id="@+id/tv_age"
android:layout_below="@+id/tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="年齡:"/>
</RelativeLayout>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="微信登錄"/>
<Button
android:id="@+id/btn_share_friend_circle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="分享到朋友圈"/>
<Button
android:id="@+id/btn_share_friend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="分享給好友"/>
<Button
android:id="@+id/btn_pay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="微信支付"/>
</LinearLayout>
布局文件很簡(jiǎn)單字币,就LinearLayout里面放了幾個(gè)TextView,跟幾個(gè)按鈕共缕。
WeiXin.java 用于EventBus來(lái)傳送消息洗出,微信sdk有個(gè)很奇怪的地方,就是不管登錄图谷、分享翩活、支付之后都得用一個(gè)Activity來(lái)接收,所以我們還得從接收的那個(gè)activity把結(jié)果信息通過(guò)EventBus傳遞給MainActivity便贵。雖然用廣播也能實(shí)現(xiàn)菠镇,但是個(gè)人喜歡用EventBus,使用靈活承璃。簡(jiǎn)單輕量利耍。
public class WeiXin {
private int type;//1:登錄 2.分享 3:微信支付
private int errCode;//微信返回的錯(cuò)誤碼
private String code;//登錄成功才會(huì)有的code
public WeiXin() {
}
public WeiXin(int type,int errCode, String code) {
this.type = type;
this.errCode=errCode;
this.code = code;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public int getErrCode() {
return errCode;
}
public void setErrCode(int errCode) {
this.errCode = errCode;
}
}
Constant.java 常量類,微信appid跟secret的值用兩個(gè)常量保存盔粹。為了保護(hù)隱私這兩個(gè)值我已經(jīng)修改過(guò)隘梨。
public class Constant {
public static String WECHAT_APPID="wxda6db2aec81389af";
public static String WECHAT_SECRET="8fed5a2d510022587ef8a6194c965be3";
}
MainActivity.java 全部代碼貼出來(lái)比較亂,暫時(shí)貼出MainActivity部分代碼舷嗡。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private IWXAPI wxAPI;
private TextView tvNickname,tvAge;
public static final int IMAGE_SIZE=32768;//微信分享圖片大小限制
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);//注冊(cè)
wxAPI = WXAPIFactory.createWXAPI(this,Constant.WECHAT_APPID,true);
wxAPI.registerApp(Constant.WECHAT_APPID);
findViewById(R.id.btn_login).setOnClickListener(this);
findViewById(R.id.btn_share_friend_circle).setOnClickListener(this);
findViewById(R.id.btn_share_friend).setOnClickListener(this);
findViewById(R.id.btn_pay).setOnClickListener(this);
tvNickname= (TextView) findViewById(R.id.tv_nickname);
tvAge=(TextView) findViewById(R.id.tv_age);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_login://微信登錄
login();
break;
case R.id.btn_share_friend_circle://微信分享到朋友圈
share(true);
break;
case R.id.btn_share_friend://微信分享給朋友
share(false);
break;
case R.id.btn_pay://微信支付
// 先去服務(wù)器獲取支付信息轴猎,返回一個(gè)WeiXinPay對(duì)象,然后調(diào)用pay方法
showToast("微信支付需要服務(wù)器支持");
break;
}
}
/**
* 這里用到的了EventBus框架
* @param weiXin
*/
@Subscribe
public void onEventMainThread(WeiXin weiXin){
Log.i("ansen","收到eventbus請(qǐng)求 type:"+weiXin.getType());
if(weiXin.getType()==1){//登錄
getAccessToken(weiXin.getCode());
}else if(weiXin.getType()==2){//分享
switch (weiXin.getErrCode()){
case BaseResp.ErrCode.ERR_OK:
Log.i("ansen", "微信分享成功.....");
break;
case BaseResp.ErrCode.ERR_USER_CANCEL://分享取消
Log.i("ansen", "微信分享取消.....");
break;
case BaseResp.ErrCode.ERR_AUTH_DENIED://分享被拒絕
Log.i("ansen", "微信分享被拒絕.....");
break;
}
}else if(weiXin.getType()==3){//微信支付
if(weiXin.getErrCode()==BaseResp.ErrCode.ERR_OK){//成功
Log.i("ansen", "微信支付成功.....");
}
}
}
..........
public void showToast(String message){
Toast.makeText(this,message,Toast.LENGTH_LONG).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);//取消注冊(cè)
}
}
- onCreate 注冊(cè)EventBus咬崔,通過(guò)WXAPIFactory創(chuàng)建IWXAPI類税稼,并且注冊(cè)appid,給四個(gè)按鈕按鈕設(shè)置點(diǎn)擊事件垮斯。查找顯示名字跟年齡的兩個(gè)TextView郎仆。
- onClick 點(diǎn)擊事件監(jiān)聽(tīng),根據(jù)id來(lái)判斷點(diǎn)擊不同的按鈕兜蠕,跳轉(zhuǎn)到相應(yīng)的方法扰肌,這些方法沒(méi)貼出來(lái)來(lái),等會(huì)單獨(dú)講熊杨。
- onEventMainThread(WeiXin weiXin) 用來(lái)接收消息曙旭,這個(gè)方法有個(gè)參數(shù),用來(lái)判斷類型晶府,就是我們用EventBus發(fā)送時(shí)參數(shù)必須是WeiXin類型桂躏。首先判斷type,是登錄還是分享還是支付川陆。登錄成功情況下會(huì)獲取到code剂习,根據(jù)code然后我們就能獲取到微信用戶信息。
- showToast Toast提示
- onDestroy 取消EventBus注冊(cè)
微信登錄
微信登錄流程有以下三個(gè)步驟:
- 微信授權(quán)登陸
- 根據(jù)授權(quán)登陸code 獲取該用戶token
- 根據(jù)token獲取用戶資料
當(dāng)我們點(diǎn)擊登錄按鈕的時(shí)候,調(diào)用的是login方法鳞绕。這個(gè)方法就在MainActivity里面失仁。就是給微信發(fā)起一個(gè)登錄請(qǐng)求,彈出一個(gè)授權(quán)界面们何。
public void login(){
SendAuth.Req req = new SendAuth.Req();
req.scope = "snsapi_userinfo";
req.state = String.valueOf(System.currentTimeMillis());
wxAPI.sendReq(req);
}
在你的包名相應(yīng)目錄下新建一個(gè)wxapi目錄萄焦,然后在wxapi目錄下新增一個(gè)WXEntryActivity類,用來(lái)接收登錄授權(quán)以及分享時(shí)微信的回調(diào)信息冤竹。這個(gè)類繼承自Activity拂封,需要實(shí)現(xiàn)IWXAPIEventHandler接口。
package com.ansen.shoenet.wxapi;
public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
private IWXAPI wxAPI;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
wxAPI = WXAPIFactory.createWXAPI(this,Constant.WECHAT_APPID,true);
wxAPI.registerApp(Constant.WECHAT_APPID);
wxAPI.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent){
super.onNewIntent(intent);
wxAPI.handleIntent(getIntent(),this);
Log.i("ansen","WXEntryActivity onNewIntent");
}
@Override
public void onReq(BaseReq arg0) {
Log.i("ansen","WXEntryActivity onReq:"+arg0);
}
@Override
public void onResp(BaseResp resp){
if(resp.getType()== ConstantsAPI.COMMAND_SENDMESSAGE_TO_WX){//分享
Log.i("ansen","微信分享操作.....");
WeiXin weiXin=new WeiXin(2,resp.errCode,"");
EventBus.getDefault().post(weiXin);
}else if(resp.getType()==ConstantsAPI.COMMAND_SENDAUTH){//登陸
Log.i("ansen", "微信登錄操作.....");
SendAuth.Resp authResp = (SendAuth.Resp) resp;
WeiXin weiXin=new WeiXin(1,resp.errCode,authResp.code);
EventBus.getDefault().post(weiXin);
}
finish();
}
}
onCreate鹦蠕、onNewIntent烘苹、onReq這三個(gè)方法是固定寫法。onResp方法接收微信結(jié)果信息片部,首先判斷類型镣衡,根據(jù)不同的類型去封裝WeiXin對(duì)象,如果是登錄操作档悠,把code傳入進(jìn)去廊鸥,然后把封裝好的WeiXin對(duì)象通過(guò)EventBus發(fā)送出去。MainActivity的onEventMainThread方法就會(huì)接收到這個(gè)消息辖所。最后調(diào)用finish關(guān)閉當(dāng)前的activity惰说。
WXEntryActivity記得在AndroidManifest.xml中注冊(cè)
<activity
android:exported="true"
android:name=".wxapi.WXEntryActivity"/>
繼續(xù)回到首頁(yè)的onEventMainThread,如果登錄類型調(diào)用getAccessToken()缘回,并且傳入code吆视。根據(jù)code獲取access_token,這個(gè)url是微信公開的酥宴,需要傳入三個(gè)參數(shù)啦吧,appid、secret拙寡、code授滓。請(qǐng)求成功之后會(huì)返回access_token跟openid等信息。
public void getAccessToken(String code){
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
"appid="+Constant.WECHAT_APPID+"&secret="+Constant.WECHAT_SECRET+
"&code="+code+"&grant_type=authorization_code";
HTTPCaller.getInstance().get(WeiXinToken.class, url, null, new RequestDataCallback<WeiXinToken>() {
@Override
public void dataCallback(WeiXinToken obj) {
if(obj.getErrcode()==0){//請(qǐng)求成功
getWeiXinUserInfo(obj);
}else{//請(qǐng)求失敗
showToast(obj.getErrmsg());
}
}
});
}
獲取到access_token跟openid之后繼續(xù)調(diào)用getWeiXinUserInfo方法獲取用戶信息肆糕。這樣就能取到當(dāng)前微信app登錄的用戶一些信息般堆。有昵稱、年齡诚啃、頭像地址淮摔、語(yǔ)言等基本信息。在企業(yè)開發(fā)中始赎,到了這一步就可以拿著這些信息調(diào)用自己服務(wù)器的登錄接口和橙。當(dāng)然我們這邊就把昵稱跟年齡給TextView顯示下顾彰。
public void getWeiXinUserInfo(WeiXinToken weiXinToken){
String url = "https://api.weixin.qq.com/sns/userinfo?access_token="+
weiXinToken.getAccess_token()+"&openid="+weiXinToken.getOpenid();
HTTPCaller.getInstance().get(WeiXinInfo.class, url, null, new RequestDataCallback<WeiXinInfo>() {
@Override
public void dataCallback(WeiXinInfo obj) {
tvNickname.setText("昵稱:"+obj.getNickname());
tvAge.setText("年齡:"+obj.getAge());
Log.i("ansen","頭像地址:"+obj.getHeadimgurl());
}
});
}
WeiXinToken跟WeiXinInfo這兩個(gè)實(shí)體類就不貼代碼了,WeiXinToken用來(lái)映射獲取訪問(wèn)token接口返回的json胃碾。WeiXinInfo用來(lái)映射獲取用戶接口返回的json。
微信分享
分享有兩種分享到朋友圈跟分享給好友筋搏,統(tǒng)一調(diào)用share方法仆百。傳入一個(gè)boolean類型來(lái)判斷是否分享到朋友圈。
public void share(boolean friendsCircle){
WXWebpageObject webpage = new WXWebpageObject();
webpage.webpageUrl = "www.baidu.com";//分享url
WXMediaMessage msg = new WXMediaMessage(webpage);
msg.title = "分享標(biāo)題";
msg.description = "分享描述";
msg.thumbData =getThumbData();//封面圖片byte數(shù)組
SendMessageToWX.Req req = new SendMessageToWX.Req();
req.transaction = String.valueOf(System.currentTimeMillis());
req.message = msg;
req.scene = friendsCircle ? SendMessageToWX.Req.WXSceneTimeline : SendMessageToWX.Req.WXSceneSession;
wxAPI.sendReq(req);
}
分享內(nèi)容有很多格式奔脐,分享圖片俄周、分享視頻、分享消息髓迎。我們這邊就分享消息為例峦朗,也是分享比較常見(jiàn)的格式。首先new一個(gè)WXWebpageObject對(duì)象排龄,設(shè)置標(biāo)題波势、內(nèi)容、打開鏈接橄维、封面等尺铣。最后調(diào)用wxAPI的sendReq放松一個(gè)請(qǐng)求。
分享跟登錄一樣争舞,都會(huì)回調(diào)WXEntryActivity凛忿,然后又把分享結(jié)果發(fā)送給MainActivity.onEventMainThread方法。
支付
支付首先需要請(qǐng)求我們自己的服務(wù)器竞川,獲取支付信息店溢。獲取成功之后調(diào)用pay方法。
public void pay(WeiXinPay weiXinPay){
PayReq req = new PayReq();
req.appId = Constant.WECHAT_APPID;//appid
req.nonceStr=weiXinPay.getNoncestr();//隨機(jī)字符串委乌,不長(zhǎng)于32位床牧。推薦隨機(jī)數(shù)生成算法
req.packageValue=weiXinPay.getPackage_value();//暫填寫固定值Sign=WXPay
req.sign=weiXinPay.getSign();//簽名
req.partnerId=weiXinPay.getPartnerid();//微信支付分配的商戶號(hào)
req.prepayId=weiXinPay.getPrepayid();//微信返回的支付交易會(huì)話ID
req.timeStamp=weiXinPay.getTimestamp();//時(shí)間戳
wxAPI.registerApp(Constant.WECHAT_APPID);
wxAPI.sendReq(req);
}
weiXinPay的值應(yīng)該是我們從自己服務(wù)器獲取的,然后把返回信息封裝到PayReq對(duì)象中遭贸,最后調(diào)用wxAPI的sendReq方法發(fā)起請(qǐng)求叠赦。
在wxapi目錄下新增一個(gè)WXPayEntryActivity類,這個(gè)類跟WXEntryActivity同級(jí)革砸,用來(lái)接收微信支付的回調(diào)信息除秀。這個(gè)類繼承自Activity,需要實(shí)現(xiàn)IWXAPIEventHandler接口算利。
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
private IWXAPI wxAPI;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
wxAPI = WXAPIFactory.createWXAPI(this, Constant.WECHAT_APPID);
wxAPI.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent){
super.onNewIntent(intent);
setIntent(intent);
wxAPI.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq baseReq) {}
@Override
public void onResp(BaseResp resp) {
Log.i("ansen", "微信支付回調(diào) 返回錯(cuò)誤碼:"+resp.errCode+" 錯(cuò)誤名稱:"+resp.errStr);
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX){//微信支付
WeiXin weiXin=new WeiXin(3,resp.errCode,"");
EventBus.getDefault().post(weiXin);
}
finish();
}
}
其他方法都是固定寫法册踩,在onResp中判斷如果是微信登錄,就封裝一個(gè)WeiXin對(duì)象效拭,然后發(fā)送EventBus請(qǐng)求暂吉。這樣MainActivity的onEventMainThread就會(huì)接收到這個(gè)WeiXin對(duì)象胖秒。
WXPayEntryActivity記得在AndroidManifest.xml中注冊(cè)。
<activity
android:exported="true"
android:name=".wxapi.WXPayEntryActivity"/>
項(xiàng)目結(jié)構(gòu)圖如下所示慕的,從圖中我們看到軟件包名是com.ansen.shoenet阎肝。接收微信登錄支付返回的Activity的包名必須是com.ansen.shoenet.wxapi。兩個(gè)activity的名字也是固定寫法肮街。
簽名
微信登錄分享支付都有一個(gè)簽名驗(yàn)證风题,這個(gè)很麻煩,導(dǎo)致每次調(diào)試都需要重新簽名嫉父。
首先用android studio生成一個(gè)正式的簽名文件沛硅,簽名文件是.jks結(jié)尾的,這個(gè)簽名文件是你以后打線上包一直要用到的绕辖。然后用這個(gè)簽名文件生成apk摇肌。這個(gè)時(shí)候我們的app就有了正式簽名。把正式簽名的apk發(fā)送到手機(jī)上進(jìn)行安裝仪际。
并且下載一個(gè)簽名生成工具安裝围小,這個(gè)工具用于獲取安裝到手機(jī)的第三方應(yīng)用簽名的apk包。微信官方下載地址:
https://res.wx.qq.com/open/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android2.apk
以上兩個(gè)app都安裝好了之后打開從微信下載的那個(gè)app,軟件名字叫「GenSignature」,有一個(gè)輸入框树碱,輸入我們軟件的包名吩抓,點(diǎn)擊Get Signature按鈕.效果圖如下:
把那行綠色的16進(jìn)制數(shù)炒下來(lái)保存到txt文本中。
微信sdk官網(wǎng)后臺(tái)配置
官網(wǎng)地址:
https://open.weixin.qq.com/
在微信sdk首頁(yè)赴恨,有個(gè)管理中心點(diǎn)擊之后默認(rèn)就是移動(dòng)應(yīng)用疹娶,如果還沒(méi)有創(chuàng)建移動(dòng)應(yīng)用就先創(chuàng)建一個(gè),如果有了就點(diǎn)擊當(dāng)前的應(yīng)用后面的查看按鈕伦连,就會(huì)進(jìn)入應(yīng)用詳細(xì)界面雨饺。
在應(yīng)用詳細(xì)界面一直往下滾動(dòng),滾到最底部有個(gè)開發(fā)信息惑淳。點(diǎn)擊修改额港,進(jìn)入修改界面,在修改界面滾動(dòng)到最下面,效果圖如下所示:
首先我們?cè)贏ndroid應(yīng)用這里打上勾歧焦,然后填寫應(yīng)用簽名移斩,這個(gè)簽名都是我之前要你們保存到記事本上的那個(gè)值,包名就是app包名绢馍。點(diǎn)擊保存向瓷。
運(yùn)行軟件
登錄之后效果圖如下:
分享到朋友圈如下:
分享給朋友:
微信支付沒(méi)法測(cè)試,因?yàn)樾枰?wù)器支持舰涌。
微信官方開發(fā)文檔
我這偏文章只是針對(duì)現(xiàn)在微信的sdk版本接入猖任,但是sdk是有可能變化的,版本變化瓷耙、接口變化等朱躺。所以建議大家還是以官方文檔為主刁赖。我的文章提供參考。
移動(dòng)應(yīng)用微信登錄開發(fā)指南
https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317851&token=219192a54f13e8e7011ced8e4ce5b36b699629c4&lang=zh_CN
Android微信支付開發(fā)手冊(cè)
https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317784&token=219192a54f13e8e7011ced8e4ce5b36b699629c4&lang=zh_CN
注意事項(xiàng)
接入微信sdk有很多需要注意的地方长搀,這里我們最后再來(lái)做一個(gè)總結(jié)宇弛。
- 微信登陸,分享源请,支付回調(diào)的Activity包名跟類名一定要嚴(yán)格按照要求去寫
- 接收回調(diào)的是Activity一定要在AndroidManifest.xml中中注冊(cè)
- Constant里面兩個(gè)常量的值要去微信申請(qǐng)并且創(chuàng)建應(yīng)用才有的枪芒,這里需要改成你們申請(qǐng)的值。
- 需要訪問(wèn)網(wǎng)絡(luò)所以記得在AndroidManifest.xml中添加權(quán)限
- 調(diào)用微信的登陸巢钓,分享,支付你的安裝包一定要有簽名,簽名信息一定要跟你在微信官網(wǎng)上配置的簽名信息一致
- 微信沒(méi)有客服支持.....如果出了問(wèn)題看官方的Demo或者官方API
- 微信SDK經(jīng)常升級(jí)疗垛,如果你開發(fā)的時(shí)候有最新的就用最新的吧.....
最后的最后
你們直接運(yùn)行我的demo是不行的症汹,因?yàn)槟銈儧](méi)有的jks文件,沒(méi)法簽名贷腕,并且源碼中的appid跟secret被我修改過(guò)了背镇,是不能使用的,但是你們可能又想看運(yùn)行效果泽裳,所以我在項(xiàng)目下建了個(gè)apk文件夾瞒斩,里面放了一個(gè)可以測(cè)試微信登錄分享的apk安裝包。
如果你想第一時(shí)間看我的后期文章涮总,掃碼關(guān)注公眾號(hào)胸囱,每周不定期推送Android開發(fā)實(shí)戰(zhàn)教程文章...
Android開發(fā)666 - 安卓開發(fā)技術(shù)分享
掃描二維碼加關(guān)注