Capítulo I ContentProvider de los cuatro componentes principales (3)

Capítulo uno Cuatro componentes

El tercer componente Proveedor de contenido

(1) Definición

Proveedor de contenido

(Dos) papel

Realice la interacción y el intercambio de datos (comunicación entre procesos) entre varias aplicaciones / procesos de aplicaciones
ContentProvider = rol de mediador (portero), la fuente de datos real para almacenar y operar datos es la forma original de almacenar datos (base de datos (sqlite), archivos, XML , Red, etc.)
ContentProvider generalmente proporciona una interfaz unificada para almacenar y recuperar datos, y puede compartir datos entre diferentes aplicaciones.
Hay varias razones para usar ContentProvider:
1. Abstracción de la base de datos subyacente: ContentProvider proporciona una abstracción del almacenamiento de datos subyacente. Por ejemplo, en la siguiente figura, la capa inferior usa una base de datos SQLite. Después de usar el paquete ContentProvider, incluso si cambia la base de datos a MongoDB, no afectará los datos de la capa superior usando el código de capa.
2. Encapsular tipos de datos unificados: algunas clases en el marco de Android requieren datos de tipo ContentProvider. Si desea que sus datos se utilicen en clases como SyncAdapter, Loader, CursorAdapter, etc., debe crear una capa de encapsulación ContentProvider para sus datos.
3. Encapsule de manera segura: ContentProvider proporciona un entorno seguro para la interacción de datos entre aplicaciones. Le permite abrir los datos de su aplicación a otras aplicaciones para agregar, eliminar, modificar y verificar según sus necesidades, sin preocuparse por los problemas de seguridad causados ​​por abrir directamente los permisos de la base de datos.
Inserte la descripción de la imagen aquí

(3) Principio

El mecanismo Binder
en Android Binder es una forma de implementar la comunicación entre procesos (IPC) en Android
Inserte la descripción de la imagen aquí

(4) Uso específico

(1) Identificador uniforme de recursos

(1) Definición: Identificador Uniforme de Recursos, es decir, Identificador Uniforme de Recursos
(2) Función: Identificar de manera exclusiva al Proveedor de Contenido y los datos que contiene: Los procesos externos encuentran el Proveedor de Contenido y los datos correspondientes a través del URI y luego realizan operaciones de datos
(3) Uso específico: El URI se divide en preajuste y personalizado del sistema, que corresponde a los datos integrados del sistema (como la libreta de direcciones, calendario, etc.) y la base de datos personalizada
Inserte la descripción de la imagen aquí

// 设置URI
Uri uri = Uri.parse("content://com.carson.provider/User/1")
// 上述URI指向的资源是:名为 `com.carson.provider`的`ContentProvider` 中表名 为`User` 中的 `id`为1的数据

// 特别注意:URI模式存在匹配通配符* & #
// *:匹配任意长度的任何有效字符的字符串 #:匹配任意长度的数字字符的字符串
// 以下的URI 表示 匹配provider的任何内容
content://com.example.app.provider/* 
// 以下的URI 表示 匹配provider中的table表的所有行
content://com.example.app.provider/table/#

(2) tipo de datos MIME

(1) Función: ContentProvider devuelve el tipo MIME de acuerdo con el URI
(2) Composición = tipo padre + tipo hijo (el tipo padre es tipo fijo, se usa para distinguir registros únicos / múltiples, el subtipo se puede personalizar)
Forma 1: registro único: vnd .android.cursor.item / custom
form 2: registros múltiples: instancia vnd.android.cursor.dir / custom
(3)

// 单个记录的MIME类型
vnd.android.cursor.item/vnd.yourcompanyname.contenttype
// 若一个Uri如下:content://com.example.transportationprovider/trains/122
// 则ContentProvider会通过ContentProvider.geType(url)返回以下MIME类型
vnd.android.cursor.item/vnd.example.rail

// 多个记录的MIME类型
vnd.android.cursor.dir/vnd.yourcompanyname.contenttype
// 若一个Uri如下:content://com.example.transportationprovider/trains
// 则ContentProvider会通过ContentProvider.geType(url)返回以下MIME类型
vnd.android.cursor.dir/vnd.example.rail

(3) clase ContentProvider

