您的位置:首页 > 理论基础 > 计算机网络

网络请求框架——OkGo解读(一)——数据的缓存

2016-10-31 11:50 417 查看

前言:

OkGo —— OkHttpUtils-2.0.0升级后改名 OkGo,全新完美支持RxJava。


该库是封装了okhttp的网络框架,可以与RxJava完美结合,比Retrofit更简单易用。支持大文件上传下载,上传进度回调,下载进度回调,表单上传(多文件和多参数一起上传),链式调用,可以自定义返回对象,支持Https和自签名证书,支持超时自动重连,支持cookie自动管理,支持四种缓存模式缓存网络数据,支持301、302重定向,扩展了统一的上传管理和下载管理功能

声明:本人才疏学浅,只能大致的分析下大神封装的东西。

使用:

public class GApp extends Application {

@Override
public void onCreate() {
super.onCreate();

//---------这里给出的是示例代码,告诉你可以这么传,实际使用的时候,根据需要传,不需要就不传-------------//
HttpHeaders headers = new HttpHeaders();
headers.put("commonHeaderKey1", "commonHeaderValue1");    //header不支持中文
headers.put("commonHeaderKey2", "commonHeaderValue2");
HttpParams params = new HttpParams();
params.put("commonParamsKey1", "commonParamsValue1");     //param支持中文,直接传,不要自己编码
params.put("commonParamsKey2", "这里支持中文参数");
//-----------------------------------------------------------------------------------//

//必须调用初始化
OkGo.init(this);

//以下设置的所有参数是全局参数,同样的参数可以在请求的时候再设置一遍,那么对于该请求来讲,请求中的参数会覆盖全局参数
//好处是全局参数统一,特定请求可以特别定制参数
try {
//以下都不是必须的,根据需要自行选择,一般来说只需要 debug,缓存相关,cookie相关的 就可以了
OkGo.getInstance()

//打开该调试开关,控制台会使用 红色error 级别打印log,并不是错误,是为了显眼,不需要就不要加入该行
.debug("OkGo")

//如果使用默认的 60秒,以下三行也不需要传
.setConnectTimeout(OkGo.DEFAULT_MILLISECONDS)  //全局的连接超时时间
.setReadTimeOut(OkGo.DEFAULT_MILLISECONDS)     //全局的读取超时时间
.setWriteTimeOut(OkGo.DEFAULT_MILLISECONDS)    //全局的写入超时时间

//可以全局统一设置缓存模式,默认是不使用缓存,可以不传,具体其他模式看 github 介绍 https://github.com/jeasonlzy0216/ .setCacheMode(CacheMode.NO_CACHE)

//可以全局统一设置缓存时间,默认永不过期,具体使用方法看 github 介绍
.setCacheTime(CacheEntity.CACHE_NEVER_EXPIRE)

//可以全局统一设置超时重连次数,默认为三次,那么最差的情况会请求4次(一次原始请求,三次重连请求),不需要可以设置为0
.setRetryCount(3)

//如果不想让框架管理cookie,以下不需要
//                .setCookieStore(new MemoryCookieStore())                //cookie使用内存缓存(app退出后,cookie消失)
.setCookieStore(new PersistentCookieStore())          //cookie持久化存储,如果cookie不过期,则一直有效

//可以设置https的证书,以下几种方案根据需要自己设置
//                    .setCertificates()                                  //方法一:信任所有证书(选一种即可)
//                    .setCertificates(getAssets().open("srca.cer"))      //方法二:也可以自己设置https证书(选一种即可)
//                    .setCertificates(getAssets().open("aaaa.bks"), "123456", getAssets().open("srca.cer"))//方法三:传入bks证书,密码,和cer证书,支持双向加密

//可以添加全局拦截器,不会用的千万不要传,错误写法直接导致任何回调不执行
//                .addInterceptor(new Interceptor() {
//                    @Override
//                    public Response intercept(Chain chain) throws IOException {
//                        return chain.proceed(chain.request());
//                    }
//                })

//这两行同上,不需要就不要传
.addCommonHeaders(headers)                                         //设置全局公共头
.addCommonParams(params);                                          //设置全局公共参数
} catch (Exception e) {
e.printStackTrace();
}
}
}


