Retrofit使用入门

 Retrofit是Square开发的一个用于网络请求的开源库,内部封装了okhttp,并且和RxAndroid完美的兼容,使得Android的开发效率增加不少的同时也使代码变得清晰易读。

本次的学习建立在上次okhttp学习的基础之上,service端的程序也是通过自己搭建并完成的。服务端的程序比较简单,本次的retrofit学习不对服务端的程序进行过多的讲解。如果有疑问,可以参考上次okhttp的相关内容。首先还是先列举出本次学习用到的资源。


搭建使用环境

下载最新的jar或者构建Maven仓库:

<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>retrofit</artifactId>
  <version>2.2.0</version>
</dependency>

或者直接在项目的build.gradle中添加如下的依赖

compile 'com.squareup.retrofit2:retrofit:2.2.0'

ps,在添加依赖之前,最好先去github上看当前的最新版本。
在使用之前需要先添加网络访问权限。

<uses-permission android:name="android.permission.INTERNET"/>

使用示例

下面通过使用retrofit实现get、post、文件上传、下载来演示retrofit的使用。

get请求

retrofit在使用的过程中,需要定一个接口对象,我们将它命名为IUserService:

public interface IUserService {
    @GET("rlogin")
    Call<ResponseBody> loginByGet(@Query("user") String user, @Query("passwd") String passwd);
}

然后在需要MainActivity中构建Retrofit,并生成一个实现接口的方法。

  retrofit = new Retrofit.Builder()
                .baseUrl("http://172.18.9.31:8080/OkhttpTestService/")
                .build();//构建一个retrofit 对象
 IUserService repo = retrofit.create(IUserService.class);
  Call<ResponseBody> call =   repo.loginByGet("reoger","123456");//实例loginByGet对象
 call.enqueue(new Callback<ResponseBody>() {//异步执行
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

                try {
                    mImageView.setText(response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                mImageView.setText("file to get");
            }
        });

简要的说明一下上后面的代码。首先,在IUserSevice接口中,通过@GET(“rlogin”)注释指定了路径,然后通过后面的loginByGet具体化了url。结合BaseUrl,实例化出来的Url实际上是:http://172.18.9.31:8080/OkhttpTestService/rlogin?user=reoger&passwd=123456,后面的user和passwd是在实例化时传入的。
可能这么讲会有点难懂,先看一个简单的例子。
注解中的参数为请求的相对URL路径@GET("users/list")
就相当与在BaseUrl后加上/users/list。在本例中就相当于:

http://172.18.9.31:8080/OkhttpTestService/users/list

当然,我们可以会遇到URL并不是固定的那种情况。这个时候我们就可以这么写:

@GET("group/{id}/users") //注意 字符串id
List<ResponseBody> groupList(@Path("id") int groupId); //注意 Path注解的参数要和前面的字符串一样 id

这个时候我们构造groupList时会传入id,而这个id的值会代替传入的groupId值代替。
{}用于表示是可以替换的区域,而函数参数必须用@Path注解声明。参见上例。
然后,当需要用我们的请求含有参数的时候,这个时候就需要使用@Query注解来完成。
例如访问:http://baseurl/users?user=username
就需要:

 @GET("users")
    Call<ResponseBody> getUsersBySort(@Query("user") String sort);

点击之后,发现服务端能正确接收来自客服端的请求,并且客服端也能正确接收来自服务端的反馈信息。
这里有一点还需要提出来一下,Call 中的T可以是返回的数据对象,如果返回的是Json数据,我们可以将其解析成一个Java对象的话,可以直接使用该Bean作为返回值,在构建retrofit的时候加上转换方法即可。

为了后面后面的代码比较简洁,我们直接在这里先将后面用到的retrofit和repo对象,已经实现方法executeByEn()统一声明。

   retrofit = new Retrofit.Builder()
                .baseUrl("http://172.18.9.31:8080/OkhttpTestService/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        repo = retrofit.create(IUserService.class);
private void executeByEn(Call<ResponseBody> call) {
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

                try {
                    mImageView.setText(response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                mImageView.setText("file to get");
            }
        });
    }

Post请求

还是先在IUserService中进行申明:

@POST("rlogin")
    @FormUrlEncoded
    Call<ResponseBody> loginByPost(@Field("user")String user,@Field("passwd") String passwd);

可以看到,这里我们使用Post作为注解,说明这是一个Post请求。添加FormUrlEncoded,然后通过@Field添加参数即可。
访问的代码:

  Call<ResponseBody> call =   repo.loginByPost("reoger","12346");
        executeByEn(call);

此时,我们应该就能同get请求一样将数据传递到服务端,并接收来及服务端的消息。

Post向服务端传递String对象。

首先还是在IUserService中进行声明:

  @POST("rpostString")
    Call<ResponseBody>  postString(@Body RequestBody user);

通过 @Body 注解可以声明一个对象作为请求体发送到服务器。
访问代码:

  RequestBody body=RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),"Here is my server to pass the data, can be a string, but of course, can also be JSON data");
        Call<ResponseBody> call = repo.postString(body);
        executeByEn(call);

