当前位置:   article > 正文

Android ContentProvider

android contentprovider

认识

Android 中,ContentProvider 是四大组件之一,用于在不同应用程序之间共享数据。它提供了一种统一的接口来访问数据,并确保数据安全。

ContentProvider 可以用于共享各种类型的数据,包括:

  • 联系人
  • 日历事件
  • 照片
  • 视频
  • 音频
  • 文件

要使用 ContentProvider,需要先创建一个 ContentProvider 类。该类必须继承自 ContentProvider 抽象类,并实现以下方法:

  • onCreate():初始化 ContentProvider。
  • query():查询数据。
  • insert():插入数据。
  • update():更新数据。
  • delete():删除数据。

ContentProvider 类还可以提供其他方法,例如:

  • getType():返回 ContentProvider 提供的数据类型。
  • getUriMatcher():返回用于匹配 URI 的 UriMatcher 对象。

要访问 ContentProvider 中的数据,可以使用 ContentResolver 类。ContentResolver 类提供了一系列方法来查询、插入、更新和删除数据。

例如,以下代码将查询 ContentProvider 中的所有联系人:

Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);

以下代码将插入一条新联系人到 ContentProvider:

ContentValues values = new ContentValues();
values.put(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, "John Doe");
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "123-456-7890");

getContentResolver().insert(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, values);
  • 1
  • 2
  • 3
  • 4
  • 5

ContentProvider 是 Android 中共享数据的重要机制。它提供了一种安全、统一的方式来访问不同应用程序中的数据。

以下是 ContentProvider 的一些优点:

  • 安全性:ContentProvider 可以控制对数据的访问权限,确保数据安全。
  • 统一性:ContentProvider 提供了一种统一的接口来访问数据,简化了开发人员的工作。
  • 可扩展性:ContentProvider 可以很容易地扩展,以支持新的数据类型。

以下是 ContentProvider 的一些缺点:

  • 复杂性:ContentProvider 的实现比较复杂,需要开发人员有一定的开发经验。
  • 性能:ContentProvider 可能会影响应用程序的性能。

总体而言,ContentProvider 是 Android 中共享数据的重要机制。它提供了一种安全、统一的方式来访问不同应用程序中的数据。

ContentProvider 即是内容提供者,内容提供给谁?当然是当前Project本身(进程内)或者是Android系统中的其他Project(进程间),仅此而已。可以理解成提供数据给Android系统中的所有应用,这就是他存在的意义。


原理

ContentProvider 的实现原理主要基于 Binder 和共享内存机制。

1. Binder 机制

Binder 是 Android 中一种进程间通信 (IPC) 机制,它允许不同进程之间相互调用方法。ContentProvider 使用 Binder 机制来实现跨进程的数据共享。

2. 共享内存机制

共享内存是一种 IPC 机制,它允许不同进程共享同一块内存区域。ContentProvider 使用共享内存机制来提高数据传输效率。

具体来说,ContentProvider 的实现原理如下:

  1. 应用程序 A 要访问应用程序 B 提供的数据,首先通过 ContentResolver 类获取 ContentProvider 对象。
  2. ContentResolver 对象通过 Binder 机制连接到应用程序 B 的 ContentProvider 对象。
  3. 应用程序 A 通过 ContentProvider 对象调用应用程序 B 提供的数据操作方法。
  4. 应用程序 B 的 ContentProvider 对象将数据通过共享内存机制传递给应用程序 A。

CP启动时机和流程

