【第一部分】歷史文章:
Android學習筆記(一)——創建第一個Android項目
Android學習筆記(二)android studio基本控件及佈局(實現圖片查看器)
Android學習筆記(三)android studio中CheckBox自定義樣式(更換複選框左側的勾選圖像)
Android學習筆記(四)Android 中Activity頁面的跳轉及傳值
Android學習筆記(五)——Toast提示、Dialog對話框、Menu菜單
Android學習筆記(六)——自定義ListView佈局+AsyncTask異步任務
Android學習筆記(七)——數據存儲(共享參數SharedPreferences)
Android學習筆記(八)——數據存儲(SD卡文件操作)
Android學習筆記(九)——網絡技術
Android學習筆記(十)——實現新聞列表案例
Android學習筆記(十一)——一些高級控件的使用
Android學習筆記(十二)——數據存儲(SQLite數據庫)
Android學習筆記(十三)——數據存儲(LitePal操作數據庫)
【第二部分】主要問題解決:
Android Studio(存)讀取不了SD卡上的文件——【已解決】
第一行代碼
內容提供器介紹
- 內容提供器主要用於在不同的應用程序之間實現數據共享的功能,它提供了一套完整的機制,允許一個程序訪問另一個程序中的數據,同時還能保證被訪問數據的安全性。目前,使用內容提供器是Android實現跨程序共享數據的標準方式。
- 不同於文件存儲和 SharedPreferences 存儲中的兩種全局可讀寫操作模式,內容提供器可
以選擇只對哪一部分數據進行共享,從而保證我們程序中的隱私數據不會有泄漏的風險。
在正式學習內容提供器之前,我們先了解一下Android運行時的權限:
- 在Android6.0版本之前,權限都是一條龍服務的,只要用戶安裝完,AndroidManifest清單上申請的權限都會被系統默認授權,並且授權後也撤銷不了。
- 在Android 6.0版本之後,權限分爲
普通權限
和危險權限
。
普通權限
:這個權限類型並不直接威脅到用戶的隱私,可以直接在AndroidManifest清單裏註冊,系統會幫我們默認授權。
危險權限
:這個權限可能會觸及到用戶隱私或者對設備安全性造成影響。
注:危險權限
不僅需要在AndroidManifest清單裏註冊,同時在運行的時候,需要向系統請求授權。危險權限
共9組24個權限(見下表),一旦用戶授權某個危險權限,那麼該權限所對應權限組中所有其它權限也會同時被授權。
注:
- 除了上表中的全部危險權限外,剩餘的都是普通權限。
- 當我們使用一個權限時,如果是上表中的危險權限,就需要進行運行時權限處理;如果不是上表中的權限,只需要在
AndroidManifest.xml
文件中添加以下權限聲明就可以了。
在程序運行時申請權限
下面是一個撥打電話功能的實現:
關鍵代碼
然後修改AndroidManifest.xml
文件,聲明權限:
<uses-permission android:name="android.permission.CALL_PHONE" />
具體分析
1、首先我們用ContextCompat.checkSelfPermission()
來判斷用戶是否已經給我們授權。
checkSelfPermission()
方法:接收兩個參數,第一個參數是Context;第二個參數是:具體的權限名;這裏我們用的是打電話的權限名: Manifest.permission.CALL_PHONE
。
2、我們使用方法的返回值與PackageManager.PERMISSION_GRANTED做比較,相同就說明用戶已經授權了,不同則表示用戶沒有授權。
3、
- 如果
授權了
,使用Intent完成你想要的操作就可以了。 - 如果
沒有授權
,那麼我們調用ActivityCompat.requestPermissions
方法來向用戶申請權限。
requestPermissions方法
:接受三個參數,第一個參數是Activity的實例;第二個參數是String數組,把要申請的權限給他;第三個參數是請求碼。
當系統彈出對話框詢問完用戶是否申請授權後,會回調onRequestPermissionResult()
方法,授權的結果封裝在了grantResults
參數中。最後判斷最後的授權結果,用戶同意則會調用call()
方法撥打電話,用戶拒絕,則會彈出提示信息。
點擊CALL按鈕,沒有同意授權則會彈出失一個操作失敗的提示(下圖2);如果同意授權結果如(下圖3)。
訪問其他程序中的數據
內容提供器的用法一般有兩種: - 一種是使用現有的內容提供器來讀取和操作相應程序中 的數據。
- 另一種是創建自己的內容提供器給我們程序的數據提供外部訪問接口。
當一個應用程序通過內容提供器對其數據提供了外部訪問接口,那麼任何其他的應用程序就 都可以對這部分數據進行訪問。Android 系統中自帶的電話簿、短信、媒體庫等程序都提供 了類似的訪問接口,這就使得第三方應用程序可以充分地利用這部分數據來實現更好的功能。
1、ContentResolver類的用法
對於每個程序來說,如果想要訪問內容提供器中的數據,一定要藉助ContentResolver類
,可以通過Context中的getContentResolver方法得到該類的實例。ContentResolver中提供了一系列的方法用於對數據進行CRUD操作。
- insert()方法用於添加數據。
- update()方法用於更新數據。
- delete()方法用於刪除數據。
- query()方法用於查詢數據。
ContentResolver
中的增刪改查方法接受的是Uri參數
,被稱爲內容URI
。由authority
和path
兩部分組成的。
- authority:一般包名.provider。
- path:表名。
在得到了內容URI字符串之後,我們需要把它解析成Uri對
象纔可以作爲參數傳入。
Uri uri = uri.parse("content://com.example.app.provider/table1");
只需要調用Uri.parse()
方法,就可以將內容URI字符串解析成Uri對象了。
下面是具體的四種CRUD操作介紹:
1.1、查詢操作
Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder)
參數說明:
query()方法參數 | 對應的SQL部分 | 描述 |
---|---|---|
uri | from table_name | 指定查詢某個應用程序下的某一張表 |
projection | select column1,column2 | 指定查詢的列名 |
selection | where column=value | 指定where的約束條件 |
selectionArgs | - | 爲where中的佔位符提供具體的值 |
sortOrder | order by column1,column2 | 指定查詢結果的排序方式 |
注:查詢後返回的是Cursor對象
,讀取的方式是通過遊標的位置來遍歷Cursor的所有行,然後取出相應列的值。
示例:
if (cursor != null){
while (cursor.moveToNext()){
String column1 = cursor.getString(cursor.getColumIndex("column1"));
int column2 = cursor.getInt(cursor.getColumIndex("column2"));
}
}
1.2、添加操作
將添加的數據組裝到ContentValues
中,調用ContentResolver的insert()
方法,將Uri和ContentValues
作爲參數傳入。
ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
getContentResolver.insert(uri, values);
1.3、更新操作
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver.update(uri, values, "column1 = ?" and column2=?, new String[] {"text" , "1"});
1.4、刪除操作
getContentResolver().delete(uri,"column2=",newString[]{"1"});
下面是一個讀取手機聯繫人的例子:
首先我們先添加一個手機聯繫人。填入姓名及手機號,點擊右上角的SAVE。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.edu.hznu.contactstest.MainActivity">
<ListView
android:id="@+id/contacts_view"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</RelativeLayout>
MainActivity.java
package cn.edu.hznu.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);
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(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_CONTACTS }, 1);
} else {
readContacts();
}
}
private void readContacts() {
Cursor cursor = null;
try {
// 查詢聯繫人數據
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
// 獲取聯繫人姓名
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
// 獲取聯繫人手機號
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();
}
}
}
@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, "You denied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}
在AndroidManifest.xml聲明權限
注意:在readContacts()
方法中,使用了ContentResolver的query()
方法來查詢系統的聯繫人。卻沒有調用Uri.parse()
方法解析內容URI字符串。因爲ContactsContract.CommonDataKinds.Phone.CONTENT_URI
類已經幫我們做了封裝,提供了CONTENT_URI
常量,這個常量是使用Uri.parse()
方法解析出來的結果。
運行程序:
若文章中有錯誤的地方歡迎大家反饋或者留言,十分感謝!!!