这里还是给出服务端的代码吧,服务端接收到数据后会给客户端返回一个收到的信息。

            HttpServletRequest request = ServletActionContext.getRequest();
             ServletInputStream is = request.getInputStream();

             StringBuilder sb = new StringBuilder();
             int len = 0;
             byte[] buf = new byte[1024];
             while((len=is.read(buf))!=-1){
                 sb.append(new String(buf,0,len));
             }
             System.out.println(sb.toString());

             HttpServletResponse response = ServletActionContext.getResponse();
             PrintWriter writer = response.getWriter();
              writer.write("reces: "+sb.toString());
              writer.flush();

            return null;

访问之后,会发现我们的服务端正确接收到了我们发送的string数据,客户端也成功接收到了来自服务端的数据。

Post 上传Json格式数据

如果你只是单独想上传Json格式的数据到服务器,你完全可以写的更加简单。

@POST("rpostString")
    Call<ResponseBody> postJson(@Body User user);

访问接口:

  Call<ResponseBody> call = repo.postJson(new User("reoger","love"));
        executeByEn(call);

是吧。两三行代码就解决了。那为什么我们传入User这样一个Bean对象传到服务端的时候就变成了Json数据呢。主要原因我我们在构造retrofit的时候添加的转换方法、

 .addConverterFactory(GsonConverterFactory.create())

为此,我们还需要添加额外的依赖:

    compile 'com.squareup.retrofit2:converter-gson:2.0.2'

具体的转换过程就完成不需要我们来实现了。是不是很方便。

Post上传单个文件

还是先声明:

  @Multipart
    @POST("rpostSingerFile")
    Call<ResponseBody> uploadSingerFile(@Part MultipartBody.Part mPhoto, @Part("user")RequestBody user,@Part("passwd") RequestBody passwd);

这里@MultiPart的意思就是允许多个@Part了,我们这里使用了3个@Part,第一个我们准备上传个文件,使用了MultipartBody.Part类型,其余两个均为简单的键值对。
使用:

  File file = new File(Environment.getExternalStorageDirectory(),"test.jpg");
        if(!file.exists()){
            Log.e("TAG","file is not exit!");
            return ;
        }
        RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
        MultipartBody.Part photo = MultipartBody.Part.createFormData("mPhoto", "test.jpg", photoRequestBody);

        Call<ResponseBody> call = repo.uploadSingerFile(photo, RequestBody.create(null, "abc"), RequestBody.create(null, "123"));
       executeByEn(call);

代码形式和前面的Okhttp基本差不多。

Post上传多个文件:

