0%

安卓内容提供者

内容提供者

这个东西主要是用来提供外部访问自身数据库的,也就是相对于给我们一个出口,将我们的数据对外提供。
这个东西一般是和数据库进行配合使用。
所以首先我们先构造一个数据库:

1
2
MySQLiteOpenHelper mso = new MySQLiteOpenHelper(getBaseContext());
mso.getReadableDatabase();

先写一个自己的数据库,然后实例化创建我们的数据库,数据库代码:

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
package com.example.android_study13.database;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class MySQLiteOpenHelper extends SQLiteOpenHelper {

public MySQLiteOpenHelper(Context context) {
super(context, "DATA.db", null, 1);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table student(id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,age INTEGER)");
db.execSQL("create table teacher(id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,kemu TEXT)");
}

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

}

}

就是在创建的时候我们创建我们的数据库。
一个学生表和老师表。
下面是关键,我们需要写一个继承于ContentProvider的类,这个类是用来重写那些方法的,而且这个类也是我们需要配置的:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public boolean onCreate() {
Log.d("wker", "Oncreate");
try {
SQLiteOpenHelper open = new MySQLiteOpenHelper(getContext());
database = open.getWritableDatabase();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;//返回初始化是否成功
}
}

首先我们在这个方法中重写,主要使用来获取数据库操作对象的。
下面写插入数据:

1
2
3
4
5
6
7
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.d("wker", "query");
ContentResolver resolver = getContext().getContentResolver();
resolver.notifyChange(uri, null);
return null;
}

返回一个数据库的迭代器,意思就是将我们数据库查询的对象返回回去,我这里没写查询,但是要看下面的两句代码,这个就是通过上下文环境获取内容解析者,获取到解析者之后通知内容监听者,我们有新的信息了。
添加方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d("wker", "insert");
long result=-1;
switch(matcher.match(uri))
{
case STUDENT:
result = database.insert("student", null, values);
break;
case TEACHER:
result = database.insert("teacher", null, values);
break;
}
return Uri.parse("result://io.wker666/"+result);
}

这个就是数据库的插入,传进来一个Uri的对象,这个对象中有我们要进行操作的数据,我们可以通过UriMatcher的匹配方法判断是不是满足某个条件的Uri。
所以我们先要去创建一个UriMatcher的对象:

1
2
3
4
5
6
7
8
9
10
private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int STUDENT = 1;
private static final int TEACHER = 2;

static
{
//添加URI匹配方法
matcher.addURI("io.wker666", "student", STUDENT);//匹配的时候返回的值
matcher.addURI("io.wker666", "teacher", TEACHER);//匹配的时候返回的值
}

UriMatcher对象是专门用来进行匹配Uri的就有点像正则匹配器,在初始化的时候传进去一个int值,这个值代表的是我们没有匹配到结果的时候我们返回的结果,addURI这个方法是用来添加Uri匹配的,第一个参数是Uri的域名,第二个数路径,第三个参数是我们的返回值,这里我们返回一个常量,上面定义了的。
所以我们在插入数据的时候,我们首先匹配这个Uri对象,然后根据返回值来给特定的表段进行增删改查。
最后插入完毕之后我们就将我们的结果返回回去。
比较特别的是一个getType()方法,这个方法主要是用来判断Uri对象所对应的类型的:

1
2
3
4
public String getType(Uri uri) {
Log.d("wker", "getType");
return null;
}

这里我没改,返回的一般是一个MIME的字符串。
写完之后,我们就要将我们的这个类配置我们XML文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="com.example.android_study13.provider.MyContentProvider"
android:authorities="io.wker666"
android:exported="true"
/>
</application>

最关键的就是那个provider这个节点,name属性使我们需要对外提供的全类名,如果不想写全类名,那么就要将我们的包写在配置文件根节点上,authorities这个属性使用来表示我们的根Uri的,exported这个属性必须要要,否则会抛出异常,true代表是对外可以访问。

内容解析者

用来获取内容提供给我们的数据的:

1
2
3
4
5
6
public void onQuery(View view)
{
ContentResolver resolver = getContentResolver();//内容解析者
Uri uri = Uri.parse("content://io.wker666");
resolver.query(uri, null, null, null, null);
}

比较简单,收我们先要获取内容解析者,这个东西应该是个单例对象,获取之后我们就能调用相对应的内容提供者中的方法了,但是之前我们先要配一下我们的Uri对象,配置的时候,我们不能忘记丢掉我们的协议名content://,也就是我们内容提供者中所提供的类名,然后进行查询就好了,也就会调用我们在内容提供者中编写的方法。

1
2
3
4
5
6
7
8
9
10
11
public void onInsert(View view)
{
ContentResolver resolver = getContentResolver();//内容解析者
Uri uri = Uri.parse("content://io.wker666/"+"teacher");

ContentValues values = new ContentValues();
values.put("name", "wker");
values.put("kemu", "c");
Uri reu= resolver.insert(uri, values );
Log.d("wker", reu.toString());
}

这个和上面类似,也就是配置我们的Uri,配置好之后,我们要加入的数据,通过ContentValues这个键值对的对象,然后将返回给我们的Uri接受一下,也就是得到我们加入了第几行。

内容监听者

这个东西也是比较简单,主要是重写父类的方法,新建一个类,继承ContentObserver这个类,然后重写我们的onChange方法,也就是在内容提供者有对应操作的时候会给我们提供数据:
public void onChange(boolean selfChange, Uri uri),可以看到传进来一个Uri,也就是我们要的数据。
我们在使用这个东西之前我们先要注册这个内容监听者:

1
2
3
4
5
6
7
8
private MyObserver mco;//也就是我们自定义的那个内容监听者
/*
一些代码
...
*/
mco = new MyObserver(new Handler());
Uri uri = Uri.parse("content://sms");
getContentResolver().registerContentObserver(uri, true, mco);

注册也比较简单,就是通过内容解析者来注册这个内容监听者,第一个参数是我们要监听的Uri,第二个参数是我们是否想要进行模糊匹配,第三个参数就是我们的实例对象。
之后我们有数据传输的时候我们就会调用onChange方法了。
我们如果想要注销的话呢:
getContentResolver().unregisterContentObserver(mco);
这个样子就可以进行注销内容监听者了。
但是有一点要注意,我们一定要在数据传输的时候要使用内容解析者的notify方法通知内容监听者:

1
2
ContentResolver resolver = getContext().getContentResolver();
resolver.notifyChange(uri, null);

也就是这个东西,否则我们是接收不到的。