使用android-async-http来封装Android网络请求框架

版权声明:本文为寻梦-finddreams原创文章,请关注:http://blog.csdn.net/finddreams https://blog.csdn.net/finddreams/article/details/50887506

  上一篇为大家讲解了如何《基于Retrofit2.0+RxJava+Dragger2实现不一样的Android网络构架搭建》  http://blog.csdn.net/finddreams/article/details/50849385
  文中有谈到目前Android开发中使用的比较多的网络框架有android-async-http,Volley,OkHttp等,Retrofit2.0就是基于OkHttp的,各大网络框架都有各自的优点,今天讲的android-async-http的优点就是api调用简单,学习成本低,jar包的体积小等。

概述:

  android-async-http是一个基于Apache的HttpClient的异步的Android请求框架,所有的请求全在UI(主)线程之外执行,而callback成功失败的回调都是在主线程中执行。
  
 Github地址: https://github.com/loopj/android-async-http

特点:

  1. 使用HttpClient 4.3.6版本而不是Android 提供的DefaultHttpClient;
  2. 兼容安卓API 23(6.0)和更高的版本;
  3. 库很小,jar包小于100K;
  4. 支持重试机制;
  5. cookie的管理,内部实现用的是Android的SharedPreferences;
  6. 通过BaseJsonHttpResponseHandler和各种json库集成;
    ……………

核心类介绍:

  1. AsyncHttpResponseHandler ——这是一个请求返回处理,成功,失败,开始,完成,等自定义处理请求的类;

  2. BinaryHttpResponseHandler extends AsyncHttpResponseHandler ——继承AsyncHttpResponseHandler的子类,这是一个字节流返回处理的类, 该类用于处理图片,流的形式;

  3. JsonHttpResponseHandler extends AsyncHttpResponseHandler ——继承AsyncHttpResponseHandler的子类,这是一个json请求返回处理服务器与客户端用json交流时使用的类;

  4. AsyncHttpRequest implements Runnable ——基于线程的子类,用于 异步请求类, 通过AsyncHttpResponseHandler回调。

  5. TextHttpResponseHandler 是把字节流转成了字符串文本的形式,方便使用;

  6. PersistentCookieStore implements CookieStore ——这是一个基于CookieStore的子类, 使用SharedPreferences来保存Cookie数据,并具备添加清楚Cookie的功能。

  7. RequestParams 封装了请求参数的类,是键值对的形式,相当于HashMap。有add和put方法,add方法的值只能是String,而put方法的值则可以添加很多的其他数据类型;

    这里写图片描述

使用方法:

   1. 引入到咱们的项目中,目前的最新版本是1.4.9,解决了Android6.0以上HttpClient的不兼容问题,请使用最新的版本:

扫描二维码关注公众号,回复: 3256501 查看本文章
  compile 'com.loopj.android:android-async-http:1.4.9'

  引入之后我们发现依赖库中不只有android-async-http:1.4.9.jar还多了一个叫httpclient 4.3.6.jar的包,这是什么原因了?
  
  HttpClient和HttpURLConnection都很熟悉了,HttpClient 的API实现起来简单,HttpURLConnection则比较麻烦一点。Android 2.2版本之前因为HttpURLConnection存在一些bug,所以谷歌官方推荐使用HttpClient,但是后来慢慢修正了HttpURLConnection的问题, google 不再维护 HttpClient 了,以致于Android5.1里面已经把 HttpClient 标注为过期。所有Android4.0之后应该用HttpURLConnection来作为网络请求,这个想必大家都已经知道了。
  
  所以为了修复android-async-http这个网络库对于高版本(API大于23)的android系统存在不兼容的问题,他们引入了http client这个开源的网络库,那样就可以继续使用android-async-http在Android上开发了,因为已经兼容了安卓API 23(6.0)和更高的版本,所以大可放心使用。

  2.用单例模式封装一下AsyncHttpClient,方便调用管理:

/**
 * AsyncHttpClientUtils的单例
 *
 * @Author finddreams
 * @Address http://blog.csdn.net/finddreams
 * @Time 2016/3/14
 */
public class AsyncHttpClientUtils {
    public static final String TAG = AsyncHttpClientUtils.class.getSimpleName();
    public static final int SOCKET_TIMEOUT = 20 * 1000;//默认超时时间
    //    public static final int DEFAULT_SOCKET_TIMEOUT = 10 * 1000;
    private static AsyncHttpClientUtils instance = new AsyncHttpClientUtils();
    // 实例话对象
    private static AsyncHttpClient client = new AsyncHttpClient();

    static {
//        client.setConnectTimeout(SOCKET_TIMEOUT);  //连接时间
//        client.setResponseTimeout(SOCKET_TIMEOUT); //响应时间
        client.setTimeout(SOCKET_TIMEOUT); // 设置连接超时,如果不设置,默认为10s
    }

    private PersistentCookieStore cookieStore;

    private AsyncHttpClientUtils() {

    }

    public static AsyncHttpClientUtils getInstance() {
        return instance;
    }

