編輯:關於Android編程
可能你會有些疑惑,為什麼要將我們程序中的數據共享給其他程序呢?當然,這個要視情況而定的,比如說賬號和密碼這樣的隱私數據顯然是不能共享給其他程序的,不過一些可以讓其他程序進行二次開發的基礎性數據,我們還是可以選擇將其共享的。例如系統的電話簿程序,它的數據庫中保存了很多的聯系人信息,如果這些數據都不允許第三方的程序進行訪問的話,恐怕很多應用的功能都要大打折扣了。除了電話簿之外,還有短信、媒體庫等程序都實現了跨程序數據共享的功能,而使用的技術當然就是內容提供器了,下面我們就來對這一技術進行深入的探討。
內容提供器簡介
內容提供器(Content Provider)主要用於在不同的應用程序之間實現數據共享的功能,它提供了一套完整的機制,允許一個程序訪問另一個程序中的數據,同時還能保證被訪數據的安全性。目前,使用內容提供器是Android 實現跨程序共享數據的標准方式。不同於文件存儲和SharedPreferences 存儲中的兩種全局可讀寫操作模式,內容提供器可
以選擇只對哪一部分數據進行共享,從而保證我們程序中的隱私數據不會有洩漏的風險。內容提供器的用法一般有兩種,一種是使用現有的內容提供器來讀取和操作相應程序中的數據,另一種是創建自己的內容提供器給我們程序的數據提供外部訪問接口。那麼接下來我們就一個一個開始學習吧,首先從使用現有的內容提供器開始。
訪問其他程序中的數據
當一個應用程序通過內容提供器對其數據提供了外部訪問接口,任何其他的應用程序就都可以對這部分數據進行訪問。Android 系統中自帶的電話簿、短信、媒體庫等程序都提供了類似的訪問接口,這就使得第三方應用程序可以充分地利用這部分數據來實現更好的功能。下面我們就來看一看,內容提供器到底是如何使用的。
ContentResolver 的基本用法
對於每一個應用程序來說,如果想要訪問內容提供器中共享的數據,就一定要借助ContentResolve 類,可以通過Context 中的getContentResolver()方法獲取到該類的實例。ContentResolver 中提供了一系列的方法用於對數據進行CRUD 操作,其中insert()方法用於添加數據,update()方法用於更新數據,delete()方法用於刪除數據,query()方法用於查詢數據。有沒有似曾相識的感覺?沒錯,SQLiteDatabase 中也是使用的這幾個方法來進行CRUD操作的,只不過它們在方法參數上稍微有一些區別。不同於SQLiteDatabase,ContentResolver 中的增刪改查方法都是不接收表名參數的,而是使用一個Uri 參數代替,這個參數被稱為內容URI。內容URI 給內容提供器中的數據建立了唯一標識符,它主要由兩部分組成,權限(authority)和路徑(path)。權限是用於對不同的應用程序做區分的,一般為了避免沖突,都會采用程序包名的方式來進行命名。比如某個程序的包名是com.example.app,那麼該程序對應的權限就可以命名為com.example.app.provider。路徑則是用於對同一應用程序中不同的表做區分的,通常都會添加到權限的後面。比如某個程序的數據庫裡存在兩張表,table1 和table2,這時就可以將路徑分別命名為/table1和/table2,然後把權限和路徑進行組合,內容URI 就變成了com.example.app.provider/table1和com.example.app.provider/table2。不過,目前還很難辨認出這兩個字符串就是兩個內容URI,我們還需要在字符串的頭部加上協議聲明。因此,內容URI 最標准的格式寫法如下:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
有沒有發現,內容URI 可以非常清楚地表達出我們想要訪問哪個程序中哪張表裡的數據。也正是因此ContentResolver 中的增刪改查方法才都接收Uri 對象作為參數,因為使用表名的話系統將無法得知我們期望訪問的是哪個應用程序裡的表。在得到了內容URI 字符串之後,我們還需要將它解析成Uri 對象才可以作為參數傳入。
解析的方法也相當簡單,代碼如下所示:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
只需要調用Uri.parse()方法,就可以將內容URI 字符串解析成Uri 對象了。
現在我們就可以使用這個Uri 對象來查詢table1 表中的數據了,代碼如下所示:
Cursor cursor = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
這些參數和SQLiteDatabase 中query()方法裡的參數很像,但總體來說要簡單一些,畢竟這是在訪問其他程序中的數據,沒必要構建過於復雜的查詢語句。下表對使用到的這部分參數進行了詳細的解釋。
查詢完成後返回的仍然是一個Cursor 對象,這時我們就可以將數據從Cursor 對象中逐個讀取出來了。讀取的思路仍然是通過移動游標的位置來遍歷Cursor 的所有行,然後再取出每一行中相應列的數據,代碼如下所示:
if (cursor != null) { while (cursor.moveToNext()) { String column1 = cursor.getString(cursor.getColumnIndex("column1")); int column2 = cursor.getInt(cursor.getColumnIndex("column2")); } cursor.close(); }
掌握了最難的查詢操作,剩下的增加、修改、刪除操作就更不在話下了。我們先來看看如何向table1 表中添加一條數據,代碼如下所示:
ContentValues values = new ContentValues(); values.put("column1", "text"); values.put("column2", 1); getContentResolver().insert(uri, values);
可以看到,仍然是將待添加的數據組裝到ContentValues 中,然後調用ContentResolver的insert()方法,將Uri 和ContentValues 作為參數傳入即可。
現在如果我們想要更新這條新添加的數據, 把column1 的值清空, 可以借助ContentResolver 的update()方法實現,代碼如下所示:
ContentValues values = new ContentValues(); values.put("column1", ""); getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text", "1"});
注意上述代碼使用了selection 和selectionArgs 參數來對想要更新的數據進行約束,以防止所有的行都會受影響。
最後,可以調用ContentResolver 的delete()方法將這條數據刪除掉,代碼如下所示:getContentResolver().delete(uri, "column2 = ?", new String[] { "1" });
到這裡為止,我們就把ContentResolver 中的增刪改查方法全部學完了。是不是感覺非常簡單?因為這些知識在學習SQLiteDatabase 的時候就已經掌握了,所需特別注意的就只有uri 這個參數而已。那麼接下來,我們就利用目前所學的知識,看一看如何讀取系統電話簿中的聯系人信息。
讀取系統聯系人
由於我們之前一直使用的都是模擬器,電話簿裡面並沒有聯系人存在,所以現在需要自己手動添加幾個,以便稍後進行讀取。打開電話簿程序,界面如圖所示。
這樣准備工作就做好了,現在新建一個Modu,讓我們開始動手吧。
首先還是來編寫一下布局文件,這裡我們希望讀取出來的聯系人信息能夠在ListView 中顯示,因此,修改activity_main.xml 中的代碼,如下所示:
簡單起見,LinearLayout 裡就只放置了一個ListView。接著修改MainActivity 中的代碼,如下所示:
import android.database.Cursor; import android.provider.ContactsContract; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private ListView contacts_lv_main; private ArrayAdapterarrayAdapter; private List contactsList = new ArrayList (); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); contacts_lv_main = (ListView) findViewById(R.id.contacts_lv_main); arrayAdapter = new ArrayAdapter (this,android.R.layout.simple_list_item_1,contactsList); contacts_lv_main.setAdapter(arrayAdapter); readContacts(); } private void readContacts(){ Cursor cursor = null; try { cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null); while (cursor.moveToNext()){ String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); contactsList.add(name + "\n" + number); } }catch (Exception e){ e.printStackTrace(); }finally { if (cursor != null){ cursor.close(); } } } }
在onCreate()方法中,我們首先獲取了ListView 控件的實例,並給它設置好了適配器,然後就去調用readContacts()方法。下面重點看下readContacts()方法,可以看到,這裡使用了ContentResolver 的query()方法來查詢系統的聯系人數據。不過傳入的Uri 參數怎麼有些奇怪啊, 為什麼沒有調用Uri.parse() 方法去解析一個內容URI 字符串呢?
這是因為ContactsContract.CommonDataKinds.Phone類已經幫我們做好了封裝,提供了一個CONTENT_URI常量,而這個常量就是使用Uri.parse()方法解析出來的結果。接著我們對Cursor 對象進行遍歷, 將聯系人姓名和手機號這些數據逐個取出, 聯系人姓名這一列對應的常量是ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,聯系人手機號這一列對應的常量是ContactsContract.CommonDataKinds.Phone.NUMBER。兩個數據都取出之後,將它們進行拼接,並且中間加上換行符,然後將拼接後的數據添加到ListView 裡。最後千萬不要忘記將Cursor 對象關閉掉。
這樣就結束了嗎?還差一點點,讀取系統聯系人也是需要聲明權限的,因此修改AndroidManifest.xml 中的代碼,如下所示:
加入了android.permission.READ_CONTACTS權限,這樣我們的程序就可以訪問到系統的聯系人數據了。現在才算是大功告成,讓我們來運行一下程序吧,效果如圖所示。
在上一篇博文《Android之——殺死用戶選中的進程優化》一文中,我向大家介紹了如何優化用戶的體驗,那麼這篇博文中,我將向大家介紹如何進行系統進程
Android程序的基本組件有4個,分別為:Activity,BroadcastReceiver,ContentProvider和Service。Activity(活動窗
學習目的: 1、掌握在Android中如何建立Button 2、掌握Button的常用屬性 3、掌握Button按鈕的點擊事件(監聽器) Button是各種UI中最常用的
轉載請注明出處,本文來自【 Mr.Simple的博客 】。 我正在參加博客之星,點擊這裡投我一票吧,謝謝~ 前言 最近這一兩年,Android App使用插件化技術