今天看了朋友一个项目用到了Hawk,然后写了这边文章

一、了解一下概念

Android Hawk数据库github开源项目 Hawk是一个非常便捷的数据库.操作数据库只需一行代码,能存任何数据类型.
相信大家应该很熟悉SharedPreferences。它是一种轻量级的存储简单配置信息的存储机制,以key-value的形式保存数据。 本文介绍就是基于SharedPreferences的的存储框架,是由Orhan Obut大神写的, 它是Secure,simple key-value storage for Android。安全简单的Android存储工具。

这里附上Orhan Obut大神的Github 地址 https://github.com/orhanobut

**

二、用法

**

1.添加依赖

  compile 'com.orhanobut:hawk:2.0.1'

2.初始化

      Hawk.init(this).build();

这两步就可以正常使用了,那就看一下API吧

<T> :Saves any type including any collection, primitive values or custom objects任何数据类型()
存数据
public static <T> boolean put(String key, T value) {return hawkFacade.put(key, value);}取数据public static <T> T get(String key) {return hawkFacade.get(key);}
总数量public static long count() {return hawkFacade.count();}删除全部数据public static boolean deleteAll() {return hawkFacade.deleteAll();}删除某个数据public static boolean delete(String key) {return hawkFacade.delete(key);}是否包含有某个的数据public static boolean contains(String key) {return hawkFacade.contains(key);}
来验证如果准备使用。如果正确地初始化和建造@return真实。否则错误。public static boolean isBuilt() {return hawkFacade.isBuilt();}public static void destroy() {hawkFacade.destroy();}

下面是在官网图片 让我们看看它是怎么工作的

看图片一目了然左边的PUT方法中,是把T value 存储到Disk当中。大致流程是,先将数据进行toString的转换,接下来是加密,然后进行序列化,最后是存储,用的就是SharePreference的存储。获取数据时就正好逆过来啦。

三、源码解析开始啦

先看看初始化的代码

   Hawk.init(this).build();
     /*** This will init the hawk without password protection.** @param context is used to instantiate context based objects.*                ApplicationContext will be used*/public static HawkBuilder init(Context context) {HawkUtils.checkNull("Context", context);hawkFacade = null;return new HawkBuilder(context);}static void build(HawkBuilder hawkBuilder) {hawkFacade = new DefaultHawkFacade(hawkBuilder);}

初始化中创建了HawkBuilder对象,下面如下代码

 public HawkBuilder(Context context) {HawkUtils.checkNull("Context", context);this.context = context.getApplicationContext();}public void build() {Hawk.build(this);}

其中也用到了 HawkUtils,看一下代码
这个工具类就是对数据的判空处理

