精选案例基础程序员

App网络框架封装

2017-05-29  本文已影响347人  码农一颗颗

本文介绍对App网络框架封装的一些建议,只是个人观点欢迎大家讨论。
刚开始开发Android github上没有那么多好用的开源网络框架,就连async-http-client都没有,更不用说现在火的没朋友的okhttp+retrofit,开发者只能自己封装相对底层的库apache-httpclient,本人在Retrofit出现之前也一直在封装网络框架,最开始封装HttpClient,后来封装async-http-client,总结了一点小小经验来和大家分享。
Retrofit也不是完美的也需要根据App的实际使用情况进行封装,只不过要比之前框架的好太多了,尤其是可以和RxJava连用。

目录

  1. 访问网络Api
  2. CallBack监听
  3. Response支持FastJson解析
  4. 缓存机制
  5. Https网络安全

1.访问网络Api

App通过网络框架进行访问服务器,我们目前用的最多的Api是Get和Post请求,有的App干脆全部都用Post请求,这样设计接口的原因很大程度上是由于当时开发时间紧没有时间细化接口,或者某些历史遗留问题。
如果服务端是RESTful Api 那你的网络架构需要支持更多的请求方式。

2.CallBack监听

Android 3.0 开始系统要求网络访问必须在子线程中进行, 否则网络访问将会失败并抛出NetworkOnMainThreadException这个异常, 这样做是为了避免主线程由于耗时操作所阻塞从而出现ANR现象。对于多线程来说网络框架的回调就非常重要了。

public interface IBaseStateListener {
    /**
     * 网络请求之前回调,可以进行一些预处理或者弹出网络等待对话框
     */
    public void onStart();
    /**
     * 文件上传进度,文件下载进度
     * @param bytesWritten
     * @param totalSize
     */
    public void onProgress(int bytesWritten, int totalSize);
    /**
     * 网络请求完成回调,请求成功,或者Error之后调用,和onStart成对出现
     */
    public void onFinish();
}
{
    "state":-1 , //-1错误,0返回空,1返回正确
    "msg":"缺少字段",
    "data"{}
}
public interface IBaseErrorListener {
    /**
     * 自定义错误监听
     * @param statusCode
     * @param errCode
     * @param headers
     * @param responseBody
     * @param error
     */
    public void onFailure(int statusCode, int errCode, Header[] headers, Throwable error);
}
public interface IBaseResponse {
    /**
     * 用于返回文本,在内存中存储
     * @param statusCode
     * @param headers
     * @param responseBody
     * @throws Exception
     */
    public void onResponse(int statusCode, Header[] headers, byte[] responseBody) throws Exception;
    /**
     * 用于返回文件,保存在硬盘上
     * @param statusCode
     * @param headers
     * @param file
     * @throws Exception
     */
    public void onResponse(int statusCode, Header[] headers, File file) throws Exception;
}

以上是三个主要的监听:
网络框架中回调监听也需要做判断,当前请求是否取消,如果取消了跳过所有监听直接回调IBaseStateListener. onFinish()。当前页面是否关闭,如果关闭了不回调所有监听,否则你在监听中的操作有可能会因为当前界面关闭(Activity.finish())导致NullPointerException
网络框架发起Url请求都是在子线程中,同时用ThreadLocal保存监听对象,这就意味着每个Url请求对应的都是不同的监听对象,这样就会避免在同一个页面并发请求导致监听丢失的问题,例如:在一个Activity页面并发获取数据,如果网络框架内部多个线程共用一个监听,就会导致只能接收到最后一次回调,之前设置的都丢失了,用ThreadLocal保存就不会出现这种情况,每个线程都是保存一份监听。

3.Response支持FastJson解析

网络框架Response支持自动解析,目前用的最多的就是Gson和FastJson
继承返回监听来实现,三种返回:
返回对象BaseEntityResponse
返回列表BaseListResponse
返回状态BaseSuccessResponse(成功或失败)。

/**
 * 
 * @Description: 网络返回对象
 * @version V1.0.0
 */