private void handleBindApplication(AppBindData data) {
    //在运行时将当前执行线程注册为敏感线程
    VMRuntime.registerSensitiveThread();
    ...
    //标记进程起始时间
    Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
    ...
    //设置进程名字
    Process.setArgV0(data.processName);
    
    //设置一个标记位,androidQ及以上版本一些不明确数组相关的类会抛出数组越界异常
    //例如[SparseArray的keyAt和setValueAt在Q之前的版本不会抛出异常](/https://blog.csdn.net/wzz18749670290/article/details/109352466)
    UtilConfig.setThrowExceptionForUpperArrayOutOfBounds(
                data.appInfo.targetSdkVersion >= Build.VERSION_CODES.Q);
    //androidP之前用BitmapFactory解码Bitmap,会放大density。android P及以后,用ImageDecoder解码Bitmap,会跳过upscale节约内存
    ImageDecoder.sApiLevel = data.appInfo.targetSdkVersion;
    
    //重置系统时区
    TimeZone.setDefault(null);
    //断点调试相关
    if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
        // XXX should have option to change the port.
        ...
    }
    ...
    //渲染调试相关
    boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
    HardwareRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE);
    HardwareRenderer.setPackageName(data.appInfo.packageName);
    
    //初始化HTTP代理
    final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
    if (b != null) {
        final IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
        try {
            Proxy.setHttpProxySystemProperty(service.getProxyForNetwork(null));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    //创建Instrumentation并初始化
    // Continue loading instrumentation.
    if (ii != null) {
        ApplicationInfo instrApp;
        try {
            instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0,
                    UserHandle.myUserId());
        } catch (RemoteException e) {
            instrApp = null;
        }
        if (instrApp == null) {
            instrApp = new ApplicationInfo();
        }
        ii.copyTo(instrApp);
        instrApp.initForUser(UserHandle.myUserId());
        final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                appContext.getClassLoader(), false, true, false);

        final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
                appContext.getOpPackageName());
        try {
            //通过ClassLoader创建Instrumentation
            final ClassLoader cl = instrContext.getClassLoader();
            mInstrumentation = (Instrumentation)
                cl.loadClass(data.instrumentationName.getClassName()).newInstance();
        } catch (Exception e) {
           ...
        }
        //初始化Instrumentation
        final ComponentName component = new ComponentName(ii.packageName, ii.name);
        mInstrumentation.init(this, instrContext, appContext, component,
                data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
    } else {
        mInstrumentation = new Instrumentation();
        mInstrumentation.basicInit(this);
    }
    
    ...
    
    

    Application app;
    try {
        //创建application,这里面会调用application的attachBaseContext,这里的info对应的class是LoadedApk.java,最终也是通过classLoader创建Application
        app = data.info.makeApplication(data.restrictedBackupMode, null);
        ...
        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                //这里调用installProvider()->AppComponentFactory.instantiateProvider->
                //localProvider.attachInfo()->ContentProvider.onCreate();
                //看到这里就明白了为什么LeakCanary2.0不需要在Application中手动初始化
                installContentProviders(app, data.providers);
            }
        }
        
        //调用application的onCreate
        mInstrumentation.callApplicationOnCreate(app);
    }
    //预加载字体资源
    FontsContract.setApplicationContextForResources(appContext);
    ...
}
  • 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
  • 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

installContentProviders 方法就是会调用到 provider#onCreate() 方法,所以启动时机是application#attch 到 onCreate 之间。

详细:https://blog.csdn.net/guojingbu/article/details/117676114

使用

要在 manifest 内配置 ContentProvider,需要在 <application> 标签内添加 <provider> 子标签。<provider> 标签的属性如下:

  • android:name:ContentProvider 类的完整类名。
  • android:authorities:ContentProvider 的权限。
  • android:exported:是否允许其他应用程序访问 ContentProvider。
  • android:process:ContentProvider 所运行的进程。
  • android:initOrder:ContentProvider 的初始化顺序。

以下是一个示例:

<application>
  <provider
    android:name=".MyContentProvider"
    android:authorities="com.example.myapp.provider"
    android:exported="true" />
</application>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

以下是各个属性的详细说明:

  • android:name

属性值:ContentProvider 类的完整类名。

说明:该属性是必需的,用于指定 ContentProvider 的实现类。

  • android:authorities

属性值:ContentProvider 的权限。

说明:该属性是必需的,用于指定 ContentProvider 的访问权限。权限是一个字符串,通常以 com. 开头,后面是应用程序的包名和 ContentProvider 的名称。

  • android:exported

属性值:true 或 false。

说明:该属性默认为 false,表示 ContentProvider 只能被同一应用程序的其他组件访问。如果将其设置为 true,则 ContentProvider 可以被其他应用程序访问。

  • android:process

属性值:ContentProvider 所运行的进程。

