Android/Java中使用Protobuf的Any类型实现泛型解析

上一篇博客中只讲解到了简单的使用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,能不能实现泛型呢?答案是肯定的

proto文件的定义

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),拷出备用

服务端处理【可选,Android开发可忽略此步骤】

//次注解需要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(泛型类)即可

APP端处理

HttpURLConnection处理

/**
 * 开始登录(基于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)

Retrofit+RxJava处理

/**
 * 登录
 *
 * @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应该还有更好的方式处理,知道的朋友可以留言告诉我一下】

最后来看看demo的效果:
Android/Java中使用Protobuf的Any类型实现泛型解析_第1张图片

好了本篇《Android/Java中使用Protobuf的Any类型实现泛型解析》的介绍就到这里了,有问题的博友可留言讨论。

你可能感兴趣的:(android,protobuf,java)