(1) Método de
organización de datos ContentProvider organiza principalmente datos en forma de tablas (tablas, registros, campos)
(2) Utiliza principalmente el método
1, agrega datos

public Uri insert(Uri uri, ContentValues values)// 外部进程向 ContentProvider 中添加数据

2. Eliminar datos

public int delete(Uri uri, String selection, String[] selectionArgs)// 外部进程 删除 ContentProvider 中的数据

3. Consulta de datos

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)// 外部进程更新 ContentProvider 中的数据

4. Modificar los datos

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  String sortOrder) // 外部应用 获取 ContentProvider 中的数据
  1. Los cuatro métodos anteriores son devueltos por un proceso externo y se ejecutan en el grupo de subprocesos Binder del proceso ContentProvider (no el subproceso principal)
  2. Hay acceso concurrente multiproceso, y la sincronización de subprocesos debe implementarse
    a. Si el método de almacenamiento de datos de ContentProvider es usar SQLite & one, no es necesario, porque SQLite implementa la sincronización de subprocesos internamente, si se necesitan múltiples SQLite, porque los objetos SQL no pueden Sincronización de subprocesos
    b. Si el método de almacenamiento de datos de ContentProvider es la memoria, debe implementar la sincronización de subprocesos usted mismo

5. Otros métodos

public boolean onCreate()
// ContentProvider创建后 或 打开系统后其它进程第一次访问该ContentProvider时 由系统进行调用
// 注:运行在ContentProvider进程的主线程,故不能做耗时操作
public String getType(Uri uri)
// 得到数据类型,即返回当前 Url 所代表数据的MIME类型

(3) Android proporciona un ContentProvider predeterminado incorporado para datos comunes (como libreta de direcciones, calendario, etc.) y también puede personalizar el ContentProvider según las necesidades. Los 6 métodos anteriores deben reescribirse. La clase ContentProvider no interactúa directamente con procesos externos, sino a través de la clase ContentResolver.

(4) herramientas auxiliares

1. Clase ContentPesolver

1. Rol: gestión unificada de operaciones entre diferentes proveedores de contenido

  1. Es decir, puede manipular los datos en diferentes proveedores de contenido a través del URI
  2. El proceso externo interactúa con la clase ContentProvider a través de la clase ContentResolver

2. Una aplicación necesita usar múltiples ContentProviders (se pueden instalar muchas aplicaciones que contienen proveedores en el teléfono móvil, como aplicaciones de contacto, aplicaciones de calendario, aplicaciones de diccionario, etc.), de modo que la gestión unificada de todos los ContentProviders con un ContentResolver puede reducir los costos operativos y lograr dificultades Pequeño.
ContentResolver distingue diferentes ContentProviders por URL
3. Método de uso
3.1) Uso específico: ContentResolver proporciona 4 métodos con el mismo nombre y función que la clase ContentProvider

// 外部进程向 ContentProvider 中添加数据
public Uri insert(Uri uri, ContentValues values) 
// 外部进程 删除 ContentProvider 中的数据
public int delete(Uri uri, String selection, String[] selectionArgs)
// 外部进程更新 ContentProvider 中的数据
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 
// 外部应用 获取 ContentProvider 中的数据
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

3.2) Ejemplos

// 使用ContentResolver前,需要先获取ContentResolver
// 可通过在所有继承Context的类中 通过调用getContentResolver()来获得ContentResolver
ContentResolver resolver =  getContentResolver();
// 设置ContentProvider的URI
Uri uri = Uri.parse("content://cn.scu.myprovider/user");
// 根据URI 操作 ContentProvider中的数据
// 此处是获取ContentProvider中 user表的所有记录 
Cursor cursor = resolver.query(uri, null, null, null, "userid desc"); 
2. ContentUris

2.1) Función: operación URI
2.2) Uso específico: withAppendedId () & parseId ()

//withAppendedId()作用:向URI追加一个id
Uri uri = Uri.parse("content://cn.scu.myprovider/user")
Uri resultUri = ContentUris.withAppendedId(uri, 7);
// 最终生成后的Uri为:content://cn.scu.myprovider/user/7

// parseId()作用:从URL中获取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")
long personid = ContentUris.parseId(uri);
//获取的结果为:7
3. clase UriMatcher