说明:该属性默认为 :process,表示 ContentProvider 与应用程序的主进程运行在同一个进程中。如果将其设置为 :remote,则 ContentProvider 将运行在单独的进程中。

  • android:initOrder

属性值:ContentProvider 的初始化顺序。

说明:该属性的取值范围是 0 到 99999,数字越大,初始化顺序越靠前。

注意

  • ContentProvider 的权限必须与应用程序的权限一致。
  • 如果 ContentProvider 需要访问数据库,则需要在 manifest 中添加 <uses-permission> 标签。

以下是一个使用 ContentProvider 访问数据库的示例:

<application>
  <provider
    android:name=".MyContentProvider"
    android:authorities="com.example.myapp.provider"
    android:exported="true" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</application>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

代码示例:

public class MyContentProvider extends ContentProvider {

  @Override
  public boolean onCreate() {
    // 初始化数据库
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    // 创建表
    db.execSQL("CREATE TABLE IF NOT EXISTS my_table (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)");

    return true;
  }

  @Override
  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    // 查询数据库
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    Cursor cursor = db.query("my_table", projection, selection, selectionArgs, null, null, sortOrder);

    return cursor;
  }

  @Override
  public Uri insert(Uri uri, ContentValues values) {
    // 插入数据到数据库
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    long rowId = db.insert("my_table", null, values);

    Uri resultUri = Uri.parse("content://com.example.myapp.provider/my_table/" + rowId);

    return resultUri;
  }

  @Override
  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    // 更新数据库中的数据
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    int count = db.update("my_table", values, selection, selectionArgs);

    return count;
  }

  @Override
  public int delete(Uri uri, String selection, String[] selectionArgs) {
    // 删除数据库中的数据
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    int count = db.delete("my_table", selection, selectionArgs);
  • 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
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

更多属性,ContentProvider 还有以下几个属性:

  • android:permission

  • android:multiprocess:指示 ContentProvider 是否可以在多个进程中运行。默认值为 false,表示 ContentProvider 只能在单个进程中运行。如果设置为 true,则 ContentProvider 可以运行在多个进程中。

  • android:readPermission:指定读取 ContentProvider 数据所需的权限。如果未设置,则使用 android:permission 属性的值。

  • android:writePermission:指定写入 ContentProvider 数据所需的权限。如果未设置,则使用 android:permission 属性的值。

  • android:grantUriPermissions:指示 ContentProvider 是否可以授予其他应用程序对特定 URI 的访问权限。默认值为 false,表示 ContentProvider 不能授予其他应用程序对 URI 的访问权限。如果设置为 true,则 ContentProvider 可以授予其他应用程序对 URI 的访问权限。

  • android:pathPermission:指定对 ContentProvider 中特定路径的访问权限。该属性可以用于定义更细粒度的权限控制。

  • android:syncable:指示 ContentProvider 是否支持数据同步。默认值为 false,表示 ContentProvider 不支持数据同步。如果设置为 true,则 ContentProvider 支持数据同步。

以下是这些属性的详细说明:

  • android:multiprocess

属性值:true 或 false。

说明:该属性的默认值为 false,表示 ContentProvider 只能在单个进程中运行。如果设置为 true,则 ContentProvider 可以运行在多个进程中。

  • android:readPermission

属性值:权限字符串。

说明:该属性指定读取 ContentProvider 数据所需的权限。如果未设置,则使用 android:permission 属性的值。

  • android:writePermission

属性值:权限字符串。

说明:该属性指定写入 ContentProvider 数据所需的权限。如果未设置,则使用 android:permission 属性的值。

  • android:grantUriPermissions

属性值:true 或 false。

说明:该属性的默认值为 false,表示 ContentProvider 不能授予其他应用程序对 URI 的访问权限。如果设置为 true,则 ContentProvider 可以授予其他应用程序对 URI 的访问权限。

  • android:pathPermission

属性值:路径权限列表。

说明:该属性可以用于定义更细粒度的权限控制。例如,可以指定只有拥有 com.example.myapp.permission.READ_CONTACTS 权限的应用程序才能读取联系人数据。

  • android:syncable

属性值:true 或 false。

说明:该属性的默认值为 false,表示 ContentProvider 不支持数据同步。如果设置为 true,则 ContentProvider 支持数据同步。

自定义权限

android:permission 属性用于配置 ContentProvider 的读写权限。

以下是使用 android:permission 配置权限的示例:

<application>
  <provider
    android:name=".MyContentProvider"
    android:authorities="com.example.myapp.provider"
    android:exported="true"
    android:permission="com.example.myapp.permission.READ_WRITE" />
</application>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

代码示例:

public class MyContentProvider extends ContentProvider {

  @Override
  public boolean onCreate() {
    // 初始化数据库
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    // 创建表
    db.execSQL("CREATE TABLE IF NOT EXISTS my_table (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)");

    return true;
  }

  @Override
  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    // 检查权限
    if (getContext().checkCallingOrSelfPermission("com.example.myapp.permission.READ") != PackageManager.PERMISSION_GRANTED) {
      throw new SecurityException("Permission denied to read data");
    }

    // 查询数据库
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    Cursor cursor = db.query("my_table", projection, selection, selectionArgs, null, null, sortOrder);

    return cursor;
  }

  @Override
  public Uri insert(Uri uri, ContentValues values) {
    // 检查权限
    if (getContext().checkCallingOrSelfPermission("com.example.myapp.permission.WRITE") != PackageManager.PERMISSION_GRANTED) {
      throw new SecurityException("Permission denied to write data");
    }

    // 插入数据到数据库
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    long rowId = db.insert("my_table", null, values);

    Uri resultUri = Uri.parse("content://com.example.myapp.provider/my_table/" + rowId);

    return resultUri;
  }

  @Override
  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    // 检查权限
    if (getContext().checkCallingOrSelfPermission("com.example.myapp.permission.WRITE") != PackageManager.PERMISSION_GRANTED) {
      throw new SecurityException("Permission denied to write data");
    }

    // 更新数据库中的数据
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    int count = db.update("my_table", values, selection, selectionArgs);

    return count;
  }

  @Override
  public int delete(Uri uri, String selection, String[] selectionArgs) {
    // 检查权限
    if (getContext().checkCallingOrSelfPermission("com.example.myapp.permission.WRITE") != PackageManager.PERMISSION_GRANTED) {
      throw new SecurityException("Permission denied to write data");
    }

    // 删除数据库中的数据
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
        getContext().getDatabasePath("my.db"), null);

    int count = db.delete("my_table", selection, selectionArgs);

    return count;
  }
}
  • 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
  • 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

