深入分析 Android ContentProvider (十一)

文章目录

    • [深入分析 Android ContentProvider (十一)](#深入分析 Android ContentProvider (十一))
    • [ContentProvider 的系统代码分析](#ContentProvider 的系统代码分析)
      • [1. ContentProvider 的核心类](#1. ContentProvider 的核心类)
      • [2. ContentProvider 的工作流程](#2. ContentProvider 的工作流程)
      • [3. 详细示例分析](#3. 详细示例分析)
        • [3.1 数据库和 ContentProvider 的初始化](#3.1 数据库和 ContentProvider 的初始化)
        • [3.2 查询操作](#3.2 查询操作)
        • [3.3 数据更新通知](#3.3 数据更新通知)
      • [4. 总结](#4. 总结)

深入分析 Android ContentProvider (十一)

ContentProvider 的系统代码分析

为了更深入地了解 Android 中 ContentProvider 的工作原理,我们需要查看其底层实现和运行机制。以下是对 ContentProvider 在 Android 系统代码中的详细分析。

1. ContentProvider 的核心类

ContentProvider 的核心类主要包括:

  1. ContentProvider
  2. ContentResolver
  3. UriMatcher

我们将逐个分析这些类的关键部分和工作原理。

1.1. ContentProvider

ContentProvider 是一个抽象类,开发者需要继承它并实现核心的 CRUD 操作。以下是 ContentProvider 的一些关键方法和流程:

java 复制代码
public abstract class ContentProvider {
    private Transport mTransport = new Transport();

    /**
     * Called when the provider is being started.
     */
    public abstract boolean onCreate();

    /**
     * Implement this to handle query requests from clients.
     */
    public abstract Cursor query(Uri uri, String[] projection, String selection,
                                 String[] selectionArgs, String sortOrder);

    /**
     * Implement this to handle requests for the MIME type of the data at the given URI.
     */
    public abstract String getType(Uri uri);

    /**
     * Implement this to handle requests to insert a new row.
     */
    public abstract Uri insert(Uri uri, ContentValues values);

    /**
     * Implement this to handle requests to delete one or more rows.
     */
    public abstract int delete(Uri uri, String selection, String[] selectionArgs);

    /**
     * Implement this to handle requests to update one or more rows.
     */
    public abstract int update(Uri uri, ContentValues values, String selection,
                               String[] selectionArgs);

    /**
     * This is called when a client starts a query.
     */
    public final Cursor query(Uri uri, String[] projection, String selection,
                              String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
        return query(uri, projection, selection, selectionArgs, sortOrder);
    }

    // ... other methods and inner classes ...
}
核心方法分析:
  • onCreate(): 当 ContentProvider 被创建时调用,通常用于初始化操作。
  • query(): 处理查询请求,返回 Cursor 对象。
  • getType(): 返回给定 URI 的 MIME 类型。
  • insert(): 处理插入请求,返回新插入行的 URI。
  • delete(): 处理删除请求,返回删除的行数。
  • update(): 处理更新请求,返回更新的行数。

这些方法定义了 ContentProvider 的基本操作接口,具体的实现由开发者提供。

1.2. ContentResolver

ContentResolver 是用于与 ContentProvider 交互的客户端接口。通过 ContentResolver,应用可以查询、插入、更新和删除数据。

java 复制代码
public abstract class ContentResolver {
    // ... other methods ...

    public final Cursor query(final Uri uri, String[] projection, String selection,
                              String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
        IContentProvider provider = acquireProvider(uri);
        if (provider == null) {
            throw new IllegalArgumentException("Unknown URI " + uri);
        }

        try {
            return provider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
        } catch (RemoteException e) {
            throw new RuntimeException("Failed to query: " + uri, e);
        } finally {
            releaseProvider(provider);
        }
    }

    // ... other methods ...
}
核心方法分析:
  • query(): 通过 IContentProvider 接口与 ContentProvider 通信,执行查询操作。
  • acquireProvider(): 获取指定 URI 的 ContentProvider。
  • releaseProvider(): 释放 ContentProvider 的引用。
1.3. UriMatcher

UriMatcher 用于匹配和解析 URI。在 ContentProvider 中,常常使用 UriMatcher 来区分不同的 URI 请求。

java 复制代码
public class UriMatcher {
    private static final int NO_MATCH = -1;
    private HashMap<String, Object> mValues;

    public UriMatcher(int code) {
        mCode = code;
        mValues = new HashMap<>();
    }

    public void addURI(String authority, String path, int code) {
        if (code < 0) {
            throw new IllegalArgumentException("code " + code + " is invalid: it must be positive");
        }
        mValues.put(makeKey(authority, path), code);
    }

    public int match(Uri uri) {
        String key = makeKey(uri.getAuthority(), uri.getPath());
        Integer code = (Integer) mValues.get(key);
        return code != null ? code : NO_MATCH;
    }

    private String makeKey(String authority, String path) {
        return authority + "/" + path;
    }
}
核心方法分析:
  • addURI(): 添加 URI 和对应的 code。
  • match(): 匹配给定 URI,返回对应的 code。

2. ContentProvider 的工作流程

  1. 初始化 :当应用启动时,ContentProvider 会通过 onCreate() 方法进行初始化。这个方法通常用于数据库的初始化或其他资源的准备工作。

  2. 请求处理 :当应用通过 ContentResolver 发起数据请求时,系统会通过 IContentProvider 接口调用 ContentProvider 的对应方法(如 query()insert()update()delete())。这些方法会在后台线程中执行,以避免阻塞主线程。

  3. URI 匹配 :ContentProvider 使用 UriMatcher 来解析和匹配请求 URI。根据匹配结果,执行不同的操作逻辑。

  4. 数据操作 :执行数据库操作(如查询、插入、更新、删除)。这些操作通常使用 SQLiteDatabase 进行。

  5. 结果返回 :操作完成后,返回结果给调用方。对于查询操作,会返回一个 Cursor 对象;对于插入操作,会返回新记录的 URI;对于更新和删除操作,会返回影响的行数。

  6. 通知变化 :数据发生变化时,通过 ContentResolver.notifyChange() 通知观察者,触发数据刷新。

3. 详细示例分析

假设我们有一个 ContentProvider,用于管理音乐播放列表。我们将从系统代码的角度分析其工作流程。

3.1 数据库和 ContentProvider 的初始化
java 复制代码
public class PlaylistProvider extends ContentProvider {
    private static final String AUTHORITY = "com.example.musicprovider";
    private static final String BASE_PATH = "playlists";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);

    private static final int PLAYLISTS = 1;
    private static final int PLAYLIST_ID = 2;

    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        uriMatcher.addURI(AUTHORITY, BASE_PATH, PLAYLISTS);
        uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", PLAYLIST_ID);
    }

    private SQLiteDatabase database;

    @Override
    public boolean onCreate() {
        DatabaseHelper helper = new DatabaseHelper(getContext());
        database = helper.getWritableDatabase();
        return true;
    }

    // ... other methods ...
}
3.2 查询操作
java 复制代码
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                    @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    queryBuilder.setTables(DatabaseHelper.TABLE_PLAYLIST);

    switch (uriMatcher.match(uri)) {
        case PLAYLISTS:
            break;
        case PLAYLIST_ID:
            queryBuilder.appendWhere(DatabaseHelper.COLUMN_ID + "=" + uri.getLastPathSegment());
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }

    Cursor cursor = queryBuilder.query(database, projection, selection, selectionArgs, null, null, sortOrder);
    cursor.setNotificationUri(getContext().getContentResolver(), uri);
    return cursor;
}
3.3 数据更新通知
java 复制代码
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
    int rowsUpdated;
    switch (uriMatcher.match(uri)) {
        case PLAYLISTS:
            rowsUpdated = database.update(DatabaseHelper.TABLE_PLAYLIST, values, selection, selectionArgs);
            break;
        case PLAYLIST_ID:
            selection = DatabaseHelper.COLUMN_ID + "=?";
            selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
            rowsUpdated = database.update(DatabaseHelper.TABLE_PLAYLIST, values, selection, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    getContext().getContentResolver().notifyChange(uri, null);
    return rowsUpdated;
}

在这个示例中,ContentProvider 会在数据更新后通过 notifyChange() 方法通知数据变化,触发观察者(如 CursorLoader)重新加载数据。

4. 总结

通过对 ContentProvider 系统代码的分析,我们可以深入理解其工作原理和机制。ContentProvider 提供了一个标准接口,用于跨应用的数据共享和管理。通过 ContentResolver,应用可以方便地与 ContentProvider 进行交互,而 UriMatcher 则简化了 URI 的解析和匹配。掌握这些底层实现和工作流程,可以帮助开发者更好地设计和优化 ContentProvider,在实际项目中实现高效、安全的数据操作。

|----------------------------------|
| 欢迎点赞|关注|收藏|评论,您的肯定是我创作的动力 |

相关推荐
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android