OkGo.java 的内部成员变量

public static final int DEFAULT_MILLISECONDS = 60000;       //默认的超时时间

private Handler mDelivery;                                  //用于在主线程执行的调度器
private OkHttpClient.Builder okHttpClientBuilder;           //ok请求的客户端
private HttpParams mCommonParams;                           //全局公共请求参数
private HttpHeaders mCommonHeaders;                         //全局公共请求头
private CacheMode mCacheMode;                               //全局缓存模式
private int mRetryCount = 3;                                    //全局超时重试次数
private long mCacheTime = CacheEntity.CACHE_NEVER_EXPIRE;   //全局缓存过期时间,默认永不过期
private static Application context;                         //全局上下文
private CookieJarImpl cookieJar;                            //全局 Cookie 实例


使用了 静态内部类来对 OkGo对象的创建 ,并使用 静态方法获得 OkGo对象的实例

private OkGo() {
okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder.hostnameVerifier(new DefaultHostnameVerifier());                 //主机名校验
okHttpClientBuilder.connectTimeout(DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);
okHttpClientBuilder.readTimeout(DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);
okHttpClientBuilder.writeTimeout(DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS);
mDelivery = new Handler(Looper.getMainLooper());                               // 使用handler——在主线程中执行的调度器
}

public static OkGo getInstance() {
return OkGoHolder.holder;
}

private static class OkGoHolder {
private static OkGo holder = new OkGo();
}

/** 必须在全局Application先调用,获取context上下文,否则缓存无法使用 */
public static void init(Application app) {
context = app;
}

/** 获取全局上下文 */
public static Context getContext() {
if (context == null) throw new IllegalStateException("请先在全局Application中调用 OkGo.init() 初始化!");
return context;
}



构建 :okHttpClientBuilder

public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;                         // 协议
connectionSpecs = DEFAULT_CONNECTION_SPECS;           // 连接参数
proxySelector = ProxySelector.getDefault();            // 代理选择器
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;       // 主机名校验
certificatePinner = CertificatePinner.DEFAULT;        // 证书
proxyAuthenticator = Authenticator.NONE;              // 代理认证器
authenticator = Authenticator.NONE;                   // 认证器
connectionPool = new ConnectionPool();                // 连接池
dns = Dns.SYSTEM;                                     // 域名
followSslRedirects = true;                           // 跟随SSL协议的重定向
followRedirects = true;                              // 重定向
retryOnConnectionFailure = true;                     // 连接失败时重新连接服务器
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
}


主机名的校验:

/**
* 此类是用于主机名验证的基接口。 在握手期间,如果 URL 的主机名和服务器的标识主机名不匹配,
* 则验证机制可以回调此接口的实现程序来确定是否应该允许此连接。策略可以是基于证书的或依赖于其他验证方案。
* 当验证 URL 主机名使用的默认规则失败时使用这些回调。如果主机名是可接受的,则返回 true
*/
public class DefaultHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}

调试 Log设置的为默认打开:

/** 调试模式,默认打开所有的异常调试 */
public OkGo debug(String tag) {
debug(tag, true);
return this;
}

全局的联网超时时间:

/** 全局连接超时时间 */
public OkGo setConnectTimeout(long connectTimeout) {
okHttpClientBuilder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS);
return this;
}


/** 超时重试次数 */
public OkGo setRetryCount(int retryCount) {
if (retryCount < 0) throw new IllegalArgumentException("retryCount must > 0");
mRetryCount = retryCount;
return this;
}

全局缓存模式:

/** 全局的缓存模式 */
public OkGo setCacheMode(CacheMode cacheMode) {
mCacheMode = cacheMode;
return this;
}