final class HawkUtils {private HawkUtils() {//no instance}public static void checkNull(String message, Object value) {if (value == null) {throw new NullPointerException(message + " should not be null");}}public static void checkNullOrEmpty(String message, String value) {if (isEmpty(value)) {throw new NullPointerException(message + " should not be null or empty");}}public static boolean isEmpty(String text) {return text == null || text.trim().length() == 0;}
}

上面有个最重的没有看完 DefaultHawkFacade
先看一下源码吧

public DefaultHawkFacade(HawkBuilder builder) {encryption = builder.getEncryption();storage = builder.getStorage();converter = builder.getConverter();serializer = builder.getSerializer();logInterceptor = builder.getLogInterceptor();logInterceptor.onLog("Hawk.init -> Encryption : " + encryption.getClass().getSimpleName());}@Override public <T> boolean put(String key, T value) {// ValidateHawkUtils.checkNull("Key", key);log("Hawk.put -> key: " + key + ", value: " + value);// If the value is null, delete itif (value == null) {log("Hawk.put -> Value is null. Any existing value will be deleted with the given key");return delete(key);}// 1. Convert to textString plainText = converter.toString(value);log("Hawk.put -> Converted to " + plainText);if (plainText == null) {log("Hawk.put -> Converter failed");return false;}// 2. Encrypt the textString cipherText = null;try {cipherText = encryption.encrypt(key, plainText);log("Hawk.put -> Encrypted to  " + cipherText);} catch (Exception e) {e.printStackTrace();}if (cipherText == null) {log("Hawk.put -> Encryption failed");return false;}// 3. Serialize the given object along with the cipher textString serializedText = serializer.serialize(cipherText, value);log("Hawk.put -> Serialized to" + serializedText);if (serializedText == null) {log("Hawk.put -> Serialization failed");return false;}// 4. Save to the storageif (storage.put(key, serializedText)) {log("Hawk.put -> Stored successfully");return true;} else {log("Hawk.put -> Store operation failed");return false;}}@Override public <T> T get(String key) {log("Hawk.get -> key: " + key);if (key == null) {log("Hawk.get -> null key, returning null value ");return null;}// 1. Get serialized text from the storageString serializedText = storage.get(key);log("Hawk.get -> Fetched from storage : " + serializedText);if (serializedText == null) {log("Hawk.get -> Fetching from storage failed");return null;}// 2. DeserializeDataInfo dataInfo = serializer.deserialize(serializedText);log("Hawk.get -> Deserialized");if (dataInfo == null) {log("Hawk.get -> Deserialization failed");return null;}// 3. DecryptString plainText = null;try {plainText = encryption.decrypt(key, dataInfo.cipherText);log("Hawk.get -> Decrypted to : " + plainText);} catch (Exception e) {log("Hawk.get -> Decrypt failed: " + e.getMessage());}if (plainText == null) {log("Hawk.get -> Decrypt failed");return null;}// 4. Convert the text to original data along with original typeT result = null;try {result = converter.fromString(plainText, dataInfo);log("Hawk.get -> Converted to : " + result);} catch (Exception e) {log("Hawk.get -> Converter failed");}return result;}@Override public <T> T get(String key, T defaultValue) {T t = get(key);if (t == null) return defaultValue;return t;}@Override public long count() {return storage.count();}@Override public boolean deleteAll() {return storage.deleteAll();}@Override public boolean delete(String key) {return storage.delete(key);}@Override public boolean contains(String key) {return storage.contains(key);}@Override public boolean isBuilt() {return true;}@Override public void destroy() {}private void log(String message) {logInterceptor.onLog(message);}
}

解析一下DefaultHawkFacade
我们存数据之前应该怎么做??跟买东西一个道理先检查一下有没有开封,有没有用过。它这也是

