当前位置:   article > 正文

深入分析 Android ContentProvider (十二)

深入分析 Android ContentProvider (十二)

深入分析 Android ContentProvider (十二)

Android 中 ContentProvider 的系统代码分析(续)

我们继续深入分析 Android 系统中 ContentProvider 的底层实现,进一步理解其工作流程及设计逻辑。

1. ContentProvider 的内部实现机制

ContentProvider 是 Android 中用于实现跨应用数据共享的组件。为了更详细地理解其内部工作机制,我们将探讨以下几个方面:

  1. ContentProvider 的创建与生命周期管理
  2. ContentProvider 的数据访问与处理
  3. ContentProvider 的权限管理与安全性
1.1. ContentProvider 的创建与生命周期管理

ContentProvider 的创建和生命周期管理由 Android 系统框架负责。当某个应用尝试访问 ContentProvider 时,系统会根据需要实例化该 ContentProvider 并调用其 onCreate() 方法。

在 Android 系统中,ContentProvider 的实例化是通过 ActivityThread 类来完成的。以下是相关的系统代码片段:

// ActivityThread.java
private IActivityManager mActivityManager;

public final IContentProvider acquireProvider(Context c, String auth, int userId, boolean stable) {
    IContentProvider provider = null;
    try {
        provider = mActivityManager.getContentProvider(c.getIApplicationThread(), auth, userId, stable);
    } catch (RemoteException e) {
        // Handle exception
    }
    return provider;
}

public final void installContentProviders(Context context, List<ProviderInfo> providers) {
    for (ProviderInfo info : providers) {
        ContentProviderHolder holder = installProvider(context, null, info, false, true, true);
        mAllProviders.put(holder.provider.getClass().getName(), holder);
    }
}

private ContentProviderHolder installProvider(Context context, IContentProvider provider,
        ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = (ContentProvider)provider;
    localProvider.attachInfo(context, info);
    return new ContentProviderHolder(localProvider);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
1.2. ContentProvider 的数据访问与处理

ContentProvider 的数据访问与处理是通过 ContentResolver 实现的。ContentResolver 是应用访问 ContentProvider 的主要接口,通过它可以进行查询、插入、更新和删除操作。

在系统代码中,ContentResolver 通过 IContentProvider 接口与 ContentProvider 进行通信。以下是相关的系统代码片段:

// ContentResolver.java
public final Cursor query(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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这个过程中,ContentResolver 会通过 acquireProvider() 方法获取 ContentProvider 的实例,并通过 IContentProvider 接口调用相应的方法(如 query()insert()update()delete())来执行数据库操作。

1.3. ContentProvider 的权限管理与安全性

ContentProvider 的权限管理是通过 AndroidManifest.xml 中的 provider 标签进行配置的。开发者可以通过 android:permission 属性来限制访问权限。

在 ContentProvider 的内部实现中,可以通过检查调用方的权限来进一步增强安全性。以下是一个示例:

public class SecureContentProvider extends ContentProvider {

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        checkPermission();
        // 执行查询操作
        return null;
    }

    private void checkPermission() {
        Context context = getContext();
        if (context != null) {
            int permission = context.checkCallingOrSelfPermission("com.example.permission.READ_DATA");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException("Permission denied");
            }
        }
    }

    // ... 其他方法的实现 ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

通过在操作前调用 checkPermission() 方法,可以确保只有具备相应权限的应用才能访问数据,提升数据安全性。

2. ContentProvider 的通知机制

ContentProvider 的数据变化通知机制通过 ContentObserverContentResolver.notifyChange() 实现。当数据发生变化时,ContentProvider 会通知观察者,以便其更新数据。

示例:通知数据变化
@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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

通过 notifyChange() 方法,ContentProvider 可以通知 ContentObserver 数据已更新,触发 UI 更新或其他相关操作。

3. ContentProvider 的测试

为了确保 ContentProvider 的功能正确,可以编写单元测试进行验证。使用 Android 的 Instrumented Tests,可以在模拟设备上测试 ContentProvider 的行为。

示例:ContentProvider 的单元测试
@RunWith(AndroidJUnit4.class)
public class PlaylistProviderTest {
    private ContentResolver contentResolver;

    @Before
    public void setUp() {
        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        contentResolver = context.getContentResolver();
    }

    @Test
    public void testInsert() {
        ContentValues values = new ContentValues();
        values.put(DatabaseHelper.COLUMN_NAME, "Test Playlist");
        Uri newUri = contentResolver.insert(PlaylistProvider.CONTENT_URI, values);
        assertNotNull(newUri);
    }

    @Test
    public void testQuery() {
        Cursor cursor = contentResolver.query(PlaylistProvider.CONTENT_URI, null, null, null, null);
        assertNotNull(cursor);
        assertTrue(cursor.getCount() > 0);
    }

    @Test
    public void testUpdate() {
        ContentValues values = new ContentValues();
        values.put(DatabaseHelper.COLUMN_NAME, "Updated Playlist");
        int rowsUpdated = contentResolver.update(PlaylistProvider.CONTENT_URI, values, DatabaseHelper.COLUMN_ID + "=?", new String[]{"1"});
        assertEquals(1, rowsUpdated);
    }

    @Test
    public void testDelete() {
        int rowsDeleted = contentResolver.delete(PlaylistProvider.CONTENT_URI, DatabaseHelper.COLUMN_ID + "=?", new String[]{"1"});
        assertEquals(1, rowsDeleted);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

通过单元测试,可以验证 ContentProvider 的各项功能,确保其行为符合预期。

4. 总结

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

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

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/937257
推荐阅读
相关标签
  

闽ICP备14008679号