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 的返回方式:

OkHttpClient : OkHttpClient client = new OkHttpClient();

Request :  Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.build(); //第一种
Response response = client.newCall(request).execute();
// 第二种
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, Throwable throwable) { }
@Override public void onResponse(Response response) throws IOException { }
}

  Retrofit 的方式:

interface GitHubService {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> repoContributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
Call<List<Contributor>> call =
gitHubService.repoContributors("square", "retrofit"); response = call.execute();

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

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

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

public interface MovieDatabaseAPI { /************Retrofit 1.x ,使用异步的方式返回 ****************/

    @GET("/movie/popular")
void getPopularMovies(
@Query("api_key") String apiKey,
Callback<MoviesWrapper> callback); @GET("/movie/{id}")
void getMovieDetail (
@Query("api_key") String apiKey,
@Path("id") String id,
Callback<MovieDetail> callback
); @GET("/movie/popular")
void getPopularMoviesByPage(
@Query("api_key") String apiKey,
@Query("page") String page,
Callback<MoviesWrapper> callback
); @GET("/configuration")
void getConfiguration (
@Query("api_key") String apiKey,
Callback<ConfigurationResponse> response
); @GET("/movie/{id}/reviews")
void getReviews (
@Query("api_key") String apiKey,
@Path("id") String id,
Callback<ReviewsWrapper> response
); @GET("/movie/{id}/images")
void getImages (
@Query("api_key") String apiKey,
@Path("id") String movieId,
Callback<ImagesWrapper> response
);
}

  

public class RestMovieSource implements RestDataSource {

    private final MovieDatabaseAPI moviesDBApi;
private final Bus bus; /***********使用了Otto**************/ public RestMovieSource(Bus bus) { RestAdapter movieAPIRest = new RestAdapter.Builder() /*** Retrofit 1.x ***/
.setEndpoint(Constants.MOVIE_DB_HOST)
.setLogLevel(RestAdapter.LogLevel.HEADERS_AND_ARGS)
.build(); moviesDBApi = movieAPIRest.create(MovieDatabaseAPI.class);
this.bus = bus;
} @Override
public void getMovies() { moviesDBApi.getPopularMovies(Constants.API_KEY, retrofitCallback);
} @Override
public void getDetailMovie(String id) { moviesDBApi.getMovieDetail(Constants.API_KEY, id,
retrofitCallback);
} @Override
public void getReviews(String id) { moviesDBApi.getReviews(Constants.API_KEY, id,
retrofitCallback);
} @Override
public void getConfiguration() { moviesDBApi.getConfiguration(Constants.API_KEY, retrofitCallback);
} @Override
public void getImages(String movieId) { moviesDBApi.getImages(Constants.API_KEY, movieId,
retrofitCallback);
} public Callback retrofitCallback = new Callback() { /******************这里统一的Callback,根据不同的返回值使用事件总线进行返回**************************/
@Override
public void success(Object o, Response response) { if (o instanceof MovieDetail) { MovieDetail detailResponse = (MovieDetail) o;
bus.post(detailResponse); } else if (o instanceof MoviesWrapper) { MoviesWrapper moviesApiResponse = (MoviesWrapper) o;
bus.post(moviesApiResponse); } else if (o instanceof ConfigurationResponse) { ConfigurationResponse configurationResponse = (ConfigurationResponse) o;
bus.post(configurationResponse); } else if (o instanceof ReviewsWrapper) { ReviewsWrapper reviewsWrapper = (ReviewsWrapper) o;
bus.post(reviewsWrapper); } else if (o instanceof ImagesWrapper) { ImagesWrapper imagesWrapper = (ImagesWrapper) o;
bus.post(imagesWrapper);
}
} @Override
public void failure(RetrofitError error) { System.out.printf("[DEBUG] RestMovieSource failure - " + error.getMessage());
}
}; @Override
public void getMoviesByPage(int page) { moviesDBApi.getPopularMoviesByPage(
Constants.API_KEY,
page + "",
retrofitCallback
);
}
}

  

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

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

public interface MovieDatabaseAPI {

