概述
ContentProvider是Android中提供的专门用于不同应用间数据交互和共享的组件。
ContentProvider实际上是对SQLiteOpenHelper的进一步封装,以一个或多个表的形式将数据呈现给外部应用,通过Uri映射来选择需要操作数据库中的哪个表,并对表中的数据进行增删改查处理。ContentProvider其底层使用了Binder来完成APP进程之间的通信,同时使用匿名共享内存来作为共享数据的载体。ContentProvider支持访问权限管理机制,以控制数据的访问者及访问方式,保证数据访问的安全性。
使用ContentProvider进行数据访问
Content Provider分为两类:
- Native Content Provider
- Custom Content Provider
我们首先来看看Custom Content Provider,在Custom Content Provider中,又可以分为两个部分:Content Provider和Customer,我们接下来通过ContentProvider创建一个数据库以及提供访问的接口,并由Customer进行数据的插入操作。
- Content Provider(数据提供方)的实现方法
a. create a Custom Content Prov (创建一个自定义的数据提供者)
首先我们创建一个名为phoneProvider的应用程序
在项目中,我们创建一个名为MyPro的类来实现ContentProvide的功能。
package com.example.phoneprovider;
import android.content.ContentProvider;
public class MyProv extends ContentProvider {}
使用自动补全将所有继承ContentProvider类需要实现的方法补全。
得到代码如下:
package com.example.phoneprovider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class MyProv extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}
b. Implement query handling methods(实现访问的处理方法)
在这里,我们以数据库的插入操作作为访问的处理方法示例。
我们需要在MyPro类中先实现 继承 SQLiteOpenHelper 类中的方法,并通过该继承的子类进行数据库的访问操作。
private static class OpenHelper extends SQLiteOpenHelper {
public OpenHelper(Context cont ){
super(cont,"test.db",null,1);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {}
}
声明 SQLiteDatabase 和 SQLiteOpenHelper 类的对象。
SQLiteDatabase sqlDB = null;
SQLiteOpenHelper oh = null;
实现 MyPro 中的 insert 方法
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
oh = new OpenHelper(getContext());
sqlDB = oh.getWritableDatabase();
sqlDB.insert("tb1",null,contentValues); //将提供的contentValues插入到表tb1中
return null;
}
我们实现完插入数据的方法,当然我们还需要在phoneProvider这个应用程序中先生成数据库和表,我们在MainAcitivity中实现该功能,这样,在应用程序启动时,会自动在程序的目录中创建数据库 test.db 和表 tb1 。
具体生成路径可参考:https://blog.csdn.net/GUYIIT/article/details/100591538
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SQLiteDatabase sqlDB = openOrCreateDatabase("test.db", SQLiteDatabase.CREATE_IF_NECESSARY, null);
sqlDB.execSQL("CREATE TABLE tb1 (id INTEGER PRIMARY KEY,name TEXT,phone LONG)");
sqlDB.close();
}
c. Specify the URI of Content Prov (指定一个Content Prov的URI,URI是统一资源标识符)
d. Register a custom content provider(注册一个自定义的Content provider,也就是我们需要向系统进行注册,注册完成之后,系统才知道有这么一个数据源,当使用者通过地址来找的时候,系统才可以找得到数据提供者)
那么我们怎么进行注册呢?
只需要向项目中 AndroidMainfest.xml 文件的 <application> 中加入这段代码即可。
<provider
android:name="MyProv"
android:authorities="com.example.phoneprovider"
android:exported="true">
</provider>
该段代码中的 android:name 写的是 ContentProvider 的类名,android:authorities 中写的是项目的名称,在这里我们仅仅使用到了 provider 中的三个属性,更多的属性使用方法可以参考 android 的开发文档。
到这里,我们就实现了整个Content Provider 的应用程序了,接下来我们需要另外创建一个项目作为 Customer (数据使用方),并利用它对我们已经写好的 phoneProvider 这个应用程序的数据库进行插入操作。
- Customer(数据使用方)
我们首先创建一个名为phoneUser的项目。
在该项目中,我们使用按钮触发事件的方式,但用户点击按钮时,触发事件,向 phoneProvider 应用程序中的数据库进行数据插入操作。因此我们需要在main_activity.xml加入一个button,并在MainActivity中定义该button的事件处理方法。
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
tools:layout_editor_absoluteX="167dp"
tools:layout_editor_absoluteY="262dp" />
public class MainActivity extends AppCompatActivity {
private final static String urlPro = "content://com.example.phoneprovider/tb1";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bt1 = (Button)findViewById(R.id.button);
bt1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}
}
a. ContentResolver (解析器,根据地址查找数据源)
在 onClick 事件中定义 ContentResolver 对象,用于查找数据源。
ContentResolver cr = getContentResolver();
定义数据源地址
Uri url = Uri.parse(urlPro);
进行数据的插入操作
ContentValues cv = new ContentValues();
cv.put("name","name1");
cv.put("phone",135125222222L);
b. Cursor c = ContentResolver.query() (在获得ContentResolver之后,调用其中的query方法来返回查找的指针)
使用 ContentResolver 对象完成插入数据操作
cr.insert(url,cv);
c. c.moveToNext() (遍历访问我们所查找的数据,类似与Android中对数据库的操作)
结果展示:
启动 phoneProvider 程序
查看 phoneProvider 数据库以及表的生成情况,可以看到已经成功生成了 test.db 以及 tb1 ,并且 tb1 中没有任何数据。
启动 phoneUser 程序
点击按钮后,查看数据库情况(点击一次按钮以及两次按钮的情况)
完整代码展示:
phoneProvider
/*MainActivity.java*/
package com.example.phoneprovider;
import androidx.appcompat.app.AppCompatActivity;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SQLiteDatabase sqlDB = openOrCreateDatabase("test.db", SQLiteDatabase.CREATE_IF_NECESSARY, null);
sqlDB.execSQL("CREATE TABLE tb1 (id INTEGER PRIMARY KEY,name TEXT,phone LONG)");
sqlDB.close();
}
}
/*MyPro.java*/
package com.example.phoneprovider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class MyProv extends ContentProvider {
SQLiteDatabase sqlDB = null;
SQLiteOpenHelper oh = null;
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
oh = new OpenHelper(getContext());
sqlDB = oh.getWritableDatabase();
sqlDB.insert("tb1",null,contentValues);
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
private static class OpenHelper extends SQLiteOpenHelper {
public OpenHelper(Context cont ){
super(cont,"test.db",null,1);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
}
/*AndroidManifest.xml*/
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.phoneprovider">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="MyProv"
android:authorities="com.example.phoneprovider"
android:exported="true">
</provider>
</application>
</manifest>
phoneUser:
/*MainActivity.java*/
package com.example.phoneuser;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private final static String urlPro = "content://com.example.phoneprovider/tb1";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bt1 = (Button)findViewById(R.id.button);
bt1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ContentResolver cr = getContentResolver();
Uri url = Uri.parse(urlPro);
ContentValues cv = new ContentValues();
cv.put("name","name1");
cv.put("phone",135125222222L);
cr.insert(url,cv);
}
});
}
}
/*activity_main.xml*/
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
tools:layout_editor_absoluteX="167dp"
tools:layout_editor_absoluteY="262dp" />
</androidx.constraintlayout.widget.ConstraintLayout>