public enum CacheMode {
/** 按照HTTP协议的默认缓存规则,例如有304响应头时缓存 */
DEFAULT,

/** 不使用缓存 */
NO_CACHE,

/** 请求网络失败后,读取缓存 */
REQUEST_FAILED_READ_CACHE,

/** 如果缓存不存在才请求网络,否则使用缓存 */
IF_NONE_CACHE_REQUEST,

/** 先使用缓存,不管是否存在,仍然请求网络 */
FIRST_CACHE_THEN_REQUEST,
}

缓存的过期时间:

/** 全局的缓存过期时间 */
public OkGo setCacheTime(long cacheTime) {
if (cacheTime <= -1) cacheTime = CacheEntity.CACHE_NEVER_EXPIRE;
mCacheTime = cacheTime;
return this;
}

缓存实体类:

public class CacheEntity<T> implements Serializable {

private static final long serialVersionUID = -4337711009801627866L;

public static final long CACHE_NEVER_EXPIRE = -1;        //缓存永不过期

private long id;
private String key;
private HttpHeaders responseHeaders;              // 响应头
private T data;
private long localExpire;                          // 本地到期

//该变量不必保存到数据库,程序运行起来后会动态计算
private boolean isExpire;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getKey() {
return key;
}

public void setKey(String key) {
this.key = key;
}

public HttpHeaders getResponseHeaders() {
return responseHeaders;
}

public void setResponseHeaders(HttpHeaders responseHeaders) {
this.responseHeaders = responseHeaders;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}

public long getLocalExpire() {
return localExpire;
}

public void setLocalExpire(long localExpire) {
this.localExpire = localExpire;
}

public boolean isExpire() {
return isExpire;
}

public void setExpire(boolean expire) {
isExpire = expire;
}

/**
* @param cacheTime 允许的缓存时间
* @param baseTime  基准时间,小于当前时间视为过期
* @return 是否过期
*/
public boolean checkExpire(CacheMode cacheMode, long cacheTime, long baseTime) {
//304的默认缓存模式,设置缓存时间无效,需要依靠服务端的响应头控制
if (cacheMode == CacheMode.DEFAULT) return getLocalExpire() < baseTime;
if (cacheTime == CACHE_NEVER_EXPIRE) return false;
return getLocalExpire() + cacheTime < baseTime;
}

public static <T> ContentValues getContentValues(CacheEntity<T> cacheEntity) {
ContentValues values = new ContentValues();
values.put(CacheHelper.KEY, cacheEntity.getKey());
values.put(CacheHelper.LOCAL_EXPIRE, cacheEntity.getLocalExpire());

HttpHeaders headers = cacheEntity.getResponseHeaders();
ByteArrayOutputStream headerBAOS = null;
ObjectOutputStream headerOOS = null;
try {
if (headers != null) {
headerBAOS = new ByteArrayOutputStream();
headerOOS = new ObjectOutputStream(headerBAOS);
headerOOS.writeObject(headers);
headerOOS.flush();
byte[] headerData = headerBAOS.toByteArray();
values.put(CacheHelper.HEAD, headerData);
}
} catch (IOException e) {
OkLogger.e(e);
} finally {
try {
if (headerOOS != null) headerOOS.close();
if (headerBAOS != null) headerBAOS.close();
} catch (IOException e) {
OkLogger.e(e);
}
}

T data = cacheEntity.getData();
ByteArrayOutputStream dataBAOS = null;
ObjectOutputStream dataOOS = null;
try {
if (data != null) {
dataBAOS = new ByteArrayOutputStream();
dataOOS = new ObjectOutputStream(dataBAOS);
dataOOS.writeObject(data);
dataOOS.flush();
byte[] dataData = dataBAOS.toByteArray();
values.put(CacheHelper.DATA, dataData);
}
} catch (IOException e) {
OkLogger.e(e);
} finally {
try {
if (dataOOS != null) dataOOS.close();
if (dataBAOS != null) dataBAOS.close();
} catch (IOException e) {
OkLogger.e(e);
}
}
return values;
}

public static <T> CacheEntity<T> parseCursorToBean(Cursor cursor) {
CacheEntity<T> cacheEntity = new CacheEntity<>();
cacheEntity.setId(cursor.getInt(cursor.getColumnIndex(CacheHelper.ID)));
cacheEntity.setKey(cursor.getString(cursor.getColumnIndex(CacheHelper.KEY)));
cacheEntity.setLocalExpire(cursor.getLong(cursor.getColumnIndex(CacheHelper.LOCAL_EXPIRE)));

byte[] headerData = cursor.getBlob(cursor.getColumnIndex(CacheHelper.HEAD));
ByteArrayInputStream headerBAIS = null;
ObjectInputStream headerOIS = null;
try {
if (headerData != null) {
headerBAIS = new ByteArrayInputStream(headerData);
headerOIS = new ObjectInputStream(headerBAIS);
Object header = headerOIS.readObject();
cacheEntity.setResponseHeaders((HttpHeaders) header);
}
} catch (Exception e) {
OkLogger.e(e);
} finally {
try {
if (headerOIS != null) headerOIS.close();
if (headerBAIS != null) headerBAIS.close();
} catch (IOException e) {
OkLogger.e(e);
}
}

byte[] dataData = cursor.getBlob(cursor.getColumnIndex(CacheHelper.DATA));
ByteArrayInputStream dataBAIS = null;
ObjectInputStream dataOIS = null;
try {
if (dataData != null) {
dataBAIS = new ByteArrayInputStream(dataData);
dataOIS = new ObjectInputStream(dataBAIS);
T data = (T) dataOIS.readObject();
cacheEntity.setData(data);
}
} catch (Exception e) {
OkLogger.e(e);
} finally {
try {
if (dataOIS != null) dataOIS.close();
if (dataBAIS != null) dataBAIS.close();
} catch (IOException e) {
OkLogger.e(e);
}
}

return cacheEntity;
}

@Override
public String toString() {
return "CacheEntity{" +
"id=" + id +
", key='" + key + '\'' +
", responseHeaders=" + responseHeaders +
", data=" + data +
", localExpire=" + localExpire +
'}';
}
}