  1. 验证,如果是空的删除
  2. 转换 数据转成字符串 HawkConverter看一下源码
/*** Concrete implementation of encoding and decoding.* List types will be encoded/decoded by parser* Serializable types will be encoded/decoded object stream* Not serializable objects will be encoded/decoded by parser*/编码和解码的具体实现。列表类型将被解析器可序列化的编码/解码类型将编码/解码对象流可序列化的对象不会被解析器编码/解码;
final class HawkConverter implements Converter {private final Parser parser;public HawkConverter(Parser parser) {if (parser == null) {throw new NullPointerException("Parser should not be null");}this.parser = parser;}@Override public <T> String toString(T value) {if (value == null) {return null;}return parser.toJson(value);}@SuppressWarnings("unchecked")@Override public <T> T fromString(String value, DataInfo info) throws Exception {if (value == null) {return null;}HawkUtils.checkNull("data info", info);Class<?> keyType = info.keyClazz;Class<?> valueType = info.valueClazz;switch (info.dataType) {case DataInfo.TYPE_OBJECT:return toObject(value, keyType);case DataInfo.TYPE_LIST:return toList(value, keyType);case DataInfo.TYPE_MAP:return toMap(value, keyType, valueType);case DataInfo.TYPE_SET:return toSet(value, keyType);default:return null;}}private <T> T toObject(String json, Class<?> type) throws Exception {return parser.fromJson(json, type);}@SuppressWarnings("unchecked")private <T> T toList(String json, Class<?> type) throws Exception {if (type == null) {return (T) new ArrayList<>();}List<T> list = parser.fromJson(json,new TypeToken<List<T>>() {}.getType());int size = list.size();for (int i = 0; i < size; i++) {list.set(i, (T) parser.fromJson(parser.toJson(list.get(i)), type));}return (T) list;}@SuppressWarnings("unchecked")private <T> T toSet(String json, Class<?> type) throws Exception {Set<T> resultSet = new HashSet<>();if (type == null) {return (T) resultSet;}Set<T> set = parser.fromJson(json, new TypeToken<Set<T>>() {}.getType());for (T t : set) {String valueJson = parser.toJson(t);T value = parser.fromJson(valueJson, type);resultSet.add(value);}return (T) resultSet;}@SuppressWarnings("unchecked")private <K, V, T> T toMap(String json, Class<?> keyType, Class<?> valueType) throws Exception {Map<K, V> resultMap = new HashMap<>();if (keyType == null || valueType == null) {return (T) resultMap;}Map<K, V> map = parser.fromJson(json, new TypeToken<Map<K, V>>() {}.getType());for (Map.Entry<K, V> entry : map.entrySet()) {String keyJson = parser.toJson(entry.getKey());K k = parser.fromJson(keyJson, keyType);String valueJson = parser.toJson(entry.getValue());V v = parser.fromJson(valueJson, valueType);resultMap.put(k, v);}return (T) resultMap;}
  1. 加密 这里用到的是脸书的加密
    先是成是字节数组,然后进行Base64编码得到字符串数据。
       class ConcealEncryption implements Encryption {private final Crypto crypto;public ConcealEncryption(Context context) {SharedPrefsBackedKeyChain keyChain = new SharedPrefsBackedKeyChain(context, CryptoConfig.KEY_256);crypto = AndroidConceal.get().createDefaultCrypto(keyChain);}@Override public boolean init() {return crypto.isAvailable();}@Override public String encrypt(String key, String plainText) throws Exception {Entity entity = Entity.create(key);byte[] bytes = crypto.encrypt(plainText.getBytes(), entity);return Base64.encodeToString(bytes, Base64.NO_WRAP);}@Override public String decrypt(String key, String cipherText) throws Exception {Entity entity = Entity.create(key);byte[] decodedBytes = Base64.decode(cipherText, Base64.NO_WRAP);byte[] bytes = crypto.decrypt(decodedBytes, entity);return new String(bytes);}

4.序列化 HawkSerializer
运用反射获取原数据的数据类型。是List,map,set还是对象,根据不同类型保存不同数据。返回字符串类型的值,这个值是原始key的类型,原始数据的类型,数据类型和密文的拼接,这样就可以存储啦。

class HawkSerializer implements Serializer {private static final char DELIMITER = '@';private static final String INFO_DELIMITER = "#";private static final char NEW_VERSION = 'V';private final LogInterceptor logInterceptor;HawkSerializer(LogInterceptor logInterceptor) {this.logInterceptor = logInterceptor;}@Override public <T> String serialize(String cipherText, T originalGivenValue) {HawkUtils.checkNullOrEmpty("Cipher text", cipherText);HawkUtils.checkNull("Value", originalGivenValue);String keyClassName = "";String valueClassName = "";char dataType;if (List.class.isAssignableFrom(originalGivenValue.getClass())) {List<?> list = (List<?>) originalGivenValue;if (!list.isEmpty()) {keyClassName = list.get(0).getClass().getName();}dataType = DataInfo.TYPE_LIST;} else if (Map.class.isAssignableFrom(originalGivenValue.getClass())) {dataType = DataInfo.TYPE_MAP;Map<?, ?> map = (Map) originalGivenValue;if (!map.isEmpty()) {for (Map.Entry<?, ?> entry : map.entrySet()) {keyClassName = entry.getKey().getClass().getName();valueClassName = entry.getValue().getClass().getName();break;}}} else if (Set.class.isAssignableFrom(originalGivenValue.getClass())) {Set<?> set = (Set<?>) originalGivenValue;if (!set.isEmpty()) {Iterator<?> iterator = set.iterator();if (iterator.hasNext()) {keyClassName = iterator.next().getClass().getName();}}dataType = DataInfo.TYPE_SET;} else {dataType = DataInfo.TYPE_OBJECT;keyClassName = originalGivenValue.getClass().getName();}return keyClassName + INFO_DELIMITER +valueClassName + INFO_DELIMITER +dataType + NEW_VERSION + DELIMITER +cipherText;}@Override public DataInfo deserialize(String serializedText) {String[] infos = serializedText.split(INFO_DELIMITER);char type = infos[2].charAt(0);// if it is collection, no need to create the class objectClass<?> keyClazz = null;String firstElement = infos[0];if (firstElement != null && firstElement.length() != 0) {try {keyClazz = Class.forName(firstElement);} catch (ClassNotFoundException e) {logInterceptor.onLog("HawkSerializer -> " + e.getMessage());}}Class<?> valueClazz = null;String secondElement = infos[1];if (secondElement != null && secondElement.length() != 0) {try {valueClazz = Class.forName(secondElement);} catch (ClassNotFoundException e) {logInterceptor.onLog("HawkSerializer -> " + e.getMessage());}}

终于到最后一步了,那就是存数据了
Hawk给出的默认存储实现是SharedPreferenceStorage。从类名都可以看出,其实就是用SharedPreferences来存储数据。这里就不多说啦。毕竟SharedPreferences用法很简单。

final class SharedPreferencesStorage implements Storage {private final SharedPreferences preferences;SharedPreferencesStorage(Context context, String tag) {preferences = context.getSharedPreferences(tag, Context.MODE_PRIVATE);}SharedPreferencesStorage(SharedPreferences preferences) {this.preferences = preferences;}@Override public <T> boolean put(String key, T value) {HawkUtils.checkNull("key", key);return getEditor().putString(key, String.valueOf(value)).commit();}@SuppressWarnings("unchecked")@Override public <T> T get(String key) {return (T) preferences.getString(key, null);}@Override public boolean delete(String key) {return getEditor().remove(key).commit();}@Override public boolean contains(String key) {return preferences.contains(key);}@Override public boolean deleteAll() {return getEditor().clear().commit();}@Override public long count() {return preferences.getAll().size();}private SharedPreferences.Editor getEditor() {return preferences.edit();}}
  **存储数据put的源码就分析到这里,总结下其实就是开始那幅图显示的,把数据转换成字符串,加密,序列化,存储四个步骤搞定。获取数据get就是put过程反过来,没什么好说的了。至于其他的方法,delete(),deleteAll(),contains(),count()其实都是运用SharedPreferences在打交道。 Hawk存储的源码已经很清晰了。在分析中我们也提到了,有些实现是已经给出的默认实现,其实我们也可以根据需求来定义响应的接口实现。**
Hawk.init(context).setEncryption(new NoEncryption()).setLogInterceptor(new MyLogInterceptor()).setConverter(new MyConverter()).setParser(new MyParser()).setStorage(new MyStorage()).build();

HawkBuilder中还有一些API是供开发者自定义的

public HawkBuilder setStorage(Storage storage) {this.cryptoStorage = storage;return this;}public HawkBuilder setParser(Parser parser) {this.parser = parser;return this;}public HawkBuilder setSerializer(Serializer serializer) {this.serializer = serializer;return this;}public HawkBuilder setLogInterceptor(LogInterceptor logInterceptor) {this.logInterceptor = logInterceptor;return this;}public HawkBuilder setConverter(Converter converter) {this.converter = converter;return this;}public HawkBuilder setEncryption(Encryption encryption) {this.encryption = encryption;return this;}

看完代码 也正如Orhan Obut大神说的
Secure, simple key-value storage for android 安全、简单的Android存储工具
有什么写的不好的地方可以在评论区评论

Android Hawk数据库的源码解析,Github开源项目,基于SharedPreferences的的存储框架相关推荐