    @GET("/movie/popular")
Observable<MovieWrapper> getPopularMovies(
@Query("api_key") String apiKey,
); @GET("/movie/{id}")
Observable<MovideDetail> getMovieDetail (
@Query("api_key") String apiKey,
@Path("id") String id,
);
}  

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

public interface RestApi {   /************定义API接口*****************/
String API_BASE_URL = "http://www.android10.org/myapi/"; /** Api url for getting all users */
String API_URL_GET_USER_LIST = API_BASE_URL + "users.json";
/** Api url for getting a user profile: Remember to concatenate id + 'json' */
String API_URL_GET_USER_DETAILS = API_BASE_URL + "user_"; /**
* Retrieves an {@link rx.Observable} which will emit a List of {@link UserEntity}.
*/
Observable<List<UserEntity>> userEntityList(); /**
* Retrieves an {@link rx.Observable} which will emit a {@link UserEntity}.
*
* @param userId The user id used to get user data.
*/
Observable<UserEntity> userEntityById(final int userId);
}

  

/**** 使用Rx Observable 实现 RestApi 接口,实际调用的是 ApiConnection 里面的方法  ****/
public class RestApiImpl implements RestApi { /***注意这里没有使用Retrofit,而是对上面接口的实现***/ private final Context context;
private final UserEntityJsonMapper userEntityJsonMapper; /**
* Constructor of the class
*
* @param context {@link android.content.Context}.
* @param userEntityJsonMapper {@link UserEntityJsonMapper}.
*/
public RestApiImpl(Context context, UserEntityJsonMapper userEntityJsonMapper) {
if (context == null || userEntityJsonMapper == null) {
throw new IllegalArgumentException("The constructor parameters cannot be null!!!");
}
this.context = context.getApplicationContext();
this.userEntityJsonMapper = userEntityJsonMapper;
} @RxLogObservable(SCHEDULERS)
@Override
public Observable<List<UserEntity>> userEntityList() {
return Observable.create(subscriber -> {
if (isThereInternetConnection()) {
try {
String responseUserEntities = getUserEntitiesFromApi();
if (responseUserEntities != null) {
subscriber.onNext(userEntityJsonMapper.transformUserEntityCollection(
responseUserEntities));
subscriber.onCompleted();
} else {
subscriber.onError(new NetworkConnectionException());
}
} catch (Exception e) {
subscriber.onError(new NetworkConnectionException(e.getCause()));
}
} else {
subscriber.onError(new NetworkConnectionException());
}
});
} @RxLogObservable(SCHEDULERS)
@Override
public Observable<UserEntity> userEntityById(final int userId) {
return Observable.create(subscriber -> {
if (isThereInternetConnection()) {
try {
String responseUserDetails = getUserDetailsFromApi(userId);
if (responseUserDetails != null) {
subscriber.onNext(userEntityJsonMapper.transformUserEntity(responseUserDetails));
subscriber.onCompleted();
} else {
subscriber.onError(new NetworkConnectionException());
}
} catch (Exception e) {
subscriber.onError(new NetworkConnectionException(e.getCause()));
}
} else {
subscriber.onError(new NetworkConnectionException());
}
});
} private String getUserEntitiesFromApi() throws MalformedURLException {
return ApiConnection.createGET(RestApi.API_URL_GET_USER_LIST).requestSyncCall();
} private String getUserDetailsFromApi(int userId) throws MalformedURLException {
String apiUrl = RestApi.API_URL_GET_USER_DETAILS + userId + ".json";
return ApiConnection.createGET(apiUrl).requestSyncCall();
} /**
* Checks if the device has any active internet connection.
*
* @return true device with internet connection, otherwise false.
*/
private boolean isThereInternetConnection() {
boolean isConnected; ConnectivityManager connectivityManager =
(ConnectivityManager) this.context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
isConnected = (networkInfo != null && networkInfo.isConnectedOrConnecting()); return isConnected;
}
}

  

public class ApiConnection implements Callable<String> {  /***********************网络接口的实际实现********************************/