    public AsyncHttpClient getAsyncHttpClient() {
        return client;
    }

    /**
     * get方法带参数
     */
    public RequestHandle get(String url, RequestParams params,
                             HttpCallBack httpCallBack) {
        Log.i(TAG, client.getUrlWithQueryString(true, url, params));
        RequestHandle requestHandle = client.get(url, params, httpCallBack);
        return requestHandle;
    }

    /**
     * post请求,带参数
     */
    public RequestHandle post(String url, RequestParams params,
                              HttpCallBack httpCallBack) {
        Log.i(TAG, client.getUrlWithQueryString(true, url, params));
        RequestHandle requestHandle = client.post(url, params, httpCallBack);
        return requestHandle;
    }

    /**
     * 设置Cookie
     *
     * @param context
     */
    public void setCookie(Context context) {
        cookieStore = new PersistentCookieStore(context);
        client.setCookieStore(cookieStore);
    }

    /**
     * 清楚Cookie
     */
    public void clearSession() {
        if (cookieStore != null) {
            cookieStore.clear();
        }
    }

    /**
     * 设置重试机制
     */
    public void setRetry() {
        client.setMaxRetriesAndTimeout(2, SOCKET_TIMEOUT);
        client.allowRetryExceptionClass(SocketTimeoutException.class);
        client.blockRetryExceptionClass(SSLException.class);
    }

    /**
     * 取消所有请求
     *
     * @param context
     */
    public void cancelAllRequests(Context context) {
        if (client != null) {
            Log.i(TAG, "cancel");
            client.cancelRequests(context, true); //取消请求
            client.cancelAllRequests(true);
        }
    }

    /*
     * 文件下载
     *
     * @param paramString
     * @param paramBinaryHttpResponseHandler
     */
    public void downFile(String paramString,
                         BinaryHttpResponseHandler paramBinaryHttpResponseHandler) {
        try {
            client.get(paramString, paramBinaryHttpResponseHandler);
            return;
        } catch (IllegalArgumentException localIllegalArgumentException) {
            Log.d("hhxh", "URL路径不正确!!");
        }
    }

}

   client.setTimeout(SOCKET_TIMEOUT); // 设置连接超时时间,如果不设置,AsyncHttpClient默认的超时时间为为10s,如果我们的用户量过大,服务器处理请求所需要的时间比较多的话,可以增加超时时间,不要使用默认的。

 public static final int DEFAULT_SOCKET_TIMEOUT = 10 * 1000;

  setCookie()方法中新建一个PersistentCookieStore类来管理AsyncHttpClient请求中的Cookie,
PersistentCookieStore是可自动将cookie保存到Android设备的SharedPreferences中,如果你的项目中使用cookie来管理验证会话,验证请求权限的话,这个非常简单有用,因为cookie信息已经持久化在SharedPreferenceswen文件中,就算是退出了,你的登录信息还是会保持一段有效时间。

  setRetry()方法中可以设置重试机制。client.setMaxRetriesAndTimeout(2, SOCKET_TIMEOUT)方法的参数一是重试的次数,第二个是重试的时间;

client.allowRetryExceptionClass(SocketTimeoutException.class);
client.blockRetryExceptionClass(SSLException.class);

  client.allowRetryExceptionClass(SocketTimeoutException.class)是设置出现异常的后重试的白名单,意思是当网络异常是SocketTimeoutException则可以重试。

public static void allowRetryExceptionClass(Class<?> cls) {
        if (cls != null) {
            RetryHandler.addClassToWhitelist(cls);
        }
    }

   client.blockRetryExceptionClass(SSLException.class);则是添加重试机制的黑名单,当出现SSLException异常的时候,则网络请求不会重连。
   
  class RetryHandler implements HttpRequestRetryHandler 类的源码中,默认NoHttpResponseException,UnknownHostException,SocketException都是在白名单;而InterruptedIOException,SSLException则默认是添加在黑名单中,不会重连。

 private final static HashSet<Class<?>> exceptionWhitelist = new HashSet<Class<?>>();
    private final static HashSet<Class<?>> exceptionBlacklist = new HashSet<Class<?>>();

    static {
        // Retry if the server dropped connection on us
        exceptionWhitelist.add(NoHttpResponseException.class);
        // retry-this, since it may happens as part of a Wi-Fi to 3G failover
        exceptionWhitelist.add(UnknownHostException.class);
        // retry-this, since it may happens as part of a Wi-Fi to 3G failover
        exceptionWhitelist.add(SocketException.class);

        // never retry timeouts
        exceptionBlacklist.add(InterruptedIOException.class);
        // never retry SSL handshake failures
        exceptionBlacklist.add(SSLException.class);
    }

  Log.i(TAG, AsyncHttpClient.getUrlWithQueryString(true, url, params));
  client.getUrlWithQueryString(true, url, params)
  这个方法可以把请求中的url和请求参数作为String返回,方便我们调试;比如:  

 I/AsyncHttpClientUtils: http://apis.baidu.com/apistore/weatherservice/cityname?cityname=北京

  
  3.请求的响应处理Handler 有很多种BinaryHttpResponseHandler,JsonHttpResponseHandler,TextHttpResponseHandler他们都是继承自AsyncHttpResponseHandler,TextHttpResponseHandler使用起来很简单:

 private TextHttpResponseHandler responseHandler =  new TextHttpResponseHandler(){
        @Override
        public void onStart() {
            Log.e(tag, "onStart====");
        }

        @Override
        public void onSuccess(int statusCode, Header[] headers, String response) {
            Log.e(tag, response);
        }

        @Override
        public void onFailure(int statusCode, Header[] headers, String errorResponse, Throwable e) {


        }

    };

  TextHttpResponseHandler直接把字节流包装成String来返回,使用起来也很方便;
  
  4.前面说BaseJsonHttpResponseHandler 是可以集成各自json库,看到这句话,有很多人可能不知道是如何做到的,下面就是集成了Gson库的解析类:

/**
 * 基于BaseJsonHttpResponseHandler封装的Gson泛型解析
 *
 * @author finddreams
 * @address http://blog.csdn.net/finddreams
 */
public abstract class HttpCallBack<T> extends BaseJsonHttpResponseHandler<T> {
    @Override
    public abstract void onSuccess(int statusCode, Header[] headers, String rawJsonResponse, T response);

    @Override
    public void onFailure(int statusCode, Header[] headers, Throwable throwable, String rawJsonData, T errorResponse) {
        Log.i("onFailure", throwable.getMessage());
    }


    @Override
    public void onStart() {
        super.onStart();
    }

    @Override
    public void onFinish() {
        super.onFinish();
    }

    @Override
    public void onUserException(Throwable error) {
        super.onUserException(error);
    }

    @Override
    protected T parseResponse(String rawJsonData, boolean isFailure) throws Throwable {
        Log.i("rawJsonData", rawJsonData);
        Log.i("isFailure", isFailure + "");

        T t = new Gson().fromJson(rawJsonData, getSuperclassTypeParameter(getClass()));

        return t;
    }

    /**
     * 返回gson类型
     */
    public static Type getSuperclassTypeParameter(Class<?> subclass) {
        Type superclass = subclass.getGenericSuperclass();
        if (superclass instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        }
        ParameterizedType parameterized = (ParameterizedType) superclass;
        return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
    }

}

  HttpCallBack这个类来做为请求的回调,就可以通过泛型的方式,你想把请求的结果解析成什么类就会返回什么类,这样使用起来就方便了很多。

5.最后还是用百度api提供的天气接口,来实现AsyncHttpClientget请求的调用:

public class MainActivity extends AppCompatActivity {

    @Bind(R.id.content)
    EditText content;
    @Bind(R.id.getresult)
    Button getresult;
    @Bind(R.id.result)
    TextView result;
    public static String BaiduUrl = "http://apis.baidu.com/apistore/weatherservice/cityname";  //百度api地址
    public static String baiduKey = "";  //百度申请的apikey
    private AsyncHttpClientUtils clientUtils;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        clientUtils = AsyncHttpClientUtils.getInstance();
    }

    @OnClick(R.id.getresult)
    public void onClick(View view) {
        getQueryResult();
    }

    private void getQueryResult() {
        RequestParams params = new RequestParams();
        AsyncHttpClient client = clientUtils.getAsyncHttpClient();
        client.addHeader("apikey", MainActivity.baiduKey);
        params.add("cityname", content.getText().toString());
        RequestHandle requestHandle = clientUtils.get(BaiduUrl, params, new HttpCallBack<WeatherResultBean>() {
            @Override
            public void onSuccess(int statusCode, Header[] headers, String rawJsonResponse, WeatherResultBean response) {
                WeatherResultBean.RetDataEntity retData = response.getRetData();
                result.setText(retData.getCity() + ":" + retData.getWeather() + ":" + retData.getDate());
            }

            @Override
            public void onFailure(int statusCode, Header[] headers, Throwable throwable, String rawJsonData, WeatherResultBean errorResponse) {
                super.onFailure(statusCode, headers, throwable, rawJsonData, errorResponse);
            }
        });

//        requestHandle.cancel(true);//取消单个请求,
//        clientUtils.cancelAllRequests(this); //取消所有请求
    }
}

  是不是调用起来非常的简单,不用太多的封装,非常快速的就可以完成get,post请求的调用,很适合一些中小项目的敏捷开发,而且android-async-http的jar包体积非常的小,对于apk的体积大小来说,基本上可以忽略。
  同时android-async-http的api很简单,结构清晰,比较适合深入研究。还有很多实用的功能等待大家去探索,比如本文没有讲的文件上传与下载,HTTP Basic Auth身份认证等等; 
  
  最后把运行的结果图附上,调用的百度天气api接口,实现天气查询功能:
  
这里写图片描述

猜你喜欢

转载自blog.csdn.net/finddreams/article/details/50887506