3.1) Función:
1. Registrar URI en ContentProvider (2) Hacer coincidir la tabla de datos correspondiente a ContentProvider de acuerdo con URI
3.2) Uso específico:

// 步骤1:初始化UriMatcher对象
    UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    //常量UriMatcher.NO_MATCH  = 不匹配任何路径的返回码
    // 即初始化时不匹配任何东西

// 步骤2:在ContentProvider 中注册URI(addURI())
    int URI_CODE_a = 1;
        int URI_CODE_b = 2;
        matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a);
        matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b);
// 若URI资源路径 = content://cn.scu.myprovider/user1 ,则返回注册码URI_CODE_a
// 若URI资源路径 = content://cn.scu.myprovider/user2 ,则返回注册码URI_CODE_b

// 步骤3:根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())

@Override
public String getType(Uri uri) {
        Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");

        switch(matcher.match(uri)){
        // 根据URI匹配的返回码是URI_CODE_a
        // 即matcher.match(uri) == URI_CODE_a
        case URI_CODE_a:
        return tableNameUser1;
        // 如果根据URI匹配的返回码是URI_CODE_a,则返回ContentProvider中的名为tableNameUser1的表
        case URI_CODE_b:
        return tableNameUser2;
        // 如果根据URI匹配的返回码是URI_CODE_b,则返回ContentProvider中的名为tableNameUser2的表
        }
        }
4. Clase ContentObserver

4.1) Definición: Content Observer
4.2) Función: Observe que Uri causa cambios en los datos en ContentProvider y notifique al mundo exterior (es decir, los visitantes que acceden a los datos): cuando los datos en ContentProvider cambian (agrega, elimina y cambia), se activará La clase ContentObserver notifica los cambios en los datos.
Escenarios aplicables: bases de datos que deben verificarse con frecuencia o si ciertos datos han cambiado. Si usa hilos para operar, no es económico y lleva mucho tiempo.
4.3) Uso específico:

// 步骤1:注册内容观察者ContentObserver
    getContentResolver().registerContentObserver(uri);
// 通过ContentResolver类进行注册,并指定需要观察的URI

// 步骤2:当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extends ContentProvider {
    public Uri insert(Uri uri, ContentValues values) {
        db.insert("user", "userid", values);
        getContext().getContentResolver().notifyChange(uri, null);
        // 通知访问者
    }
}
// 步骤3:解除观察者
    getContentResolver().unregisterContentObserver(uri);
// 同样需要通过ContentResolver类进行解除

4.4) Ejemplo:
los datos de mensajes cortos del sistema de observación han cambiado. Cuando se supervisa el cambio de datos de SMS, se consultan y se muestran todos los SMS enviados.
(1) Cree una clase derivada de ContentObserver SMSContentObserver.java que observe los cambios en la base de datos de mensajes cortos en el sistema

//用来观察系统里短消息的数据库变化 ”表“内容观察者,只要信息数据库发生变化,都会触发该ContentObserver 派生类
public class SMSContentObserver extends ContentObserver {
private static String TAG = "SMSContentObserver";
private int MSG_OUTBOXCONTENT = 2 ;
private Context mContext ;
private Handler mHandler ; //用Handler更新UI线程
public SMSContentObserver(Context context,Handler handler) {
super(handler);
mContext = context ;
mHandler = handler ;
}

//当所监听的Uri中数据发生变化回收,就会回调该方法。
@Override
public void onChange(boolean selfChange){
Log.i(TAG, "the sms table has changed");
//查询发件箱里的内容
Uri outSMSUri = Uri.parse("content://sms/sent") ;
Cursor c = mContext.getContentResolver().query(outSMSUri, null, null, null,"date desc");
if(c != null){
Log.i(TAG, "the number of send is"+c.getCount()) ;
StringBuilder sb = new StringBuilder() ;
//循环遍历
while(c.moveToNext()){
// sb.append("发件人手机号码: "+c.getInt(c.getColumnIndex("address")))
// .append("信息内容: "+c.getInt(c.getColumnIndex("body")))
// .append("是否查看: "+c.getInt(c.getColumnIndex("read")))
// .append("发送时间: "+c.getInt(c.getColumnIndex("date")))
// .append("\n");
sb.append("发件人手机号码: "+c.getInt(c.getColumnIndex("address")))
.append("信息内容: "+c.getString(c.getColumnIndex("body")))
.append("\n");
}
c.close();
mHandler.obtainMessage(MSG_OUTBOXCONTENT, sb.toString()).sendToTarget();
}
}
}

