微信的开发,需要参考前文提到的官方地址
https://mp.weixin.qq.com/debug/cgibin/sandboxinfo?action=showinfo&t=sandbox/index
https://mp.weixin.qq.com/wiki
在自己阅读的过程中,感到这些非常抽象,感觉自己可以在一边实践一边编码。恰好徐学长正写着一个微信的框架,我便在这个基础上进行理解和学习。有了这样的一个框架,感觉就稍显轻松一些。
根据微信开发指导,access_token是一个很关键的东西,内容如下:
1. access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。
2. access_token的存储至少要保留512个字符空间。
3. access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
Test类作为一个单独的线程,负责维护access_token和展示Request_menu(进入公众号的菜单)
单独运行时可以用于调试界面的展示部分内容和access_token更新情况。展示部分会由errcode进行展示。access_token每360000秒获取一次
显示内容大致如下:
考虑到部分项目内容可能会用于现实使用,故将部分内容(如数据库地址、密码)进行模糊化或者进行加密处理,展示的是密文内容。
尽管开发阶段使用的是一个申请的测试号,但还是希望可以尽量避免信息泄露。
{"access_token":"76bNnRFOXdqAhBKSik7i6N6068Br9ojOO2kJ4wN4yRXACWs2Q9DFJMCC9xCKXEkg0yVGvnsJtD7zuVZw3W4LqF37cBWdXFIU0OBzFzTz4zp-JUo0UQ4frV6pKu2yKADSBGhAHAJSX":7200}
menu:{"button":[{"name":"完成调查","type":"click","key":"finish"},{"name":"查看问卷","sub_button":[{"type":"click","name":"二级BA","key":"二级BA"},{"type":"click","name":"虎扑","key":"hupu"}]},{"name":"账号管理","type":"click","key":"am"}]}
{"errcode":0,"errmsg":"ok"}
具体的access_token的获取方法由Request_accesstoken类实现,代码如下:
package request;
import request_bean.Access_token;
import com.google.gson.Gson;
import util.ParameterStatic;
public class Request_accesstoken extendsRequest_base{
Access_tokenaccess_token;
@Override
public void run() {
while (true) {
result=get();
if(result!=null)
access_token=newGson().fromJson(result, Access_token.class);
ParameterStatic.ACCESS_TOKEN=access_token.getAccess_token();
try {
sleep(360000);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
protectedvoid seturl() {
str_url=ParameterStatic.URL_ACCESSTOKEN+"&appid="+ParameterStatic.APPID+"&secret="+ParameterStatic.APP_SECRET;
}
@Override
protectedvoid initmap() {
//TODO Auto-generated method stub
}
@Override
protectedvoid setdata_out() {
//TODO Auto-generated method stub
}
@Override
protectedvoid setmethod() {
//TODO Auto-generated method stub
method="GET";
}
}
所有主动请求类的父类,使用线程。
access_token和Request_menu这些都属于服务器主动向用户传递的,区别于用户点击产生时间,交给服务器处理的Service。因此这些都可以作为一个Request_base的子类存在,所有的主动服务包括ip获取,二维码产生,都需要继承自这个父类。
这个类是由学长写的,我大致了解了它的一些功能,和子类继承需要重写的方法;由于后续开发应该不会用到主动的部分(不会主动推送消息给用户),因此内部的业务逻辑实现没有具体去了解。
Request_base的主体代码如下:
package request;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.security.SecureRandom;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.apache.http.client.HttpClient;
import util.HttpsTrustManager;
import com.google.gson.JsonObject;
public abstract class Request_base extendsThread {
protectedString str_url;
protectedHttpClient httpClient;
protectedString data_out;
protectedString result="";
protectedString method;
protectedMap
protectedabstract void seturl();
protectedabstract void initmap();
protectedabstract void setdata_out();
protectedabstract void setmethod(
);
@Override
publicvoid run() {
init();
if(method.endsWith("GET")){
result=get();
}
else{
result=post();
}
}
privatevoid init(){
seturl();
initmap();
setdata_out();
setmethod();
}
protectedString post() {
JsonObjectjsonObject = null;
StringBufferbuffer = new StringBuffer();
try{
//创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[]tm = { new HttpsTrustManager() };
SSLContextsslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null,tm, new SecureRandom());
//从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactoryssf = sslContext.getSocketFactory();
URLurl = new URL(str_url);
HttpsURLConnectionhttpUrlConn = (HttpsURLConnection) url
.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);//不使用缓冲
//设置请求方式(GET/POST)
httpUrlConn.setRequestMethod("POST");
httpUrlConn.connect();
//当有数据需要提交时
if(data_out!=null) {
OutputStream outputStream =httpUrlConn.getOutputStream();
//注意编码格式,防止中文乱码
outputStream.write(data_out.getBytes("UTF-8"));
outputStream.close();
}
//将返回的输入流转换成字符串
InputStreaminputStream = httpUrlConn.getInputStream();
InputStreamReaderinputStreamReader = new InputStreamReader(
inputStream,"utf-8");
BufferedReaderbufferedReader = new BufferedReader(
inputStreamReader);
Stringstr = null;
while((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
//释放资源
inputStream.close();
inputStream= null;
httpUrlConn.disconnect();
}catch (ConnectException ce) {
System.out.println("Weixinserver connection timed out.");
}catch (Exception e) {
System.out.println("httpsrequest error:{}" + e);
}
System.out.println(buffer.toString());
returnbuffer.toString();
}
protectedString get() {
init();
JsonObjectjsonObject = null;
StringBufferbuffer = new StringBuffer();
try{
//创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[]tm = { new HttpsTrustManager() };
SSLContextsslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null,tm, new SecureRandom());
//从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactoryssf = sslContext.getSocketFactory();
URLurl = new URL(str_url);
HttpsURLConnectionhttpUrlConn = (HttpsURLConnection) url
.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);//不使用缓冲
//设置请求方式(GET/POST)
httpUrlConn.setRequestMethod("GET");
httpUrlConn.connect();
//当有数据需要提交时
if(data_out!=null) {
OutputStreamoutputStream = httpUrlConn.getOutputStream();
//注意编码格式,防止中文乱码
outputStream.write(data_out.getBytes("UTF-8"));
outputStream.close();
}
//将返回的输入流转换成字符串
InputStreaminputStream = httpUrlConn.getInputStream();
InputStreamReaderinputStreamReader = new InputStreamReader(
inputStream,"utf-8");
BufferedReaderbufferedReader = new BufferedReader(
inputStreamReader);
Stringstr = null;
while((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
//释放资源
inputStream.close();
inputStream= null;
httpUrlConn.disconnect();
}catch (ConnectException ce) {
System.out.println("Weixinserver connection timed out.");
}catch (Exception e) {
System.out.println("httpsrequest error:{}" + e);
}
System.out.println(buffer.toString());
returnbuffer.toString();
}
}
为了展示公众号的菜单,需要编写Menu,这个由Request_menu类完成。主要重写了setdata_out方法,设置了输出内容,即展示的界面内容
在学习过程中编写的界面如下,由于具体功能可能需要变动,以了解方式为主。这里有一点值得一提,在修改了界面后,需要重写关注公众号才可显示,使得测试过程更加烦琐。
目前的代码如下: 后面可能会因为业务逻辑的更改而进行增删。
Request_menu:
packagerequest;
importjava.util.ArrayList;
importcom.google.gson.Gson;
importrequest_bean.Button;
importrequest_bean.Menu;
importrequest_bean.SubButton;
importutil.ParameterStatic;
publicclass Request_menu extends Request_base{
private Menu menu;
@Override
protected void seturl() {
str_url=ParameterStatic.URL_MENU+ParameterStatic.ACCESS_TOKEN;
}
@Override
protected void initmap() {
// TODO Auto-generated method stub
}
@Override
protected void setdata_out() {
menu=new Menu();
menu.button=newArrayList
Button a1=new Button();
a1.name="完成调查";
//a1.sub_button=newArrayList
a1.key = "finish";
a1.type = "click";
Button a2=new Button();
a2.name="查看问卷";
a2.sub_button=newArrayList
Button a3=new Button();
a3.name="账号管理";
//a3.sub_button=newArrayList
a3.key = "am";
a3.type = "click";
/* SubButton s11=new SubButton();
s11.name="一级AA";
s11.type="click";
s11.key="二级AA";
SubButton s12=new SubButton();
s12.name="绑定账户";
s12.type="click";
s12.key="bd";
SubButton s13=new SubButton();
s13.name="一级AB";
s13.type="click";
s13.key="二级BC";*/
SubButton s21=new SubButton();
s21.name="二级BA";
s21.type="click";
s21.key="二级BA";
SubButton s22=new SubButton();
s22.name="虎扑";
s22.type="click";
s22.key="hupu";
menu.button.add(a1);
menu.button.add(a2);
menu.button.add(a3);
/* a1.sub_button.add(s11);
a1.sub_button.add(s12);
a1.sub_button.add(s13);*/
a2.sub_button.add(s21);
a2.sub_button.add(s22);
data_out=new Gson().toJson(menu);
System.err.println("menu:"+data_out);
}
@Override
protected void setmethod() {
// TODO Auto-generated method stub
method="POST";
}
}