转载

Android 网络通信API的选择和实现实例

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,良好的网络接口模块封装是非常重要的。

原文  http://www.cnblogs.com/zhuyp1015/p/5172916.html
正文到此结束
Loading...