您的位置:首页 > 移动开发 > Android开发

Android20_自定义ContentProvider

2016-01-20 09:55 435 查看
自定义ContentProvider


一、自定义ContentProvider:
(一)、操作步骤:

1、编写一个类,必须继承自ContentProvider类;

2、实现ContentProvider类中所有的抽象方法;

需要实现:onCreate() 、getType() 、query()
、insert() 、update()、delete() 等方法。

【备注:】

ContentProvider暴露出来的数据和方法并不是给自身调用的,而是给其他应用程序来调用。其他应用程序通过其ContentResolver对象调用的query() 、insert() 、update()、delete()
等方法就是我们在这里暴露出来的ContentProvider类中的重写后的query() 、insert() 、update()、delete()
方法。

3、定义ContentProvider的Uri。这个Uri是ContentResolver对象执行CRUD操作时重要的参数;

4、使用UriMatcher对象映射Uri返回代码;

5、在AndroidMainfest.xml文件中使用<provider>标签注册ContentProvider。

(二)、ContentProvider类中的六个抽象方法:

1、boolean onCreate()

2、Uri insert(Uri uri, ContentValues values)

3、int delete(Uri uri, String selection, String[] selectionArgs)

4、int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

5、Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

6、String getType(Uri uri)

(三)、ContentProvider类中六个抽象方法的说明:

1、onCreate() 初始化provider

2、query() 返回数据给调用者

3、insert() 插入新数据到ContentProvider

4、update() 更新ContentProvider已经存在的数据

5、delete() 从ContentProvider中删除数据

6、getType() 返回ContentProvider数据的Mime类型

(四)、在清单文件中声明注册ContentProvider:


<provider android:name=".MyWordsProvider"
android:authorities="com.steven.wordscontentprovider"
android:exported="true"
/>

//android:name属性的值是:ContentProvider类的子类的完整路径;
//android:authorities属性的值是:content:URI中authority部分。一般就是将name属性的值全小写。
//android:exported属性是否允许其他应用调用。如果是false,则该ContentProvider不允许其他应用调用。


(五)、UriMatcher:
继承ContentProvider类后发现,ContentProvider类中只有一个onCreate()生命周期方法——当其他应用程序通过ContentResolver第一次访问该ContentProvider时,onCreate()会被回调。
其他应用在通过ContentResolver对象执行CRUD操作时,都需要一个重要的参数Uri。为了能顺利提供这个Uri参数,Android系统提供了一个UriMatcher工具类。

UriMatcher工具类提供了两个方法:

1、void addURI(String authority , String path , int code) : 该方法用于向UriMatcher对象注册Uri。其中authority和path是Uri中的重要部分。而code代表该Uri对应的标示符。
2、int match(Uri uri) : 根据注册的Uri来判断指定的Uri对应的标示符。如果找不到匹配的标示符,该方法返回-1。

private static UriMatcher matcher = null;
static {

// 定义一个Uri匹配器。将UriMatcher.NO_MATCH,即-1作为参数。

matcher = new UriMatcher(UriMatcher.NO_MATCH);

// 定义一组匹配规则

matcher.addURI(AUTHORITY, "words", 1);

matcher.addURI(AUTHORITY, "newwords", 2);

}


【备注:】
ContentProvider是单例模式的,当多个应用程序通过使用ContentResolver 来操作使用ContentProvider 提供的数据时,ContentResolver
调用的数据操作会委托给同一个ContentProvider 来处理。这样就能保证数据的一致性。

(六)、示例代码:


一、清单配置文件中注册ContentProvider:<provider android:name=".MyWordsProvider" android:authorities="com.steven.wordscontentprovider" android:exported="true" />