  1. Android Hawk的源码解析,一款基于SharedPreferences的存储框架

    转载请标注:http://blog.csdn.net/friendlychen/article/details/76218033 一.概念 SharedPreferences的使用大家应该非常熟悉啦. ...

  2. android handler2--消息队列源码解析

    android handler2–消息队列源码解析 1.Looper 对于Looper主要是prepare()和loop()两个方法. 首先看prepare()方法 public static fin ...

  3. Android上百实例源码分析以及开源分析集合打包

    感谢网友banketree的收集,压缩包的内容如下: 1.360新版特性界面源代码 实现了360新版特性界面的效果,主要涉及到Qt的一些事件处理与自定义控件.但源码好像是c++. 2.aidl跨进程调 ...

  4. Android手游 “2048” 源码解析

    转载请写明出处:http://blog.csdn.net/big_heart_c 下面所解析的源码是来自极客学院"Android 2048 "中的源码,读者可以从 https:// ...

  5. springfox源码_【开源项目】springfox-bridge:随心所欲地为非restful接口生成API文档...

    一.引言 目前,利用swagger框架为restful接口编写API文档非常流行,在spring web项目中,利用springfox+swagger更是可以通过注解的方式直接进行API文档的生成,这 ...

  6. Android EdgeEffect 使用 和 源码解析