缓存使用的是 数据库 进行实现数据的缓存:

class CacheHelper extends SQLiteOpenHelper {

public static final String DB_CACHE_NAME = "okgo_cache.db";
public static final int DB_CACHE_VERSION = 3;
public static final String TABLE_NAME = "cache_table";

//表中的五个字段
public static final String ID = "_id";
public static final String KEY = "key";
public static final String LOCAL_EXPIRE = "localExpire";
public static final String HEAD = "head";
public static final String DATA = "data";

//四条sql语句
private static final String SQL_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + //
ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +//
KEY + " VARCHAR, " +//
LOCAL_EXPIRE + " INTEGER, " +//
HEAD + " BLOB, " +//
DATA + " BLOB)";
private static final String SQL_CREATE_UNIQUE_INDEX = "CREATE UNIQUE INDEX cache_unique_index ON " + TABLE_NAME + "(\"key\")";
private static final String SQL_DELETE_TABLE = "DROP TABLE " + TABLE_NAME;
private static final String SQL_DELETE_UNIQUE_INDEX = "DROP INDEX cache_unique_index";

public CacheHelper() {
super(OkGo.getContext(), DB_CACHE_NAME, null, DB_CACHE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.beginTransaction();
try {
db.execSQL(SQL_CREATE_TABLE);
//建立key的唯一索引后,方便使用 replace 语句
db.execSQL(SQL_CREATE_UNIQUE_INDEX);
db.setTransactionSuccessful();
} catch (Exception e) {
OkLogger.e(e);
} finally {
db.endTransaction();
}
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion != oldVersion) {
db.beginTransaction();
try {
db.execSQL(SQL_DELETE_UNIQUE_INDEX);
db.execSQL(SQL_DELETE_TABLE);
db.execSQL(SQL_CREATE_TABLE);
db.execSQL(SQL_CREATE_UNIQUE_INDEX);
db.setTransactionSuccessful();
} catch (Exception e) {
OkLogger.e(e);
} finally {
db.endTransaction();
}
}
}

@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
}


