Retrofit使用入门

来源:互联网 发布:电脑游戏录制视频软件 编辑:程序博客网 时间:2024/06/08 00:18

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

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

  • 官方教程
  • github地址
  • 李江东博客
  • 鸿洋大神
  • 推酷上的一篇文章
  • 怪盗kidou
  • 比较好的资料

搭建使用环境

下载最新的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") //注意 字符串idList<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);            }        });

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

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

五一快乐~~~。

1 0
原创粉丝点击