这里可能和服务端实际接收的代码有关,我这里是用了一种简单的方法来接收多个文件。服务端的代码如下:

 public List<File> image; // 上传的文件
     public List<String> imageFileName; // 文件名称
     public List<String> imageContentType; // 文件类型

    //上传图片
    public String upLoadMulitFile() throws IOException{
         ServletActionContext.getRequest().setCharacterEncoding("UTF-8");
        System.out.println("开始接收文件");
         String dir = ServletActionContext.getServletContext().getRealPath("files");
            // 取得需要上传的文件数组
            List<File> files = image;

            if (files != null && files.size() > 0) {
                System.out.println("image ="+image.get(0).getName());
                for (int i = 0; i < files.size(); i++) {
                    FileOutputStream fos = new FileOutputStream(dir + "\\" + imageFileName.get(i));
                    FileInputStream fis = new FileInputStream(files.get(i));
                    byte[] buffer = new byte[1024];
                    int len = 0;
                    while ((len = fis.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }
                    fis.close();
                    fos.close();
                }
            }
        return null;
    }

客服端我们是这么写的。:

    @Multipart
    @POST("rpostMulitFile")
    Call<ResponseBody> upload(@Part()List<MultipartBody.Part> parts);

使用:

   File file1 = new File(Environment.getExternalStorageDirectory(),"test.jpg");
        File file2 = new File(Environment.getExternalStorageDirectory(),"test.JPEG");

        RequestBody photoRequestBody1 = RequestBody.create(MediaType.parse("application/octet-stream"), file1);
        RequestBody photoRequestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file2);
        MultipartBody.Part photo1 = MultipartBody.Part.createFormData("image", "test22.jpg", photoRequestBody1);
        MultipartBody.Part photo2 = MultipartBody.Part.createFormData("image", "test33.jpg", photoRequestBody2);

        List<MultipartBody.Part> parts = new ArrayList<>();
        parts.add(photo1);
        parts.add(photo2);
        Call<ResponseBody> call = repo.upload(parts);
        executeByEn(call);

其实还有很多可优化的余地,只是当作demo,学习,就并没有使用跟好的写法。比如鸿洋大神是这么干的。

 public interface IUserBiz
 {
     @Multipart
     @POST("register")
      Call<User> registerUser(@PartMap Map<String, RequestBody> params,  @Part("password") RequestBody password);
}

执行的代码:

File file = new File(Environment.getExternalStorageDirectory(), "messenger_01.png");
        RequestBody photo = RequestBody.create(MediaType.parse("image/png", file);
Map<String,RequestBody> photos = new HashMap<>();
photos.put("photos\"; filename=\"icon.png", photo);
photos.put("username",  RequestBody.create(null, "abc"));
Call<User> call = userBiz.registerUser(photos, RequestBody.create(null, "123"));

但是我并没有将其实现,可能是我服务端的代码限制了吧 。

下载

还是在IUserService中添加声明:

 @GET("files/test.jpg")
    Call<ResponseBody> download();

执行:

 Call<ResponseBody> call = repo.download();
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                //save file in here

                Log.d("TAG","downFile...");
                InputStream is = response.body().byteStream();
                int len;
                try {
                    File file = new File(Environment.getExternalStorageDirectory(),"download.jpg");
                    FileOutputStream fos = new FileOutputStream(file);
                    byte[] buf = new byte[128];
                    while( (len=is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    fos.flush();
                    fos.close();
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                Log.d("TAG","down success!");
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {

            }
        });

也比较简单吧。不过对象OkHttp好像也没有特别大的优势。
OkHttp的写法是这样的:

  Request.Builder builder = new Request.Builder();
        Request request = builder.get().url(BASE_URL+"files/test2.jpg").build();
        okhttp3.Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                Log.e("TAG","Error"+e);
            }
            @Override
            public void onResponse(okhttp3.Call call, Response response) throws IOException {

                //保存到本地,
                downSavetoFile(response);
            }
        });

当然,这些都只是一些比较简单的用法,也算是比较核心的用法了。接下来我们要学的可能就是细节上的设置和一些优化、封装等等了。暂时先告一段落吧~。明天看看还能能不能更一篇。

最后,有需要源代码的,可以戳这里

五一快乐~~~。

猜你喜欢

转载自blog.csdn.net/Reoger/article/details/71036045