缓存的管理:

public enum CacheManager {

INSTANCE;

private Lock mLock;
private CacheDao<Object> cacheDao;

CacheManager() {
mLock = new ReentrantLock();                // 可重入的锁
cacheDao = new CacheDao<>();
}

/**
* 获取缓存
*
* @param key 缓存的Key
* @return 缓存的对象实体
*/
public CacheEntity<Object> get(String key) {
mLock.lock();
try {
return cacheDao.get(key);
} finally {
mLock.unlock();
}
}

/** 返回带泛型的对象,注意必须确保泛型和对象对应才不会发生类型转换异常 */
@SuppressWarnings("unchecked")
public <T> CacheEntity<T> get(String key, Class<T> clazz) {
return (CacheEntity<T>) get(key);
}

/**
* 获取所有缓存
*
* @return 缓存的对象实体
*/
public List<CacheEntity<Object>> getAll() {
mLock.lock();
try {
return cacheDao.getAll();
} finally {
mLock.unlock();
}
}

/**
* 更新缓存,没有就创建,有就替换
*
* @param key    缓存的key
* @param entity 需要替换的的缓存
* @return 被替换的缓存
*/
@SuppressWarnings("unchecked")
public <T> CacheEntity<T> replace(String key, CacheEntity<T> entity) {
mLock.lock();
try {
entity.setKey(key);
cacheDao.replace((CacheEntity<Object>) entity);
return entity;
} finally {
mLock.unlock();
}
}

/**
* 移除缓存
*
* @param key 缓存的key
* @return 是否移除成功
*/
public boolean remove(String key) {
if (key == null) return true;
mLock.lock();
try {
return cacheDao.remove(key);
} finally {
mLock.unlock();
}
}

/**
* 清空缓存
*
* @return 是否清空成功
*/
public boolean clear() {
mLock.lock();
try {
return cacheDao.deleteAll() > 0;
} finally {
mLock.unlock();
}
}
}


缓存数据访问对象:

class CacheDao<T> extends DataBaseDao<CacheEntity<T>> {

public CacheDao() {
super(new CacheHelper());
}

/** 根据key获取缓存 */
public CacheEntity<T> get(String key) {
String selection = CacheHelper.KEY + "=?";
String[] selectionArgs = new String[]{key};
List<CacheEntity<T>> cacheEntities = get(selection, selectionArgs);
return cacheEntities.size() > 0 ? cacheEntities.get(0) : null;
}

/** 移除一个缓存 */
public boolean remove(String key) {
String whereClause = CacheHelper.KEY + "=?";
String[] whereArgs = new String[]{key};
int delete = delete(whereClause, whereArgs);
return delete > 0;
}

@Override
public CacheEntity<T> parseCursorToBean(Cursor cursor) {
return CacheEntity.parseCursorToBean(cursor);
}

@Override
public ContentValues getContentValues(CacheEntity<T> cacheEntity) {
return CacheEntity.getContentValues(cacheEntity);
}

@Override
protected String getTableName() {
return CacheHelper.TABLE_NAME;
}
}


基本数据访问对象:

public abstract class DataBaseDao<T> {

private SQLiteOpenHelper helper;

public DataBaseDao(SQLiteOpenHelper helper) {
this.helper = helper;
}

protected final SQLiteDatabase openReader() {
return helper.getReadableDatabase();
}

protected final SQLiteDatabase openWriter() {
return helper.getWritableDatabase();
}

protected final void closeDatabase(SQLiteDatabase database, Cursor cursor) {
if (cursor != null && !cursor.isClosed()) cursor.close();
if (database != null && database.isOpen()) database.close();
}

/** 获取对应的表名 */
protected abstract String getTableName();

/** 需要数据库中有个 _id 的字段 */
public int count() {
return countColumn("_id");
}

/** 返回一列的总记录数量 */
public int countColumn(String columnName) {
String sql = "SELECT COUNT(?) FROM " + getTableName();
SQLiteDatabase database = openReader();
Cursor cursor = null;
try {
database.beginTransaction();
cursor = database.rawQuery(sql, new String[]{columnName});
int count = 0;
if (cursor.moveToNext()) {
count = cursor.getInt(0);
}
database.setTransactionSuccessful();
return count;
} catch (Exception e) {
OkLogger.e(e);
} finally {
database.endTransaction();
closeDatabase(database, cursor);
}
return 0;
}

/** 删除所有数据 */
public int deleteAll() {
return delete(null, null);
}

/** 根据条件删除数据库中的数据 */
public int delete(String whereClause, String[] whereArgs) {
SQLiteDatabase database = openWriter();
try {
database.beginTransaction();
int result = database.delete(getTableName(), whereClause, whereArgs);
database.setTransactionSuccessful();
return result;
} catch (Exception e) {
OkLogger.e(e);
} finally {
database.endTransaction();
closeDatabase(database, null);
}
return 0;
}

/** 查询并返回所有对象的集合 */
public List<T> getAll() {
return get(null, null);
}

/** 按条件查询对象并返回集合 */
public List<T> get(String selection, String[] selectionArgs) {
return get(null, selection, selectionArgs, null, null, null, null);
}

/** 按条件查询对象并返回集合 */
public List<T> get(String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
SQLiteDatabase database = openReader();
List<T> list = new ArrayList<>();
Cursor cursor = null;
try {
database.beginTransaction();
cursor = database.query(getTableName(), columns, selection, selectionArgs, groupBy, having, orderBy, limit);
while (!cursor.isClosed() && cursor.moveToNext()) {
list.add(parseCursorToBean(cursor));
}
database.setTransactionSuccessful();
} catch (Exception e) {
OkLogger.e(e);
} finally {
database.endTransaction();
closeDatabase(database, cursor);
}
return list;
}

/**
* replace 语句有如下行为特点
* 1. replace语句会删除原有的一条记录, 并且插入一条新的记录来替换原记录。
* 2. 一般用replace语句替换一条记录的所有列, 如果在replace语句中没有指定某列, 在replace之后这列的值被置空 。
* 3. replace语句根据主键的值确定被替换的是哪一条记录
* 4. 如果执行replace语句时, 不存在要替换的记录, 那么就会插入一条新的记录。
* 5. replace语句不能根据where子句来定位要被替换的记录
* 6. 如果新插入的或替换的记录中, 有字段和表中的其他记录冲突, 那么会删除那条其他记录。
*/
public long replace(T t) {
SQLiteDatabase database = openWriter();
try {
database.beginTransaction();
long id = database.replace(getTableName(), null, getContentValues(t));
database.setTransactionSuccessful();
return id;
} catch (Exception e) {
OkLogger.e(e);
} finally {
database.endTransaction();
closeDatabase(database, null);
}
return 0;
}

/** 创建一条记录 */
public long create(T t) {
SQLiteDatabase database = openWriter();
try {
database.beginTransaction();
long id = database.insert(getTableName(), null, getContentValues(t));
database.setTransactionSuccessful();
return id;
} catch (Exception e) {
OkLogger.e(e);
} finally {
database.endTransaction();
closeDatabase(database, null);
}
return 0;
}

/** 更新一条记录 */
public int update(T t, String whereClause, String[] whereArgs) {
SQLiteDatabase database = openWriter();
try {
database.beginTransaction();
int count = database.update(getTableName(), getContentValues(t), whereClause, whereArgs);
database.setTransactionSuccessful();
return count;
} catch (Exception e) {
OkLogger.e(e);
} finally {
database.endTransaction();
closeDatabase(database, null);
}
return 0;
}

/** 将Cursor解析成对应的JavaBean */
public abstract T parseCursorToBean(Cursor cursor);

/** 需要替换的列 */
public abstract ContentValues getContentValues(T t);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  OkGo OkGo详读
相关文章推荐