├── .gitattributes ├── .gitignore ├── README.md └── cachemanager ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── lujianchao │ └── cachemanager │ └── ExampleInstrumentedTest.java ├── main ├── AndroidManifest.xml ├── java │ └── com │ │ └── lujianchao │ │ └── cachemanager │ │ ├── BasicApp.java │ │ ├── CacheManager.java │ │ └── MainActivity.java └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── test └── java └── com └── lujianchao └── cachemanager └── ExampleUnitTest.java /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | 38 | # Keystore files 39 | *.jks 40 | 41 | # ========================= 42 | # Operating System Files 43 | # ========================= 44 | 45 | # OSX 46 | # ========================= 47 | 48 | .DS_Store 49 | .AppleDouble 50 | .LSOverride 51 | 52 | # Thumbnails 53 | ._* 54 | 55 | # Files that might appear in the root of a volume 56 | .DocumentRevisions-V100 57 | .fseventsd 58 | .Spotlight-V100 59 | .TemporaryItems 60 | .Trashes 61 | .VolumeIcon.icns 62 | 63 | # Directories potentially created on remote AFP share 64 | .AppleDB 65 | .AppleDesktop 66 | Network Trash Folder 67 | Temporary Items 68 | .apdisk 69 | 70 | # Windows 71 | # ========================= 72 | 73 | # Windows image file caches 74 | Thumbs.db 75 | ehthumbs.db 76 | 77 | # Folder config file 78 | Desktop.ini 79 | 80 | # Recycle Bin used on file shares 81 | $RECYCLE.BIN/ 82 | 83 | # Windows Installer files 84 | *.cab 85 | *.msi 86 | *.msm 87 | *.msp 88 | 89 | # Windows shortcuts 90 | *.lnk 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Android 简单实现缓存机制(反射和数据库) 2 | 3 | ####  现在的app每个页面都有从服务器后台拿的数据,数据基本是以文本形式返回的,大多数是json格式(json是一种文本形式的文本,跟xml一样,赋予了一定意义),很多app的首页或者朋友圈等类似的功能是没必要总向后台请求的,我们可以把每次请求的数据存起来,下次先拿缓存,有新数据了再追加或替换。 4 | 5 | QQ:1264957104 6 | 7 | Web:http://lujianchao.com 8 | 9 | GitHub:https://github.com/hnsugar 10 | 11 | CSDN:http://blog.csdn.net/hnsugar 12 | 13 | ##一.首先大家会问,为什么要用缓存? 14 | 15 |   如果断网了,app页面还有数据,提示断网,是不是显示不单调了,体验会好不少。有的app断网了就是大白板。。。。说句心里话,我看见大白板的app就想卸载了。 16 | 17 | ##二.原理 18 |    19 | 20 | 简述原理:创建一个表,表结构包括key、value和lasttime等,分别存储键值和保存时间,其他的都是备用,系统的SQLiteOpenHelper有onCreat和onUpgrade方法,触发这两个方法后调用检查数据方法,用反射得到HistoryCache.class的所有属性,遍历属性集合查看是否有key为相应属性的数据,有就不管,没有就添加,数据库表里数据多出的数据被删除,这就是用反射根据类属性动态控制表数据了(不敢说表结构)。剩下的就是update数据和取数据了。 21 | ##三.为什么不动态改变表结构? 22 | 23 | 24 |   仔细看下图,表结构是固定的,有多少条数据也是固定的,根据HistoryCache类决定,但是,数据库增删数据快还是更改表结构快?当然是数据啦,每条数据就是一条缓存,而且,同一个缓存只有一个,只能被更新。如果在app运行时可以随意添加key,那。。。。再次拿缓存就麻烦了,数据库会越来越大,我觉得一条数据代表特定意义的缓存是合理的。 25 | 重要的是,结构有多少条是开发时就订好了,缓存哪里的数据是一一对应的,改不了,这样不会出现脏数据。 26 |    27 | 28 | 基于这种想法,缓存只有获取get方法和更新update方法两个。 29 |    30 | 31 | 说明一下:截图内容是用的https://github.com/amitshekhariitbhu/Android-Debug-Database 这个库可以直接访问局域网手机的数据库,调试时很方便的。推荐 32 | 33 | ![这里写图片描述](http://img.blog.csdn.net/20170314153438086?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaG5zdWdhcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 34 | 35 | ##四.开始写代码 36 | 37 | ###1.创建HistoryCache类 38 | 39 | /** 40 | * Created by lujianchao on 2016/11/29. 41 | * 数据结构 42 | * 添加或者删除属性变量值,都必须更改数据库版本号,否则不会修改 43 | * 44 | * @author lujianchao 45 | */ 46 | 47 | public static class HistoryCache { 48 | /** 49 | * 首页 50 | */ 51 | public static String IndexPage = "IndexPage"; 52 | /** 53 | * 朋友圈 54 | */ 55 | public static String FriendLife = "FriendLife"; 56 | /** 57 | * 首页Banner图片 58 | */ 59 | public static String BannerList = "BannerList"; 60 | } 61 | 62 | ###2.写SQLiteOpenHelper 63 | sql语句 64 | 65 |   private static final String CREATE_CacheTABLE = "create table historycache (id integer primary key autoincrement, key text, value text, lasttime long, bak text, flag text)"; 66 | 67 | 提示,我定义的是传入的类的名字和表名是同一个,自定义的dbhelper内封装了根据类属性动态改变数据结构的方法,只在数据库升级时执行。 68 | ``` 69 | /** 70 | * Created by lujianchao on 2016/11/29. 71 | * SQLiteOpenHelper 72 | * 73 | * @author lujianchao 74 | */ 75 | public static class DBHelper extends SQLiteOpenHelper { 76 | 77 | private static final String CREATE_CacheTABLE = "create table historycache (id integer primary key autoincrement, key text, value text, lasttime long, bak text, flag text)"; 78 | public DBHelper (final Context context, final String name, final SQLiteDatabase.CursorFactory factory, final int version) { 79 | super (context, name, factory, version); 80 | } 81 | 82 | @Override 83 | public void onCreate (final SQLiteDatabase db) { 84 | db.execSQL (CREATE_CacheTABLE); 85 | updatetable (db, HistoryCache.class); 86 | } 87 | 88 | @Override 89 | public void onUpgrade (final SQLiteDatabase db, final int oldVersion, final int newVersion) { 90 | updatetable (db, HistoryCache.class); 91 | } 92 | 93 | /** 94 | * 传入的类名即为表名,传入的类的属性即为表内的记录,字段固定,用来实现动态增减记录,记录为缓存内容,所以数量较少, 95 | * 只需要更改实体类属性,就可以管理数据库了,动态升级。 96 | * 97 | * @param db 98 | * @param mClass 99 | */ 100 | private void updatetable (final SQLiteDatabase db, Class mClass) { 101 | /** 102 | * 通过反射拿到当前所有cache名 103 | */ 104 | List mList = new ArrayList<> (); 105 | Field[] fields = mClass.getDeclaredFields (); 106 | for (Field fd : fields) { 107 | fd.setAccessible (true); 108 | mList.add (fd.getName ()); 109 | } 110 | 111 | Cursor mCursor = db.rawQuery ("select * from " + mClass.getSimpleName (), null); 112 | while (mCursor.moveToNext ()) { 113 | boolean ishave = false; 114 | String string = mCursor.getString (1); 115 | Iterator mStringIterator = mList.iterator (); 116 | while (mStringIterator.hasNext ()) { 117 | if (mStringIterator.next ().equals (string)) { 118 | ishave = true; 119 | mStringIterator.remove (); 120 | break; 121 | } 122 | } 123 | /** 124 | * 类里没有这个缓存名就将其删掉 125 | */ 126 | if (!ishave) { 127 | db.delete (mClass.getSimpleName (), "key=?", new String[]{string}); 128 | } 129 | } 130 | mCursor.close (); 131 | for (int mI = 0; mI < mList.size (); mI++) { 132 | ContentValues values = new ContentValues (); 133 | values.put ("key", mList.get (mI)); 134 | values.put ("lasttime", System.currentTimeMillis ()); 135 | db.insert (mClass.getSimpleName (), null, values); 136 | } 137 | } 138 | } 139 | ``` 140 | 141 | ###3.写CacheManager管理类 142 | 143 |   根据之前的分析,我们只需要updateCache和getCache方法,还有就是deleteDatabase。 144 |    145 |   **1.写updateCache()方法:** 146 |    147 |   根据需求,需要根据key更新value值,所以方法如下: 148 | 149 | ``` 150 | /** 151 | * 更新缓存 152 | * 153 | * @param key 预定义名称 154 | * @param value 待缓存数据 155 | */ 156 | public synchronized static void updateCache (String key, String value) { 157 | updateCache (new CacheEntity ().setKey (key).setValue (value)); 158 | } 159 | ``` 160 | 一直点下去调用方法是不是很好用呢,我的android tips分类里会介绍如何自定义模板。 161 | 162 | 163 | ``` 164 | /** 165 | * 更新缓存 166 | * 不能手动更新id、key和lasttime 167 | * 168 | * @param mCacheEntity 169 | */ 170 | public synchronized static void updateCache (CacheEntity mCacheEntity) { 171 | if (mCacheDBHelper == null) { 172 | mCacheDBHelper = new DBHelper (mContext, DBName, null, DBVersion); 173 | } 174 | if (mSQLiteDatabase == null) { 175 | mSQLiteDatabase = mCacheDBHelper.getWritableDatabase (); 176 | } 177 | ContentValues m = new ContentValues (); 178 | m.put ("value", mCacheEntity.value); 179 | m.put ("lasttime", System.currentTimeMillis ()); 180 | m.put ("bak", mCacheEntity.bak); 181 | m.put ("flag", mCacheEntity.flag); 182 | try { 183 | mSQLiteDatabase.update (HistoryCache.class.getSimpleName (), m, "key=?", new String[]{mCacheEntity.key}); 184 | } catch (Exception mE) { 185 | mE.printStackTrace (); 186 | } 187 | } 188 | ``` 189 | 190 | 不能手动更新id、key和lasttime,这些是自动的,可以手动设置就出问题了,道理大家都懂的<( ̄︶ ̄)> 191 | 192 | 忘了介绍CacheEntity这个类了,代码如下: 193 | 194 | 195 | ``` 196 | public static class CacheEntity { 197 | private String value=""; 198 | private String key=""; 199 | private int id; 200 | private long lasttime; 201 | private String bak=""; 202 | private String flag=""; 203 | 204 | public String getValue () { 205 | return value; 206 | } 207 | 208 | public CacheEntity setValue (final String mValue) { 209 | value = mValue; 210 | return this; 211 | } 212 | 213 | public String getKey () { 214 | return key; 215 | } 216 | 217 | public CacheEntity setKey (final String mKey) { 218 | key = mKey; 219 | return this; 220 | } 221 | 222 | public int getId () { 223 | return id; 224 | } 225 | 226 | public CacheEntity setId (final int mId) { 227 | id = mId; 228 | return this; 229 | } 230 | 231 | public long getLasttime () { 232 | return lasttime; 233 | } 234 | 235 | public CacheEntity setLasttime (final long mLasttime) { 236 | lasttime = mLasttime; 237 | return this; 238 | } 239 | 240 | public String getBak () { 241 | return bak; 242 | } 243 | 244 | public CacheEntity setBak (final String mBak) { 245 | bak = mBak; 246 | return this; 247 | } 248 | 249 | public String getFlag () { 250 | return flag; 251 | } 252 | 253 | public CacheEntity setFlag (final String mFlag) { 254 | flag = mFlag; 255 | return this; 256 | } 257 | } 258 | ``` 259 | 至此更新方法就写完了。为什么属性要初始化?总不能让数据库存null吧! 260 | 261 | **2.写getCache()方法:** 262 | 直接上代码了: 263 | 264 | ``` 265 | /** 266 | * 获取缓存数据 267 | * 268 | * @param key 预定义名称 269 | * @return 缓存数据,异常或者不存在则返回null 270 | */ 271 | public static CacheEntity getCache (String key) { 272 | CacheEntity mCacheEntity = new CacheEntity (); 273 | if (mCacheDBHelper == null) { 274 | mCacheDBHelper = new DBHelper (mContext, DBName, null, DBVersion); 275 | } 276 | if (mSQLiteDatabase == null) { 277 | mSQLiteDatabase = mCacheDBHelper.getWritableDatabase (); 278 | } 279 | Cursor mCursor = null; 280 | try { 281 | mCursor = mSQLiteDatabase.rawQuery ("select * from " + HistoryCache.class.getSimpleName () + " where key=?", new String[]{key}); 282 | if (mCursor != null && mCursor.getCount () == 1) { 283 | mCursor.moveToNext (); 284 | mCacheEntity.id = mCursor.getInt (0); 285 | mCacheEntity.key = mCursor.getString (1); 286 | mCacheEntity.value = mCursor.getString (2); 287 | mCacheEntity.lasttime = mCursor.getLong (3); 288 | mCacheEntity.bak = mCursor.getString (4); 289 | mCacheEntity.flag = mCursor.getString (5); 290 | 291 | } 292 | } catch (Exception mE) { 293 | mE.printStackTrace (); 294 | } finally { 295 | if (mCursor != null) { 296 | mCursor.close (); 297 | } 298 | return mCacheEntity; 299 | } 300 | } 301 | ``` 302 | #####**3.删除数据库** 303 | 304 | /** 305 | * 删除数据库 306 | */ 307 | public synchronized static void deleteDB () { 308 | mContext.deleteDatabase (DBName); 309 | } 310 | 311 | ###五.调用总结 312 | 313 | public void test(View mView){ 314 | CacheManager.updateCache (CacheManager.HistoryCache.BannerList,"{\"name\":\"张三\",\"age\":34}"); 315 | Toast.makeText (this,CacheManager.getCache (CacheManager.HistoryCache.BannerList).getValue (),Toast.LENGTH_LONG).show (); 316 | } 317 | 318 | OK,测试成功,不截图了,还有就是动态更新表数据结构如图。ok,更改成功,至于$change和serialVersionUID是Object类的,我就不过滤对的那么仔细了,无伤大雅︿( ̄︶ ̄)︿。 319 | ![这里写图片描述](http://img.blog.csdn.net/20170314163843023?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaG5zdWdhcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 320 | ![这里写图片描述](http://img.blog.csdn.net/20170314163859288?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaG5zdWdhcg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 321 | ####**小尾巴** 322 | 个人在倒腾自己的网站:http://lujianchao.com 风格或者数据什么的偶尔出问题,那是我在调试。。。。 323 | GitHub:https://github.com/hnsugar 324 | CSDN:http://blog.csdn.net/hnsugar 325 | 项目地址:我自己的Git服务器 http://lujianchao.com:8081/gitblit/summary/AndroidTips!AndroidCacheManager.git 326 | -------------------------------------------------------------------------------- /cachemanager/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /cachemanager/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.lujianchao.cachemanager" 9 | minSdkVersion 19 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | debug { 23 | resValue("string", "PORT_NUMBER", "8081") 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | compile fileTree(dir: 'libs', include: ['*.jar']) 30 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 31 | exclude group: 'com.android.support', module: 'support-annotations' 32 | }) 33 | compile 'com.android.support:appcompat-v7:25.2.0' 34 | debugCompile 'com.amitshekhar.android:debug-db:1.0.0' 35 | } 36 | -------------------------------------------------------------------------------- /cachemanager/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /cachemanager/src/androidTest/java/com/lujianchao/cachemanager/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.cachemanager; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith (AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext () throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext (); 23 | 24 | assertEquals ("com.lujianchao.cachemanager", appContext.getPackageName ()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cachemanager/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /cachemanager/src/main/java/com/lujianchao/cachemanager/BasicApp.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.cachemanager; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Created by hnvfh on 2017/3/14. 7 | */ 8 | 9 | public class BasicApp extends Application { 10 | 11 | @Override 12 | public void onCreate () { 13 | super.onCreate (); 14 | CacheManager.init (this); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cachemanager/src/main/java/com/lujianchao/cachemanager/CacheManager.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.cachemanager; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.database.sqlite.SQLiteOpenHelper; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.ArrayList; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by lujianchao on 2016/11/29. 16 | * 17 | * @author lujianchao 18 | */ 19 | 20 | public class CacheManager { 21 | 22 | 23 | 24 | public static final String DBName = "appinfo.db"; 25 | /** 26 | * 更改类文件必须更改版本号,否则不会更新缓存结构 27 | */ 28 | public static final int DBVersion = 1; 29 | private static DBHelper mCacheDBHelper; 30 | private static SQLiteDatabase mSQLiteDatabase; 31 | private static Context mContext; 32 | 33 | public static void init (Context mContext1) { 34 | mContext = mContext1; 35 | 36 | } 37 | 38 | /** 39 | * 删除数据库 40 | */ 41 | public synchronized static void deleteDB () { 42 | mContext.deleteDatabase (DBName); 43 | } 44 | 45 | /** 46 | * 更新缓存 47 | * 48 | * @param key 预定义名称 49 | * @param value 待缓存数据 50 | */ 51 | public synchronized static void updateCache (String key, String value) { 52 | updateCache (new CacheEntity ().setKey (key).setValue (value)); 53 | } 54 | 55 | /** 56 | * 更新缓存 57 | * 不能手动更新id、key和lasttime 58 | * 59 | * @param mCacheEntity 60 | */ 61 | public synchronized static void updateCache (CacheEntity mCacheEntity) { 62 | if (mCacheDBHelper == null) { 63 | mCacheDBHelper = new DBHelper (mContext, DBName, null, DBVersion); 64 | } 65 | if (mSQLiteDatabase == null) { 66 | mSQLiteDatabase = mCacheDBHelper.getWritableDatabase (); 67 | } 68 | ContentValues m = new ContentValues (); 69 | m.put ("value", mCacheEntity.value); 70 | m.put ("lasttime", System.currentTimeMillis ()); 71 | m.put ("bak", mCacheEntity.bak); 72 | m.put ("flag", mCacheEntity.flag); 73 | try { 74 | mSQLiteDatabase.update (HistoryCache.class.getSimpleName (), m, "key=?", new String[]{mCacheEntity.key}); 75 | } catch (Exception mE) { 76 | mE.printStackTrace (); 77 | } 78 | } 79 | 80 | /** 81 | * 获取缓存数据 82 | * 83 | * @param key 预定义名称 84 | * @return 缓存数据,异常或者不存在则返回null 85 | */ 86 | public static CacheEntity getCache (String key) { 87 | CacheEntity mCacheEntity = new CacheEntity (); 88 | if (mCacheDBHelper == null) { 89 | mCacheDBHelper = new DBHelper (mContext, DBName, null, DBVersion); 90 | } 91 | if (mSQLiteDatabase == null) { 92 | mSQLiteDatabase = mCacheDBHelper.getWritableDatabase (); 93 | } 94 | Cursor mCursor = null; 95 | try { 96 | mCursor = mSQLiteDatabase.rawQuery ("select * from " + HistoryCache.class.getSimpleName () + " where key=?", new String[]{key}); 97 | if (mCursor != null && mCursor.getCount () == 1) { 98 | mCursor.moveToNext (); 99 | mCacheEntity.id = mCursor.getInt (0); 100 | mCacheEntity.key = mCursor.getString (1); 101 | mCacheEntity.value = mCursor.getString (2); 102 | mCacheEntity.lasttime = mCursor.getLong (3); 103 | mCacheEntity.bak = mCursor.getString (4); 104 | mCacheEntity.flag = mCursor.getString (5); 105 | 106 | } 107 | } catch (Exception mE) { 108 | mE.printStackTrace (); 109 | } finally { 110 | if (mCursor != null) { 111 | mCursor.close (); 112 | } 113 | return mCacheEntity; 114 | } 115 | } 116 | 117 | /** 118 | * 数据库表结构 119 | */ 120 | public static class CacheEntity { 121 | private String value=""; 122 | private String key=""; 123 | private int id; 124 | private long lasttime; 125 | private String bak=""; 126 | private String flag=""; 127 | 128 | public String getValue () { 129 | return value; 130 | } 131 | 132 | public CacheEntity setValue (final String mValue) { 133 | value = mValue; 134 | return this; 135 | } 136 | 137 | public String getKey () { 138 | return key; 139 | } 140 | 141 | public CacheEntity setKey (final String mKey) { 142 | key = mKey; 143 | return this; 144 | } 145 | 146 | public int getId () { 147 | return id; 148 | } 149 | 150 | public CacheEntity setId (final int mId) { 151 | id = mId; 152 | return this; 153 | } 154 | 155 | public long getLasttime () { 156 | return lasttime; 157 | } 158 | 159 | public CacheEntity setLasttime (final long mLasttime) { 160 | lasttime = mLasttime; 161 | return this; 162 | } 163 | 164 | public String getBak () { 165 | return bak; 166 | } 167 | 168 | public CacheEntity setBak (final String mBak) { 169 | bak = mBak; 170 | return this; 171 | } 172 | 173 | public String getFlag () { 174 | return flag; 175 | } 176 | 177 | public CacheEntity setFlag (final String mFlag) { 178 | flag = mFlag; 179 | return this; 180 | } 181 | } 182 | 183 | /** 184 | * Created by lujianchao on 2016/11/29. 185 | * SQLiteOpenHelper 186 | * 187 | * @author lujianchao 188 | */ 189 | public static class DBHelper extends SQLiteOpenHelper { 190 | private static final String CREATE_CacheTABLE = "create table historycache (id integer primary key autoincrement, key text, value text, lasttime long, bak text, flag text)"; 191 | 192 | public DBHelper (final Context context, final String name, final SQLiteDatabase.CursorFactory factory, final int version) { 193 | super (context, name, factory, version); 194 | } 195 | 196 | @Override 197 | public void onCreate (final SQLiteDatabase db) { 198 | db.execSQL (CREATE_CacheTABLE); 199 | updatetable (db, HistoryCache.class); 200 | } 201 | 202 | @Override 203 | public void onUpgrade (final SQLiteDatabase db, final int oldVersion, final int newVersion) { 204 | updatetable (db, HistoryCache.class); 205 | } 206 | 207 | /** 208 | * 传入的类名即为表名,传入的类的属性即为表内的记录,字段固定,用来实现动态增减记录,记录为缓存内容,所以数量较少, 209 | * 只需要更改实体类属性,就可以管理数据库了,动态升级。 210 | * 211 | * @param db 212 | * @param mClass 213 | */ 214 | private void updatetable (final SQLiteDatabase db, Class mClass) { 215 | /** 216 | * 通过反射拿到当前所有cache名 217 | */ 218 | List mList = new ArrayList<> (); 219 | Field[] fields = mClass.getDeclaredFields (); 220 | for (Field fd : fields) { 221 | fd.setAccessible (true); 222 | mList.add (fd.getName ()); 223 | } 224 | 225 | Cursor mCursor = db.rawQuery ("select * from " + mClass.getSimpleName (), null); 226 | while (mCursor.moveToNext ()) { 227 | boolean ishave = false; 228 | String string = mCursor.getString (1); 229 | Iterator mStringIterator = mList.iterator (); 230 | while (mStringIterator.hasNext ()) { 231 | if (mStringIterator.next ().equals (string)) { 232 | ishave = true; 233 | mStringIterator.remove (); 234 | break; 235 | } 236 | } 237 | /** 238 | * 类里没有这个缓存名就将其删掉 239 | */ 240 | if (!ishave) { 241 | db.delete (mClass.getSimpleName (), "key=?", new String[]{string}); 242 | } 243 | } 244 | mCursor.close (); 245 | for (int mI = 0; mI < mList.size (); mI++) { 246 | ContentValues values = new ContentValues (); 247 | values.put ("key", mList.get (mI)); 248 | values.put ("lasttime", System.currentTimeMillis ()); 249 | db.insert (mClass.getSimpleName (), null, values); 250 | } 251 | } 252 | } 253 | 254 | /** 255 | * Created by lujianchao on 2016/11/29. 256 | * 数据结构 257 | * 添加或者删除属性变量值,都必须更改数据库版本号,否则不会修改 258 | * 259 | * @author lujianchao 260 | */ 261 | 262 | public static class HistoryCache { 263 | /** 264 | * 首页 265 | */ 266 | public static String IndexPage = "IndexPage"; 267 | /** 268 | * 朋友圈 269 | */ 270 | public static String FriendLife = "FriendLife"; 271 | /** 272 | * 首页Banner图片 273 | */ 274 | public static String BannerList = "BannerList"; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /cachemanager/src/main/java/com/lujianchao/cachemanager/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lujianchao.cachemanager; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.Toast; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate (Bundle savedInstanceState) { 12 | super.onCreate (savedInstanceState); 13 | setContentView (R.layout.activity_main); 14 | CacheManager.updateCache (CacheManager.HistoryCache.BannerList,"{\"name\":\"张三\",\"age\":34}"); 15 | } 16 | public void test(View mView){ 17 | CacheManager.updateCache (CacheManager.HistoryCache.BannerList,"{\"name\":\"张三\",\"age\":34}"); 18 | Toast.makeText (this,CacheManager.getCache (CacheManager.HistoryCache.BannerList).getValue (),Toast.LENGTH_LONG).show (); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cachemanager/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 |