安卓四大组件之ContentProvider

 

一、ContentProvider简介:

(一)、引入:
         数据库在Android当中是私有的,不能将数据库设为WORLD_READABLE,每个数据库都只能创建它的包访问。这意味着只有创建这个数据库的应用程序才可访问它。也就是说不能跨越进程和包的边界,直接访问别的应用程序的数据库。那么如何在应用程序间交换数据呢? 如果需要在进程间传递数据,可以使用ContentProvider来实现。

(二)、ContentProvider的功能和意义:
        为了在应用程序之间交换数据,Android提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API。当一个应用程序需要把自己的数据暴露给其他应用程序使用时,该应用程序可以通过提供ContentProvider来实现;而其他应用程序需要使用这些数据时,可以通过ContentResolver来操作ContentProvider暴露的数据。
        一旦某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过该接口来操作被暴露的内部数据,包括增加数据、删除数据、修改数据、查询数据等。
        虽然大部分使用ContentProvider操作的数据都来自于数据库,但是也可以来自于文件、SharedPreferences、XML或网络等其他存储方式。

(三)、核心类:
    1、ContentProvider:(A应用暴露数据)
  •  一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据暴露出去;
  •  外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以修改程序的数据。
    2、ContentResolver:(操作A应用所暴露的数据)
  •   外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据
  •   ContentResolve可以理解成是HttpClient的作用。
    3、 Uri:Uri是ContentResolver和ContentProvider进行数据交换的标识。
  •   每个ContentProvider提供公共的URI来唯一标识其数据集。管理多个数据集的(多个表)的 ContentProvider 为每个数据集提供了单独的URI。
  •   Uri 的标准前缀:以“content://”作为前缀,这个是标准的前缀,表示该数据由 ContentProvider 管理。
  •   Uri 的authority部分:该部分是完整的类名。(使用小写形式)。
  •   Uri 的path部分(资源部分、数据部分): 用于决定哪类数据被请求。
  •   被请求的特定记录的id值。如果请求不仅限于某个单条数据,该部分及其前面的斜线应该删除。
  •   为了将一个字符串转换成Uri,Android中提供了Uri的parse()静态方法来实现。
        
【备注:】URI、URL、URN的区别:
  • 首先,URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。
  • URLuniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。
  • URNuniform resource name,统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com。
        也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式URL和URN都是一种URI
        总结一下:URL是一种具体的URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。URI是一种语义上的抽象概念,可以是绝对的,也可以是相对的,而URL则必须提供足够的信息来定位,所以,是绝对的。

(四)、系统内置的预定义ContentProvider:
1、Contacts:获取、修改、保存联系人的信息;
2、MediaStore:访问声音、视频、图片等多媒体文件;
3、CallLog :查看或更新通话记录;
4、Browser:读取或修改浏览历史、网络搜索、书签;
5、Setting:查看和获取蓝牙设置、铃声设置等设备首选项。

二、使用ContentResolver 管理联系人:

(一)、 使用ContentResolver 操作数据的步骤:
1、调用Context的getContentResolver()方法获得ContentResolver 对象;
2、调用使用ContentResolver 的insert()、delete()、update()、query()方法操作数据。
  • Uri insert(Uri uri, ContentValues values)
  • int delete(Uri uri, String where, String[] whereArgs)
  • int update(Uri uri, ContentValues values, String where, String[] whereArgs)
  • Cursor query(Uri uri, String[] projection, String where, String[] whereArgs, String sortOrder)
参数解释:
String   where:表示带有占位符的where子句组成的字符串;
String[]   whereArgs:表示替换where参数中占位符的数据组成的字符串数组;
String   sortOrder:表示select语句中的order by子句组成的字符串;
String[]   projection:表示select语句中需要查询的所有的字段组成的字符串数组。
ContentValues values:是由数据库中表字段和往该字段中放置的数据所组成的键值对对象。
【备注:】以上四个方法的参数分别是2、3、4、5个。

