You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xiangmuyi/NotesProvider.java

300 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package net.micode.notes.data;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.R;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
// NotesProvider类继承自ContentProvider是Android中用于在不同应用组件间共享数据的抽象基类
// 本类主要负责处理与笔记相关的数据操作,如查询、插入、删除、更新等,并与底层数据库进行交互
public class NotesProvider extends ContentProvider {
// UriMatcher用于根据传入的Uri来匹配相应的操作码以确定要执行何种数据操作
// 这里定义为静态成员变量方便在类的不同方法中使用相同的匹配规则来判断Uri类型
private static final UriMatcher mMatcher;
// NotesDatabaseHelper的实例用于辅助进行数据库相关操作例如获取数据库连接、执行SQL语句等
private NotesDatabaseHelper mHelper;
// 用于日志记录的标签,方便在查看日志时快速识别是该类中输出的日志信息
private static final String TAG = "NotesProvider";
// 以下定义了不同的整数常量作为不同类型Uri的匹配码用于区分不同的请求路径对应的操作
private static final int URI_NOTE = 1;
private static final int URI_NOTE_ITEM = 2;
private static final int URI_DATA = 3;
private static final int URI_DATA_ITEM = 4;
private static final int URI_SEARCH = 5;
private static final int URI_SEARCH_SUGGEST = 6;
// 静态初始化块用于初始化UriMatcher添加各种不同格式的Uri匹配规则
// 通过这些规则后续可以根据传入的Uri快速判断其对应的操作类型
static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加匹配规则当Uri的权限Authority为Notes.AUTHORITY路径为"note"时匹配码为URI_NOTE
// 通常用于对所有笔记进行批量操作的请求
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
// 当Uri的权限为Notes.AUTHORITY路径为"note/数字"#表示数字占位符匹配码为URI_NOTE_ITEM
// 一般用于对单个笔记进行操作的请求数字部分对应具体笔记的ID
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
// 为搜索建议相关的Uri添加匹配规则用于处理搜索建议的请求路径
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
}
// 定义用于搜索结果投影Projection的字符串指定了从数据库查询搜索结果时要返回的列及格式
// 这里涉及到将笔记的一些字段进行处理后返回,例如去除换行符、设置图标、指定意图相关信息等
// 以便符合搜索结果展示和后续操作的要求
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
+ R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
// 定义用于查询笔记片段搜索的SQL语句模板基于上面的投影字符串构建
// 根据笔记片段SNIPPET字段进行模糊匹配查询同时添加了一些筛选条件如排除回收站文件夹、只查询笔记类型等
private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
+ " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.SNIPPET + " LIKE?"
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
// 重写ContentProvider的onCreate方法在内容提供器创建时调用
// 主要用于初始化NotesDatabaseHelper实例获取数据库操作的辅助对象若初始化成功则返回true表示创建成功
@Override
public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
// 重写ContentProvider的query方法用于处理数据查询请求
// 根据传入的Uri、投影、选择条件等参数从数据库中查询相应的数据并返回结果集Cursor
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor c = null;
// 获取可读数据库实例,用于执行查询操作,确保不会对数据库进行写操作
SQLiteDatabase db = mHelper.getReadableDatabase();
String id = null;
// 使用UriMatcher匹配传入的Uri根据匹配结果执行不同的查询逻辑
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 如果是URI_NOTE类型的Uri直接对笔记表TABLE.NOTE进行查询使用传入的投影、选择条件等参数
c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_NOTE_ITEM:
// 如果是URI_NOTE_ITEM类型的Uri先获取路径中的笔记ID即uri.getPathSegments().get(1)
// 然后在查询笔记表时添加基于笔记ID的筛选条件确保查询的是指定ID的单个笔记
id = uri.getPathSegments().get(1);
c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_DATA:
// 针对URI_DATA类型的Uri对数据表TABLE.DATA进行查询同样使用传入的相关参数
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case URI_DATA_ITEM:
// 对于URI_DATA_ITEM类型的Uri获取路径中的数据ID然后在查询数据表时添加基于该数据ID的筛选条件
id = uri.getPathSegments().get(1);
c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_SEARCH:
case URI_SEARCH_SUGGEST:
// 如果是搜索或搜索建议相关的Uri类型
if (sortOrder!= null || projection!= null) {
// 若传入了排序顺序或投影参数,则抛出异常,因为对于这种类型的查询不应该指定这些参数
throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
}
String searchString = null;
// 根据具体是搜索还是搜索建议的Uri类型获取不同方式传入的搜索字符串
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
if (uri.getPathSegments().size() > 1) {
searchString = uri.getPathSegments().get(1);
}
} else {
searchString = uri.getQueryParameter("pattern");
}
if (TextUtils.isEmpty(searchString)) {
// 如果搜索字符串为空则直接返回null因为没有内容可查询
return null;
}
try {
// 对搜索字符串进行格式化,添加模糊匹配的通配符(%),用于构建模糊查询条件
searchString = String.format("%%%s%%", searchString);
// 使用格式化后的搜索字符串执行原始的SQL查询语句NOTES_SNIPPET_SEARCH_QUERY来获取搜索结果
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
} catch (IllegalStateException ex) {
// 如果在查询过程中出现异常,记录错误日志
Log.e(TAG, "got exception: " + ex.toString());
}
break;
default:
// 如果传入的Uri无法匹配任何已知的类型则抛出异常表示未知的Uri请求
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (c!= null) {
// 如果查询到了有效的结果集Cursor设置通知Uri以便在数据变化时通知相关观察者
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
// 重写ContentProvider的insert方法用于处理数据插入请求
// 根据传入的Uri和要插入的数据ContentValues将数据插入到相应的数据库表中并返回插入后数据的Uri
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mHelper.getWritableDatabase();
long dataId = 0, noteId = 0, insertedId = 0;
// 使用UriMatcher匹配传入的Uri根据匹配结果执行不同的插入逻辑
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 如果是URI_NOTE类型的Uri直接向笔记表TABLE.NOTE插入数据获取插入后笔记的ID
insertedId = noteId = db.insert(TABLE.NOTE, null, values);
break;
case URI_DATA:
// 对于URI_DATA类型的Uri先检查要插入的数据中是否包含笔记IDDataColumns.NOTE_ID
if (values.containsKey(DataColumns.NOTE_ID)) {
noteId = values.getAsLong(DataColumns.NOTE_ID);
} else {
// 如果没有笔记ID则记录警告日志表示数据格式错误
Log.d(TAG, "Wrong data format without note id:" + values.toString());
}
// 向数据表TABLE.DATA插入数据获取插入后数据的ID
insertedId = dataId = db.insert(TABLE.DATA, null, values);
break;
default:
// 如果传入的Uri无法匹配任何已知的类型则抛出异常表示未知的Uri请求
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果插入的笔记ID大于0即成功插入了笔记数据通知与笔记相关的Uri发生了变化
// 以便相关观察者如使用ContentObserver监听的组件可以做出响应
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// 如果插入的数据ID大于0即成功插入了数据通知与数据相关的Uri发生了变化
if (dataId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
// 返回插入数据后对应的Uri通过在原始Uri基础上添加插入数据的ID构建
return ContentUris.withAppendedId(uri, insertedId);
}
// 重写ContentProvider的delete方法用于处理数据删除请求
// 根据传入的Uri、选择条件等参数从相应的数据库表中删除匹配的数据并返回删除的行数
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
boolean deleteData = false;
// 使用UriMatcher匹配传入的Uri根据匹配结果执行不同的删除逻辑
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 如果是URI_NOTE类型的Uri在原选择条件基础上添加笔记ID大于0的条件确保删除合法的笔记记录
// 然后从笔记表TABLE.NOTE中删除匹配的数据获取删除的行数
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 如果是URI_NOTE_ITEM类型的Uri获取路径中的笔记ID
id = uri.getPathSegments().get(1);
/**
* ID小于等于0的是系统文件夹不允许删除移动到回收站这里进行判断跳过此类操作
*/
long noteId = Long.valueOf(id);
if (noteId <= 0) {
break;
}
// 根据笔记ID从笔记表中删除相应的记录获取删除的行数
count = db.delete(TABLE.NOTE,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
break;
case URI_DATA:
// 针对URI_DATA类型的Uri从数据表TABLE.DATA中删除匹配的数据获取删除的行数并标记正在删除数据
count = db.delete(TABLE.DATA, selection, selectionArgs);
deleteData = true;
break;
case URI_DATA_ITEM:
// 对于URI_DATA_ITEM类型的Uri获取路径中的数据ID然后从数据表中删除相应的数据记录获取删除的行数并标记正在删除数据
id = uri.getPathSegments().get(1);
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true;
break;
default:
// 如果传入的Uri无法匹配任何已知的类型则抛出异常表示未知的Uri请求
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
// 如果成功删除了数据删除行数大于0
if (deleteData) {
// 如果正在删除数据通知与笔记相关的Uri发生了变化因为数据的变化可能影响到笔记相关的展示等情况
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
// 通知与当前操作的Uri发生了变化以便相关观察者做出响应
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
// 重写ContentProvider的update方法用于处理数据更新请求
// 根据传入的Uri、要更新的数据ContentValues、选择条件等参数更新相应数据库表中的数据并返回更新的行数
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
boolean updateData = false;
// 使用UriMatcher匹配传入的Uri根据匹配结果执行不同的更新逻辑
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 如果是URI_NOTE类型的Uri先调用increaseNoteVersion方法来更新笔记的版本号可能用于版本控制等逻辑
increaseNoteVersion(-1, selection, selectionArgs);
// 然后根据传入的参数更新笔记表TABLE.NOTE中的数据获取更新的行数
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 如果是URI_NOTE_ITEM类型的Uri获取路径中的笔记ID
id = uri.getPathSegments().get(1);
// 调用increaseNoteVersion方法根据笔记ID来更新笔记的版本号
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
// 根据笔记ID和其他传入参数更新笔记表中的相应记录获取更新的行数
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
break;
case URI_DATA:
// 针对URI_DATA类型的Uri直接根据传入参数更新数据表TABLE.DATA中的数据获取更新的行数并标记正在更新数据
count = db.update(TABLE.DATA, values, selection, selectionArgs);
updateData = true;
break;
case URI_DATA_ITEM:
// 对于