(2) Proceso principal MainActivity registra observadores de contenido y monitorea cambios de datos

public class MainActivity extends Activity {

private TextView tvAirplane;
private EditText etSmsoutbox;
// Message 类型值
private static final int MSG_OUTBOXCONTENT = 1;
private SMSContentObserver smsContentObserver;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tvAirplane = (TextView) findViewById(R.id.tvAirplane);
etSmsoutbox = (EditText) findViewById(R.id.smsoutboxContent);
// 创建ContentObserver对象
smsContentObserver = new SMSContentObserver(this, mHandler);
//步骤1:注册内容观察者
registerContentObservers() ;
}
private void registerContentObservers() {
//指定监听Uri并注册监听该Uri,该Uri的contentProvider中数据变化会执行notifychange()通知Observer数据变化
Uri smsUri = Uri.parse("content://sms");
getContentResolver().registerContentObserver(smsUri, true,smsContentObserver);
}

private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
System.out.println("---mHanlder----");
switch (msg.what) {
case MSG_OUTBOXCONTENT:
String outbox = (String) msg.obj;
//显示短信信息
etSmsoutbox.setText(outbox);
break;
default:
break;
}
}
};
}

(5) Descripción de ejemplo (la fuente de datos adopta SQLite)

1. Comunicación en proceso

Descripción del paso:

  • Crear una clase de base de datos
  • Clase ContentProvider personalizada
  • Clase ContentProvider creada por registro
  • Acceso en proceso a los datos de ContentProvider

(1) Crear una clase de base de datos (clase de paquete de base de datos SQLite)

public class DBHelper extends SQLiteOpenHelper {

    // 数据库名
    private static final String DATABASE_NAME = "finch.db";

    // 表名
    public static final String USER_TABLE_NAME = "user";
    public static final String JOB_TABLE_NAME = "job";

    private static final int DATABASE_VERSION = 1;
    //数据库版本号

    public DBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

        // 创建两个表格:用户表 和职业表
        db.execSQL("CREATE TABLE IF NOT EXISTS " + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");
        db.execSQL("CREATE TABLE IF NOT EXISTS " + JOB_TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " job TEXT)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)   {

    }
}

(2) clase Custom ContentProvider

public class MyProvider extends ContentProvider {

    private Context mContext;
    DBHelper mDbHelper = null;
    SQLiteDatabase db = null;

    public static final String AUTOHORITY = "cn.scu.myprovider";
    // 设置ContentProvider的授权信息(唯一标识),一般为包名

    public static final int User_Code = 1;
    public static final int Job_Code = 2;

    // UriMatcher类使用:在ContentProvider 中注册URI
    private static final UriMatcher mMatcher;
    static{
        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        // 初始化
        mMatcher.addURI(AUTOHORITY,"user", User_Code);
        mMatcher.addURI(AUTOHORITY, "job", Job_Code);
        // 若URI资源路径 = content://cn.scu.myprovider/user ,则返回注册码User_Code
        // 若URI资源路径 = content://cn.scu.myprovider/job ,则返回注册码Job_Code
    }

    // 重写ContentProvider的6个方法