(二)、 联系人中管理ContentProvider的几个Uri:
1、联系人的Uri==>      content://com.android.contacts/contacts 和 content://com.android.contacts/raw_contacts
2、电话号码的Uri==>  content://com.android.contacts/data/phones
3、EMAIL的URI==>     content://com.android.contacts/data/emails

不过为了方便记忆,系统中提供了以下常量来替代以上的Uri字符串。
    • Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;
    • Uri PHONE_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
    • Uri EMAIL_URI = ContactsContract.CommonDataKinds.Email.CONTENT_URI;

(三)、示例代码1——查看通讯录中的联系人姓名、id、电话、Email等信息:

publicstatic List<Map<String, Object>> selectContactsMsg(ContentResolver resolver, ContentValues values) {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
String uri_contacts = "content://com.android.contacts/raw_contacts";
String uri_contacts_phones = "content://com.android.contacts/data/phones";
String uri_contacts_emails = "content://com.android.contacts/data/emails";
// 从raw_contacts表中或许联系人的id和联系人的姓名。
Cursor cursor_contacts = resolver.query(Uri.parse (uri_contacts),
new String[] { "_id", "display_name" }, null, null, null);//Cursor query(Uri uri, String[] projection, String where, String[] whereArgs, String sortOrder)

// 遍历所有的联系人的信息
while (cursor_contacts.moveToNext()) {
int contacts_id = cursor_contacts.getInt(cursor_contacts.getColumnIndex("_id"));
String display_name = cursor_contacts.getString(cursor_contacts.getColumnIndex("display_name"));

Map<String, Object> map = new HashMap<String, Object>();
map.put("id", contacts_id);
map.put("display_name", display_name);

// 以下开始获取电话号码
// 根据每个联系人的id再去data表中查找相应的电话号码。
Cursor cursor_phones = resolver.query(Uri.parse(uri_contacts_phones), new String[] {
                  "raw_contact_id", "data1" }, "raw_contact_id=?",new String[] { contacts_id + "" }, null);


// 因为电话号码可能是多个,所以需要再遍历,组合在一起形成一个电话号码的字符串,放到StringBuilder中
StringBuilder sb = new StringBuilder();
while (cursor_phones.moveToNext()) {
sb.append(cursor_phones.getString(1));//?
sb.append(" | ");
}

// 将生成的电话号码放到map集合中
map.put("phones", sb.toString());

// 以下开始获取Email信息
Cursor cursor_emails = resolver.query(Uri.parse(uri_contacts_emails), new String[] {
"raw_contact_id", "data1" }, "raw_contact_id=?",new String[] { contacts_id + "" }, null);
StringBuilder sb2 = new StringBuilder();
while (cursor_emails.moveToNext()) {
sb2.append(cursor_emails.getString(1));
sb2.append(" | ");
}
map.put("emails", sb2.toString());

// 将包含有id、联系人姓名、手机号码、emails的map放到list集合中
list.add(map);
}
return list;
}

(四)、示例代码2——实现新添加通讯录信息:

// 以下方法是利用常量值所写的插入联系人的方法。如果希望封装成方法,再增加一个Map集合的参数,用来传入信息即可。
publicvoid insertContacts(ContentValues values, ContentResolver resolver) {
// 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
Uri rawContactUri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);

// 往data表入姓名数据
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE,
          ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);

values.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,"王向军");
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,"王向军");
resolver.insert(ContactsContract.Data.CONTENT_URI, values);

// 往data表入电话数据
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "13520551441");
values.put(ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
resolver.insert(ContactsContract.Data.CONTENT_URI, values);


// 往data表入Email数据
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Email.DATA,"237235475@qq.com");
values.put(ContactsContract.CommonDataKinds.Email.TYPE,
ContactsContract.CommonDataKinds.Email.TYPE_WORK);
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
}

(五)、封装ContentResolver操作系统联系人的工具类ContactsResolverHelper :【重要】

