Android开发网络通信一开始的时候使用的是AsyncTask封装HttpClient,没有使用原生的HttpURLConnection就跳到了Volley,随着OkHttp的流行又开始迁移到OkHttp上面,随着Rxjava的流行又了解了Retrofit,随着Retrofit的发展又从1.x到了2.x......。好吧,暂时到这里。

  那么的多的使用工具有时候有点眼花缭乱,今天来总结一下现在比较流行的基于OkHttp 和 Retrofit 的网络通信API设计方法。有些同学可能要想,既然都有那么好用的Volley和Okhttp了,在需要用到的地方创建一个Request然后交给RequestQueue(Volley的方式)或者 Call(Okhttp的方式)就行了吗,为什么还那么麻烦? 但是我认为这种野生的网络库的用法还是是有很多弊端(弊端就不说了,毕竟是总结新东西),在好的Android架构中都不会出现这样的代码。

  网络通信都是异步完成,设计网络API我觉得首先需要考虑异步结果的返回机制。基于Okhttp或Retrofit,我们考虑如何返回异步的返回结果,有几种方式:

  1. 直接返回:

  OkHttp 的返回方式:

  1. OkHttpClient : OkHttpClient client = new OkHttpClient();
  2.  
  3. Request : Request request = new Request.Builder()
  4. .url("https://api.github.com/repos/square/okhttp/issues")
  5. .header("User-Agent", "OkHttp Headers.java")
  6. .addHeader("Accept", "application/json; q=0.5")
  7. .addHeader("Accept", "application/vnd.github.v3+json")
  8. .build();
  9.  
  10. //第一种
  11. Response response = client.newCall(request).execute();
  12. // 第二种
  13. client.newCall(request).enqueue(new Callback() {
  14. @Override
  15. public void onFailure(Request request, Throwable throwable) {
  16.  
  17. }
  18. @Override public void onResponse(Response response) throws IOException {
  19.  
  20. }
  21. }

  Retrofit 的方式:

  1. interface GitHubService {
  2. @GET("/repos/{owner}/{repo}/contributors")
  3. Call<List<Contributor>> repoContributors(
  4. @Path("owner") String owner,
  5. @Path("repo") String repo);
  6. }
  7. Call<List<Contributor>> call =
  8. gitHubService.repoContributors("square", "retrofit");
  9.  
  10. response = call.execute();

  上面的方式适用于野生的返回网络请求的内容。

  2. 使用事件总线(Otto,EventBus,RxBus(自己使用PublishSubject封装))

  代码来源:https://github.com/saulmm/Material-Movies

  1. public interface MovieDatabaseAPI { /************Retrofit 1.x ,使用异步的方式返回 ****************/
  2.  
  3. @GET("/movie/popular")
  4. void getPopularMovies(
  5. @Query("api_key") String apiKey,
  6. Callback<MoviesWrapper> callback);
  7.  
  8. @GET("/movie/{id}")
  9. void getMovieDetail (
  10. @Query("api_key") String apiKey,
  11. @Path("id") String id,
  12. Callback<MovieDetail> callback
  13. );
  14.  
  15. @GET("/movie/popular")
  16. void getPopularMoviesByPage(
  17. @Query("api_key") String apiKey,
  18. @Query("page") String page,
  19. Callback<MoviesWrapper> callback
  20. );
  21.  
  22. @GET("/configuration")
  23. void getConfiguration (
  24. @Query("api_key") String apiKey,
  25. Callback<ConfigurationResponse> response
  26. );
  27.  
  28. @GET("/movie/{id}/reviews")
  29. void getReviews (
  30. @Query("api_key") String apiKey,
  31. @Path("id") String id,
  32. Callback<ReviewsWrapper> response
  33. );
  34.  
  35. @GET("/movie/{id}/images")
  36. void getImages (
  37. @Query("api_key") String apiKey,
  38. @Path("id") String movieId,
  39. Callback<ImagesWrapper> response
  40. );
  41. }

  

  1. public class RestMovieSource implements RestDataSource {
  2.  
  3. private final MovieDatabaseAPI moviesDBApi;
  4. private final Bus bus; /***********使用了Otto**************/
  5.  
  6. public RestMovieSource(Bus bus) {
  7.  
  8. RestAdapter movieAPIRest = new RestAdapter.Builder() /*** Retrofit 1.x ***/
  9. .setEndpoint(Constants.MOVIE_DB_HOST)
  10. .setLogLevel(RestAdapter.LogLevel.HEADERS_AND_ARGS)
  11. .build();
  12.  
  13. moviesDBApi = movieAPIRest.create(MovieDatabaseAPI.class);
  14. this.bus = bus;
  15. }
  16.  
  17. @Override
  18. public void getMovies() {
  19.  
  20. moviesDBApi.getPopularMovies(Constants.API_KEY, retrofitCallback);
  21. }
  22.  
  23. @Override
  24. public void getDetailMovie(String id) {
  25.  
  26. moviesDBApi.getMovieDetail(Constants.API_KEY, id,
  27. retrofitCallback);
  28. }
  29.  
  30. @Override
  31. public void getReviews(String id) {
  32.  
  33. moviesDBApi.getReviews(Constants.API_KEY, id,
  34. retrofitCallback);
  35. }
  36.  
  37. @Override
  38. public void getConfiguration() {
  39.  
  40. moviesDBApi.getConfiguration(Constants.API_KEY, retrofitCallback);
  41. }
  42.  
  43. @Override
  44. public void getImages(String movieId) {
  45.  
  46. moviesDBApi.getImages(Constants.API_KEY, movieId,
  47. retrofitCallback);
  48. }
  49.  
  50. public Callback retrofitCallback = new Callback() { /******************这里统一的Callback,根据不同的返回值使用事件总线进行返回**************************/
  51. @Override
  52. public void success(Object o, Response response) {
  53.  
  54. if (o instanceof MovieDetail) {
  55.  
  56. MovieDetail detailResponse = (MovieDetail) o;
  57. bus.post(detailResponse);
  58.  
  59. } else if (o instanceof MoviesWrapper) {
  60.  
  61. MoviesWrapper moviesApiResponse = (MoviesWrapper) o;
  62. bus.post(moviesApiResponse);
  63.  
  64. } else if (o instanceof ConfigurationResponse) {
  65.  
  66. ConfigurationResponse configurationResponse = (ConfigurationResponse) o;
  67. bus.post(configurationResponse);
  68.  
  69. } else if (o instanceof ReviewsWrapper) {
  70.  
  71. ReviewsWrapper reviewsWrapper = (ReviewsWrapper) o;
  72. bus.post(reviewsWrapper);
  73.  
  74. } else if (o instanceof ImagesWrapper) {
  75.  
  76. ImagesWrapper imagesWrapper = (ImagesWrapper) o;
  77. bus.post(imagesWrapper);
  78. }
  79. }
  80.  
  81. @Override
  82. public void failure(RetrofitError error) {
  83.  
  84. System.out.printf("[DEBUG] RestMovieSource failure - " + error.getMessage());
  85. }
  86. };
  87.  
  88. @Override
  89. public void getMoviesByPage(int page) {
  90.  
  91. moviesDBApi.getPopularMoviesByPage(
  92. Constants.API_KEY,
  93. page + "",
  94. retrofitCallback
  95. );
  96. }
  97. }

  

  3. 返回Observable(这里也可以考虑直接返回Observable 和间接返回Observable)

  直接的返回 Observable,在创建 apiService 的时候使用 Retrofit.create(MovieDatabaseAPI)就行了(见下面代码)

  1. public interface MovieDatabaseAPI {
  2.  
  3. @GET("/movie/popular")
  4. Observable<MovieWrapper> getPopularMovies(
  5. @Query("api_key") String apiKey,
  6. );
  7.  
  8. @GET("/movie/{id}")
  9. Observable<MovideDetail> getMovieDetail (
  10. @Query("api_key") String apiKey,
  11. @Path("id") String id,
  12. );
  13. }  

  间接返回Observable,这里参考了AndroidCleanArchitecture:

  1. public interface RestApi { /************定义API接口*****************/
  2. String API_BASE_URL = "http://www.android10.org/myapi/";
  3.  
  4. /** Api url for getting all users */
  5. String API_URL_GET_USER_LIST = API_BASE_URL + "users.json";
  6. /** Api url for getting a user profile: Remember to concatenate id + 'json' */
  7. String API_URL_GET_USER_DETAILS = API_BASE_URL + "user_";
  8.  
  9. /**
  10. * Retrieves an {@link rx.Observable} which will emit a List of {@link UserEntity}.
  11. */
  12. Observable<List<UserEntity>> userEntityList();
  13.  
  14. /**
  15. * Retrieves an {@link rx.Observable} which will emit a {@link UserEntity}.
  16. *
  17. * @param userId The user id used to get user data.
  18. */
  19. Observable<UserEntity> userEntityById(final int userId);
  20. }

  

  1. /**** 使用Rx Observable 实现 RestApi 接口,实际调用的是 ApiConnection 里面的方法 ****/
  2. public class RestApiImpl implements RestApi { /***注意这里没有使用Retrofit,而是对上面接口的实现***/
  3.  
  4. private final Context context;
  5. private final UserEntityJsonMapper userEntityJsonMapper;
  6.  
  7. /**
  8. * Constructor of the class
  9. *
  10. * @param context {@link android.content.Context}.
  11. * @param userEntityJsonMapper {@link UserEntityJsonMapper}.
  12. */
  13. public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) {
  14. if (context == null || userEntityJsonMapper == null) {
  15. throw new IllegalArgumentException("The constructor parameters cannot be null!!!");
  16. }
  17. this.context = context.getApplicationContext();
  18. this.userEntityJsonMapper = userEntityJsonMapper;
  19. }
  20.  
  21. @RxLogObservable(SCHEDULERS)
  22. @Override
  23. public Observable<List<UserEntity>> userEntityList() {
  24. return Observable.create(subscriber -> {
  25. if (isThereInternetConnection()) {
  26. try {
  27. String responseUserEntities = getUserEntitiesFromApi();
  28. if (responseUserEntities != null) {
  29. subscriber.onNext(userEntityJsonMapper.transformUserEntityCollection(
  30. responseUserEntities));
  31. subscriber.onCompleted();
  32. } else {
  33. subscriber.onError(new NetworkConnectionException());
  34. }
  35. } catch (Exception e) {
  36. subscriber.onError(new NetworkConnectionException(e.getCause()));
  37. }
  38. } else {
  39. subscriber.onError(new NetworkConnectionException());
  40. }
  41. });
  42. }
  43.  
  44. @RxLogObservable(SCHEDULERS)
  45. @Override
  46. public Observable<UserEntity> userEntityById(final int userId) {
  47. return Observable.create(subscriber -> {
  48. if (isThereInternetConnection()) {
  49. try {
  50. String responseUserDetails = getUserDetailsFromApi(userId);
  51. if (responseUserDetails != null) {
  52. subscriber.onNext(userEntityJsonMapper.transformUserEntity(responseUserDetails));
  53. subscriber.onCompleted();
  54. } else {
  55. subscriber.onError(new NetworkConnectionException());
  56. }
  57. } catch (Exception e) {
  58. subscriber.onError(new NetworkConnectionException(e.getCause()));
  59. }
  60. } else {
  61. subscriber.onError(new NetworkConnectionException());
  62. }
  63. });
  64. }
  65.  
  66. private String getUserEntitiesFromApi() throws MalformedURLException {
  67. return ApiConnection.createGET(RestApi.API_URL_GET_USER_LIST).requestSyncCall();
  68. }
  69.  
  70. private String getUserDetailsFromApi(int userId) throws MalformedURLException {
  71. String apiUrl = RestApi.API_URL_GET_USER_DETAILS + userId + ".json";
  72. return ApiConnection.createGET(apiUrl).requestSyncCall();
  73. }
  74.  
  75. /**
  76. * Checks if the device has any active internet connection.
  77. *
  78. * @return true device with internet connection, otherwise false.
  79. */
  80. private boolean isThereInternetConnection() {
  81. boolean isConnected;
  82.  
  83. ConnectivityManager connectivityManager =
  84. (ConnectivityManager) this.context.getSystemService(Context.CONNECTIVITY_SERVICE);
  85. NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
  86. isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting());
  87.  
  88. return isConnected;
  89. }
  90. }

  

  1. public class ApiConnection implements Callable<String> { /***********************网络接口的实际实现********************************/
  2.  
  3. private static final String CONTENT_TYPE_LABEL = "Content-Type";
  4. private static final String CONTENT_TYPE_VALUE_JSON = "application/json; charset=utf-8";
  5.  
  6. private URL url;
  7. private String response;
  8.  
  9. private ApiConnection(String url) throws MalformedURLException {
  10. this.url = new URL(url);
  11. }
  12.  
  13. public static ApiConnection createGET(String url) throws MalformedURLException {
  14. return new ApiConnection(url);
  15. }
  16.  
  17. /**
  18. * Do a request to an api synchronously.
  19. * It should not be executed in the main thread of the application.
  20. *
  21. * @return A string response
  22. */
  23. @Nullable
  24. public String requestSyncCall() {
  25. connectToApi();
  26. return response;
  27. }
  28.  
  29. private void connectToApi() {
  30. OkHttpClient okHttpClient = this.createClient(); /*******************使用OKhttp的实现*******************/
  31. final Request request = new Request.Builder()
  32. .url(this.url)
  33. .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_VALUE_JSON)
  34. .get()
  35. .build();
  36.  
  37. try {
  38. this.response = okHttpClient.newCall(request).execute().body().string();
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43.  
  44. private OkHttpClient createClient() {
  45. final OkHttpClient okHttpClient = new OkHttpClient();
  46. okHttpClient.setReadTimeout(10000, TimeUnit.MILLISECONDS);
  47. okHttpClient.setConnectTimeout(15000, TimeUnit.MILLISECONDS);
  48.  
  49. return okHttpClient;
  50. }
  51.  
  52. @Override
  53. public String call() throws Exception {
  54. return requestSyncCall();
  55. }
  56. }

  这里简单总结了一下OkHttp和Retrofit该如何封装,这样的封装放在整个大的代码框架中具有很好的模块化效果。对于使用MVP架构或者类似架构的APP,良好的网络接口模块封装是非常重要的。

Android 网络通信API的选择和实现实例的更多相关文章

  1. 【NFC】Android NFC API Reference中英文

    0 Near Field Communication Near Field Communication (NFC) is a set of   short-range wireless technol ...

  2. Android网络通信(8):WiFi Direct

    Android网络通信之WiFi Direct 使用Wi-Fi Direct技术可以让具备硬件支持的设备在没有中间接入点的情况下进行直接互联.Android 4.0(API版本14)及以后的系统都提供 ...

  3. Android网络通信(7):NFC

    Android网络通信之 NFC NFC:近场通信,是一种超近距离的无线通信技术.Android从2.3版本的SDK开始支持基于NFC通信.基于NFC的识别和通信可分为三个步骤:1.Android通过 ...

  4. 地图API的选择和使用

    在我们程序员的日常开发中,总会时不时的需要用到地图开发,我也在多次碰到之后,写下我对地图开发的理解经验和总结. 一.地图的选择 回想一下我们生活中用到的地图工具,数了一下,百度地图,高德地图,腾讯地图 ...

  5. Android开发-API指南-<activity>

    <activity> 英文原文:http://developer.android.com/guide/topics/manifest/activity-element.html 采集(更新 ...

  6. Android N API预览

    Android N for Developers 重要的开发人员功能 多窗体支持 通知 JIT/AOT 编译 高速的应用安装路径 外出瞌睡模式 后台优化 Data Saver 高速设置图块 API 号 ...

  7. Android经典项目开发之天气APP实例分享

    原文:Android经典项目开发之天气APP实例分享 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/mzc186/article/details/5 ...

  8. [转载]android网络通信解析

    原文地址:android网络通信解析作者:clunyes 网络编程的目的就是直接戒间接地通过网络协议不其他计算机进行通讯. 网络编程中有两个主要的问题, 一个是如何准确的定位网络上一台戒多台指主机: ...

  9. Android 从图库到选择图片onActivityResult接收注意的问题

    从图库选择图片然后返回数据接收处理的时候,这个时候我们可能会遇到一个问题.就是明明我走了返回的代码.但是为什么我的图片路径没有拿到?这个时候可能是Android的api不同导致,因为Android4. ...

随机推荐

  1. String、String.valueOf、toString 它们三者的区别总结

    今天在使用这个的时候发现,他们三者好像在某些场所都是可以用的,但是不免会让人想到那既然它们三者这么的相似,那么总有些什么区别吧.我也在网上找了一些资料看.自己也看了API文档,就将他们三的区别总结一下 ...

  2. php学习笔记2016.1

    基本类型    PHP是一种弱类型语言.      PHP类型检查函数   is_bool()    is_integer()  is_double()  is_string()   is_objec ...

  3. linux-9基本命令之-wget

    1.wget 命令用于下载网络文件,格式:"wget[参数] 下载地址" wget 参数 -b 后台下载模式 -O 下载到指定的目录 -t 最大尝试次数 -p 下载页面内所有的资源 ...

  4. vc编译 curl 7.36.0

    CURL邮件列表中提到官方最新版本的windows devel包中缺少文件,而我又用不到https,所以我就自己下载源码包来编译了 下载源码包:http://curl.haxx.se/download ...

  5. objective-c(反射)

    objective-c中提供类似JAVA的反射特性,给出基本例子如下: #import <Foundation/Foundation.h> @interface ClassA : NSOb ...

  6. asp.net core开发环境准备

    1.1  安装sdk和运行时 浏览器打开网址https://www.microsoft.com/net/download, 到.Net Core下载页面. 根据操作系统,下载对应的SDK进行安装.安装 ...

  7. C#Light Unity逻辑热更新解决方案0.20 发布

    之前一直是Beta,这次已经实际运用到项目中间了,去掉beta状态 在项目中使用面对一些新的问题,还有以前没注意的bug. 更新列表 一.增加类中类的支持 二.增加对foreach的支持,同C#语法 ...

  8. NanoProfiler - 适合生产环境的性能监控类库 之 实践ELK篇

    上期回顾 上一期:NanoProfiler - 适合生产环境的性能监控类库 之 大数据篇 上次介绍了NanoProfiler的大数据分析理念,一晃已经时隔一年多了,真是罪过! 有朋友问到何时开源的问题 ...

  9. 创业6&7

    周末两天泡咖啡店. 起不来,只好下午去. 周六5点到9点. 周日3点到12点. 1)整理直播课程讲义.完成50%. 2)修改GMTC演讲稿.完成. 招行的单子还是拒了,目前还没准备好高可用的App服务 ...

  10. ssl小结

    有几篇不错的文章: http://www.cnblogs.com/pen-ink/archive/2011/01/17/1937680.html 这个应该是最简单的示例了. 不过,当两个passwor ...