1.0 在我的上一篇,相當(dāng)于運行時權(quán)限的初體驗抢肛,這一篇主要是怎么訪問其他程序的數(shù)據(jù),比如接下來的這個例子碳柱,將手機聯(lián)系人應(yīng)用中的聯(lián)系人數(shù)據(jù)捡絮,讀取出來,在APP中展示莲镣。
2.0 一個應(yīng)用程序通過內(nèi)容提供器可以對其數(shù)據(jù)提供外部訪問接口福稳,其他任何應(yīng)用程序都可以對這部分?jǐn)?shù)據(jù)進(jìn)行訪問。
Android系統(tǒng)自帶的電話簿瑞侮、短信的圆、媒體庫等程序都提供了類似的訪問接口,這使得第三方應(yīng)用程序可以充分地利用這部分?jǐn)?shù)據(jù)來實現(xiàn)更好的功能半火。
3.0對于每一個應(yīng)用程序來說越妈,如果想訪問內(nèi)容提供器中共享的數(shù)據(jù),就一定要借助ContentResolver類钮糖,可以通過Context中的getContentResolve方法獲得該類的實例梅掠。
ContentResolver中提供了一系列的方法用于增刪查改操作。不錯店归,和SQLiteDatabase很相似阎抒。
不同于SQLiteDatabase,ContentResolver使用的是內(nèi)容URI參數(shù)娱节。
標(biāo)準(zhǔn)格式為:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
內(nèi)容URI主要由2部分組成:authority和path挠蛉。
authority:用于對不同應(yīng)用程序做區(qū)分的祭示,一般為了避免沖突肄满,都會采用程序包名的方式來命名谴古。
比如某個程序包名為com.example.app
,該程序的authority就可以命名為com.example.app.provider
path:用于對同一應(yīng)用程序中不同的表做以區(qū)分稠歉,通常添加在authority后面掰担。
4.0 閑話放最后說,先上車直接干代碼怒炸,新建項目ContactsTest带饱,目錄如下:
5.0 我們現(xiàn)在模擬器聯(lián)系人中添加兩位聯(lián)系人資料:
6.0 在布局文件activity_main.xml添加一個ListView,這里就不用RecyclerView阅羹,雖好但也增加了代碼量勺疼,這里重點也不在這個。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ListView
android:id="@+id/contacts_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"></ListView>
</android.support.constraint.ConstraintLayout>
7.0 接著修改MainActivity.java中的代碼:
package com.example.contactstest;
import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ArrayAdapter<String> adapter;
List<String> contactsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//老老實實3步布置好一個簡單的ListView列表適配器捏鱼。
ListView contactsView = (ListView) findViewById(R.id.contacts_view);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contactsList);
contactsView.setAdapter(adapter);
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
//注意执庐,換了權(quán)限,READ_CONTACTS危險權(quán)限导梆,獲取聯(lián)系人信息
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_CONTACTS}, 1);
} else {
readContacts();
}
}
private void readContacts() {
Cursor cursor = null;
try {
//查詢聯(lián)系人數(shù)據(jù)
//這里沒有傳入一個URI參數(shù)轨淌,沒有調(diào)用Uri.parse()方法去解析一個內(nèi)容URI字符串
//ContactsContract.CommonDataKinds.Phone類已經(jīng)做好了封裝
//.CONTENT_URI常量就是Uri.parse()解析出來的結(jié)果
cursor =
getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI
, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
//獲取聯(lián)系人姓名
String displayName =
cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
//獲取聯(lián)系人手機號
String number =
cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactsList.add(displayName + "\n" + number);
}
adapter.notifyDataSetChanged();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
//調(diào)用完requestPermissions()方法后,系統(tǒng)會彈出一個權(quán)限申請的對話框
// 無論結(jié)果如何看尼,最終都會回調(diào)onRequestPermissionsResult()方法
//授權(quán)的結(jié)果递鹉,會封裝在grantResults中。
// 判斷一下藏斩,如果同意了授權(quán)就打電話躏结,沒有就涼涼了……
@Override
public void onRequestPermissionsResult(int requestCode,String[] permissions,
int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readContacts();
} else {
Toast.makeText(this, "抱歉,沒有該權(quán)限狰域!", Toast.LENGTH_SHORT).show();
}
}
}
}
這里沒有傳入一個URI參數(shù)窜觉,沒有調(diào)用Uri.parse()方法去解析一個內(nèi)容URI字符串,ContactsContract.CommonDataKinds.Phone類已經(jīng)做好了封裝北专,CONTENT_URI常量就是Uri.parse()解析出來的結(jié)果禀挫。
最后千萬不要忘記將Cursor對象關(guān)閉掉。
9.0 到這里還差最后一步拓颓,讀取系統(tǒng)聯(lián)系人的權(quán)限千萬不能忘記聲明语婴。修改AndroidManifest.xml中的代碼,增加<uses-permission android:name="android.permission.READ_CONTACTS"/>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.contactstest">
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
10.0 運行驶睦,如下:
點擊“拒絕”:
重新運行該程序砰左,點擊“允許”:
11.0 這里繼續(xù)補上枯燥的理論知識。
既然已經(jīng)知道內(nèi)容URI字符串的格式场航,那么加載解析成Uri對象可以作為參數(shù)傳入的代碼格式如下:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
只需要調(diào)用Uri.parse()方法缠导,就可以將內(nèi)容URI字符串解析成Uri對象。
12.0 查詢table1表中的數(shù)據(jù):
Cursor cursor =getContentResolver().query(
uri,
projection,
selection,
selectionArgs,
sortOrder);
??query()方法參數(shù) | ???對應(yīng)的SQL語句部分 | ?????描述 |
---|---|---|
uri | from table_name | 指定查詢某個應(yīng)用程序下的某一張表 |
projection | select column1,column2 | 指定查詢的列名 |
selection | where column= value | 指定where的約束條件 |
selectionArgs | —— | 為where中的占位符提供具體的值 |
sortOrder | order by column1,column2 | 指定查詢結(jié)果的排序方式 |
查詢完后返回的也是一個Cursor對象溉痢,逐個讀出即可僻造,通過移動游標(biāo)位置來遍歷Cursor所有行憋他,然后再去除每一行相應(yīng)列的數(shù)據(jù):
if (cursor != null) {
while (cursor.moveToNext()) {
String column1=
cursor.getString(cursor.getColumnIndex("column1"));
int column2 =
cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
13.0 講完最難的查詢操作,增加髓削、修改竹挡、刪除操作更不在話下。
向table1表中添加一條數(shù)據(jù):
ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri,values);
可以看到立膛,將待添加的數(shù)據(jù)封裝到ContentValues 中揪罕,然后調(diào)用getContentResolver的insert()方法,將Uri和ContentValues 作為參數(shù)傳入即可宝泵。
14.0 向table1表中更新一條數(shù)據(jù):
比如把 13.0 中添加的數(shù)據(jù)中column1的值清空:
ContentValues values = new ContentValues();
values.put("column1","");
getContentResolver().update(uri,values,"column1 = ? and column2 = ?",new String[] {"text","1"});
15.0 最后好啰,把table1表中這條數(shù)據(jù)刪除掉:
getContentResolver().delete(uri,"column2 = ? ",new String[] {"1"});
END