publicclass ContactsResolverHelper {


/*
* 以下代码是插入新联系人的子过程。其中第三个参数:List集合表示要插入到通讯录中的新数据(该集合中有三个值,
    *  分别是:姓名、电话、email)。
    */

publicvoid insertContacts(ContentResolver resolver, ContentValues values,List<String> list) {
String uri_rawcontacts = "content://com.android.contacts/raw_contacts";
           String uri_data = "content://com.android.contacts/data";


Uri uri = Uri.parse(uri_contacts);
// 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
Uri rawContactUri = resolver.insert(Uri.parse(uri_rawcontacts), values);
long contact_id = ContentUris.parseId(rawContactUri);

// 往data表中插入一条用户的名称信息
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/name");
values.put("data1", list.get(0));
values.put("data2", list.get(0));
resolver.insert(uri_data, values);

// 往data表中插入电话信息
values.clear();
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
values.put("data1", list.get(1));
values.put("data2", 2);// 2,Phone.TYPE_MOBILE ,表示手机号码
resolver.insert(uri_data, values);

// 往data表中插入Email信息
values.clear();
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/email_v2");
values.put("data1", list.get(2));
values.put("data2", 2);// 2,Email.TYPE_WORK , 表示工作用Email号码
resolver.insert(uri_data, values);
}

// 以下代码是删除联系人信息的方法。
publicstaticint deleteContacts(ContentResolver resolver, String where, String[] whereArgs) {
String uri_contacts = "content://com.android.contacts/raw_contacts";
return resolver.delete(Uri.parse(uri_contacts), where, whereArgs);
}

// 以下代码是查询联系人信息的方法。
publicstatic Cursor selectContactsName(ContentResolver resolver,
          String[] projection, String where, String[] whereArgs,String sortOrder) {

String uri_contacts = "content://com.android.contacts/raw_contacts";
return resolver.query(Uri.parse(uri_contacts), projection, where, whereArgs, sortOrder);
}

publicstatic List<Map<String, Object>> selectContactsMsg(ContentResolver resolver, ContentValues values) {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
String uri_contacts = "content://com.android.contacts/raw_contacts";
String uri_contacts_phones = "content://com.android.contacts/data/phones";
String uri_contacts_emails = "content://com.android.contacts/data/emails";
// 从raw_contacts表中或许联系人的id和联系人的姓名。
Cursor cursor_contacts = resolver.query(Uri.parse(uri_contacts),
new String[] { "_id", "display_name" }, null, null, null);

// 遍历所有的联系人的信息
while (cursor_contacts.moveToNext()) {
int contacts_id = cursor_contacts.getInt(cursor_contacts.getColumnIndex("_id"));
String display_name = cursor_contacts.getString(cursor_contacts.getColumnIndex("display_name"));

Map<String, Object> map = new HashMap<String, Object>();
map.put("id", contacts_id);
map.put("display_name", display_name);

// 以下开始获取电话号码
// 根据每个联系人的id再去data表中查找相应的电话号码。
Cursor cursor_phones = resolver.query(Uri.parse(uri_contacts_phones), new String[] {
"raw_contact_id", "data1" }, "raw_contact_id=?",new String[] { contacts_id + "" }, null);

// 因为电话号码可能是多个,所以需要再遍历,组合在一起形成一个电话号码的字符串,放到StringBuilder中
StringBuilder sb = new StringBuilder();
while (cursor_phones.moveToNext()) {
sb.append(cursor_phones.getString(1));
sb.append(" | ");
}

// 将生成的电话号码放到map集合中
map.put("phones", sb.toString());

// 以下开始或许Email信息
Cursor cursor_emails = resolver.query(Uri.parse(uri_contacts_emails), new String[] {
"raw_contact_id", "data1" }, "raw_contact_id=?", new String[] { contacts_id + "" }, null);
StringBuilder sb2 = new StringBuilder();
while (cursor_emails.moveToNext()) {
sb2.append(cursor_emails.getString(1));
sb2.append(" | ");
}
map.put("emails", sb2.toString());

// 将包含有id、联系人姓名、手机号码、emails的map放到list集合中
list.add(map);
}
return list;
}


// 以下代码是更新联系人姓名的方法。
publicstaticint updateContactsName(ContentValues values,
         ContentResolver resolver, String oldName, String newName) {

String uri_contacts = "content://com.android.contacts/raw_contacts";
String uri_contacts_data = "content://com.android.contacts/data";
// 先更新raw_contacts表中的联系人姓名,共有四列需要更新
values.put("display_name", newName);
values.put("display_name_alt", newName);
values.put("sort_key", newName);
values.put("sort_key_alt", newName);
int count1 = resolver.update(Uri.parse(uri_contacts), values,"display_name=?", new String[] { oldName });
// 再更新data表中联系人的姓名,共有两列
values.clear();
values.put("data1", newName);
values.put("data2", newName);
int count2 = resolver.update(Uri.parse(uri_contacts_data), values, "data1=? and mimetype_id=?"
          new String[] { oldName, "7" });

// 返回2,则说明六项都被更新。如果只返回1,则说明没有修改完全
return count1 + count2;
}
}

