复制代码
1 package com.example.providertest;
2
3 import android.net.Uri;
4 import android.provider.BaseColumns;
5
6 /**
7 * 常量类
8 */
9 public final class StudentProfile {
10
11 /**
12 * 一般来说 我们的authority都是设置成 我们这个常量类的包名+类名
13 */
14 public static final String AUTHORITY = "com.example.providertest.StudentProfile";
15
16 /**
17 * 注意这个构造函数 是私有的 目的就是让他不能被初始化
18 */
19 private StudentProfile() {
20
21 }
22
23 /**
24 * 实现了这个BaseColumns接口 可以让我们少写几行代码
25 *
26 */
27 public static final class Students implements BaseColumns {
28 /**
29 * 这个类同样也是不能被初始化的
30 */
31 private Students() {
32
33 }
34
35 // 定义我们的表名
36 public static final String TABLE_NAME = "students";
37
38 /**
39 * 下面开始uri的定义
40 */
41
42 // uri的scheme部分 这个部分是固定的写法
43 private static final String SCHEME = "content://";
44
45 // 部分学生
46 private static final String PATH_STUDENTS = "/students";
47
48 // 某一个学生
49 private static final String PATH_STUDENTS_ID = "/students/";
50
51 /**
52 * path这边的第几个值是指的位置 我们设置成第一个位置
53 */
54 public static final int STUDENT_ID_PATH_POSITION = 1;
55
56 // 这个表的基本的uri格式
57 public static final Uri CONTENT_URI = Uri.parse(SCHEME + AUTHORITY
58 + PATH_STUDENTS);
59 // 某一条数据的基本uri格式 这个通常在自定義的provider的insert方法里面被调用
60 public static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME
61 + AUTHORITY + PATH_STUDENTS_ID);
62
63 /**
64 * 定义一下我们的mime类型 注意一下mime类型的写法
65 *
66 * 一般都是后面vnd.应用程序的包名.表名
67 */
68
69 // 多行的mime类型
70 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.example.providertest.students";
71 // 单行的mime类型
72 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.com.example.providertest.students";
73
74 /**
75 * 既然provider提供了查询的方法 我们肯定要设置一个默认的排序方式 这里我们就默认让他根据创建的时间 来降序排序
76 */
77 public static final String DEFAULT_SORT_ORDER = "created DESC";
78
79 /**
80 * 下面就是表的列定义了
81 */
82
83 // 学生的名字
84 public static final String COLUMN_NAME_NAME = "name";
85 // 学生的年龄
86 public static final String COLUMN_NAME_AGE = "age";
87 // 学生的学号
88 public static final String COLUMN_NAME_NUMBER = "number";
89 // 这个学生创建的时间
90 public static final String COLUMN_NAME_CREATE_DATE = "created";
91 // 这个学生入库以后修改的时间
92 public static final String COLUMN_NAME_MODIFICATION_DATE = "modified";
93
94 }
95
96 }
复制代码
复制代码
1 package com.example.providertest;
2
3 import java.util.HashMap;
4
5 import android.content.ContentProvider;
6 import android.content.ContentUris;
7 import android.content.ContentValues;
8 import android.content.Context;
9 import android.content.UriMatcher;
10 import android.database.Cursor;
11 import android.database.SQLException;
12 import android.database.sqlite.SQLiteDatabase;
13 import android.database.sqlite.SQLiteOpenHelper;
14 import android.database.sqlite.SQLiteQueryBuilder;
15 import android.net.Uri;
16 import android.text.TextUtils;
17 import android.util.Log;
18
19 public class StudentProfileProvider extends ContentProvider {
20
21 // tag 打日志用
22 private static final String TAG = "StudentProfileProvider";
23
24 // 数据库的名字
25 private static final String DATABASE_NAME = "students_info.db";
26
27 // 数据库版本号
28 private static final int DATABASE_VERSION = 1;
29
30 /**
31 * A UriMatcher instance
32 */
33 private static final UriMatcher sUriMatcher;
34
35 // 匹配成功的返回值 这里代表多行匹配成功
36 private static final int STUDENTS = 1;
37
38 // 匹配成功的返回值 这里代表多单行匹配成功
39 private static final int STUDENTS_ID = 2;
40
41 /**
42 * 注意看一下这个哈希表 这个哈希表实际上是主要为了SQLiteQueryBuilder这个类的 setProjectionMap这个方法使用的
43 *
44 * 他的值的初始化我放在静态代码块里面,这个地方实际上主要是为了多表查询而存在的
45 *
46 * 比如你要多表查询的时候 你有2个表 一个表A 一个表B 你join的时候 肯定需要重命名某个表的某个列
47 *
48 * 比如你要把表A的 name1 这个列名重命名成 a.name1 那你就可以add一个key value对,key为name1
49 *
50 * value 为a.name1 即可。当然咯 如果你不想重命名或者只是单表查询那就只需要吧key 和value
51 *
52 * 的值都写成 一样的即可
53 *
54 */
55 private static HashMap<String, String> sStudentsProjectionMap;
56
57 // 定义数据库helper.
58 private DatabaseHelper mOpenHelper;
59
60 // 静态代码块执行
61 static {
62
63 // 先构造urimatcher
64 sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
65
66 sUriMatcher.addURI(StudentProfile.AUTHORITY, "students", STUDENTS);
67
68 // #代表任意数字 *一般代表任意文本
69 sUriMatcher.addURI(StudentProfile.AUTHORITY, "students/#", STUDENTS_ID);
70
71 // 因为我们这里是单表查询 所以这个地方key和value的值都写成固定的就可以了
72 sStudentsProjectionMap = new HashMap<String, String>();
73
74 sStudentsProjectionMap.put(StudentProfile.Students._ID,
75 StudentProfile.Students._ID);
76
77 sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_AGE,
78 StudentProfile.Students.COLUMN_NAME_AGE);
79
80 sStudentsProjectionMap.put(
81 StudentProfile.Students.COLUMN_NAME_CREATE_DATE,
82 StudentProfile.Students.COLUMN_NAME_CREATE_DATE);
83
84 sStudentsProjectionMap.put(
85 StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE,
86 StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE);
87
88 sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_NAME,
89 StudentProfile.Students.COLUMN_NAME_NAME);
90
91 sStudentsProjectionMap.put(StudentProfile.Students.COLUMN_NAME_NUMBER,
92 StudentProfile.Students.COLUMN_NAME_NUMBER);
93 }
94
95 @Override
96 public boolean onCreate() {
97 // TODO Auto-generated method stub
98 mOpenHelper = new DatabaseHelper(getContext());
99 return true;
100 }
101
102 /**
103 * 对于自定义contentprovider来说CRUD的这几个方法的写法 要尽量保证 代码优美 和 容错性高
104 *
105 */
106
107 @Override
108 public Cursor query(Uri uri, String[] projection, String selection,
109 String[] selectionArgs, String sortOrder) {
110 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
111 qb.setTables(StudentProfile.Students.TABLE_NAME);
112
113 // 先匹配uri
114 switch (sUriMatcher.match(uri)) {
115 // 多行查询
116 case STUDENTS:
117 qb.setProjectionMap(sStudentsProjectionMap);
118 break;
119 // 单行查询
120 case STUDENTS_ID:
121 qb.setProjectionMap(sStudentsProjectionMap);
122 qb.appendWhere(StudentProfile.Students._ID
123 + "="
124 + uri.getPathSegments().get(
125 StudentProfile.Students.STUDENT_ID_PATH_POSITION));
126 break;
127 default:
128 throw new IllegalArgumentException("Unknown uri" + uri);
129 }
130
131 // 如果没有传orderby的值过来 那我们就使用默认的
132 String orderBy;
133 if (TextUtils.isEmpty(sortOrder)) {
134 orderBy = StudentProfile.Students.DEFAULT_SORT_ORDER;
135 } else {
136 // 如果传过来了 就使用传来的值
137 orderBy = sortOrder;
138 }
139
140 // 开始操作数据库
141 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
142
143 Cursor c = qb.query(db, projection, selection, selectionArgs, null,
144 null, orderBy);
145
146 // 这个地方要解释一下 这句语句的作用,很多人自定义provider的时候 在query方法里面都忘记
147 // 写这句话,有的人写了也不知道这句话是干嘛的,实际上这句话就是给我们的cursor加了一个观察者
148 // 有兴趣的可以看一下sdk里面这个函数的源码,非常简单。那么他的实际作用就是如果返回的cursor
149 // 被用在SimpleCursorAdapter 类似的这种adapter的话,一旦uri所对应的provider数据发生了变化
150 // 那么这个adapter里的数据是会自己变化刷新的。这句话起的就是这个作用 有兴趣的可以自己写代码
151 // 验证一下 如果把这句话删除掉的话 adapter里的数据是不会再uri更新的时候 自动更新的
152 c.setNotificationUri(getContext().getContentResolver(), uri);
153 return c;
154 }
155
156 /**
157 * 这个地方的返回值 一定要和manifest你配置activity的时候data 字段的值相同 不然会报错
158 */
159 @Override
160 public String getType(Uri uri) {
161 switch (sUriMatcher.match(uri)) {
162 case STUDENTS:
163 return StudentProfile.Students.CONTENT_TYPE;
164 case STUDENTS_ID:
165 return StudentProfile.Students.CONTENT_ITEM_TYPE;
166 default:
167 // 注意这个地方记得不匹配的时候抛出异常信息 这样当比人调用失败的时候会知道哪里不对
168 throw new IllegalArgumentException("Unknown uri" + uri);
169 }
170
171 }
172
173 @Override
174 public Uri insert(Uri uri, ContentValues initialValues) {
175
176 if (sUriMatcher.match(uri) != STUDENTS) {
177 throw new IllegalArgumentException("Unknown URI " + uri);
178 }
179
180 ContentValues values;
181
182 if (initialValues != null) {
183 values = new ContentValues(initialValues);
184 } else {
185 values = new ContentValues();
186 }
187
188 // 下面几行代码实际上就是告诉我们对于某些表而言 默认的字段的值 可以在insert里面自己写好
189 // 不要让调用者去手动再做重复劳动,我们应该允许调用者写入最少的字段的值 来完成db的insert
190 // 操作
191 Long now = Long.valueOf(System.currentTimeMillis());
192
193 if (values.containsKey(StudentProfile.Students.COLUMN_NAME_CREATE_DATE) == false) {
194 values.put(StudentProfile.Students.COLUMN_NAME_CREATE_DATE, now);
195 }
196 if (values
197 .containsKey(StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE) == false) {
198 values.put(StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE,
199 now);
200 }
201
202 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
203
204 long rowId = db.insert(StudentProfile.Students.TABLE_NAME,
205 StudentProfile.Students.COLUMN_NAME_NAME, values);
206
207 if (rowId > 0) {
208 Uri stuUri = ContentUris.withAppendedId(
209 StudentProfile.Students.CONTENT_ID_URI_BASE, rowId);
210 // 用于通知所有观察者数据已经改变
211 getContext().getContentResolver().notifyChange(stuUri, null);
212 return stuUri;
213 }
214
215 // 如果插入失败也最好抛出异常 通知调用者
216 throw new SQLException("Failed to insert row into " + uri);
217
218 }
219
220 @Override
221 public int delete(Uri uri, String where, String[] whereArgs) {
222 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
223 String finalWhere;
224
225 int count;
226
227 switch (sUriMatcher.match(uri)) {
228
229 case STUDENTS:
230 count = db.delete(StudentProfile.Students.TABLE_NAME, where,
231 whereArgs);
232 break;
233
234 case STUDENTS_ID:
235 finalWhere = StudentProfile.Students._ID
236 + " = "
237 + uri.getPathSegments().get(
238 StudentProfile.Students.STUDENT_ID_PATH_POSITION);
239
240 if (where != null) {
241 finalWhere = finalWhere + " AND " + where;
242 }
243
244 count = db.delete(StudentProfile.Students.TABLE_NAME, finalWhere,
245 whereArgs);
246 break;
247
248 default:
249 throw new IllegalArgumentException("Unknown URI " + uri);
250 }
251
252 getContext().getContentResolver().notifyChange(uri, null);
253
254 return count;
255 }
256
257 @Override
258 public int update(Uri uri, ContentValues values, String where,
259 String[] whereArgs) {
260 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
261 int count;
262 String finalWhere;
263
264 switch (sUriMatcher.match(uri)) {
265
266 case STUDENTS:
267
268 count = db.update(StudentProfile.Students.TABLE_NAME, values,
269 where, whereArgs);
270 break;
271
272 case STUDENTS_ID:
273
274 finalWhere = StudentProfile.Students._ID
275 + " = "
276 + uri.getPathSegments().get(
277 StudentProfile.Students.STUDENT_ID_PATH_POSITION);
278
279 if (where != null) {
280 finalWhere = finalWhere + " AND " + where;
281 }
282
283 count = db.update(StudentProfile.Students.TABLE_NAME, values,
284 finalWhere, whereArgs);
285 break;
286 default:
287 throw new IllegalArgumentException("Unknown URI " + uri);
288 }
289
290 getContext().getContentResolver().notifyChange(uri, null);
291
292 return count;
293 }
294
295 // 自定义helper
296 static class DatabaseHelper extends SQLiteOpenHelper {
297
298 DatabaseHelper(Context context) {
299 super(context, DATABASE_NAME, null, DATABASE_VERSION);
300 }
301
302 @Override
303 public void onCreate(SQLiteDatabase db) {
304 // TODO Auto-generated method stub
305 db.execSQL("CREATE TABLE " + StudentProfile.Students.TABLE_NAME
306 + " (" + StudentProfile.Students._ID
307 + " INTEGER PRIMARY KEY,"
308 + StudentProfile.Students.COLUMN_NAME_NAME + " TEXT,"
309 + StudentProfile.Students.COLUMN_NAME_NUMBER + " TEXT,"
310 + StudentProfile.Students.COLUMN_NAME_AGE + " INTEGER,"
311 + StudentProfile.Students.COLUMN_NAME_CREATE_DATE
312 + " INTEGER,"
313 + StudentProfile.Students.COLUMN_NAME_MODIFICATION_DATE
314 + " INTEGER" + ");");
315 }
316
317 @Override
318 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
319 // TODO Auto-generated method stub
320 // 数据库升级的时候 这边的代码 不写了,看各自的业务逻辑了,一般建议大家在这个地方多打一些日志
321 }
322
323 }
324 }
复制代码