    private static final String CONTENT_TYPE_LABEL = "Content-Type";
private static final String CONTENT_TYPE_VALUE_JSON = "application/json; charset=utf-8"; private URL url;
private String response; private ApiConnection(String url) throws MalformedURLException {
this.url = new URL(url);
} public static ApiConnection createGET(String url) throws MalformedURLException {
return new ApiConnection(url);
} /**
* Do a request to an api synchronously.
* It should not be executed in the main thread of the application.
*
* @return A string response
*/
@Nullable
public String requestSyncCall() {
connectToApi();
return response;
} private void connectToApi() {
OkHttpClient okHttpClient = this.createClient(); /*******************使用OKhttp的实现*******************/
final Request request = new Request.Builder()
.url(this.url)
.addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_VALUE_JSON)
.get()
.build(); try {
this.response = okHttpClient.newCall(request).execute().body().string();
} catch (IOException e) {
e.printStackTrace();
}
} private OkHttpClient createClient() {
final OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(10000, TimeUnit.MILLISECONDS);
okHttpClient.setConnectTimeout(15000, TimeUnit.MILLISECONDS); return okHttpClient;
} @Override
public String call() throws Exception {
return requestSyncCall();
}
}

  这里简单总结了一下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. 01 选择 Help > Install New Software,在出现的对话框里,点击Add按钮,在对话框的name一栏输入“ADT”,点击Archive...选择离线的ADT文件,contact all update ....千万不要勾选点击Add按钮,在对话框的name一栏输入“ADT”,点击Archive...选择离线的ADT文件,contact all update ....千万不要勾

    引言 好久没碰过android,今天重新搭建了一次环境,遇到的问题记录下载.共以后使用. 安装 软件的软件有jdk+eclipse+adt+sdk 主要记录安装adt和sdk的过程,注意,adt和sd ...

  2. halcon读取一张照片,并转化为灰度图像

    dev_close_window () read_image (Image, 'E:/图片/123.jpg') get_image_size (Image, Width, Height) dev_op ...

  3. go runtime.Gosched() 和 time.Sleep() 做协程切换

    网上看到个问题: package main import ( "fmt" "time" ) func say(s string) { ; i < ; i+ ...

  4. Understanding ASP.NET MVC Filters and Attributes

    这篇文章把Asp.net MVC的filter介绍的很详细,值得收藏. http://www.dotnet-tricks.com/Tutorial/mvc/b11a280114-Understandi ...

  5. 使用WPF动态生成Code 39条形码

    最近在看些条形码方面相关的资料,而如果只是看的话,效果似乎并不怎么好,所以决定动手做点Demo,以增强对相关知识的记忆. 这里是一个我编写的使用WPF生成Code 39的例子,Code 39的编码很简 ...

  6. Java设计模式6:策略模式

    策略模式 策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立类中,从而使得它们可以相互替换.策略模式使得算法可以在不影响到客户端的情况下发生变化. 策略模式的结构 策略模式是对算法的包 ...

  7. 程序员必须知道的几个Git代码托管平台

    上一篇博客中2015继续任性——不会Git命令,照样玩转Git我们简单的介绍了在VS2013中使用Git,和GitHub客户端的使用.那么使用Git到底有什么好处呢?最为明显的是支持Git代码托管的平 ...

  8. linux shell program summary

    from:Sep 23 2016 mathematical operation: floating number,bc calculator: we can also use bc in shell ...

  9. cmd 下通过NTML代理访问Maven 库

    公司用web代理,NTLM验证的,这样在普通CMD下无法使用mvn命令访问网上的maven库,使用CNTLM工具解决. 下载CNTLM工具,安装,修改安装路径下的cntlm.ini,改成实际的ntlm ...

  10. iOS-性能优化3

    iOS-性能优化3 UITableView性能优化与卡顿问题 1.最常用的就是cell的重用, 注册重用标识符 如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell 如果 ...