public abstract class BaseEntityResponse<T> extends IBaseResponse {
    /**表示返回json文件中需要取出的Object对象的key,这个在网络协议中必须事先定义好,返回对象的协议都是统一的*/
    private static final String RESULT_OBJ = "result";
    private Class<T> clazz;
    private T object;
    public BaseEntityResponse(Class<T> clazz) {
        this.clazz = clazz;
    }
    @Override
    public void onSuccess(int statusCode, Header[] headers,
            JSONObject mainJsonObject) throws Exception {   
        object = mainJsonObject.getObject(RESULT_OBJ, clazz);
        onSuccess(object);
    }
    public abstract void onSuccess(T t) throws Exception;
}
/**
 * 
 * @Description: 网络返回,List
 * @version V1.0.0
 */
public abstract class BaseListResponse<T> extends BaseHttpJsonResponse {
    /**表示返回json文件中需要取出的List Object对象的key,这个在网络协议中必须事先定义好,返回对象的协议都是统一的*/
    private static final String RESULT_OBJ = "result";
    private Class<T> clazz;
    private List<T> list;
    // 消息记录数
    public int total;
    public BaseListResponse(Class<T> clazz) {
        this.clazz = clazz;
    }
    @Override
    public void onSuccess(int statusCode, Header[] headers,
            JSONObject mainJsonObject) throws Exception {
        JSONArray jsonArray = mainJsonObject.getJSONArray(RESULT_OBJ);
        list = new ArrayList<T>();
        if (null == jsonArray || 0 == jsonArray.size()) {
            // 返回空
        } else {
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                T t = JSON.toJavaObject(jsonObject, clazz);
                list.add(t);
            }
        }
        onSuccess(list);
    }
    public abstract void onSuccess(List<T> list) throws Exception;
}
/**
 * 
 * @Description: 返回正确还是错误
 * @version V1.0.0
 */
public abstract class BaseSuccessResponse extends BaseHttpJsonResponse {
    @Override
    public void onSuccess(int statusCode, Header[] headers,
            JSONObject mainJsonObject) throws Exception {
        onSuccess();
    }
    public abstract void onSuccess() throws Exception;
}

4. 缓存机制

网络框架的缓存机制也是非常重要的。
缓存策略:

缓存保存形式:缓存保存形式有很多种,数据库,sharepreference,序列化对象,文件等。目前我用的是文件保存网络返回的json字符串,文件的名字是URL+param的MD5,这样保证一个请求对应一个缓存文件,将缓存文件统一保存在预先设置好的目录中。这样做的好处是保存和读取缓存比较简单,读出的json字符串和网络返回走同一套逻辑在返回监听中解析返回对应的mode。

5. Https网络安全

Http请求不安全可以轻松的通过软件(fiddler charles)进行拦截,查看Request和Response的信息。
服务器可以通过配置Https证书,客户端发起Https请求来解决拦截,但是仍然无法阻止中间人攻击的拦截方式。
可以将服务器配置的Https证书的公钥写在客户端中每次请求都去校验这样可以有效防止拦截,但是客户端一旦被破解其他人拿到公钥仍然会拦截信息,因此我们对Apk进行混淆用第三方平台进行加固防止被恶意破解。
如下代码是对HttpsURLConnection设置Https

 public static String CERT = "-----BEGIN CERTIFICATE-----\n" +
"-----END CERTIFICATE-----\n";
public static KeyStore getKeyStore(){
        try {
            InputStream inputStream = new ByteArrayInputStream(CERT.getBytes("UTF-8"));
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            Certificate ca;
            try {
                ca = cf.generateCertificate(inputStream);
//                System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
            } finally {
                inputStream.close();
            }
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);
            return keyStore;
        }catch (Exception e){
            e.printStackTrace();
        }
       return null;
    }
public synchronized static void trustCertificate(HttpsURLConnection httpsURLConnection) {
        try {
            KeyStore keyStore = getKeyStore();
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);
            final SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(), null);
            httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());
            httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession sslSession) {
                        //对域名进行校验
                        if (SystemFunction.getHost().equalsIgnoreCase(hostname)) {
                            return true;
                        }
                        return false;
                    }
                });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

总结

以下几点只是我对网络框架封装的一些心得,可能写的不全或者有考虑不周到的地方希望和大家讨论。

上一篇 下一篇

猜你喜欢

热点阅读