├── .gitignore ├── AndroidDataStorage.iml ├── LICENSE.txt ├── README-ZH-CN.md ├── README.md ├── android-data-storage ├── .gitignore ├── android-data-storage.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── xiaofei │ │ └── library │ │ └── datastorage │ │ ├── ApplicationTest.java │ │ ├── Test01.java │ │ └── database │ │ ├── DbCacheConcurrency2Test.java │ │ ├── DbCacheConcurrencyTest.java │ │ ├── DbCacheExtremelyLongObjectTest.java │ │ └── DbCacheTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── xiaofei │ │ │ └── library │ │ │ └── datastorage │ │ │ ├── DataStorageFactory.java │ │ │ ├── IDataStorage.java │ │ │ ├── annotation │ │ │ ├── AnnotationProcessor.java │ │ │ ├── ClassId.java │ │ │ ├── FieldMember.java │ │ │ ├── Member.java │ │ │ ├── MethodMember.java │ │ │ └── ObjectId.java │ │ │ ├── database │ │ │ ├── DatabaseStorage.java │ │ │ ├── DbCache.java │ │ │ ├── DbConfig.java │ │ │ ├── DbConst.java │ │ │ ├── DbOpenHelper.java │ │ │ ├── DbService.java │ │ │ ├── DefaultCoderHook.java │ │ │ ├── GsonObjectCoder.java │ │ │ ├── GzipCoderHook.java │ │ │ ├── ICoderHook.java │ │ │ ├── IDbOperation.java │ │ │ └── SerialObjectCoder.java │ │ │ └── util │ │ │ ├── CodeUtils.java │ │ │ ├── Condition.java │ │ │ └── GzipUtils.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── xiaofei │ └── library │ └── datastorage │ └── ExampleUnitTest.java ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── xiaofei │ │ └── library │ │ └── datastoragetest │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── xiaofei │ │ │ └── library │ │ │ └── datastoragetest │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── xiaofei │ └── library │ └── datastoragetest │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /AndroidDataStorage.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015-2016 Xiaofei 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | 16 | Apache License 17 | Version 2.0, January 2004 18 | http://www.apache.org/licenses/ 19 | 20 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 21 | 22 | 1. Definitions. 23 | 24 | "License" shall mean the terms and conditions for use, reproduction, 25 | and distribution as defined by Sections 1 through 9 of this document. 26 | 27 | "Licensor" shall mean the copyright owner or entity authorized by 28 | the copyright owner that is granting the License. 29 | 30 | "Legal Entity" shall mean the union of the acting entity and all 31 | other entities that control, are controlled by, or are under common 32 | control with that entity. For the purposes of this definition, 33 | "control" means (i) the power, direct or indirect, to cause the 34 | direction or management of such entity, whether by contract or 35 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 36 | outstanding shares, or (iii) beneficial ownership of such entity. 37 | 38 | "You" (or "Your") shall mean an individual or Legal Entity 39 | exercising permissions granted by this License. 40 | 41 | "Source" form shall mean the preferred form for making modifications, 42 | including but not limited to software source code, documentation 43 | source, and configuration files. 44 | 45 | "Object" form shall mean any form resulting from mechanical 46 | transformation or translation of a Source form, including but 47 | not limited to compiled object code, generated documentation, 48 | and conversions to other media types. 49 | 50 | "Work" shall mean the work of authorship, whether in Source or 51 | Object form, made available under the License, as indicated by a 52 | copyright notice that is included in or attached to the work 53 | (an example is provided in the Appendix below). 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object 56 | form, that is based on (or derived from) the Work and for which the 57 | editorial revisions, annotations, elaborations, or other modifications 58 | represent, as a whole, an original work of authorship. For the purposes 59 | of this License, Derivative Works shall not include works that remain 60 | separable from, or merely link (or bind by name) to the interfaces of, 61 | the Work and Derivative Works thereof. 62 | 63 | "Contribution" shall mean any work of authorship, including 64 | the original version of the Work and any modifications or additions 65 | to that Work or Derivative Works thereof, that is intentionally 66 | submitted to Licensor for inclusion in the Work by the copyright owner 67 | or by an individual or Legal Entity authorized to submit on behalf of 68 | the copyright owner. For the purposes of this definition, "submitted" 69 | means any form of electronic, verbal, or written communication sent 70 | to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, 72 | and issue tracking systems that are managed by, or on behalf of, the 73 | Licensor for the purpose of discussing and improving the Work, but 74 | excluding communication that is conspicuously marked or otherwise 75 | designated in writing by the copyright owner as "Not a Contribution." 76 | 77 | "Contributor" shall mean Licensor and any individual or Legal Entity 78 | on behalf of whom a Contribution has been received by Licensor and 79 | subsequently incorporated within the Work. 80 | 81 | 2. Grant of Copyright License. Subject to the terms and conditions of 82 | this License, each Contributor hereby grants to You a perpetual, 83 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 84 | copyright license to reproduce, prepare Derivative Works of, 85 | publicly display, publicly perform, sublicense, and distribute the 86 | Work and such Derivative Works in Source or Object form. 87 | 88 | 3. Grant of Patent License. Subject to the terms and conditions of 89 | this License, each Contributor hereby grants to You a perpetual, 90 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 91 | (except as stated in this section) patent license to make, have made, 92 | use, offer to sell, sell, import, and otherwise transfer the Work, 93 | where such license applies only to those patent claims licensable 94 | by such Contributor that are necessarily infringed by their 95 | Contribution(s) alone or by combination of their Contribution(s) 96 | with the Work to which such Contribution(s) was submitted. If You 97 | institute patent litigation against any entity (including a 98 | cross-claim or counterclaim in a lawsuit) alleging that the Work 99 | or a Contribution incorporated within the Work constitutes direct 100 | or contributory patent infringement, then any patent licenses 101 | granted to You under this License for that Work shall terminate 102 | as of the date such litigation is filed. 103 | 104 | 4. Redistribution. You may reproduce and distribute copies of the 105 | Work or Derivative Works thereof in any medium, with or without 106 | modifications, and in Source or Object form, provided that You 107 | meet the following conditions: 108 | 109 | (a) You must give any other recipients of the Work or 110 | Derivative Works a copy of this License; and 111 | 112 | (b) You must cause any modified files to carry prominent notices 113 | stating that You changed the files; and 114 | 115 | (c) You must retain, in the Source form of any Derivative Works 116 | that You distribute, all copyright, patent, trademark, and 117 | attribution notices from the Source form of the Work, 118 | excluding those notices that do not pertain to any part of 119 | the Derivative Works; and 120 | 121 | (d) If the Work includes a "NOTICE" text file as part of its 122 | distribution, then any Derivative Works that You distribute must 123 | include a readable copy of the attribution notices contained 124 | within such NOTICE file, excluding those notices that do not 125 | pertain to any part of the Derivative Works, in at least one 126 | of the following places: within a NOTICE text file distributed 127 | as part of the Derivative Works; within the Source form or 128 | documentation, if provided along with the Derivative Works; or, 129 | within a display generated by the Derivative Works, if and 130 | wherever such third-party notices normally appear. The contents 131 | of the NOTICE file are for informational purposes only and 132 | do not modify the License. You may add Your own attribution 133 | notices within Derivative Works that You distribute, alongside 134 | or as an addendum to the NOTICE text from the Work, provided 135 | that such additional attribution notices cannot be construed 136 | as modifying the License. 137 | 138 | You may add Your own copyright statement to Your modifications and 139 | may provide additional or different license terms and conditions 140 | for use, reproduction, or distribution of Your modifications, or 141 | for any such Derivative Works as a whole, provided Your use, 142 | reproduction, and distribution of the Work otherwise complies with 143 | the conditions stated in this License. 144 | 145 | 5. Submission of Contributions. Unless You explicitly state otherwise, 146 | any Contribution intentionally submitted for inclusion in the Work 147 | by You to the Licensor shall be under the terms and conditions of 148 | this License, without any additional terms or conditions. 149 | Notwithstanding the above, nothing herein shall supersede or modify 150 | the terms of any separate license agreement you may have executed 151 | with Licensor regarding such Contributions. 152 | 153 | 6. Trademarks. This License does not grant permission to use the trade 154 | names, trademarks, service marks, or product names of the Licensor, 155 | except as required for reasonable and customary use in describing the 156 | origin of the Work and reproducing the content of the NOTICE file. 157 | 158 | 7. Disclaimer of Warranty. Unless required by applicable law or 159 | agreed to in writing, Licensor provides the Work (and each 160 | Contributor provides its Contributions) on an "AS IS" BASIS, 161 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 162 | implied, including, without limitation, any warranties or conditions 163 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 164 | PARTICULAR PURPOSE. You are solely responsible for determining the 165 | appropriateness of using or redistributing the Work and assume any 166 | risks associated with Your exercise of permissions under this License. 167 | 168 | 8. Limitation of Liability. In no event and under no legal theory, 169 | whether in tort (including negligence), contract, or otherwise, 170 | unless required by applicable law (such as deliberate and grossly 171 | negligent acts) or agreed to in writing, shall any Contributor be 172 | liable to You for damages, including any direct, indirect, special, 173 | incidental, or consequential damages of any character arising as a 174 | result of this License or out of the use or inability to use the 175 | Work (including but not limited to damages for loss of goodwill, 176 | work stoppage, computer failure or malfunction, or any and all 177 | other commercial damages or losses), even if such Contributor 178 | has been advised of the possibility of such damages. 179 | 180 | 9. Accepting Warranty or Additional Liability. While redistributing 181 | the Work or Derivative Works thereof, You may choose to offer, 182 | and charge a fee for, acceptance of support, warranty, indemnity, 183 | or other liability obligations and/or rights consistent with this 184 | License. However, in accepting such obligations, You may act only 185 | on Your own behalf and on Your sole responsibility, not on behalf 186 | of any other Contributor, and only if You agree to indemnify, 187 | defend, and hold each Contributor harmless for any liability 188 | incurred by, or claims asserted against, such Contributor by reason 189 | of your accepting any such warranty or additional liability. 190 | 191 | END OF TERMS AND CONDITIONS 192 | -------------------------------------------------------------------------------- /README-ZH-CN.md: -------------------------------------------------------------------------------- 1 | # AndroidDataStorage 2 | 3 | 一个简洁易用并且具有高性能的Android存储库 4 | 5 | ##特色 6 | 7 | 1、实现安卓设备上的对象持久化,能存储和读取任何对象。 8 | 9 | 2、读写速度比SharedPreference等基于文件的存储方案高。 10 | 11 | 3、大量对象读写操作的场景下,使用此框架能极大提升性能,比通常的数据库方案高效。 12 | 13 | 4、上层使用缓存,读写数据快速高效。底层使用数据库,在进程被终止并重新启动后能快速恢复对象。 14 | 15 | 5、接口简单易用,用户无需了解安卓存储机制。 16 | 17 | 6、经过严格测试,有良好的容错能力和稳定性。 18 | 19 | ##Gradle 20 | 21 | ``` 22 | dependencies { 23 | compile 'xiaofei.library:android-data-storage:1.3.0' 24 | } 25 | ``` 26 | 27 | ##Maven 28 | 29 | ``` 30 | 31 | xiaofei.library 32 | android-data-storage 33 | 1.3.0 34 | pom 35 | 36 | ``` 37 | 38 | ##用法 39 | 40 | ###1. 获取接口 41 | 42 | 获取IDataStorage接口,目前只支持数据库类型。 43 | ``` 44 | IDataStorage dataStorage = DataStorageFactory.getInstance( 45 | getApplicationContext(), 46 | DataStorageFactory.TYPE_DATABASE); 47 | ``` 48 | ###2、存储原理 49 | 50 | 存储数据的时候,索引是类id和对象id。如果这两个id相同,则老数据被覆盖。读取数据的时候也需要提供这两个id。 51 | 52 | ###3、类id 53 | 54 | 给需要存储的类加上ClassId注解,里面写上类id。需要保证不同的类有不同的id。 55 | 56 | 如果不同版本的代码在混淆后,能保证该类的类名不变,那就不需要加注解。框架将类的包名和类名作为类id。 57 | 58 | ###4、对象id 59 | 60 | 如果类的某个字段就是对象id,那在那个字段之前加上ObjectId注解。该字段必须是String。框架将这个字段作为对象id。 61 | 62 | 如果没有这个字段,那读写数据的时候需要提供对象id作为函数的参数。 63 | 64 | 以下给出一个例子: 65 | 66 | ``` 67 | @ClassId("Order") 68 | public class Order { 69 | @ObjectId 70 | private String mId; 71 | 72 | private int mState; 73 | 74 | public int getState() { 75 | return mState; 76 | } 77 | ... 78 | ... 79 | } 80 | ``` 81 | 82 | ###5、APIs 83 | 84 | 下文介绍存储和读取数据的API。 85 | 86 | 所有API操作都发生在内存中,而相应的数据库操作放在了后台线程中,所以这些API都可以在主线程调用,不会阻塞主线程。 87 | 88 | ####1、存储数据 89 | 90 | 存储对象order: 91 | 92 | ``` 93 | dataStorage.saveOrUpdate(order); 94 | ``` 95 | 96 | 存储一个order列表: 97 | 98 | ``` 99 | List list = new ArrayList(); 100 | ... 101 | dataStorage.saveOrUpdate(list); 102 | ``` 103 | 104 | 如果Order内部没有对象id的字段: 105 | 106 | ``` 107 | dataStorage.saveOrUpdate(order, "1001"); 108 | 109 | List list = new ArrayList(); 110 | List ids = new ArrayList(); 111 | ... 112 | //list和ids一一对应 113 | dataStorage.saveOrUpdate(list, ids); 114 | ``` 115 | 116 | ####2、读取数据 117 | 118 | 读取一个数据 119 | 120 | ``` 121 | Order order = dataStorage.load(Order.class, "1001"); 122 | ``` 123 | 124 | 读取所有Order 125 | 126 | ``` 127 | List list = dataStorage.loadAll(Order.class); 128 | ``` 129 | 130 | 读取mState为10的所有Order 131 | 132 | ``` 133 | List list = dataStorage.load(Order.class, new Condition() { 134 | @Override 135 | public boolean satisfy(Order o) { 136 | return o.getState() == 10; 137 | } 138 | }); 139 | ``` 140 | 141 | 读取一批id的Order 142 | 143 | ``` 144 | List ids = new ArrayList(); 145 | ... 146 | List list = dataStorage.load(Order.class, ids); 147 | ``` 148 | 149 | 以上函数都有一个参数Comparator供选择,提供Comparator后,获取的List是经过排序的。 150 | 151 | ``` 152 | List list = dataStorage.loadAll(Order.class, comparator); 153 | ``` 154 | 155 | 我还做了一个工具类,可以方便地自动生成Comparator。详见[这里](https://github.com/Xiaofei-it/ComparatorGenerator)。 156 | 157 | ####3、删除数据 158 | 159 | 删除一个数据 160 | 161 | ``` 162 | dataStorage.delete(order); 163 | ``` 164 | 165 | 如果Order类里没有提供对象id,那么 166 | 167 | ``` 168 | dataStorage.delete(Order.class, "1001"); 169 | ``` 170 | 171 | 删除所有Order 172 | 173 | ``` 174 | dataStorage.deleteAll(Order.class); 175 | ``` 176 | 177 | 删除一批Order 178 | 179 | ``` 180 | List list = new ArrayList(); 181 | dataStorage.delete(list); 182 | ``` 183 | 184 | 如果Order类里没有提供对象id,那么 185 | 186 | ``` 187 | List ids = new ArrayList(); 188 | ... 189 | dataStorage.delete(Order.class, ids); 190 | ``` 191 | 192 | 删除mState为10的Order 193 | 194 | 195 | ``` 196 | dataStorage.delete(Order.class, new Condition() { 197 | @Override 198 | public boolean satisfy(Order o) { 199 | return o.getState() == 10; 200 | } 201 | }); 202 | ``` 203 | 204 | ####4、其他API 205 | 206 | 还有许多API,具体请看[xiaofei.library.datastorage.IDataStorage](https://github.com/Xiaofei-it/AndroidDataStorage/blob/master/android-data-storage/src/main/java/xiaofei/library/datastorage/IDataStorage.java)。 207 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidDataStorage 2 | An easy-to-use and high-performance library for storing data in the Android system. 3 | 4 | [Chinese Readme 中文文档](https://github.com/Xiaofei-it/AndroidDataStorage/blob/master/README-ZH-CN.md) 5 | 6 | ##Features 7 | 8 | 1. A framework for object persistence in Android, which can store any kind of objects. 9 | 10 | 2. Using cache to improve the performance of storing and accessing data. 11 | 12 | 3. Easy to use, even if you have no knowledge of the mechanics of Android storage. 13 | 14 | 4. Good Ability of fault tolerance and high stability. 15 | 16 | ##Gradle 17 | 18 | ``` 19 | dependencies { 20 | compile 'xiaofei.library:android-data-storage:1.3.0' 21 | } 22 | ``` 23 | 24 | ##Maven 25 | 26 | ``` 27 | 28 | xiaofei.library 29 | android-data-storage 30 | 1.3.0 31 | pom 32 | 33 | ``` 34 | 35 | ##Usage 36 | 37 | ###1. Obtain the implementation of data storage 38 | 39 | Obtain the implementation of data storage through DataStorageFactory. Now it only supports the database version of data storage and more versions will be supported in the future. 40 | ``` 41 | IDataStorage dataStorage = DataStorageFactory.getInstance( 42 | getApplicationContext(), 43 | DataStorageFactory.TYPE_DATABASE); 44 | ``` 45 | ###2. Principle 46 | 47 | The framework stores an object according to its class's id and its own id. The former one is called the "class id" and the latter one is called the "object id". 48 | 49 | If the framework stores two objects which have the same class id and the same object id, the one stored earlier is replaced by the one stored later. 50 | 51 | The following will tell you how to give an object a class id and an object id. 52 | 53 | ###3. Class id 54 | 55 | Add a @ClassId annotation on the class you want to store. Give the annotation a value, which indicates the class id. 56 | 57 | Distinct classes should have distinct class ids. 58 | 59 | If you guarantee that the class name remains the same in different versions of apps even after ProGuard is used, then the @ClassId annotation is not necessary. In this case, if you do not add a @ClassId annotation, the class name will be the class id. 60 | 61 | ###4. Object id 62 | 63 | Often the object contains an object id as a field in itself. If there is such a field, then add an @ObjectId annotation on the field. Note that this field must be of the String type. 64 | 65 | If there is no such a field, then you should provide an object id when storing and loading the object. 66 | 67 | The following is an example illustrating how to add @ClassId and @ObjectId: 68 | 69 | ``` 70 | @ClassId("Order") 71 | public class Order { 72 | @ObjectId 73 | private String mId; 74 | 75 | private int mState; 76 | 77 | public int getState() { 78 | return mState; 79 | } 80 | ... 81 | ... 82 | } 83 | ``` 84 | 85 | ###5. APIs 86 | 87 | The following introduces APIs for storing and loading data. 88 | 89 | All of the APIs perform operations in the memory cache and put the database operations in the background, so feel free to call the APIs in the main thread. 90 | 91 | ####1. Storing 92 | 93 | Store an order: 94 | 95 | ``` 96 | dataStorage.saveOrUpdate(order); 97 | ``` 98 | 99 | Store a list of orders: 100 | 101 | ``` 102 | List list = new ArrayList(); 103 | ... 104 | dataStorage.saveOrUpdate(list); 105 | ``` 106 | 107 | If there is no id in the Order class, then you should provide an object id: 108 | 109 | ``` 110 | dataStorage.saveOrUpdate(order, "1001"); 111 | 112 | List list = new ArrayList(); 113 | List ids = new ArrayList(); 114 | ... 115 | // Each order in the list has a corresponding id in ids. 116 | dataStorage.saveOrUpdate(list, ids); 117 | ``` 118 | 119 | ####2. Loading 120 | 121 | Load an order: 122 | 123 | ``` 124 | Order order = dataStorage.load(Order.class, "1001"); 125 | ``` 126 | 127 | Load all orders: 128 | 129 | ``` 130 | List list = dataStorage.loadAll(Order.class); 131 | ``` 132 | 133 | Load the orders whose state is 10: 134 | 135 | ``` 136 | List list = dataStorage.load(Order.class, new Condition() { 137 | @Override 138 | public boolean satisfy(Order o) { 139 | return o.getState() == 10; 140 | } 141 | }); 142 | ``` 143 | 144 | Load a list of orders: 145 | 146 | ``` 147 | List ids = new ArrayList(); 148 | ... 149 | List list = dataStorage.load(Order.class, ids); 150 | ``` 151 | 152 | Each loading method has an alternative into which you can pass a comparator as a parameter. The return value of such a method will be sorted using the comparator. 153 | 154 | ``` 155 | List list = dataStorage.loadAll(Order.class, comparator); 156 | ``` 157 | 158 | I have write a helper class to provide an easy way to generate a comparator. See [Here](https://github.com/Xiaofei-it/ComparatorGenerator). 159 | 160 | ####3. Deleting 161 | 162 | Delete an order: 163 | 164 | ``` 165 | dataStorage.delete(order); 166 | ``` 167 | 168 | If there is no id in the Order class, then you should provide an object id: 169 | 170 | ``` 171 | dataStorage.delete(Order.class, "1001"); 172 | ``` 173 | 174 | Delete all orders: 175 | 176 | ``` 177 | dataStorage.deleteAll(Order.class); 178 | ``` 179 | 180 | Delete a list of orders: 181 | 182 | ``` 183 | List list = new ArrayList(); 184 | dataStorage.delete(list); 185 | ``` 186 | 187 | If there is no id in the Order class, then you should provide object ids: 188 | 189 | ``` 190 | List ids = new ArrayList(); 191 | ... 192 | dataStorage.delete(Order.class, ids); 193 | ``` 194 | 195 | Delete the orders whose state is 10: 196 | 197 | 198 | ``` 199 | dataStorage.delete(Order.class, new Condition() { 200 | @Override 201 | public boolean satisfy(Order o) { 202 | return o.getState() == 10; 203 | } 204 | }); 205 | ``` 206 | 207 | ####4. Other APIs 208 | 209 | There are a lot of APIs. Please See [xiaofei.library.datastorage.IDataStorage](https://github.com/Xiaofei-it/AndroidDataStorage/blob/master/android-data-storage/src/main/java/xiaofei/library/datastorage/IDataStorage.java). 210 | -------------------------------------------------------------------------------- /android-data-storage/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android-data-storage/android-data-storage.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /android-data-storage/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | 5 | ext { 6 | bintrayRepo = 'maven' 7 | bintrayName = 'android-data-storage' 8 | 9 | publishedGroupId = 'xiaofei.library' 10 | libraryName = 'AndroidDataStorage' 11 | artifact = 'android-data-storage' 12 | 13 | libraryDescription = 'An easy-to-use and high-performance library for storing data in the Android system.' 14 | 15 | siteUrl = 'https://github.com/Xiaofei-it/AndroidDataStorage' 16 | gitUrl = 'https://github.com/Xiaofei-it/AndroidDataStorage.git' 17 | 18 | libraryVersion = '1.3.0' 19 | 20 | developerId = 'Xiaofei-it' 21 | developerName = 'Eric Zhao' 22 | developerEmail = 'xiaofei.it@gmail.com' 23 | 24 | licenseName = 'The Apache Software License, Version 2.0' 25 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 26 | allLicenses = ["Apache-2.0"] 27 | } 28 | 29 | 30 | android { 31 | compileSdkVersion 23 32 | buildToolsVersion "23.0.3" 33 | 34 | defaultConfig { 35 | minSdkVersion 8 36 | targetSdkVersion 23 37 | versionCode 3 38 | versionName "1.3.0" 39 | } 40 | buildTypes { 41 | release { 42 | minifyEnabled false 43 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 44 | } 45 | } 46 | defaultConfig { 47 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 48 | } 49 | } 50 | 51 | dependencies { 52 | compile fileTree(dir: 'libs', include: ['*.jar']) 53 | testCompile 'junit:junit:4.12' 54 | compile 'com.android.support:appcompat-v7:23.3.0' 55 | compile 'com.google.code.gson:gson:2.5' 56 | compile 'xiaofei.library:concurrent-utils:0.1.2' 57 | androidTestCompile 'com.android.support:support-annotations:23.3.0' 58 | androidTestCompile 'com.android.support.test:runner:0.5' 59 | androidTestCompile 'com.android.support.test:rules:0.5' 60 | 61 | } 62 | 63 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 64 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' 65 | -------------------------------------------------------------------------------- /android-data-storage/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 /Users/eleme/Downloads/android-sdk-macosx/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 | -------------------------------------------------------------------------------- /android-data-storage/src/androidTest/java/xiaofei/library/datastorage/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package xiaofei.library.datastorage; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /android-data-storage/src/androidTest/java/xiaofei/library/datastorage/Test01.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage; 20 | 21 | import android.support.test.runner.AndroidJUnit4; 22 | 23 | import junit.framework.TestCase; 24 | 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | import org.junit.runner.RunWith; 28 | 29 | /** 30 | * Created by Xiaofei on 16/6/16. 31 | */ 32 | @RunWith(AndroidJUnit4.class) 33 | public class Test01 extends TestCase { 34 | int i = 0; 35 | @Before 36 | public void f() { 37 | System.out.println("Before"); 38 | assertEquals(i, 0); 39 | i = 1; 40 | } 41 | 42 | @Test 43 | public void g() { 44 | System.out.println("Test"); 45 | i = i + 1; 46 | assertEquals(i, 2); 47 | } 48 | 49 | @Test 50 | public void h() { 51 | ++i; 52 | assertEquals(i, 2); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /android-data-storage/src/androidTest/java/xiaofei/library/datastorage/database/DbCacheConcurrency2Test.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.support.test.InstrumentationRegistry; 22 | import android.support.test.runner.AndroidJUnit4; 23 | import android.support.v4.util.Pair; 24 | 25 | import junit.framework.TestCase; 26 | 27 | import org.junit.After; 28 | import org.junit.Before; 29 | import org.junit.Test; 30 | import org.junit.runner.RunWith; 31 | 32 | import java.util.List; 33 | import java.util.concurrent.ExecutorService; 34 | import java.util.concurrent.Executors; 35 | import java.util.concurrent.atomic.AtomicInteger; 36 | import java.util.concurrent.locks.Condition; 37 | import java.util.concurrent.locks.Lock; 38 | import java.util.concurrent.locks.ReentrantLock; 39 | 40 | /** 41 | * Created by Xiaofei on 16/7/2. 42 | */ 43 | @RunWith(AndroidJUnit4.class) 44 | public class DbCacheConcurrency2Test extends TestCase { 45 | 46 | private static final int MAX = 20; 47 | 48 | private DbCache dbCache; 49 | 50 | private ExecutorService executor = Executors.newFixedThreadPool(MAX); 51 | 52 | private AtomicInteger finish; 53 | 54 | private Lock lock = new ReentrantLock(); 55 | 56 | private Condition condition = lock.newCondition(); 57 | 58 | @Before 59 | public void init() { 60 | dbCache = DbCache.getInstance(InstrumentationRegistry.getContext()); 61 | dbCache.clearTable(); 62 | assertEquals(dbCache.getAllObjects(String.class).size(), 0); 63 | //singleton will stay there!!! 64 | } 65 | 66 | @Test 67 | public void insert() { 68 | finish = new AtomicInteger(0); 69 | for (int i = 0; i < MAX; ++i) { 70 | final int tmp = i; 71 | executor.execute(new Runnable() { 72 | @Override 73 | public void run() { 74 | final String threadName = Thread.currentThread().getName(); 75 | for (int j = 0; j < MAX * MAX; ++j) { 76 | String value = "Haha" + threadName + " " + tmp; 77 | String id = threadName + " " + tmp + " " + j; 78 | dbCache.insertObject(value, id); 79 | //dbCache.clearCache(); 80 | List> list = dbCache.getObjects(String.class, new xiaofei.library.datastorage.util.Condition() { 81 | @Override 82 | public boolean satisfy(String o) { 83 | return o.equals("Haha" + threadName + " " + tmp); 84 | } 85 | }); 86 | assertEquals(list.size(), j + 1); 87 | } 88 | lock.lock(); 89 | finish.getAndIncrement(); 90 | condition.signalAll(); 91 | lock.unlock(); 92 | } 93 | }); 94 | } 95 | try { 96 | lock.lock(); 97 | while (finish.get() < MAX) { 98 | condition.await(); 99 | } 100 | } catch (InterruptedException e) { 101 | e.printStackTrace(); 102 | } finally { 103 | lock.unlock(); 104 | } 105 | assertEquals(dbCache.getAllObjects(String.class).size(), MAX * MAX * MAX); 106 | 107 | } 108 | 109 | @Test 110 | public void insertAndClear() { 111 | for (int j = 0; j < MAX * MAX; ++j) { 112 | String value = "Haha"; 113 | String id = "" + j; 114 | dbCache.insertObject(value, id); 115 | dbCache.clearCache(); 116 | List> list = dbCache.getAllObjects(String.class); 117 | assertEquals(list.size(), j + 1); 118 | } 119 | } 120 | 121 | 122 | @After 123 | public void finish() { 124 | dbCache.clearTable(); 125 | assertEquals(dbCache.getAllObjects(String.class).size(), 0); 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /android-data-storage/src/androidTest/java/xiaofei/library/datastorage/database/DbCacheConcurrencyTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.support.test.InstrumentationRegistry; 22 | import android.support.test.runner.AndroidJUnit4; 23 | 24 | import junit.framework.TestCase; 25 | 26 | import org.junit.After; 27 | import org.junit.Before; 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | 31 | import java.util.concurrent.ExecutorService; 32 | import java.util.concurrent.Executors; 33 | import java.util.concurrent.atomic.AtomicInteger; 34 | import java.util.concurrent.locks.Condition; 35 | import java.util.concurrent.locks.Lock; 36 | import java.util.concurrent.locks.ReentrantLock; 37 | 38 | /** 39 | * Created by Xiaofei on 16/6/17. 40 | */ 41 | @RunWith(AndroidJUnit4.class) 42 | public class DbCacheConcurrencyTest extends TestCase { 43 | 44 | private static final int MAX = 50; 45 | 46 | private DbCache dbCache; 47 | 48 | private ExecutorService executor = Executors.newFixedThreadPool(MAX); 49 | 50 | private AtomicInteger finish; 51 | 52 | private Lock lock = new ReentrantLock(); 53 | 54 | private Condition condition = lock.newCondition(); 55 | 56 | @Before 57 | public void init() { 58 | dbCache = DbCache.getInstance(InstrumentationRegistry.getContext()); 59 | dbCache.clearTable(); 60 | assertEquals(dbCache.getAllObjects(String.class).size(), 0); 61 | //singleton will stay there!!! 62 | } 63 | 64 | @Test 65 | public void insert() { 66 | finish = new AtomicInteger(0); 67 | for (int i = 0; i < MAX; ++i) { 68 | final int tmp = i; 69 | executor.execute(new Runnable() { 70 | @Override 71 | public void run() { 72 | for (int j = 0; j < MAX * MAX; ++j) { 73 | //空格很重要 74 | dbCache.insertObject("Haha", Thread.currentThread().getName() + " " + tmp + " " + j); 75 | } 76 | lock.lock(); 77 | finish.getAndIncrement(); 78 | condition.signalAll(); 79 | lock.unlock(); 80 | } 81 | }); 82 | } 83 | try { 84 | lock.lock(); 85 | while (finish.get() < MAX) { 86 | condition.await(); 87 | } 88 | } catch (InterruptedException e) { 89 | e.printStackTrace(); 90 | } finally { 91 | lock.unlock(); 92 | } 93 | assertEquals(dbCache.getAllObjects(String.class).size(), MAX * MAX * MAX); 94 | 95 | } 96 | 97 | 98 | 99 | @Test 100 | public void insertAndDelete() { 101 | finish = new AtomicInteger(0); 102 | for (int i = 0; i < MAX; ++i) { 103 | final int tmp = i; 104 | executor.execute(new Runnable() { 105 | @Override 106 | public void run() { 107 | for (int j = 0; j < MAX * MAX; ++j) { 108 | //String id = Thread.currentThread().getName() + j; 109 | String id = Thread.currentThread().getName() + " " + tmp + " " + j; 110 | dbCache.insertObject("Haha", id); 111 | dbCache.deleteObject(String.class, id); 112 | } 113 | lock.lock(); 114 | finish.getAndIncrement(); 115 | condition.signalAll(); 116 | lock.unlock(); 117 | } 118 | }); 119 | } 120 | try { 121 | lock.lock(); 122 | while (finish.get() < MAX) { 123 | condition.await(); 124 | } 125 | } catch (InterruptedException e) { 126 | e.printStackTrace(); 127 | } finally { 128 | lock.unlock(); 129 | } 130 | assertEquals(dbCache.getAllObjects(String.class).size(), 0); 131 | 132 | } 133 | 134 | @After 135 | public void finish() { 136 | //dbCache.clearTable(); 137 | 138 | //sleep(3000); 139 | //dbCache.deleteAllObjects(String.class); 140 | //assertEquals(dbCache.getAllObjects(String.class).size(), 0); 141 | /** 142 | * 这种写法在insert测试中,立马从数据库读,能读到所有数据。也就是说数据库根本还没执行。 143 | * 貌似sleep没什么用 144 | * 所以deleteAllObjects不应该remove!!! 145 | * 跟clearTable一样! 146 | */ 147 | 148 | 149 | //dbCache.deleteAllObjects(String.class); 150 | dbCache.clearTable(); 151 | assertEquals(dbCache.getAllObjects(String.class).size(), 0); 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /android-data-storage/src/androidTest/java/xiaofei/library/datastorage/database/DbCacheExtremelyLongObjectTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.support.test.InstrumentationRegistry; 22 | import android.support.test.runner.AndroidJUnit4; 23 | import android.support.v4.util.Pair; 24 | 25 | import junit.framework.TestCase; 26 | 27 | import org.junit.After; 28 | import org.junit.Before; 29 | import org.junit.Test; 30 | import org.junit.runner.RunWith; 31 | 32 | import java.util.List; 33 | import java.util.concurrent.ExecutorService; 34 | import java.util.concurrent.Executors; 35 | import java.util.concurrent.atomic.AtomicInteger; 36 | import java.util.concurrent.locks.Condition; 37 | import java.util.concurrent.locks.Lock; 38 | import java.util.concurrent.locks.ReentrantLock; 39 | 40 | /** 41 | * Created by Xiaofei on 16/7/2. 42 | */ 43 | @RunWith(AndroidJUnit4.class) 44 | public class DbCacheExtremelyLongObjectTest extends TestCase { 45 | 46 | private static final int LENGTH = 10000000; 47 | 48 | private DbCache dbCache; 49 | 50 | @Before 51 | public void init() { 52 | dbCache = DbCache.getInstance(InstrumentationRegistry.getContext()); 53 | dbCache.clearTable(); 54 | assertEquals(dbCache.getAllObjects(String.class).size(), 0); 55 | } 56 | 57 | @Test 58 | public void test() { 59 | StringBuilder sb = new StringBuilder(); 60 | for (int i = 0; i < LENGTH; ++i) { 61 | sb.append("a"); 62 | } 63 | dbCache.insertObject(sb.toString(), "key"); 64 | dbCache.clearCache(); 65 | String s = dbCache.getObject(String.class, "key"); 66 | assertEquals(s.length(), LENGTH); 67 | } 68 | 69 | 70 | @After 71 | public void finish() { 72 | dbCache.clearTable(); 73 | assertEquals(dbCache.getAllObjects(String.class).size(), 0); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /android-data-storage/src/androidTest/java/xiaofei/library/datastorage/database/DbCacheTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.support.test.InstrumentationRegistry; 22 | import android.support.test.runner.AndroidJUnit4; 23 | import android.support.v4.util.Pair; 24 | 25 | import junit.framework.TestCase; 26 | 27 | import org.junit.Before; 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | 31 | import java.util.ArrayList; 32 | import java.util.List; 33 | 34 | import xiaofei.library.datastorage.util.Condition; 35 | 36 | /** 37 | * Created by Xiaofei on 16/6/16. 38 | */ 39 | @RunWith(AndroidJUnit4.class) 40 | public class DbCacheTest extends TestCase { 41 | private DbCache dbCache; 42 | @Before 43 | public void init() { 44 | dbCache = DbCache.getInstance(InstrumentationRegistry.getContext()); 45 | dbCache.clearTable(); 46 | //singleton will stay there!!! 47 | } 48 | 49 | @Test 50 | public void storeAndLoad() { 51 | dbCache.insertObject("ok", "1"); 52 | dbCache.clearCache(); 53 | String str = dbCache.getObject(String.class, "1"); 54 | assertEquals(str, "ok"); 55 | 56 | dbCache.clearCache(); 57 | List list1 = new ArrayList<>(); 58 | List list2 = new ArrayList<>(); 59 | list1.add("A"); 60 | list1.add("B"); 61 | list2.add("2"); 62 | list2.add("3"); 63 | dbCache.insertObjects(list1, list2); 64 | dbCache.clearCache(); 65 | List> stringList = dbCache.getAllObjects(String.class); 66 | assertEquals(stringList.size(), 3); 67 | 68 | dbCache.clearCache(); 69 | stringList = dbCache.getObjects(String.class, new Condition() { 70 | @Override 71 | public boolean satisfy(String o) { 72 | return true; 73 | } 74 | }); 75 | assertEquals(stringList.size(), 3); 76 | 77 | dbCache.clearCache(); 78 | dbCache.deleteObject(String.class, "1"); 79 | dbCache.clearCache(); 80 | stringList = dbCache.getAllObjects(String.class); 81 | assertEquals(stringList.size(), 2); 82 | 83 | dbCache.clearCache(); 84 | dbCache.insertObject("C", "4"); 85 | dbCache.clearCache(); 86 | dbCache.deleteObjects(String.class, list2); 87 | dbCache.clearCache(); 88 | stringList = dbCache.getAllObjects(String.class); 89 | assertEquals(stringList.size(), 1); 90 | 91 | dbCache.clearCache(); 92 | dbCache.deleteObjects(String.class, new Condition() { 93 | @Override 94 | public boolean satisfy(String o) { 95 | return true; 96 | } 97 | }); 98 | stringList = dbCache.getAllObjects(String.class); 99 | assertEquals(stringList.size(), 0); 100 | 101 | dbCache.insertObject("A", "1"); 102 | dbCache.insertObject("B", "2"); 103 | dbCache.clearCache(); 104 | stringList = dbCache.getAllObjects(String.class); 105 | assertEquals(stringList.size(), 2); 106 | 107 | dbCache.clearCache(); 108 | dbCache.deleteAllObjects(String.class); 109 | dbCache.clearCache(); 110 | stringList = dbCache.getAllObjects(String.class); 111 | assertEquals(stringList.size(), 0); 112 | 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /android-data-storage/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/DataStorageFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage; 20 | 21 | import android.content.Context; 22 | 23 | import xiaofei.library.datastorage.database.DatabaseStorage; 24 | 25 | /** 26 | * Created by Xiaofei on 16/3/24. 27 | */ 28 | public class DataStorageFactory { 29 | 30 | public static final int TYPE_DATABASE = 0; 31 | 32 | public static final int TYPE_FILE = TYPE_DATABASE + 1; 33 | 34 | private DataStorageFactory() { 35 | 36 | } 37 | 38 | public static IDataStorage getInstance(Context context, int type) { 39 | switch (type) { 40 | case TYPE_DATABASE: 41 | return DatabaseStorage.getInstance(context); 42 | case TYPE_FILE: 43 | default: 44 | throw new IllegalArgumentException("Type " + type + " is not supported."); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/IDataStorage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage; 20 | 21 | import java.util.Comparator; 22 | import java.util.List; 23 | 24 | import xiaofei.library.datastorage.util.Condition; 25 | /** 26 | * Created by Xiaofei on 16/3/24. 27 | * 28 | * This interface defines basic methods of the data storage. 29 | * 30 | * Developers use the implementation of this interface to use the storage. 31 | * 32 | */ 33 | public interface IDataStorage { 34 | 35 | /** 36 | * Return true if the storage contains the element of the specified class 37 | * and the specified id, false otherwise. 38 | * @param clazz 39 | * @param id 40 | * @param 41 | * @return 42 | */ 43 | boolean contains(Class clazz, String id); 44 | 45 | /** 46 | * Return true if the storage contains the element, false otherwise. 47 | * 48 | * The element should have one and only one field of the String type on which 49 | * the ObjectId annotation is present. 50 | * 51 | * @param element 52 | * @param 53 | * @return 54 | */ 55 | boolean contains(T element); 56 | 57 | /** 58 | * Store or update an element of the specified id. 59 | * 60 | * If the storage previously contained one element of the same id, the old one is 61 | * replaced by the new one. 62 | * 63 | * Otherwise, the storage stores the element. 64 | * 65 | * @param element 66 | * @param id 67 | * @param 68 | */ 69 | void storeOrUpdate(T element, String id); 70 | 71 | /** 72 | * Store or update an element. 73 | * 74 | * The element should have one and only one field of the String type on which 75 | * the ObjectId annotation is present. 76 | * 77 | * If the storage previously contained one element of the same id, the old one 78 | * is replaced by the new one. 79 | * 80 | * Otherwise, the storage stores the element. 81 | * 82 | * @param element 83 | * @param 84 | */ 85 | void storeOrUpdate(T element); 86 | 87 | /** 88 | * Store or update a list of elements of the same type. 89 | * 90 | * Each element has a corresponding id at the corresponding position in the list ids. 91 | * 92 | * The two lists should be of the same size. 93 | * @param list 94 | * @param ids 95 | * @param 96 | */ 97 | void storeOrUpdate(List list, List ids); 98 | 99 | /** 100 | * Store or update a list of elements of the same type. 101 | * 102 | * The elements should have one and only one field of the String type on which 103 | * the ObjectId annotation is present. 104 | * 105 | * @param list 106 | * @param 107 | */ 108 | void storeOrUpdate(List list); 109 | 110 | /** 111 | * Return the element of the specified class and the specified id. 112 | * 113 | * @param clazz 114 | * @param id 115 | * @param 116 | * @return 117 | */ 118 | T load(Class clazz, String id); 119 | 120 | /** 121 | * Return a list of all the elements of the specified class. 122 | * 123 | * @param clazz 124 | * @param 125 | * @return 126 | */ 127 | List loadAll(Class clazz); 128 | 129 | 130 | /** 131 | * Return a list of all the elements of the specified class. 132 | * 133 | * The list is sorted according to the comparator. 134 | * 135 | * @param clazz 136 | * @param 137 | * @return 138 | */ 139 | List loadAll(Class clazz, Comparator comparator); 140 | 141 | /** 142 | * Return a list of elements of the specified class and the specified ids. 143 | * 144 | * Each element has an id at the corresponding position in the List ids. 145 | * 146 | * @param clazz 147 | * @param ids 148 | * @param 149 | * @return 150 | */ 151 | List load(Class clazz, List ids); 152 | 153 | /** 154 | * Return a list of elements of the specified class and the specified ids. 155 | * 156 | * Each element has an id at the corresponding position in the List ids. 157 | * 158 | * The returned list is sorted according to the comparator. 159 | * 160 | * @param clazz 161 | * @param ids 162 | * @param 163 | * @return 164 | */ 165 | List load(Class clazz, List ids, Comparator comparator); 166 | 167 | /** 168 | * Return a list of elements which satisfying the condition. 169 | * 170 | * @param clazz 171 | * @param condition 172 | * @param 173 | * @return 174 | */ 175 | List load(Class clazz, Condition condition); 176 | 177 | /** 178 | * Return a list of elements which satisfying the condition. 179 | * 180 | * The returned list is sorted according to the comparator. 181 | * 182 | * @param clazz 183 | * @param condition 184 | * @param 185 | * @return 186 | */ 187 | List load(Class clazz, Condition condition, Comparator comparator); 188 | 189 | /** 190 | * Delete the element. 191 | * 192 | * The element should have one and only one field of the String type on which 193 | * the ObjectId annotation is present. 194 | * 195 | * @param element 196 | * @param 197 | */ 198 | void delete(T element); 199 | 200 | /** 201 | * Delete the element of the specified class and the specified id. 202 | * 203 | * @param clazz 204 | * @param id 205 | * @param 206 | */ 207 | void delete(Class clazz, String id); 208 | 209 | /** 210 | * Delete all the elements of the specified class; 211 | */ 212 | void deleteAll(Class clazz); 213 | 214 | /** 215 | * Delete the elements of the specified class and the id in the list ids. 216 | * 217 | * @param clazz 218 | * @param ids 219 | * @param 220 | */ 221 | void delete(Class clazz, List ids); 222 | 223 | /** 224 | * Delete the elements in the list. 225 | * 226 | * The elements should have one and only one field of the String type on which 227 | * the ObjectId annotation is present. 228 | * 229 | * @param list 230 | * @param 231 | */ 232 | void delete(List list); 233 | 234 | /** 235 | * Delete the elements which are of the specified class and satisfy the condition. 236 | * 237 | */ 238 | void delete(Class clazz, Condition condition); 239 | 240 | /** 241 | * Clear all the data in the storage. 242 | */ 243 | void clear(); 244 | } 245 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/annotation/AnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.annotation; 20 | 21 | import java.lang.reflect.Field; 22 | import java.lang.reflect.Method; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | 25 | /** 26 | * Created by Xiaofei on 16/3/25. 27 | * 28 | * Annotation processor. 29 | */ 30 | public class AnnotationProcessor { 31 | 32 | private final ConcurrentHashMap, String> mClassIdMap; 33 | 34 | private final ConcurrentHashMap, Member> mObjectIdMap; 35 | 36 | private static volatile AnnotationProcessor sInstance = null; 37 | 38 | private AnnotationProcessor() { 39 | mClassIdMap = new ConcurrentHashMap, String>(); 40 | mObjectIdMap = new ConcurrentHashMap, Member>(); 41 | } 42 | 43 | public static AnnotationProcessor getInstance() { 44 | if (sInstance == null) { 45 | synchronized (AnnotationProcessor.class) { 46 | if (sInstance == null) { 47 | sInstance = new AnnotationProcessor(); 48 | } 49 | } 50 | } 51 | return sInstance; 52 | } 53 | 54 | /** 55 | * Return the class id of the specified class. 56 | * If an annotation ClassId is present on the class, the value of the annotation is returned. 57 | * Otherwise, the class name is returned. 58 | * @param clazz 59 | * @return 60 | */ 61 | public String getClassId(Class clazz) { 62 | if (clazz == null) { 63 | throw new IllegalArgumentException("Class cannot be null."); 64 | } 65 | String className = mClassIdMap.get(clazz); 66 | if (className != null) { 67 | return className; 68 | } 69 | ClassId classIdAnnotation = clazz.getAnnotation(ClassId.class); 70 | className = classIdAnnotation == null ? clazz.getName() : classIdAnnotation.value(); 71 | mClassIdMap.put(clazz, className); 72 | return className; 73 | } 74 | 75 | public String getObjectId(Object object) { 76 | Class clazz = object.getClass(); 77 | Member member = mObjectIdMap.get(clazz); 78 | if (member != null) { 79 | return (String) member.getValue(object); 80 | } else { 81 | member = getObjectIdMember(clazz); 82 | if (member == null) { 83 | throw new IllegalArgumentException("Object does not have an object id. " 84 | + "Please specify an object id when invoking method. " 85 | + "Otherwise you can add @ObjectId on the field or the non-arg method which provides the object id."); 86 | } 87 | mObjectIdMap.put(clazz, member); 88 | return (String) member.getValue(object); 89 | } 90 | } 91 | 92 | private Member getObjectIdMember(Class clazz) { 93 | Field[] fields = clazz.getDeclaredFields(); 94 | for (Field field : fields) { 95 | if (field.isAnnotationPresent(ObjectId.class) && field.getType() == String.class) { 96 | return new FieldMember(field); 97 | } 98 | } 99 | Method[] methods = clazz.getMethods(); 100 | for (Method method : methods) { 101 | if (method.isAnnotationPresent(ObjectId.class) 102 | && method.getParameterTypes().length == 0 103 | && method.getReturnType() == String.class) { 104 | return new MethodMember(method); 105 | } 106 | } 107 | return null; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/annotation/ClassId.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.annotation; 20 | 21 | import java.lang.annotation.ElementType; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | 26 | /** 27 | * Created by Xiaofei on 16/3/25. 28 | * 29 | * Indicates the class id of the annotated class. 30 | * 31 | * The data storage stores the objects according to their class ids and object ids. 32 | * 33 | * Always, the class name can be used as the unique id of each class. However, before 34 | * an app is released, its source code is always be obfuscated, so the class name is 35 | * therefore also obfuscated and it may not be the same every time the app is released. 36 | * Objects thus cannot be stored and restored. 37 | * 38 | * Therefore, an new technique should be applied to guarantee the objects stored in 39 | * earlier version can be restored in later version. This annotation indicates the class 40 | * id of the annotated class. 41 | * 42 | * Distinct classes should have distinct class ids. If not, the behavior is unspecified. 43 | */ 44 | 45 | @Target(ElementType.TYPE) 46 | @Retention(RetentionPolicy.RUNTIME) 47 | public @interface ClassId { 48 | 49 | String value(); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/annotation/FieldMember.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.annotation; 20 | 21 | import java.lang.reflect.Field; 22 | 23 | /** 24 | * Created by Xiaofei on 16/5/11. 25 | */ 26 | public class FieldMember implements Member { 27 | 28 | private Field field; 29 | 30 | public FieldMember(Field field) { 31 | if (!field.isAccessible()) { 32 | field.setAccessible(true); 33 | } 34 | this.field = field; 35 | } 36 | 37 | @Override 38 | public Object getValue(Object object) { 39 | try { 40 | return field.get(object); 41 | } catch (IllegalArgumentException e) { 42 | throw new RuntimeException(e); 43 | } catch (IllegalAccessException e) { 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/annotation/Member.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.annotation; 20 | 21 | /** 22 | * Created by Xiaofei on 16/5/11. 23 | */ 24 | public interface Member { 25 | 26 | Object getValue(Object object); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/annotation/MethodMember.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.annotation; 20 | 21 | import java.lang.reflect.InvocationTargetException; 22 | import java.lang.reflect.Method; 23 | 24 | /** 25 | * Created by Xiaofei on 16/5/11. 26 | */ 27 | public class MethodMember implements Member { 28 | 29 | private Method method; 30 | 31 | public MethodMember(Method method) { 32 | if (!method.isAccessible()) { 33 | method.setAccessible(true); 34 | } 35 | this.method = method; 36 | } 37 | 38 | @Override 39 | public Object getValue(Object object) { 40 | try { 41 | return method.invoke(object); 42 | } catch (IllegalAccessException e) { 43 | throw new RuntimeException(e); 44 | } catch (IllegalArgumentException e) { 45 | throw new RuntimeException(e); 46 | } catch (InvocationTargetException e) { 47 | throw new RuntimeException(e); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/annotation/ObjectId.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.annotation; 20 | 21 | import java.lang.annotation.ElementType; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | 26 | /** 27 | * Created by Xiaofei on 16/3/25. 28 | * 29 | * Indicates the object id of an object. 30 | * 31 | * The data storage stores the objects according to their class id and object id. 32 | * 33 | * If an object has a field of the String type annotated by the ObjectId annotation, the value of 34 | * the field is used as the object id of the object. 35 | * 36 | * If an object do not have such an annotated field or the annotated filed is not of the String type, 37 | * a specified id should be provided when the data storage stores the object. 38 | * 39 | * Distinct objects of the same class should have distinct object ids. If not, the behavior is unspecified. 40 | */ 41 | 42 | @Target({ElementType.FIELD, ElementType.METHOD}) 43 | @Retention(RetentionPolicy.RUNTIME) 44 | public @interface ObjectId { 45 | 46 | } 47 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/DatabaseStorage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.content.Context; 22 | import android.support.v4.util.Pair; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Collections; 26 | import java.util.Comparator; 27 | import java.util.List; 28 | 29 | import xiaofei.library.datastorage.IDataStorage; 30 | import xiaofei.library.datastorage.annotation.AnnotationProcessor; 31 | import xiaofei.library.datastorage.util.Condition; 32 | 33 | /** 34 | * Created by Xiaofei on 16/3/24. 35 | * 36 | * An implementation of the interface IDataStorage. 37 | * 38 | * DatabaseStorage stores data in an instance of the DbCache class. It is thread-safe, which is 39 | * guaranteed by the thread safety of DbCache. 40 | * 41 | * The methods within this class simply invoke the corresponding methods of the DbCache instance. 42 | * They seldom check the correctness of arguments, which will be checked in the invocations of the 43 | * corresponding methods. 44 | * 45 | * Developers should use DataStorageFactory to obtain DatabaseStorage. 46 | * 47 | */ 48 | public class DatabaseStorage implements IDataStorage { 49 | 50 | private static volatile DatabaseStorage sInstance = null; 51 | 52 | private DbCache mCache; 53 | 54 | private AnnotationProcessor mAnnotationProcessor; 55 | 56 | private DatabaseStorage(Context context) { 57 | mCache = DbCache.getInstance(context); 58 | mAnnotationProcessor = AnnotationProcessor.getInstance(); 59 | } 60 | 61 | public static DatabaseStorage getInstance(Context context) { 62 | if (sInstance == null) { 63 | synchronized (DatabaseStorage.class) { 64 | if (sInstance == null) { 65 | sInstance = new DatabaseStorage(context); 66 | } 67 | } 68 | } 69 | return sInstance; 70 | } 71 | 72 | @Override 73 | public boolean contains(T element) { 74 | String id = mAnnotationProcessor.getObjectId(element); 75 | return contains(element.getClass(), id); 76 | } 77 | 78 | @Override 79 | public void storeOrUpdate(T element) { 80 | String id = mAnnotationProcessor.getObjectId(element); 81 | storeOrUpdate(element, id); 82 | } 83 | 84 | @Override 85 | public void storeOrUpdate(List list, List ids) { 86 | mCache.insertObjects(list, ids); 87 | } 88 | 89 | @Override 90 | public List load(Class clazz, final List ids) { 91 | return load(clazz, ids, null); 92 | } 93 | 94 | @Override 95 | public List load(Class clazz, final List ids, Comparator comparator) { 96 | List> list = mCache.getObjects(clazz, new Condition() { 97 | @Override 98 | public boolean satisfy(T o) { 99 | String id = mAnnotationProcessor.getObjectId(o); 100 | return ids.contains(id); 101 | } 102 | }); 103 | List result = new ArrayList(); 104 | for (Pair pair : list) { 105 | result.add(pair.second); 106 | } 107 | if (comparator != null) { 108 | Collections.sort(result, comparator); 109 | } 110 | return result; 111 | } 112 | 113 | @Override 114 | public void storeOrUpdate(T element, String id) { 115 | mCache.insertObject(element, id); 116 | } 117 | 118 | @Override 119 | public void storeOrUpdate(List list) { 120 | List ids = new ArrayList(); 121 | for (T element : list) { 122 | String id = mAnnotationProcessor.getObjectId(element); 123 | if (id == null) { 124 | throw new IllegalArgumentException("Element " + element + " has not initialized its ID."); 125 | } 126 | ids.add(id); 127 | } 128 | storeOrUpdate(list, ids); 129 | } 130 | 131 | @Override 132 | public T load(Class clazz, String id) { 133 | return mCache.getObject(clazz, id); 134 | } 135 | 136 | @Override 137 | public List loadAll(Class clazz) { 138 | return loadAll(clazz, null); 139 | } 140 | 141 | @Override 142 | public List loadAll(Class clazz, Comparator comparator) { 143 | List> list = mCache.getAllObjects(clazz); 144 | List result = new ArrayList(); 145 | for (Pair pair : list) { 146 | result.add(pair.second); 147 | } 148 | if (comparator != null) { 149 | Collections.sort(result, comparator); 150 | } 151 | return result; 152 | } 153 | 154 | @Override 155 | public List load(Class clazz, Condition condition) { 156 | return load(clazz, condition, null); 157 | } 158 | 159 | @Override 160 | public List load(Class clazz, Condition condition, Comparator comparator) { 161 | List> list = mCache.getObjects(clazz, condition); 162 | List result = new ArrayList(); 163 | for (Pair pair : list) { 164 | result.add(pair.second); 165 | } 166 | if (comparator != null) { 167 | Collections.sort(result, comparator); 168 | } 169 | return result; 170 | } 171 | 172 | @Override 173 | public void delete(Class clazz, String id) { 174 | mCache.deleteObject(clazz, id); 175 | } 176 | 177 | @Override 178 | public void deleteAll(Class clazz) { 179 | mCache.deleteAllObjects(clazz); 180 | } 181 | 182 | @Override 183 | public boolean contains(Class clazz, String id) { 184 | return mCache.containsObject(clazz, id); 185 | } 186 | 187 | @Override 188 | public void delete(T element) { 189 | String id = mAnnotationProcessor.getObjectId(element); 190 | mCache.deleteObject(element.getClass(), id); 191 | } 192 | 193 | @Override 194 | public void delete(Class clazz, List ids) { 195 | mCache.deleteObjects(clazz, ids); 196 | } 197 | 198 | @Override 199 | public void delete(List list) { 200 | if (list.isEmpty()) { 201 | return; 202 | } 203 | List ids = new ArrayList(); 204 | Class clazz = list.get(0).getClass(); 205 | for (T element : list) { 206 | if (element == null || element.getClass() != clazz) { 207 | throw new IllegalArgumentException("Element " + element + " has the different type from others."); 208 | } 209 | ids.add(mAnnotationProcessor.getObjectId(element)); 210 | } 211 | delete(clazz, ids); 212 | } 213 | 214 | @Override 215 | public void delete(Class clazz, Condition condition) { 216 | mCache.deleteObjects(clazz, condition); 217 | } 218 | 219 | @Override 220 | public void clear() { 221 | mCache.clearTable(); 222 | } 223 | } 224 | //TODO multi-process acess -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/DbCache.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.content.Context; 22 | import android.database.sqlite.SQLiteFullException; 23 | import android.support.v4.util.Pair; 24 | import android.text.TextUtils; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Collection; 28 | import java.util.LinkedList; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.Set; 32 | import java.util.concurrent.ConcurrentHashMap; 33 | import java.util.concurrent.ExecutorService; 34 | import java.util.concurrent.Executors; 35 | 36 | import xiaofei.library.concurrentutils.ObjectCanary; 37 | import xiaofei.library.datastorage.annotation.AnnotationProcessor; 38 | import xiaofei.library.datastorage.util.Condition; 39 | 40 | /** 41 | * Created by Xiaofei on 15/11/9. 42 | * 43 | * This class is used as the database cache. It is thread-safe. 44 | * 45 | * In this framework, the DatabaseStorage class is an implementation of the IDataStorage interface. 46 | * 47 | * In the DatabaseStorage class, the storage has two level, a high level which is the memory cache 48 | * and a low level which is the database file. A loading operation first searches the high level 49 | * for the specified data. If the search is successful, the loading operation succeeds. Otherwise, 50 | * the low level loads the specified data from the database file (if it exists), and the high level 51 | * stores it in the memory cache and return it as the search result. Any following loading operation 52 | * of the same data obtains the data from the memory cache and thus avoids the heavy-overhead 53 | * operations of database. 54 | * 55 | * Each operation is performed both in the high level and the low level. The memory cache has an 56 | * excellent performance, so the effect of the operation is immediate. The overhead of database 57 | * operation is heavy, so the operation is performed asynchronously. To avoiding any exception 58 | * when operating the database, this class uses a single-thread executor to perform database 59 | * operations sequentially. 60 | * 61 | * Before any operation, a sync operation is performed to check whether the high level has the data 62 | * of the specified class, and if it does not, the low level simply load all the data of the specified 63 | * class from the database. 64 | * 65 | * Deletion and insertion MUST be in a synchronized block!!! 66 | */ 67 | class DbCache implements IDbOperation { 68 | 69 | private static volatile DbCache sInstance = null; 70 | 71 | private final ExecutorService mExecutorService; 72 | 73 | private DbService mDatabase; 74 | 75 | private AnnotationProcessor mAnnotationProcessor; 76 | 77 | private final ConcurrentHashMap> mCache; 78 | 79 | private DbCache(Context context) { 80 | mExecutorService = Executors.newSingleThreadExecutor(); 81 | mCache = new ConcurrentHashMap>(); 82 | try { 83 | mDatabase = DbService.getInstance(context); 84 | } catch (RuntimeException e) { 85 | e.printStackTrace(); 86 | mDatabase = null; 87 | } 88 | mAnnotationProcessor = AnnotationProcessor.getInstance(); 89 | } 90 | 91 | static DbCache getInstance(Context context) { 92 | if (sInstance == null) { 93 | synchronized (DbCache.class) { 94 | if (sInstance == null) { 95 | sInstance = new DbCache(context); 96 | } 97 | } 98 | } 99 | return sInstance; 100 | } 101 | 102 | private void sync(final Class clazz) { 103 | String className = mAnnotationProcessor.getClassId(clazz); 104 | if (!mCache.containsKey(className)) { 105 | /** 106 | * 线程1获取了数据,卡在put操作之前。 107 | * 线程2获取了数据并且之后又塞了一个进去。 108 | */ 109 | List> list; 110 | if (mDatabase == null) { 111 | list = new ArrayList>(); 112 | } else { 113 | /** 114 | * The following fix a bug. 115 | */ 116 | final ObjectCanary>> listCanary = new ObjectCanary>>(); 117 | operateDb(new Runnable() { 118 | @Override 119 | public void run() { 120 | listCanary.set(mDatabase.getAllObjects(clazz)); 121 | } 122 | }); 123 | list = listCanary.getNonNull(); 124 | } 125 | ConcurrentHashMap map = new ConcurrentHashMap(); 126 | for (Pair pair : list) { 127 | map.put(pair.first, pair.second); 128 | } 129 | mCache.putIfAbsent(className, map); 130 | } 131 | } 132 | 133 | @Override 134 | public void close() { 135 | operateDb(new Runnable() { 136 | @Override 137 | public void run() { 138 | mDatabase.close(); 139 | } 140 | }); 141 | } 142 | 143 | private void operateDb(Runnable runnable) { 144 | operateDb(runnable, true); 145 | } 146 | 147 | private void operateDb(final Runnable runnable, final boolean needHandleException) { 148 | if (mDatabase == null) { 149 | return; 150 | } 151 | mExecutorService.execute(new Runnable() { 152 | @Override 153 | public void run() { 154 | try { 155 | runnable.run(); 156 | } catch (SQLiteFullException exception) { 157 | if (needHandleException) { 158 | // If the database is full, simply delete all the data in the database 159 | // and store the data in the memory cache into the database. 160 | updateAllObjects(); 161 | } 162 | } catch (RuntimeException e) { 163 | e.printStackTrace(); 164 | } catch (Exception e) { 165 | e.printStackTrace(); 166 | } catch (Throwable t) { 167 | t.printStackTrace(); 168 | } 169 | } 170 | }); 171 | } 172 | 173 | @Override 174 | public void deleteObject(final Class clazz, final String objectId) { 175 | sync(clazz); 176 | synchronized (mCache) { 177 | ConcurrentHashMap map = mCache.get(mAnnotationProcessor.getClassId(clazz)); 178 | if (map.containsKey(objectId)) { 179 | map.remove(objectId); 180 | operateDb(new Runnable() { 181 | @Override 182 | public void run() { 183 | mDatabase.deleteObject(clazz, objectId); 184 | } 185 | }); 186 | } 187 | } 188 | } 189 | 190 | @Override 191 | public void deleteAllObjects(final Class clazz) { 192 | sync(clazz); 193 | synchronized (mCache) { 194 | ConcurrentHashMap map = mCache.get(mAnnotationProcessor.getClassId(clazz)); 195 | map.clear(); 196 | operateDb(new Runnable() { 197 | @Override 198 | public void run() { 199 | mDatabase.deleteAllObjects(clazz); 200 | } 201 | }); 202 | } 203 | } 204 | 205 | @Override 206 | public void deleteObjects(final Class clazz, final List objectIds) { 207 | sync(clazz); 208 | synchronized (mCache) { 209 | Map map = mCache.get(mAnnotationProcessor.getClassId(clazz)); 210 | for (String id : objectIds) { 211 | if (id != null) { 212 | map.remove(id); 213 | } 214 | } 215 | operateDb(new Runnable() { 216 | @Override 217 | public void run() { 218 | mDatabase.deleteObjects(clazz, objectIds); 219 | } 220 | }); 221 | } 222 | } 223 | 224 | public void deleteObjects(Class clazz, Condition condition) { 225 | sync(clazz); 226 | synchronized (mCache) { 227 | Map map = mCache.get(mAnnotationProcessor.getClassId(clazz)); 228 | Set> entries = map.entrySet(); 229 | List ids = new ArrayList<>(); 230 | for (Map.Entry entry : entries) { 231 | if (entry == null || entry.getKey() == null || entry.getValue() == null) { 232 | continue; 233 | } 234 | if (condition == null || condition.satisfy(clazz.cast(entry.getValue()))) { 235 | ids.add(entry.getKey()); 236 | } 237 | } 238 | if (!ids.isEmpty()) { 239 | deleteObjects(clazz, ids); 240 | } 241 | } 242 | } 243 | 244 | @Override 245 | public boolean containsObject(Class clazz, String objectId) { 246 | sync(clazz); 247 | Map map = mCache.get(mAnnotationProcessor.getClassId(clazz)); 248 | return map.containsKey(objectId); 249 | } 250 | 251 | @Override 252 | public void insertObject(final T object, final String objectId) { 253 | if (TextUtils.isEmpty(objectId)) { 254 | throw new IllegalArgumentException("Illegal object id!"); 255 | } 256 | sync(object.getClass()); 257 | synchronized (mCache) { 258 | mCache.get(mAnnotationProcessor.getClassId(object.getClass())).put(objectId, object); 259 | operateDb(new Runnable() { 260 | @Override 261 | public void run() { 262 | mDatabase.insertObject(object, objectId); 263 | } 264 | }); 265 | } 266 | } 267 | 268 | @Override 269 | public void insertObjects(final List objects, final List objectIds) { 270 | if (objects.size() != objectIds.size()) { 271 | throw new IllegalArgumentException("Two lists have different sizes."); 272 | } 273 | if (objects.isEmpty()) { 274 | return; 275 | } 276 | Class clazz = objects.get(0).getClass(); 277 | sync(clazz); 278 | synchronized (mCache) { 279 | String className = mAnnotationProcessor.getClassId(clazz); 280 | Map map = mCache.get(className); 281 | int size = objects.size(); 282 | for (int i = 0; i < size; ++i) { 283 | String id = objectIds.get(i); 284 | T value = objects.get(i); 285 | if (value == null || TextUtils.isEmpty(id)) { 286 | throw new IllegalArgumentException("Object is null or id is null."); 287 | } 288 | if (value.getClass() != clazz) { 289 | throw new IllegalArgumentException("Object type is different from others."); 290 | } 291 | map.put(id, value); 292 | } 293 | operateDb(new Runnable() { 294 | @Override 295 | public void run() { 296 | mDatabase.insertObjects(objects, objectIds); 297 | } 298 | }); 299 | } 300 | } 301 | 302 | @Override 303 | public List> getAllObjects(Class clazz) { 304 | sync(clazz); 305 | String className = mAnnotationProcessor.getClassId(clazz); 306 | ConcurrentHashMap map = mCache.get(className); 307 | List> result = new LinkedList>(); 308 | Set> entries = map.entrySet(); 309 | for (Map.Entry entry : entries) { 310 | result.add(new Pair(entry.getKey(), clazz.cast(entry.getValue()))); 311 | } 312 | return result; 313 | } 314 | 315 | @Override 316 | public T getObject(Class clazz, String objectId) { 317 | sync(clazz); 318 | String className = mAnnotationProcessor.getClassId(clazz); 319 | ConcurrentHashMap map = mCache.get(className); 320 | return clazz.cast(map.get(objectId)); 321 | } 322 | 323 | @Override 324 | public List> getObjects(Class clazz, Condition condition) { 325 | sync(clazz); 326 | ConcurrentHashMap map = mCache.get(mAnnotationProcessor.getClassId(clazz)); 327 | List> result = new LinkedList>(); 328 | Set> entries = map.entrySet(); 329 | for (Map.Entry entry : entries) { 330 | if (entry == null) { 331 | continue; 332 | } 333 | T value = clazz.cast(entry.getValue()); 334 | if (value != null && (condition == null || condition.satisfy(value))) { 335 | result.add(new Pair(entry.getKey(), value)); 336 | } 337 | } 338 | return result; 339 | } 340 | 341 | @Override 342 | public void clearTable() { 343 | synchronized (mCache) { 344 | Collection> maps = mCache.values(); 345 | for (ConcurrentHashMap map : maps) { 346 | map.clear(); 347 | } 348 | operateDb(new Runnable() { 349 | @Override 350 | public void run() { 351 | mDatabase.clearTable(); 352 | } 353 | }); 354 | } 355 | } 356 | 357 | /** 358 | * For unit test only!!! 359 | */ 360 | void clearCache() { 361 | synchronized (mCache) { 362 | mCache.clear(); 363 | } 364 | } 365 | 366 | public void updateAllObjects() { 367 | operateDb(new Runnable() { 368 | @Override 369 | public void run() { 370 | synchronized (mCache) { 371 | mDatabase.clearTable(); 372 | for (ConcurrentHashMap map : mCache.values()) { 373 | if (map == null) { 374 | continue; 375 | } 376 | Set> entries = map.entrySet(); 377 | if (entries.isEmpty()) { 378 | continue; 379 | } 380 | List ids = new ArrayList(); 381 | List objects = new ArrayList(); 382 | for (Map.Entry entry : entries) { 383 | String id = entry.getKey(); 384 | Object object = entry.getValue(); 385 | if (id != null && object != null) { 386 | ids.add(id); 387 | objects.add(object); 388 | } 389 | } 390 | mDatabase.insertObjects(objects, ids); 391 | } 392 | } 393 | } 394 | }, false); 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/DbConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | /** 22 | * Created by Xiaofei on 16/5/10. 23 | */ 24 | public class DbConfig { 25 | 26 | private static volatile int sVersion = 2; 27 | 28 | private DbConfig() { 29 | 30 | } 31 | 32 | public static void setVersion(int version) { 33 | sVersion = version; 34 | } 35 | 36 | public static int getVersion() { 37 | return sVersion; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/DbConst.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | /** 22 | * Created by Xiaofei on 15/11/11. 23 | */ 24 | class DbConst { 25 | 26 | public static final String TABLE_NAME = "data_storage"; 27 | 28 | public static final String ID = "id"; 29 | 30 | public static final String CLASS_ID = "class_id"; 31 | 32 | public static final String OBJECT_ID = "object_id"; 33 | 34 | public static final String OBJECT_DATA = "object_dat"; 35 | 36 | public static final String CREATE_TABLE_COMMAND = 37 | "CREATE TABLE IF NOT EXISTS " + DbConst.TABLE_NAME + " (" 38 | + "`" + DbConst.ID + "` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " 39 | + "`" + DbConst.CLASS_ID + "` varchar(100) NOT NULL, " 40 | + "`" + DbConst.OBJECT_ID + "` varchar(100) NOT NULL, " 41 | + "`" + DbConst.OBJECT_DATA + "` text NOT NULL, " 42 | + "UNIQUE (`" + DbConst.CLASS_ID + "`, `" + DbConst.OBJECT_ID + "`) " 43 | + "ON CONFLICT REPLACE);"; 44 | 45 | public static final String DELETE_TABLE_COMMAND = 46 | "DROP TABLE IF EXISTS " + DbConst.TABLE_NAME; 47 | 48 | public static final String REPLACE_COMMAND = "REPLACE INTO " + DbConst.TABLE_NAME 49 | + " (`" + DbConst.CLASS_ID + "`, `" + DbConst.OBJECT_ID + "`, `" + DbConst.OBJECT_DATA + "`)\n" 50 | + "VALUES "; 51 | 52 | private DbConst() { 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/DbOpenHelper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.content.Context; 22 | import android.database.sqlite.SQLiteDatabase; 23 | 24 | public class DbOpenHelper extends android.database.sqlite.SQLiteOpenHelper { 25 | 26 | 27 | public DbOpenHelper(Context context) { 28 | super(context.getApplicationContext(), DbConst.TABLE_NAME, null, DbConfig.getVersion()); 29 | 30 | } 31 | 32 | @Override 33 | public void onCreate(SQLiteDatabase db) { 34 | db.execSQL(DbConst.CREATE_TABLE_COMMAND); 35 | } 36 | 37 | @Override 38 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 39 | db.execSQL(DbConst.DELETE_TABLE_COMMAND); 40 | onCreate(db); 41 | } 42 | 43 | @Override 44 | public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 45 | db.execSQL(DbConst.DELETE_TABLE_COMMAND); 46 | onCreate(db); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/DbService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.content.Context; 22 | import android.database.Cursor; 23 | import android.database.sqlite.SQLiteDatabase; 24 | import android.support.v4.util.Pair; 25 | 26 | import java.util.LinkedList; 27 | import java.util.List; 28 | 29 | import xiaofei.library.datastorage.annotation.AnnotationProcessor; 30 | import xiaofei.library.datastorage.util.Condition; 31 | /** 32 | * Created by Xiaofei on 15/11/12. 33 | * 34 | * DbService performs database operations. It is not thread-safe. 35 | * 36 | */ 37 | class DbService implements IDbOperation { 38 | 39 | private Coder mCoder = null; 40 | 41 | private static final String EQUAL = "` = '"; 42 | 43 | private static volatile DbService sInstance; 44 | 45 | private DbOpenHelper mDatabaseHelper; 46 | 47 | private AnnotationProcessor mAnnotationProcessor; 48 | 49 | private SQLiteDatabase mDb = null; 50 | 51 | private DbService(Context context) { 52 | mDatabaseHelper = new DbOpenHelper(context); 53 | //The following statement may throw an exception. 54 | mDb = mDatabaseHelper.getWritableDatabase(); 55 | mCoder = new GsonObjectCoder(new GzipCoderHook()); 56 | mAnnotationProcessor = AnnotationProcessor.getInstance(); 57 | } 58 | 59 | static DbService getInstance(Context context) { 60 | if (sInstance == null) { 61 | synchronized (DbService.class) { 62 | if (sInstance == null) { 63 | sInstance = new DbService(context); 64 | } 65 | } 66 | } 67 | return sInstance; 68 | } 69 | 70 | void setCoder(Coder coder) { 71 | mCoder = coder; 72 | } 73 | 74 | private static String generateEquation(String leftValue, String rightValue) { 75 | return "`" + leftValue + EQUAL + rightValue + "'"; 76 | } 77 | 78 | private String generateReplaceValue(T object, String id) { 79 | return "('" + mAnnotationProcessor.getClassId(object.getClass()) + "', '" + id + "', '" + mCoder.encode(object) + "')"; 80 | } 81 | 82 | /** 83 | * 关闭数据库,程序退出时执行 84 | */ 85 | public void close() { 86 | mDb.close(); 87 | } 88 | 89 | @Override 90 | public boolean containsObject(Class clazz, String objectId) { 91 | Cursor c = mDb.query(DbConst.TABLE_NAME, null, 92 | generateEquation(DbConst.CLASS_ID, mAnnotationProcessor.getClassId(clazz)) + " and " + generateEquation(DbConst.OBJECT_ID, objectId), 93 | null, null, null, null, null); 94 | boolean result = c.getCount() != 0; 95 | c.close(); 96 | return result; 97 | } 98 | 99 | @Override 100 | public void insertObject(T object, String objectId) { 101 | String command = DbConst.REPLACE_COMMAND + generateReplaceValue(object, objectId) + ";"; 102 | mDb.execSQL(command); 103 | } 104 | 105 | @Override 106 | public void insertObjects(final List objects, final List objectIds) { 107 | if (objects.isEmpty()) { 108 | return; 109 | } 110 | executeInTransaction(new Runnable() { 111 | @Override 112 | public void run() { 113 | // To avoid the extremely long sql command, we insert objects one by one, instead of 114 | // insert all of them by a long sql command. 115 | // This is not the best practice. Future work should make this better. 116 | int size = objects.size(); 117 | for (int i = 0; i < size; ++i) { 118 | T object = objects.get(i); 119 | String id = objectIds.get(i); 120 | insertObject(object, id); 121 | } 122 | } 123 | }); 124 | } 125 | 126 | @Override 127 | public List> getAllObjects(Class clazz) { 128 | List> list = new LinkedList>(); 129 | Cursor cursor = mDb.query(DbConst.TABLE_NAME, null, 130 | generateEquation(DbConst.CLASS_ID, mAnnotationProcessor.getClassId(clazz)), 131 | null, null, null, null); 132 | while (cursor.moveToNext()) { 133 | String data = cursor.getString(cursor.getColumnIndex(DbConst.OBJECT_DATA)); 134 | String id = cursor.getString(cursor.getColumnIndex(DbConst.OBJECT_ID)); 135 | T object = null; 136 | try { 137 | object = mCoder.decode(data, clazz); 138 | } catch (RuntimeException e) { 139 | e.printStackTrace(); 140 | } 141 | if (object != null) { 142 | list.add(new Pair(id, object)); 143 | } 144 | } 145 | cursor.close(); 146 | return list; 147 | } 148 | 149 | @Override 150 | public T getObject(Class clazz, String objectId) { 151 | T result = null; 152 | Cursor cursor = mDb.query(DbConst.TABLE_NAME, null, 153 | generateEquation(DbConst.CLASS_ID, mAnnotationProcessor.getClassId(clazz)) + " and " + generateEquation(DbConst.OBJECT_ID, objectId), 154 | null, null, null, null); 155 | if (cursor.moveToNext()) { 156 | String data = cursor.getString(cursor.getColumnIndex(DbConst.OBJECT_DATA)); 157 | try { 158 | result = mCoder.decode(data, clazz); 159 | } catch (RuntimeException e) { 160 | e.printStackTrace(); 161 | } 162 | } 163 | cursor.close(); 164 | return result; 165 | } 166 | 167 | @Override 168 | public List> getObjects(Class clazz, Condition condition) { 169 | List> result = new LinkedList>(); 170 | Cursor cursor = mDb.query(DbConst.TABLE_NAME, null, 171 | generateEquation(DbConst.CLASS_ID, mAnnotationProcessor.getClassId(clazz)), 172 | null, null, null, null); 173 | while (cursor.moveToNext()) { 174 | String data = cursor.getString(cursor.getColumnIndex(DbConst.OBJECT_DATA)); 175 | T object = null; 176 | try { 177 | object = mCoder.decode(data, clazz); 178 | } catch (RuntimeException e) { 179 | e.printStackTrace(); 180 | } 181 | if (object == null) { 182 | continue; 183 | } 184 | if (condition == null || condition.satisfy(object)) { 185 | String id = cursor.getString(cursor.getColumnIndex(DbConst.OBJECT_ID)); 186 | result.add(new Pair(id, object)); 187 | } 188 | } 189 | cursor.close(); 190 | return result; 191 | } 192 | 193 | @Override 194 | public void clearTable() { 195 | mDb.delete(DbConst.TABLE_NAME, null, null); 196 | } 197 | 198 | @Override 199 | public void deleteObject(Class clazz, String objectId) { 200 | String whereClause = generateEquation(DbConst.CLASS_ID, mAnnotationProcessor.getClassId(clazz)) + " and " + generateEquation(DbConst.OBJECT_ID, objectId); 201 | mDb.delete(DbConst.TABLE_NAME, whereClause, null); 202 | } 203 | 204 | @Override 205 | public void deleteObjects(Class clazz, List objectIds) { 206 | if (objectIds.isEmpty()) { 207 | return; 208 | } 209 | StringBuilder whereClause = new StringBuilder( 210 | generateEquation(DbConst.CLASS_ID, mAnnotationProcessor.getClassId(clazz)) + " and `" + DbConst.OBJECT_ID + "` in ('" + objectIds.get(0) + "'"); 211 | int size = objectIds.size(); 212 | for (int i = 1; i < size; ++i) { 213 | whereClause.append(" ,'").append(objectIds.get(i)).append("'"); 214 | } 215 | whereClause.append(")"); 216 | mDb.delete(DbConst.TABLE_NAME, whereClause.toString(), null); 217 | } 218 | 219 | @Override 220 | public void deleteAllObjects(Class clazz) { 221 | String whereClause = generateEquation(DbConst.CLASS_ID, mAnnotationProcessor.getClassId(clazz)); 222 | mDb.delete(DbConst.TABLE_NAME, whereClause, null); 223 | } 224 | 225 | public void executeInTransaction(Runnable runnable) { 226 | try { 227 | mDb.beginTransaction(); 228 | try { 229 | runnable.run(); 230 | mDb.setTransactionSuccessful(); 231 | } finally { 232 | mDb.endTransaction(); 233 | } 234 | } catch (RuntimeException e) { 235 | e.printStackTrace(); 236 | } 237 | } 238 | 239 | public interface Coder { 240 | String encode(Object object); 241 | T decode(String string, Class clazz); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/DefaultCoderHook.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | /** 22 | * Created by Xiaofei on 16/5/29. 23 | */ 24 | public class DefaultCoderHook implements ICoderHook { 25 | 26 | @Override 27 | public String decode(String input) { 28 | return input; 29 | } 30 | 31 | @Override 32 | public String encode(String input) { 33 | return input; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/GsonObjectCoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import com.google.gson.Gson; 22 | 23 | import xiaofei.library.datastorage.util.CodeUtils; 24 | 25 | /** 26 | * Created by Xiaofei on 16/3/16. 27 | */ 28 | public class GsonObjectCoder implements DbService.Coder { 29 | 30 | private Gson mGson; 31 | 32 | private ICoderHook mCoderHook; 33 | 34 | public GsonObjectCoder() { 35 | this(new DefaultCoderHook()); 36 | } 37 | 38 | public GsonObjectCoder(ICoderHook coderHook) { 39 | mGson = new Gson(); 40 | mCoderHook = coderHook; 41 | } 42 | 43 | @Override 44 | public T decode(String string, Class clazz) { 45 | try { 46 | return mGson.fromJson(mCoderHook.decode(string), clazz); 47 | } catch (RuntimeException e) { 48 | //To handle the exception, just simply ignore this element. 49 | return null; 50 | } catch (Exception e) { 51 | return null; 52 | } catch (Throwable t) { 53 | return null; 54 | } 55 | } 56 | 57 | @Override 58 | public String encode(Object object) { 59 | try { 60 | return mCoderHook.encode(mGson.toJson(object)); 61 | } catch (RuntimeException e) { 62 | return null; 63 | } catch (Exception e) { 64 | return null; 65 | } catch (Throwable t) { 66 | return null; 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/GzipCoderHook.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import java.io.UnsupportedEncodingException; 22 | 23 | import xiaofei.library.datastorage.util.CodeUtils; 24 | import xiaofei.library.datastorage.util.GzipUtils; 25 | 26 | /** 27 | * Created by Xiaofei on 16/5/29. 28 | */ 29 | public class GzipCoderHook implements ICoderHook { 30 | @Override 31 | public String decode(String input) { 32 | try { 33 | return new String(GzipUtils.decompress(CodeUtils.decode(input)), "UTF-8"); 34 | } catch (UnsupportedEncodingException e) { 35 | e.printStackTrace(); 36 | } catch (RuntimeException e) { 37 | e.printStackTrace(); 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | } catch (Throwable t) { 41 | t.printStackTrace(); 42 | } 43 | return null; 44 | } 45 | 46 | @Override 47 | public String encode(String input) { 48 | try { 49 | return CodeUtils.encode(GzipUtils.compress(input.getBytes("UTF-8"))); 50 | } catch (UnsupportedEncodingException e) { 51 | e.printStackTrace(); 52 | } catch (RuntimeException e) { 53 | e.printStackTrace(); 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | } catch (Throwable t) { 57 | t.printStackTrace(); 58 | } 59 | return null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/ICoderHook.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | /** 22 | * Created by Xiaofei on 16/5/29. 23 | */ 24 | public interface ICoderHook { 25 | 26 | String encode(String input); 27 | 28 | String decode(String input); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/IDbOperation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import android.support.v4.util.Pair; 22 | 23 | import java.util.List; 24 | 25 | import xiaofei.library.datastorage.util.Condition; 26 | 27 | /** 28 | * Created by Xiaofei on 15/11/9. 29 | * 30 | * This interface defines basic methods of database operations. 31 | * 32 | */ 33 | interface IDbOperation { 34 | 35 | /** 36 | * Return true if the table contains the object of the specified class and 37 | * the specified object id. 38 | * 39 | * Return false otherwise. 40 | * 41 | * @param clazz 42 | * @param objectId 43 | * @param 44 | * @return 45 | */ 46 | boolean containsObject(Class clazz, String objectId); 47 | 48 | /** 49 | * Insert the object of the specified object id. 50 | * 51 | * If the table contains an object of the same id, it replace the old one with the new one. 52 | * 53 | * @param object 54 | * @param objectId 55 | * @param 56 | */ 57 | void insertObject(T object, String objectId); 58 | 59 | /** 60 | * Insert the objects of the specified object ids. 61 | * 62 | * If the table contains an object of the same id, it replace the old one with the new one. 63 | * 64 | * @param objects 65 | * @param objectIds 66 | * @param 67 | */ 68 | void insertObjects(List objects, List objectIds); 69 | 70 | /** 71 | * Return all the objects of the specified class. 72 | * 73 | * The result is a list of pairs, whose first element is the object id and the second element 74 | * is the corresponding object. 75 | * 76 | * @param clazz 77 | * @param 78 | * @return 79 | */ 80 | List> getAllObjects(Class clazz); 81 | 82 | /** 83 | * Return the objects of the specified class and the specified object ids. 84 | * 85 | * @param clazz 86 | * @param objectId 87 | * @param 88 | * @return 89 | */ 90 | T getObject(Class clazz, String objectId); 91 | 92 | /** 93 | * Return the objects of the specified class, which satisfy the condition. 94 | * 95 | * @param clazz 96 | * @param condition 97 | * @param 98 | * @return 99 | */ 100 | List> getObjects(Class clazz, Condition condition); 101 | 102 | /** 103 | * Delete the object of the specified class and the specified object id. 104 | * 105 | * @param clazz 106 | * @param objectId 107 | * @param 108 | */ 109 | void deleteObject(Class clazz, String objectId); 110 | 111 | /** 112 | * Delete the objects of the specified class and the specified object ids. 113 | * 114 | * @param clazz 115 | * @param objectIds 116 | * @param 117 | */ 118 | void deleteObjects(Class clazz, List objectIds); 119 | 120 | /** 121 | * Delete all the object of the specified class. 122 | * 123 | * @param clazz 124 | * @param 125 | */ 126 | void deleteAllObjects(Class clazz); 127 | 128 | /** 129 | * Close the database. 130 | */ 131 | void close(); 132 | 133 | /** 134 | * Delete all the objects in the table. 135 | */ 136 | void clearTable(); 137 | } 138 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/database/SerialObjectCoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.database; 20 | 21 | import java.io.ByteArrayInputStream; 22 | import java.io.ByteArrayOutputStream; 23 | import java.io.IOException; 24 | import java.io.NotSerializableException; 25 | import java.io.ObjectInputStream; 26 | import java.io.ObjectOutputStream; 27 | 28 | import xiaofei.library.datastorage.util.CodeUtils; 29 | 30 | 31 | /** 32 | * Created by Xiaofei on 16/3/21. 33 | */ 34 | public class SerialObjectCoder implements DbService.Coder { 35 | @Override 36 | public T decode(String string, Class clazz) { 37 | if (string == null || string.length() < 0) { 38 | throw new IllegalArgumentException(); 39 | } 40 | try { 41 | byte[] bytes = CodeUtils.decode(string); 42 | ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 43 | ObjectInputStream oos = new ObjectInputStream(bais); 44 | return (T) oos.readObject(); 45 | } catch (RuntimeException e) { 46 | e.printStackTrace(); 47 | } catch (IOException e) { 48 | e.printStackTrace(); 49 | } catch (ClassNotFoundException e) { 50 | e.printStackTrace(); 51 | } 52 | return null; 53 | } 54 | 55 | @Override 56 | public String encode(Object object) { 57 | if (object == null) { 58 | throw new IllegalArgumentException(); 59 | } 60 | try { 61 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 62 | ObjectOutputStream oos = new ObjectOutputStream(baos); 63 | oos.writeObject(object); 64 | oos.close(); 65 | byte[] bytes = baos.toByteArray(); 66 | return CodeUtils.encode(bytes); 67 | } catch (NotSerializableException e) { 68 | e.printStackTrace(); 69 | } catch (IOException e) { 70 | e.printStackTrace(); 71 | } 72 | return null; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/util/CodeUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.util; 20 | 21 | /** 22 | * Created by Xiaofei on 16/3/17. 23 | * 24 | * Utilities for encoding and decoding. 25 | */ 26 | public class CodeUtils { 27 | 28 | private static final char[] DIGITS = { 29 | '0', '1', '2', '3', '4', '5', '6', '7', 30 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 31 | }; 32 | 33 | private CodeUtils() { 34 | 35 | } 36 | 37 | public static String encode(byte[] input) { 38 | if (input.length < 0) { 39 | throw new IllegalArgumentException("Input length is less than 0."); 40 | } 41 | StringBuilder result = new StringBuilder(); 42 | int len = input.length; 43 | for (int i = 0; i < len; i++) { 44 | result.append(DIGITS[(0xF0 & input[i]) >>> 4]).append(DIGITS[0x0F & input[i]]); 45 | } 46 | return result.toString(); 47 | } 48 | 49 | private static int charToInt(char ch) { 50 | return '0' <= ch && ch <= '9' ? ch - '0' : ch - 'A' + 10; 51 | } 52 | 53 | public static byte[] decode(String input) { 54 | int len = input.length(); 55 | if (len < 0 || len % 2 != 0) { 56 | throw new IllegalArgumentException("Input error."); 57 | } 58 | byte[] result = new byte[len / 2]; 59 | for (int i = 0, j = 0; i < len;) { 60 | int tmp = charToInt(input.charAt(i++)) << 4; 61 | tmp = tmp | charToInt(input.charAt(i++)); 62 | result[j++] = (byte) tmp; 63 | } 64 | return result; 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/util/Condition.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.util; 20 | 21 | /** 22 | * Created by Xiaofei on 15/11/11. 23 | * 24 | * Indicates whether an object satisfies a condition. 25 | * 26 | * In some cases, developers want to load some objects satisfying a specified condition from the 27 | * data storage. They can provide the loading method with an argument of this interface to indicate 28 | * which kind of objects they want to load. The data storage loads the objects and pass each object 29 | * as an argument into the method satisfy(T) of the Condition interface to test whether the object 30 | * satisfies the condition. If the method satisfy(T) returns true, the object is added into a list 31 | * which will be returned as the result of the loading method after the testing of each object. 32 | * 33 | * See the IDataStorage interface for the more details of the usage of this interface. 34 | * 35 | */ 36 | public interface Condition { 37 | boolean satisfy(T o); 38 | } 39 | -------------------------------------------------------------------------------- /android-data-storage/src/main/java/xiaofei/library/datastorage/util/GzipUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastorage.util; 20 | 21 | import java.io.ByteArrayInputStream; 22 | import java.io.ByteArrayOutputStream; 23 | import java.io.IOException; 24 | import java.util.zip.GZIPInputStream; 25 | import java.util.zip.GZIPOutputStream; 26 | 27 | /** 28 | * Created by Xiaofei on 16/5/29. 29 | */ 30 | public class GzipUtils { 31 | 32 | private GzipUtils() { 33 | 34 | } 35 | 36 | public static byte[] compress(byte[] input) { 37 | try { 38 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 39 | GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream); 40 | gzipOutputStream.write(input); 41 | gzipOutputStream.close(); 42 | return byteArrayOutputStream.toByteArray(); 43 | } catch (IOException e) { 44 | e.printStackTrace(); 45 | return null; 46 | } 47 | } 48 | 49 | public static byte[] decompress(byte[] input) { 50 | try { 51 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 52 | GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(input)); 53 | byte[] buffer = new byte[1024]; 54 | int length; 55 | while ((length = gzipInputStream.read(buffer)) != -1) { 56 | byteArrayOutputStream.write(buffer, 0, length); 57 | } 58 | gzipInputStream.close(); 59 | return byteArrayOutputStream.toByteArray(); 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | return null; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /android-data-storage/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DataStorage 3 | 4 | -------------------------------------------------------------------------------- /android-data-storage/src/test/java/xiaofei/library/datastorage/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package xiaofei.library.datastorage; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "xiaofei.library.datastoragetest" 9 | minSdkVersion 9 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.3.0' 26 | compile project(':android-data-storage') 27 | } 28 | -------------------------------------------------------------------------------- /app/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 /Users/eleme/Downloads/android-sdk-macosx/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/xiaofei/library/datastoragetest/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package xiaofei.library.datastoragetest; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/xiaofei/library/datastoragetest/MainActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2015-2016 Xiaofei 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package xiaofei.library.datastoragetest; 20 | 21 | import android.os.Bundle; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.view.View; 24 | import android.widget.Toast; 25 | 26 | import java.math.BigDecimal; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import xiaofei.library.datastorage.DataStorageFactory; 31 | import xiaofei.library.datastorage.IDataStorage; 32 | import xiaofei.library.datastorage.annotation.ObjectId; 33 | 34 | public class MainActivity extends AppCompatActivity { 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_main); 40 | final IDataStorage dataStorage = DataStorageFactory.getInstance(this, DataStorageFactory.TYPE_DATABASE); 41 | findViewById(R.id.store).setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View view) { 44 | List list = new ArrayList(); 45 | list.add(new B(new A("abc"), new BigDecimal("0.9"))); 46 | list.add(new B(new A("xyz"), new BigDecimal("90"))); 47 | dataStorage.storeOrUpdate(list); 48 | dataStorage.storeOrUpdate(new C(), "1"); 49 | } 50 | }); 51 | findViewById(R.id.load).setOnClickListener(new View.OnClickListener() { 52 | @Override 53 | public void onClick(View view) { 54 | List list = dataStorage.loadAll(B.class); 55 | StringBuilder sb = new StringBuilder(); 56 | for (B b : list) { 57 | sb.append(b.a.s).append(' ').append(b.bigDecimal.toString()).append(' '); 58 | } 59 | sb.append('\n'); 60 | C c = dataStorage.load(C.class, "1"); 61 | sb.append(c.i).append("length=" + c.s.length()); 62 | Toast.makeText(getApplicationContext(), sb.toString(), Toast.LENGTH_SHORT).show(); 63 | } 64 | }); 65 | } 66 | 67 | private static class A { 68 | String s = "Test"; 69 | A(String s) { 70 | this.s = s; 71 | } 72 | } 73 | 74 | private static class B { 75 | A a; 76 | BigDecimal bigDecimal; 77 | @ObjectId 78 | public String get() { 79 | return a.s; 80 | } 81 | B(A a, BigDecimal bigDecimal) { 82 | this.a = a; 83 | this.bigDecimal = bigDecimal; 84 | } 85 | } 86 | 87 | private static class C { 88 | public int i; 89 | public String s; 90 | public C() { 91 | StringBuilder sb = new StringBuilder(); 92 | for (int i = 0; i < 100000; ++i) { 93 | sb.append(Integer.toString(i)); 94 | } 95 | s = sb.toString(); 96 | this.i = s.length(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 |