网络请求框架 —— Retrofit

作者: Tyhoo_Wu | 来源:发表于2017-09-18 01:05 被阅读331次
    bg.png

    Q:What is Retrofit ?
    A:Type-safe HTTP client for Android and Java by Square, Inc.

    Retrofit 官网:http://square.github.io/retrofit/
    Retrofit GitHub 项目地址:https://github.com/square/retrofit

    在使用 Retrofit 之前,我们需要一些前期的准备:
    ① API 接口测试工具:Chrome 浏览器推荐使用 Postman,Firefox 浏览器推荐使用 RESTClient 。
    ② 将 API 返回的数据自动生成 Java 类对象:在 Android Studio 里面安装插件 GsonFormat 。

    前期工作准备就绪,开始 Retrofit 之旅!

    本文基于 Retrofit 2 进行代码编写。

    学习任何东西都会有官网给提供的参考示例,Retrofit 也给提供了一套示例代码:
    https://github.com/square/retrofit/tree/master/samples

    本文你将学到:
    ① 使用Retrofit网络请求
    ② 使用Retrofit下载文件
    ③ 使用Rxjava+Retrofit网络请求

    一、入门篇

    本示例参考官方提供的 SimpleService 进行修改和编写:
    https://github.com/square/retrofit/blob/master/samples/src/main/java/com/example/retrofit/SimpleService.java

    示例 GIF:


    Loading to Content.gif

    代码部分:

    ① 在 Gradle 里面添加依赖库

    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    

    ② 申请网络权限

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

    ③ API 接口
    使用 GitHub 提供的 API 接口,因为是做 Retrofit 的演示,所以就以 Retrofit 的接口为例

    https://api.github.com/repos/square/retrofit/contributors
    

    然我们来获取一下都有哪些大神对 Retrofit 做出了贡献吧。

    ④ 使用插件 GsonFormat,自动创建 Java 类对象

    public class Contributor {
    
        /**
         * login : JakeWharton
         * avatar_url : https://avatars0.githubusercontent.com/u/66577?v=4
         * html_url : https://github.com/JakeWharton
         */
    
        private String login;  // 人名
        private String avatar_url;  // 头像的 Url 地址
        private String html_url;  // 个人 GitHub 主页地址
        
        public String getLogin() {
            return login;
        }
    
        public void setLogin(String login) {
            this.login = login;
        }
    
        public String getAvatar_url() {
            return avatar_url;
        }
    
        public void setAvatar_url(String avatar_url) {
            this.avatar_url = avatar_url;
        }
    
        public String getHtml_url() {
            return html_url;
        }
    
        public void setHtml_url(String html_url) {
            this.html_url = html_url;
        }
    }
    

    ⑤ 创建 API 接口(GitHub 提供的是 GET 请求)

    public interface MainService {
    
        @GET("/repos/square/retrofit/contributors")
        Call<List<Contributor>> getCall();
    }
    

    ⑥ 我们从服务器上拿到的是一组 List 数据,所以要将 List 数据通过 RecyclerView 显示在 UI 上(RecyclerView 这部分不属于本示例的范畴内,默认大家都会用。)

    ⑦ 创建 Retrofit 实例

    private void initData() {
        // 创建一个非常简单的REST适配器,它指向 GitHub 的 API
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(API_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    
        // 创建一个我们的 GitHub API 接口的实例。
        MainService service = retrofit.create(MainService.class);
    
        // 创建一个调用实例来查找都有哪些大神对 Retrofit 做出了贡献。
        Call<List<Contributor>> call = service.getCall();
        Log.d(TAG, "main: " + call);
    
        call.enqueue(new Callback<List<Contributor>>() {
            @Override
            public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
                if (response.isSuccessful()) {
                    Log.d(TAG, "onResponse: " + "isSuccessful");
                    list = response.body();
                    Log.d(TAG, "onResponse: " + "list" + list);
    
                    if (list != null && list.size() > 0) {
                        mAdapter.setData(list);
                    }
                }
            }
    
            @Override
            public void onFailure(Call<List<Contributor>> call, Throwable t) {
    
            }
        });
    }
    

    不同于官方示例的是:我使用异步加载,而官方示例使用的是同步加载。这里也推荐使用异步加载,不要在主线程里面执行网络请求。

    ⑧ 将得到的 List 数据使用 RecyclerView 显示在 UI 上

    1. 在 Gradle 里面添加依赖库
    // RecyclerView
    implementation 'com.android.support:recyclerview-v7:27.0.2'
    
    // Glide
    implementation 'com.github.bumptech.glide:glide:4.3.1'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.3.1'
    
    1. 绑定控件,并显示在 UI 上
    @Override
    public void onBindViewHolder(final MainAdapter.MainViewHolder holder, int position) {
        Contributor contributor = mList.get(position);
    
        final String login = contributor.getLogin();
        String htmlUrl = contributor.getHtml_url();
        String avatarUrl = contributor.getAvatar_url();
    
        Log.d(TAG, "onBindViewHolder: " + login);
        Log.d(TAG, "onBindViewHolder: " + htmlUrl);
        Log.d(TAG, "onBindViewHolder: " + avatarUrl);
    
        holder.tvName.setText(login);
        holder.tvUrl.setText(htmlUrl);
        Glide.with(holder.ivAvatar.getContext()).load(avatarUrl).into(holder.ivAvatar);
    }
    

    以上就是入门篇的内容。

    示例项目已上传至 GitHub ,下载地址:
    https://github.com/cnwutianhao/Retrofit2/tree/master/app-Simple

    二、进阶篇

    下载 (将服务器的数据下载到本地)

    参考文章:
    https://futurestud.io/tutorials/retrofit-2-how-to-download-files-from-server

    还是以 GitHub 上面的 api 为例:

    https://api.github.com/repos/cnwutianhao/Retrofit2/contributors
    

    根据上面的 url 地址,我们可以得到 Json 数据:

    [
      {
        "login": "cnwutianhao",
        "id": 13990136,
        "avatar_url": "https://avatars1.githubusercontent.com/u/13990136?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/cnwutianhao",
        "html_url": "https://github.com/cnwutianhao",
        "followers_url": "https://api.github.com/users/cnwutianhao/followers",
        "following_url": "https://api.github.com/users/cnwutianhao/following{/other_user}",
        "gists_url": "https://api.github.com/users/cnwutianhao/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/cnwutianhao/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/cnwutianhao/subscriptions",
        "organizations_url": "https://api.github.com/users/cnwutianhao/orgs",
        "repos_url": "https://api.github.com/users/cnwutianhao/repos",
        "events_url": "https://api.github.com/users/cnwutianhao/events{/privacy}",
        "received_events_url": "https://api.github.com/users/cnwutianhao/received_events",
        "type": "User",
        "site_admin": false,
        "contributions": 3
      }
    ]
    

    我们下载里面的图片,作为示例演示:

    "avatar_url": "https://avatars1.githubusercontent.com/u/13990136?v=4",
    

    代码部分:
    ① 创建 Base Api

    public class MainApi {
    
        public static final String BASE_API = "https://avatars1.githubusercontent.com/";
    }
    

    ② 创建 Api 接口

    public interface MainService {
    
        // 将 ResponseBody 作为了返回类型。Retrofit 会试图解析并转换它,所以你不能使用任何其他返回类型,
        // 否则当你下载文件的时候,是毫无意义的。
        // 防止大文件,使用 @Streaming
        @Streaming
        @GET("u/13990136?v=4")
        Call<ResponseBody> downloadFileWithFixedUrl();
    }
    

    注意:这里面一定要使用 ResponseBody ,使用其他的 Bean 下载也就没有意义了。

    ③ 创建 Retrofit 实例

    private void initData() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(MainApi.BASE_API)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    
        MainService service = retrofit.create(MainService.class);
    
        Call<ResponseBody> call = service.downloadFileWithFixedUrl();
    
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, final Response<ResponseBody> response) {
                if (response.isSuccessful()) {
                    Log.d(TAG, "onResponse: Server contacted and has file");
    
                    new AsyncTask<Void, Void, Void>() {
                        @Override
                        protected Void doInBackground(Void... voids) {
                            boolean writtenToDisk = writeResponseBodyToDisk(response.body());
                            Log.d(TAG, "onResponse: file download was a success? " + writtenToDisk);
                            return null;
                        }
                    }.execute();
    
                } else {
                    Log.d(TAG, "onResponse: Server contact failed");
                }
            }
    
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.d(TAG, "onFailure: ");
            }
        });
    }
    

    ④ 创建方法,判断文件是否已经下载到本地

    private boolean writeResponseBodyToDisk(ResponseBody body) {
        try {
            File sampleFile = new File(
                    getExternalFilesDir(null) + File.separator + "icon.png");
            Log.d(TAG, "FutureStudioIconFile路径: " + sampleFile);
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                byte[] fileReader = new byte[4096];
                long fileSize = body.contentLength();
                long fileSizeDownloaded = 0;
    
                inputStream = body.byteStream();
                outputStream = new FileOutputStream(sampleFile);
    
                while (true) {
                    int read = inputStream.read(fileReader);
    
                    if (read == -1) {
                        break;
                    }
                    outputStream.write(fileReader, 0, read);
                    fileSizeDownloaded += read;
                    Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
                }
                outputStream.flush();
                return true;
            } catch (IOException e) {
                return false;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } catch (IOException e) {
            return false;
        }
    }
    

    运行代码,查看 Log 日志:

    onResponse: Server contacted and has file
    FutureStudioIconFile路径: /storage/emulated/0/Android/data/com.tnnowu.retrofit2sample/files/icon.png
    file download: 1903 of 26033
    file download: 4662 of 26033
    file download: 7421 of 26033
    file download: 10180 of 26033
    file download: 12939 of 26033
    file download: 15528 of 26033
    file download: 18287 of 26033
    file download: 21046 of 26033
    file download: 23805 of 26033
    file download: 26033 of 26033
    onResponse: file download was a success? true
    

    我们可以清晰地看到服务器上的文件被下载到本地,路径是:

    /storage/emulated/0/Android/data/com.tnnowu.retrofit2sample/files/
    

    示例项目已上传至 GitHub ,下载地址:
    https://github.com/cnwutianhao/Retrofit2/tree/master/app-Download

    RxJava 和 Retrofit 结合使用

    RxJava 是异步加载框架,Retroft进行网络请求的时候也需要进行异步加载,那么这两个结合使用是非常完美的一件事。

    导入必要的库:

    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
    

    ① 根据api中的参数,设置gettersetter

    public class GitHubBean {
    
        private String login;
        private String avatar_url;
        private String html_url;
    
        public String getLogin() {
            return login;
        }
    
        public void setLogin(String login) {
            this.login = login;
        }
    
        public String getAvatar_url() {
            return avatar_url;
        }
    
        public void setAvatar_url(String avatar_url) {
            this.avatar_url = avatar_url;
        }
    
        public String getHtml_url() {
            return html_url;
        }
    
        public void setHtml_url(String html_url) {
            this.html_url = html_url;
        }
    }
    

    ② 创建API接口

    public interface GitHubService {
    
        // Contributors
        @GET("/repos/square/retrofit/contributors")
        Observable<List<GitHubBean>> getContributors();
    }
    

    ③ 创建Retrofit实例

    public class RetrofitService {
    
        private static final String BASE_URL = "https://api.github.com";
    
        public GitHubService getService() {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
    
            return retrofit.create(GitHubService.class);
        }
    }
    

    RxJava结合Retrofit进行网络请求

    private GitHubService mService = new RetrofitService().getService();
    
    Observable<List<GitHubBean>> observable = mService.getContributors();
    observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<List<GitHubBean>>() {
                @Override
                public void onSubscribe(Disposable d) {
                    Log.d(TAG, "onSubscribe: ");
                }
    
                @Override
                public void onNext(List<GitHubBean> gitHubBeans) {
                    Log.d(TAG, "onNext: ");
    
                    Log.d(TAG, "onNext: " + gitHubBeans.size());
    
                    // 这里面进行数据的传值等操作
                }
    
                @Override
                public void onError(Throwable e) {
                    Log.d(TAG, "onError: ");
                }
    
                @Override
                public void onComplete() {
                    Log.d(TAG, "onComplete: ");
                }
            });
    

    示例项目已上传至GitHub,下载地址:
    https://github.com/cnwutianhao/Retrofit2/tree/master/app-RxAndroid

    未完待续。。。

    相关文章

      网友评论

      • d3b602e70cc9:你好博主,示例中提供的API接口,可以直接访问拿到json数据,并通过gsonformat生成实体类,但是我遇到实际中的情况是,请求的API接口是需要加动态sign和token参数的,所以就不能直接拿到json数据,也就不能直接生成实体类。需要从App端通过retrofit访问,再从respone的body中获取解析出来。到这一步,就不知道如何在没有对应实体类的情况下,将respone.body中的数据解析出来。

      本文标题:网络请求框架 —— Retrofit

      本文链接:https://www.haomeiwen.com/subject/epxytxtx.html