    /**
     * 初始化ContentProvider
     */
    @Override
    public boolean onCreate() {

        mContext = getContext();
        // 在ContentProvider创建时对数据库进行初始化
        // 运行在主线程,故不能做耗时操作,此处仅作展示
        mDbHelper = new DBHelper(getContext());
        db = mDbHelper.getWritableDatabase();

        // 初始化两个表的数据(先清空两个表,再各加入一个记录)
        db.execSQL("delete from user");
        db.execSQL("insert into user values(1,'Carson');");
        db.execSQL("insert into user values(2,'Kobe');");

        db.execSQL("delete from job");
        db.execSQL("insert into job values(1,'Android');");
        db.execSQL("insert into job values(2,'iOS');");

        return true;
    }
    /**
     * 添加数据
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {

        // 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
        // 该方法在最下面
        String table = getTableName(uri);
        // 向该表添加数据
        db.insert(table, null, values);

        // 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
        mContext.getContentResolver().notifyChange(uri, null);

//        // 通过ContentUris类从URL中获取ID
//        long personid = ContentUris.parseId(uri);
//        System.out.println(personid);

        return uri;
    }

    /**
     * 查询数据
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
        // 该方法在最下面
        String table = getTableName(uri);

//        // 通过ContentUris类从URL中获取ID
//        long personid = ContentUris.parseId(uri);
//        System.out.println(personid);

        // 查询数据
        return db.query(table,projection,selection,selectionArgs,null,null,sortOrder,null);
    }
	//更新数据
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // 由于不展示,此处不作展开
        return 0;
    }
	//删除数据
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // 由于不展示,此处不作展开
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        // 由于不展示,此处不作展开
        return null;
    }

    /**
     * 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
     */
    private String getTableName(Uri uri){
        String tableName = null;
        switch (mMatcher.match(uri)) {
            case User_Code:
                tableName = DBHelper.USER_TABLE_NAME;
                break;
            case Job_Code:
                tableName = DBHelper.JOB_TABLE_NAME;
                break;
        }
        return tableName;
    }
}

(3) Clase ContentProvider creada por registro

<provider android:name="MyProvider"
    android:authorities="cn.scu.myprovider"
/>

(4) Datos que acceden a ContentProvider en proceso

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 对User表进行操作
        // 设置URI
        Uri uri_user = Uri.parse("content://cn.scu.myprovider/user");

        // 插入表中数据
        ContentValues values = new ContentValues();
        values.put("_id", 3);
        values.put("name", "Iverson");

        // 获取ContentResolver
        ContentResolver resolver =  getContentResolver();
        // 通过ContentResolver 根据URI 向ContentProvider中插入数据
        resolver.insert(uri_user,values);

        // 通过ContentResolver 向ContentProvider中查询数据
        Cursor cursor = resolver.query(uri_user, new String[]{"_id","name"}, null, null, null);
        while (cursor.moveToNext()){
            System.out.println("query book:" + cursor.getInt(0) +" "+ cursor.getString(1));
            // 将表中数据全部输出
        }
        cursor.close();
        // 关闭游标

        //对job表进行操作
        // 和上述类似,只是URI需要更改,从而匹配不同的URI CODE,从而找到不同的数据资源
        Uri uri_job = Uri.parse("content://cn.scu.myprovider/job");

        // 插入表中数据
        ContentValues values2 = new ContentValues();
        values2.put("_id", 3);
        values2.put("job", "NBA Player");

        // 获取ContentResolver
        ContentResolver resolver2 =  getContentResolver();
        // 通过ContentResolver 根据URI 向ContentProvider中插入数据
        resolver2.insert(uri_job,values2);

        // 通过ContentResolver 向ContentProvider中查询数据
        Cursor cursor2 = resolver2.query(uri_job, new String[]{"_id","job"}, null, null, null);
        while (cursor2.moveToNext()){
            System.out.println("query job:" + cursor2.getInt(0) +" "+ cursor2.getString(1));
            // 将表中数据全部输出
        }
        cursor2.close();
        // 关闭游标
    }
}

2. Comunicación entre procesos (intercambio de datos)

(1) Proceso 1:
1. Crear una clase de base de datos
2. Personalizar la clase ContentProvider
3. Registrarse y crear la clase ContentProvider

<provider
    android:name="MyProvider"
    android:authorities="scut.carson_ho.myprovider"

// 声明外界进程可访问该Provider的权限(读 & 写)
android:permission="scut.carson_ho.PROVIDER"
// 权限可细分为读 & 写的权限
// 外界需要声明同样的读 & 写的权限才可进行相应操作,否则会报错
// android:readPermisson = "scut.carson_ho.Read"
// android:writePermisson = "scut.carson_ho.Write"
// 设置此provider是否可以被其他进程使用
android:exported="true"
/>

// 声明本应用 可允许通信的权限
<permission android:name="scut.carson_ho.Read" android:protectionLevel="normal"/>
// 细分读 & 写权限如下,但本Demo直接采用全权限
// <permission android:name="scut.carson_ho.Write" android:protectionLevel="normal"/>
// <permission android:name="scut.carson_ho.PROVIDER" android:protectionLevel="normal"/>

