预热:动态申请权限
- 调用
checkSelfPermission(String permission)
方法检查是否具有权限 - 复写
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
方法,来确认进入权限界面返回后是否获取到了对应权限。 - 本文中,想要获取到联系人的内容,则需要动态获取
Manifest.permission.CALL_PHONE
的权限,同时Manifest里面要注册该权限。
SampleCode
public class ContentProiderActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_proider);
if(checkPermission()){
//TODO do actions with permission
readContacts();
}else {
requestPermissions(new String[]{Manifest.permission.CALL_PHONE}, 1);
}
}
private void readContacts() {
}
private boolean checkPermission(){
return checkSelfPermission(Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 1:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readContacts();
} else {
Log.w("lyh", "permission not granted");
}
break;
default:
break;
}
}
}
通过ContentResolver读取数据 (跨程序调用数据)
ContentResolver其实是对于外部数据库的一个封装,可以通过其Provider提供的接口来实现对外部数据的增删改查。
实现readContacts()
具体功能:读取所有联系人姓名及其电话号码
private void readContacts() {
Cursor cursor;
try {
cursor = getContentResolver()
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null , null, null, null);
if(cursor != null) {
//we get all contact info in this while loop, use this info to do further
while (cursor.moveToNext()) {
String contactName = cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.i("lyh", "name = " + contactName + ", number = " + number);
}
}
} catch (RuntimeException e) {
e.printStackTrace();
}
}
创建ContentProvider供外部使用
1.总体使用方法(搭配前文的数据库介绍的DBOpenHelper一起使用)
ContentProvider相当于对于创建的数据库的对外部的一层封装,外部应用or模组想要对本数据库进行操作的话,则必须通过ContentProvider来进行交互。ContentPorvider可以指定用户可以访问的uri,针对uri,在ContentPorvider里进行封装好的处理动作,这样就避免了敏感信息可以被外部访问到,同时又把想要对外暴露的数据暴露了出来。
SampleCode, 这里仅针对query 进行了实现。其它几个操作的流程是类似的
public class MyProvider extends ContentProvider {
public static final String AUTHORITY = "com.example.crane.myfirstline.provider";
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
public static final int DB_VERSION = 1;
private static UriMatcher uriMatcher;
private MyLibraryDBHelper dbHelper;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
// *:匹配任意长度任意字符
// #:匹配任意长度数字
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
@Override
public boolean onCreate() {
dbHelper = new MyLibraryDBHelper(getContext(), "library.db", null, DB_VERSION);
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor c = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
//query all data in table1
c = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
//query sigle data in table1
String bookId = uri.getPathSegments().get(1);
c = db.query("book", projection, "id = ?", new String[] {bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
break;
case CATEGORY_ITEM:
break;
default:
break;
}
return c;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cusor.dir/" + AUTHORITY + ".book";
case BOOK_ITEM:
return "vnd.android.cusor.item/" + AUTHORITY + ".book";
case CATEGORY_DIR:
return "vnd.android.cusor.dir/" + AUTHORITY + ".category";
case CATEGORY_ITEM:
return "vnd.android.cusor.item/" + AUTHORITY + ".category";
default:
break;
}
return null;
}
}
2.关于getType()方法的思考
Google给的注释:
/**
* Implement this to handle requests for the MIME type of the data at the
* given URI. The returned MIME type should start with
* vnd.android.cursor.item
for a single record,
* or vnd.android.cursor.dir/
for multiple items.
* This method can be called from multiple threads, as described in
* Processes
* and Threads.
*
* Note that there are no permissions needed for an application to
* access this information; if your content provider requires read and/or
* write permissions, or is not exported, all applications can still call
* this method regardless of their access permissions. This allows them
* to retrieve the MIME type for a URI when dispatching intents.
*
* @param uri the URI to query.
* @return a MIME type string, or {@code null} if there is no type.
*/
public abstract @Nullable String getType(@NonNull Uri uri);
也就是说,通过返回这个MIME类型的字符串,可以判断出来我们在使用ContentResolver进行查询时,返回的cursor中含有多条或是单条数据,可以优化处理逻辑,提高效率;具体分析详见下方连接
对ContentProvider中getType方法的一点理解