上一篇博客中只讲解到了简单的使用protobuf,还不会的可以先去看一下【Android项目使用Protobuf教程(结合Retrofit+RxJava及HttpURLConnection使用)】,有位小伙伴问我如何使用泛型呢?
比如每次网络请求都会有一些公共字段和可变参数,如下:
请尊重原创,转载需要注明出处,大力哥的博客:https://blog.csdn.net/qq137722697
{
code:0
msg:"登录成功"
data:{
ResultResponse
}
}
即code和msg是公共字段,每次都会返回;data为具体的业务请求所返回的响应结果。如果按照之前的介绍,可能每一个接口都需要对应一个带有code、msg的proto文件,对应的每个Response都带有code和msg,能不能实现泛型呢?答案是肯定的
LoginRequest.proto
syntax = "proto3";
//需要导入any.proto文件才可实现泛型
import "google/protobuf/any.proto";
//生成的java类所在的包名
package com.zhys.protobufdemo.protobean;
//登录请求结构体
message LoginRequest {
string username = 1;
string pwd = 2;
}
//网络请求的响应体
message HttpResultResponse {
int32 code = 1;
string msg = 2;
google.protobuf.Any data = 3;
}
//登陆结果(也可以单独定义一个proto文件)
message LoginResult {
string username = 1;
string phone = 2;
}
此处的定义也没有什么特别注意的地方,只需要使用google.protobuf.Any(需要import “google/protobuf/any.proto”;)声明可变参数data即可;此时通过Clean Project即可生成对应的java类(此处叫LoginRequestOuterClass),拷出备用
//次注解需要tomcat7及以上不能才可以运行
@WebServlet("/login.action")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("请求登陆了");
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
LoginRequestOuterClass.LoginRequest loginRequest = LoginRequestOuterClass.LoginRequest.parseFrom(request.getInputStream());
System.out.println("登陆信息:username = " + loginRequest.getUsername() + "\tpwd = " + loginRequest.getPwd());
LoginRequestOuterClass.HttpResultResponse.Builder builder = LoginRequestOuterClass.HttpResultResponse.newBuilder();
if ("admin".equals(loginRequest.getUsername()) && "132".equals(loginRequest.getPwd())) {
builder.setCode(0);
builder.setMsg("登陆成功");
LoginRequestOuterClass.LoginResult loginResult = LoginRequestOuterClass.LoginResult.newBuilder().setPhone("15519099928").setUsername("大力哥的博客").build();
builder.setData(Any.pack(loginResult));//使用Any.pack()方法将泛型类
System.out.println("登陆成功");
} else {
builder.setCode(1001);
builder.setMsg("用户名或密码错误");
System.out.println("用户名或密码错误");
}
builder.build().writeTo(response.getOutputStream());
}
}
此处跟上一篇的教程区别也不大,setData的时候需要传入泛型类,只需要通过Any.pack(泛型类)即可
/**
* 开始登录(基于HttpURLConnection)
*
* @param data
*/
public void login(final byte[] data) {
new Thread() {
@Override
public void run() {
OutputStream os = null;
try {
URL url = new URL("http://192.168.0.227:8080/protobuf/login.action");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
os = conn.getOutputStream();
os.write(data);
if (conn.getResponseCode() == 200) {
//解析结果
final LoginRequestOuterClass.HttpResultResponse loginResponse = LoginRequestOuterClass.HttpResultResponse.parseFrom(conn.getInputStream());
if (loginResponse.getCode()==0) {//登陆成功之后才会有登陆结果
LoginRequestOuterClass.LoginResult loginResult = loginResponse.getData().unpack(LoginRequestOuterClass.LoginResult.class);
ELog.e("登陆结果:code = " + loginResponse.getCode() + "\tmsg = " + loginResponse.getMsg()+"\tusername = "+loginResult.getUsername()+"\tphone = "+loginResult.getPhone());
}else {
ELog.e("登陆失败信息:code = " + loginResponse.getCode() + "\tmsg = " + loginResponse.getMsg());
}
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, loginResponse.getMsg(), Toast.LENGTH_SHORT).show();
}
});
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
progressDialog.dismiss();
}
});
}
}.start();
}
只需要使用getData().unpack(泛型类.java)即可拿到对应的泛型类,此处需要注意的是protobuf解析不像Gson解析那么强大,缺少参数会抛出以下异常
com.google.protobuf.InvalidProtocolBufferException: Type of the Any message does not match the given class.
at com.google.protobuf.Any.unpack(Any.java:208)
/**
* 登录
*
* @param view
*/
public void onLogin(View view) {
progressDialog.show();
String username = etUsername.getText().toString().trim();
String pwd = etPwd.getText().toString().trim();
//开始请求
HttpSend.getInstance().login(username,pwd, new ResultCallbackListener() {
@Override
public void onSubscribe(Disposable d) {
ELog.e("-----------");
}
@Override
public void onNext(LoginRequestOuterClass.HttpResultResponse value) {
try {
if (value.getCode()==0) {//登陆成功之后才会有登陆结果
LoginRequestOuterClass.LoginResult loginResult = value.getData().unpack(LoginRequestOuterClass.LoginResult.class);
ELog.e("登陆结果:code = " + value.getCode() + "\tmsg = " + value.getMsg()+"\tusername = "+loginResult.getUsername()+"\tphone = "+loginResult.getPhone());
}else {
ELog.e("登陆失败信息:code = " + value.getCode() + "\tmsg = " + value.getMsg());
}
Toast.makeText(MainActivity.this, value.getMsg(), Toast.LENGTH_SHORT).show();
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
ELog.e("e"+e.getMessage());
}
@Override
public void onComplete() {
progressDialog.dismiss();
ELog.e("=================");
}
});
}
处理方式跟HttpURLConnection的方式一样,使用getData().unpack(泛型类.java)即可拿到对应的泛型类【Any与Retrofit应该还有更好的方式处理,知道的朋友可以留言告诉我一下】
好了本篇《Android/Java中使用Protobuf的Any类型实现泛型解析》的介绍就到这里了,有问题的博友可留言讨论。