在上述示例中:

  • <provider> 标签的 android:permission 属性设置为 com.example.myapp.permission.READ_WRITE,表示只有拥有 com.example.myapp.permission.READ_WRITE 权限的应用程序才能访问 ContentProvider。
  • query()insert()update()delete() 方法会检查调用者的权限。如果调用者没有相应的权限,则会抛出 SecurityException 异常。

使用 android:permission 配置权限的优点:

  • 更加安全,可以更好地控制对数据的访问权限。
  • 更加灵活,可以根据需要定义不同的权限级别。

使用 android:permission 配置权限的缺点:

  • 更加复杂,需要开发人员手动定义权限。
  • 可能会降低应用程序的性能,因为需要进行权限检查。

在实际开发中,可以根据需要选择使用 android:permission 或 android:grantUriPermissions 配置权限。

如果您需要更细粒度的权限控制,可以使用 android:permission 配置权限。

如果您只需要简单的读写权限控制,可以使用 android:grantUriPermissions 配置权限。

grantUriPermissions

android:grantUriPermissions 属性设置为 false 时,其他应用程序不能通过直接访问 ContentProvider 来访问数据。但是,其他应用程序可以通过以下方式访问数据:

  • 通过调用 ContentResolver 的方法

ContentResolver 提供了一系列方法来访问 ContentProvider 中的数据。其他应用程序可以使用这些方法来查询、插入、更新和删除数据。

例如,以下代码使用 ContentResolver 查询 ContentProvider 中的所有联系人:

Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);

  • 通过使用 ContentProvider 的 UriMatcher

ContentProvider 可以使用 UriMatcher 来匹配 URI 并执行相应的操作。其他应用程序可以使用 UriMatcher 来确定要调用的 ContentProvider 方法。

例如,以下代码使用 UriMatcher 来匹配 URI 并执行相应的操作:

public class MyContentProvider extends ContentProvider {

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

@Override
public boolean onCreate() {
// 初始化 UriMatcher
uriMatcher.addURI(“com.example.myapp.provider”, “contacts”, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);

return true;
  • 1

}

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// 匹配 URI
int match = uriMatcher.match(uri);

switch (match) {
  case ContactsContract.CommonDataKinds.Phone.CONTENT_URI:
    // 查询联系人
    Cursor cursor = db.query("contacts", projection, selection, selectionArgs, null, null, sortOrder);

    return cursor;
}

return null;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

}
}

  • 通过使用 ContentProvider 的共享 URI

ContentProvider 可以通过调用 grantUriPermission() 方法来授予其他应用程序对特定 URI 的访问权限。其他应用程序可以使用共享 URI 来访问数据。

例如,以下代码授予其他应用程序对联系人数据的访问权限:

public class MyContentProvider extends ContentProvider {

@Override
public boolean onCreate() {
// 初始化数据库
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
getContext().getDatabasePath(“my.db”), null);

// 创建表
db.execSQL("CREATE TABLE IF NOT EXISTS contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)");

return true;
  • 1
  • 2
  • 3
  • 4

}

@Override
public Uri insert(Uri uri, ContentValues values) {
// 插入数据到数据库
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(
getContext().getDatabasePath(“my.db”), null);

long rowId = db.insert("contacts", null, values);

Uri resultUri = Uri.parse("content://com.example.myapp.provider/contacts/" + rowId);

// 授予其他应用程序对 URI 的访问权限
getContext().grantUriPermission("com.example.otherapp", resultUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

return resultUri;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

}
}

在实际开发中,可以根据需要选择使用上述方法来访问数据。

如果您需要授予其他应用程序对数据的临时访问权限,可以使用 grantUriPermission() 方法。

如果您需要授予其他应用程序对数据的永久访问权限,可以使用 android:pathPermission 属性。

getType

getType() 方法用于获取 ContentProvider 提供的数据类型。该方法返回一个 MIME 类型字符串,用于指示数据的类型。

MIME 类型是一种用于表示数据类型的标准化方法。它由两个部分组成:

  • 类型:指示数据的类型。例如,image 表示图像数据,video 表示视频数据,text 表示文本数据。
  • 子类型:指示数据的具体类型。例如,image/png 表示 PNG 图像数据,video/mp4 表示 MP4 视频数据,text/plain 表示纯文本数据。

ContentProvider 的 getType() 方法可以用于以下目的:

  • 确定如何处理数据

应用程序可以使用 MIME 类型来确定如何处理数据。例如,如果应用程序收到一个 image/png 类型的 URI,则应用程序可以使用图像查看器来显示图像。

  • 验证数据

应用程序可以使用 MIME 类型来验证数据是否有效。例如,如果应用程序收到一个 text/plain 类型的 URI,则应用程序可以检查数据是否为纯文本。

  • 过滤数据

应用程序可以使用 MIME 类型来过滤数据。例如,如果应用程序只想显示图像,则应用程序可以使用 image/* MIME 类型来过滤其他类型的数据。

以下是一个示例:

public class MyContentProvider extends ContentProvider {

  @Override
  public String getType(Uri uri) {
    // 根据 URI 返回不同的 MIME 类型
    switch (uri.getPath()) {
      case "/contacts":
        return ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE;
      case "/images":
        return "image/*";
      case "/videos":
        return "video/*";
      default:
        return null;
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在上述示例中:

  • getType() 方法根据 URI 返回不同的 MIME 类型。
  • 对于 /contacts URI,getType() 方法返回 ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE,表示该 URI 提供联系人数据。
  • 对于 /images URI,getType() 方法返回 image/*,表示该 URI 提供图像数据。
  • 对于 /videos URI,getType() 方法返回 video/*,表示该 URI 提供视频数据。

请注意,并非所有 ContentProvider 都需要实现 getType() 方法。如果 ContentProvider 提供的数据类型是固定的,则可以省略该方法。

进行数据访问

http://t.csdnimg.cn/QdclB

参考地址

Bard 和 chatGpt

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

闽ICP备14008679号