    创建EdgeEffect private EdgeEffect leftEdgeEffect;private void init(Context context) {leftEdgeEffect = ...

  7. 技术干货 | 源码解析 Github 上 14.1k Star 的 RocketMQ

    简介: 站在发送方视角,通过源码,来分析在事务消息发送中 RocketMQ 是如何工作的. 前言 Apache RocketMQ 作为广为人知的开源消息中间件,诞生于阿里巴巴,于 2016 年捐赠给了 ...

  8. Android Handler消息机制源码解析

    好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个 ...

  9. Android微信抢红包插件源码解析

    这个Android插件可以帮助你在微信群聊抢红包时战无不胜.当检测到红包时,插件会自动点击屏幕,人工点击的速度无法比拟. 你正在查看的是dev分支,这个分支包含大量实验性的修改,不再更新.如果你希望有 ...

最新文章

  1. 如何用python批量处理图片大小_python批量修改图片大小的方法
  2. MyBatis 插件原理与自定义插件-代理和拦截是怎么实现的?
  3. 牛客题霸 [二叉树的之字形层序遍历] C++题解/答案
  4. Python 安装路径, dist-packages 和 site-packages 区别
  5. python hist2d_使用hist2d在matplotlib中创建一个对数线性图
  6. python(7)– 类的反射
  7. 深度学习推荐模型-DeepFM
  8. 【渝粤教育】21秋期末考试财政学10573k1
  9. NC文件按时序维度拆分
  10. echarts 饼状图展示位置 legend
  11. HSV颜色空间中颜色(红、黄、绿、 青、蓝、紫、 粉红、 砖红、 品红)对应的灰度范围
  12. python鸡兔同笼头35只_python_鸡兔同笼问题
  13. Microsoft Office2010每次打开都提示激活向导
  14. iptables中DNAT、SNAT和MASQUERADE的原理
  15. 13位知名科技公司CEO首份工作揭秘
  16. 如何清除系统垃圾 介绍几大方法【图解】
  17. 月入万元的SEO优化师:分享我几年接私单的经历
  18. 简述no less than和not less than区别
  19. 日本多城现共享单车 日网友:感受到中国式刺激
  20. 程序员为什么要写技术博客?都在哪些平台呢?

热门文章

  1. Tomcat 可以运行但localhost:8080打不开
  2. 客户端iOS 的自动化测试
  3. 洛谷P1710地铁涨价
  4. matlab将surfer格式转化矩阵,matlab调用surfer
  5. jquery弹出层,带朦胧层。
  6. linux卡住重启_linux死机解决办法
  7. 【单片机毕业设计】【mcuclub-jj-051】基于单片机的书桌的设计
  8. Ubuntu系统安装搜狗输入法
  9. php ci框架之创建mobel
  10. SEO必看的免费推广渠道