Android 进阶之路Android开发程序员

网络请求框架 —— Retrofit

2017-09-18  本文已影响331人  Tyhoo_Wu
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

未完待续。。。

上一篇 下一篇

猜你喜欢

热点阅读