概述
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>