三、系统自带ContentProvider的常用Uri地址:

(一)、Android系统管理联系人的Uri如下:

    1. ContactsContract.Contacts.CONTENT_URI 管理联系人的Uri
    2. ContactsContract.CommonDataKinds.Phone.CONTENT_URI 管理联系人的电话的Uri
    3. ContactsContract.CommonDataKinds.Email.CONTENT_URI 管理联系人的Email的Uri
【数据库中主要字段:】
    1. 联系人id字段名称为:ContactsContract.Contacts._ID
    2. 联系人name 字段为:ContactContract.Contracts.DISPLAY_NAME
    3. 电话信息表的外键id为:ContactsContract.CommonDataKinds.Phone.CONTACT_ID
    4. 电话号码字段为:ContactsContract.CommonDataKinds.Phone.NUMBER.
    5. Email 字段为:ContactsContract.CommonDataKinds.Email.DATA
    6. 其外键为:ContactsContract.CommonDataKinds.Email.CONTACT_ID


(二)、Android为多媒体提供的ContentProvider的Uri如下:

    1. MediaStore.Audio.Media.EXTERNAL_CONTENT_URI        存储在SD卡上的音频文件
    2. MediaStore.Audio.Video.EXTERNAL_CONTENT_URI         存储在 SD卡上的视频
    3. MediaStore.Audio.Images.EXTERNAL_CONTENT_URI       存储在 SD卡上的图片文件内容
    4. MediaStore.Audio.Media.INTERNAL_CONTENT_URI         手机内部存储器上的音频文件 
    5. MediaStore.Audio.Video.INTERNAL_CONTENT_URI          手机内部存储器上的视频
    6. MediaStore.Audio.Images.INTERNAL_CONTENT_URI        手机内部存储器上的图片

【数据库中主要字段:】

    1. 图片名称字段:Media.DISPLAY_NAME
    2. 图片的详细描述字段:Media.DESCRIPTION  
    3. 图片的保存位置字段:Media.DATA

(三)、短信Uri:

    1.  content://sms                所有短信
    2. content://sms/outbox      发送箱中的短信 
    3. content://sms/inbox       收件箱中短信
【数据库中主要字段:】
    1. 短信手机号码 : address
    2. 短信标题:  subject
    3. 短信内容:body
    4.  短信发送时间戳:date

(四)、通话记录Uri:

    1.  content://call_log/calls               所有通话记录

四、当日作业:

1、完善今天的随堂代码“ContentResolver管理联系人”,增加添加联系人功能,增加删除联系人功能。
2、利用ContentResolver制作管理SDCard上的多媒体文件管理器。


ContentProvider第二天:自定义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() 

 Notice that your provider is not created until a ContentResolver object tries to access it.

 ContentResolver不访问内容提供者的时候,内容提供者是不会创建出来的.

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对象的截取方法:

  • public abstract String getLastPathSegment ()
  • public abstract String getPath ()
  • public abstract List<String> getPathSegments ()

2、Uri对象的拼接方法,拼接path:

  • 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;
          }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值