zoukankan      html  css  js  c++  java
  • 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,良好的网络接口模块封装是非常重要的。

  • 相关阅读:
    浏览器渲染页面
    递归求1-100之和
    border属性
    ES6 Class
    数组去重
    get、post请求
    对象冒充继承
    原型链继承
    实现JS数据拷贝
    【转】centos升级curl版本
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/5172916.html
Copyright © 2011-2022 走看看