(2) Proceso 2:
(1) Declaración de permisos accesibles
(2) Acceso a ContentProvider clase
1. Declaración de permisos accesibles

// 声明本应用可允许通信的权限(全权限)
<uses-permission android:name="scut.carson_ho.PROVIDER"/>
// 细分读 & 写权限如下,但本Demo直接采用全权限
// <uses-permission android:name="scut.carson_ho.Read"/>
//  <uses-permission android:name="scut.carson_ho.Write"/>
// 注:声明的权限必须与进程1中设置的权限对应

2. Acceda a la clase ContentProvider

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /**
         * 对user表进行操作
         */
        // 设置URI
        Uri uri_user = Uri.parse("content://scut.carson_ho.myprovider/user");
        // 插入表中数据
        ContentValues values = new ContentValues();
        values.put("_id", 4);
        values.put("name", "Jordan");
        // 获取ContentResolver
        ContentResolver resolver =  getContentResolver();
        // 通过ContentResolver 根据URI 向ContentProvider中插入数据
        resolver.insert(uri_user,values);
        // 通过ContentResolver 向ContentProvider中查询数据
        Cursor cursor = resolver.query(uri_user, new String[]{"_id","name"}, null, null, null);
        while (cursor.moveToNext()){
            System.out.println("query book:" + cursor.getInt(0) +" "+ cursor.getString(1));
            // 将表中数据全部输出
        }
        cursor.close();
        // 关闭游标
        /**
         * 对job表进行操作
         */
        // 和上述类似,只是URI需要更改,从而匹配不同的URI CODE,从而找到不同的数据资源
        Uri uri_job = Uri.parse("content://scut.carson_ho.myprovider/job");
        // 插入表中数据
        ContentValues values2 = new ContentValues();
        values2.put("_id", 4);
        values2.put("job", "NBA Player");
        // 获取ContentResolver
        ContentResolver resolver2 =  getContentResolver();
        // 通过ContentResolver 根据URI 向ContentProvider中插入数据
        resolver2.insert(uri_job,values2);
        // 通过ContentResolver 向ContentProvider中查询数据
        Cursor cursor2 = resolver2.query(uri_job, new String[]{"_id","job"}, null, null, null);
        while (cursor2.moveToNext()){
            System.out.println("query job:" + cursor2.getInt(0) +" "+ cursor2.getString(1));
            // 将表中数据全部输出
        }
        cursor2.close();
        // 关闭游标
    }
}

Se puede ver que el acceso de ContentProvider entre procesos necesita declarar permisos
(1) El proceso 1 declara que procesos externos pueden acceder a los permisos del Proveedor

<provider 
               android:name="MyProvider"
               android:authorities="scut.carson_ho.myprovider"

               // 声明外界进程可访问该Provider的权限(读 & 写)
               android:permission="scut.carson_ho.PROVIDER"             
               
               // 权限可细分为读 & 写的权限
               // 外界需要声明同样的读 & 写的权限才可进行相应操作,否则会报错
               // android:readPermisson = "scut.carson_ho.Read"
               // android:writePermisson = "scut.carson_ho.Write"

               // 设置此provider是否可以被其他进程使用
               android:exported="true"
                
  />

// 声明本应用 可允许通信的权限
    <permission android:name="scut.carson_ho.Read" android:protectionLevel="normal"/>
    // 细分读 & 写权限如下,但本Demo直接采用全权限
    // <permission android:name="scut.carson_ho.Write" android:protectionLevel="normal"/>
    // <permission android:name="scut.carson_ho.PROVIDER" android:protectionLevel="normal"/>

(2) El proceso 2 declara permisos accesibles

    // 声明本应用可允许通信的权限(全权限)
    <uses-permission android:name="scut.carson_ho.PROVIDER"/>

    // 细分读 & 写权限如下,但本Demo直接采用全权限
    // <uses-permission android:name="scut.carson_ho.Read"/>
    //  <uses-permission android:name="scut.carson_ho.Write"/>
    
// 注:声明的权限必须与进程1中设置的权限对应
Publicado 74 artículos originales · alabanza ganado 15 · vistas 6256

Supongo que te gusta

Origin blog.csdn.net/qq_29966203/article/details/90382633
Recomendado
Clasificación