二、ContentProvider 的程序部分:
publicclass MyWordsProvider extends ContentProvider {
private MySQLiteOpenHelper dbHelper = null;
private SQLiteDatabase db = null;
privatestatic UriMatcher matcher = null;
privatestaticfinal String AUTHORITY = "com.steven.wordscontentprovider";

static {
matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(AUTHORITY, "words", 1);
matcher.addURI(AUTHORITY, "newwords", 2);
}

@Override
publicboolean onCreate() {
dbHelper = new MySQLiteOpenHelper(getContext());
db = dbHelper.getReadableDatabase();
returntrue;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (matcher.match(uri)) {
case 1:
Cursor cursor = db.query("tb_words", projection, selection,
selectionArgs, null, null, sortOrder);
return cursor;
case 2:
Cursor cursor2 = db.query("tb_newwords", projection, selection,
selectionArgs, null, null, sortOrder);
return cursor2;
default:
break;
}
returnnull;
}

@Override
public String getType(Uri uri) {
returnnull;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
long rowId = 0;
switch (matcher.match(uri)) {
case 1:
rowId = db.insert("tb_words", null, values);
if (rowId > 0) {
Uri newUri = ContentUris.withAppendedId(
Uri.parse("content://" + AUTHORITY + "/words"), rowId);
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
break;
case 2:
rowId = db.insert("tb_newwords", null, values);
if (rowId > 0) {
Uri newUri = ContentUris.withAppendedId(
Uri.parse("content://" + AUTHORITY + "/newwords"),
rowId);
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
break;
default:
break;
}
returnnull;
}

@Override
publicint delete(Uri uri, String selection, String[] selectionArgs) {
switch (matcher.match(uri)) {
case 1:
int count = db.delete("tb_words", selection, selectionArgs);
if (count > 0) {
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
case 2:
int count2 = db.delete("tb_newwords", selection, selectionArgs);
if (count2 > 0) {
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(uri, null);
}
return count2;
default:
break;
}
return 0;
}

@Override
publicint update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
switch (matcher.match(uri)) {
case 1:
int count = db.update("tb_words", values, selection, selectionArgs);
if (count > 0) {
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
case 2:
int count2 = db.update("tb_newwords", values, selection,
selectionArgs);
if (count2 > 0) {
// 通知监听器,数据已经改变
getContext().getContentResolver().notifyChange(uri, null);
}
return count2;
default:
break;
}
return 0;
}

}


二、Java知识回顾:

(一)、静态代码块的作用:

一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的。静态代码块和静态方法两者的区别就是:静态代码块是自动执行的;静态方法是被调用的时候才执行的。

1、在Java里,可以定义一个不需要创建对象的方法,这种方法就是静态方法。要实现这样的效果,只需要在类中定义的方法前加上static关键字。例如:

public static int maximum(int n1,int n2)

使用类的静态方法时,注意:

在静态方法里只能直接调用同类中其他的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。这是因为,对于非静态的方法和变量,需要先创建类的实例对象后才可使用,而静态方法在使用前不用创建任何对象。
静态方法不能以任何方式引用this和super关键字,因为静态方法在使用前不用创建任何实例对象,当静态方法调用时,this所引用的对象根本没有产生。

2、静态变量属于整个类,而不是属于某个对象的。注意不能把任何方法体内的变量声明为静态,例如:

fun() {

static int i=0;//非法。

}

3、一个类可以使用不包含在任何方法体中的静态代码块,当类被载入时,静态代码块被执行,且只被执行一次,静态块常用来执行类属性的初始化。例如:

static {

}

(二)、对象的初始化顺序:

静态代码块对象的初始化顺序 :内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。

注意:子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的无参构造方法。如果父类没有无参构造方法,那么子类必须用super关键子来调用父类带参构造方法,否则编译不能通过。

(三)、类装载步骤:

在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:

1、装载:查找和导入类或接口的二进制数据;

2、链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;

校验:检查导入类或接口的二进制数据的正确性;
准备:给类的静态变量分配并初始化存储空间;
解析:将符号引用转成直接引用;

3、初始化:激活类的静态变量和静态Java代码块。 初始化类中属性是静态代码块的常用用途,但只能使用一次。

三、UriMatcher对象的addURI方法的#和*问题:

(一)、#和*的含义:

1、UriMatcher.addURI(String authority, String path, int code)

Parameters:

authority the authority to match

path the path to match. * may be used as a wild card for any text, and # may be used as a wild card for numbers.

code the code that is returned when a URI is matched against the given components. Must be positive.

*和#是两个通配符,它们用在uri地址中,*可以匹配任何文本,#匹配任何数字。

(二)、截取Uri地址和拼接Uri地址的方法:【重点】

1、Uri对象的截取String方法:

public abstract String getLastPathSegment ()

public abstract String getPath ()

public abstract List<String> getPathSegments ()

2、Uri对象的拼接String方法:

public static Uri withAppendedPath (Uri baseUri, String pathSegment)

3、ContentUris对象的截取id的方法:

public static long parseId (Uri contentUri)

4、ContentUris对象的拼接id的方法:

public static Uri withAppendedId (Uri contentUri, long id)

【备注:】Content URIs 的语法规则:content://authority/path/id

(三)、创建ContentProvider的示例代码:

1、MyProvider.java中的核心代码:

publicclass MyProvider extends ContentProvider {

private MySQLiteOpenHelper dbHelper = null;

private SQLiteDatabase db = null;

privatestatic UriMatcher matcher = null;

privatestaticfinal String AUTHORITY = "com.steven.contentprovider.mywordsprovider";

static {

matcher = new UriMatcher(-1);

matcher.addURI(AUTHORITY, "words", 1);

matcher.addURI(AUTHORITY, "newwords", 2);

matcher.addURI(AUTHORITY, "words/*", 3);// 匹配任何文本

matcher.addURI(AUTHORITY, "words_id/#", 4);// 匹配任何数字

matcher.addURI(AUTHORITY, "words_zh/*", 5);// 匹配任何中文文本

}

@Override

publicboolean onCreate() {

dbHelper = new MySQLiteOpenHelper(this.getContext());

db = dbHelper.getReadableDatabase();

returntrue;

}

@Override

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder) {

switch (matcher.match(uri)) {

case 1:

returndb.query("tb_words", projection, selection, selectionArgs,

null, null, sortOrder);

case 2:

returndb.query("tb_newwords", projection, selection,

selectionArgs, null, null, sortOrder);

case 3:

Log.i("MyProvider", "==返回值是:" + matcher.match(uri));

String data = uri.getLastPathSegment();

Log.i("MyProvider", "==" + data);

returndb.query("tb_words", projection, "word like ?",

new String[] { data + "%" }, null, null, sortOrder);

case 4:

Log.i("MyProvider", "==返回值是:" + matcher.match(uri));

String data2 = uri.getLastPathSegment();

// long id = ContentUris.parseId(uri);

Log.i("MyProvider", "==" + data2);

returndb.query("tb_words", projection, "_id=?",

new String[] { data2 }, null, null, sortOrder);

case 5:

String data3 = uri.getLastPathSegment();

returndb.query("tb_words", projection, "detail like ?",

new String[] { data3 + "%" }, null, null, sortOrder);

default:

Log.i("MyProvider", "==返回值是:" + matcher.match(uri));

returnnull;

}

}

@Override

public Uri insert(Uri uri, ContentValues values) {

switch (matcher.match(uri)) {

case 1:

long id = db.insert("tb_words", null, values);

return Uri.parse("content://" + AUTHORITY + "/words/" + id);

case 2:

long id2 = db.insert("tb_newwords", null, values);

return Uri.parse("content://" + AUTHORITY + "/newwords/" + id2);

}

returnnull;

}

@Override

publicint delete(Uri uri, String selection, String[] selectionArgs) {

switch (matcher.match(uri)) {

case 1:

returndb.delete("tb_words", selection, selectionArgs);

case 2:

returndb.delete("tb_newwords", selection, selectionArgs);

}

return 0;

}

@Override

publicint update(Uri uri, ContentValues values, String selection,

String[] selectionArgs) {

switch (matcher.match(uri)) {

case 1:

returndb.update("tb_words", values, selection, selectionArgs);

case 2:

returndb.update("tb_newwords", values, selection, selectionArgs);

}

return 0;

}

@Override

public String getType(Uri uri) {

returnnull;

}

}


2、日志截图:



(四)、ContentResolver中验证ContentProvider的示例代码:【本案例正好用于学习搜索控件:SearchView】

1、XML布局文件的代码:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical">

<SearchView

android:id="@+id/searchView_main"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

</SearchView>

<ListView

android:id="@+id/listView_main"

android:layout_width="match_parent"

android:layout_height="match_parent">

</ListView>

</LinearLayout>


2、MainActivity的代码:

publicclass MainActivity extends Activity {

privatestaticfinal String TAG = "MainActivity";

private ListView listView_main;

private SearchView searchView_main;

private MySQLiteOpenHelper dbHelper = null;

private SQLiteDatabase db = null;

@Override

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

listView_main = (ListView) findViewById(R.id.listView_main);

searchView_main = (SearchView) findViewById(R.id.searchView_main);

dbHelper = new MySQLiteOpenHelper(this);

db = dbHelper.getReadableDatabase();

// SearchView控件的监听器:查询文本监听器OnQueryTextListener

searchView_main.setOnQueryTextListener(new OnQueryTextListener() {

@Override

publicboolean onQueryTextSubmit(String query) {

returnfalse;

}

// 当查询内容发生改变时执行该方法

@Override

publicboolean onQueryTextChange(String newText) {

fillListView(newText);

returnfalse;

}

});

}

publicvoid fillListView(String data) {

Cursor cursor = null;

ContentResolver resolver = getContentResolver();

Uri uri_words = Uri

.parse("content://com.steven.contentprovider.mywordsprovider/words");

// 如果查询的关键词为空,则执行没有where条件的查询,也就是显示所有数据。

if (data != null && !"".equals(data)) {

// 如果查询关键词不为空,则在原来的uri地址后追加需要查询的内容

uri_words = Uri.withAppendedPath(uri_words, data);

}

cursor = resolver.query(uri_words, null, null, null, null);

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,

R.layout.item_listview_main, cursor, new String[] { "word",

"detail" }, newint[] { R.id.text_item_word,

R.id.text_item_detail }, 2);

listView_main.setAdapter(adapter);

}

/*

* 以下代码跟本案例《SearchView实现查询》无关,而是测试Cursor是否为null的例子。

* 如果数据库的表为空,返回的Cursor是否为null? 结果证明cursor都不为null,只不过其中没有数据而已,但是存在表结构。

* 也就是说:即便是没有一条数据的cursor也可以获取到列名称。

*/

publicvoid clickButton(View view) {

switch (view.getId()) {

case R.id.button_main_show:

// 第一种cursor的生成方法

String sql = "select id ,abc  from tb_words";

Cursor cursor1 = dbHelper.selectCursor(sql, null);

// 第二种cursor的生成方法

Cursor cursor2 = db.query("tb_words", null, null, null, null, null,

null);

if (!cursor1.moveToFirst()) {

Toast.makeText(this, "数据为空!", Toast.LENGTH_LONG).show();

} else {

Toast.makeText(this, cursor1.moveToFirst() + "",

Toast.LENGTH_LONG).show();

String[] arrClos = cursor1.getColumnNames();

StringBuilder sb = new StringBuilder();

for (int i = 0; i < arrClos.length; i++) {

sb.append(arrClos[i]);

sb.append(",");

}

Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();

}

break;

default:

break;

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: