1. 程式人生 > >第一行程式碼——第七章:跨程式共享資料——探究內容提供器

第一行程式碼——第七章:跨程式共享資料——探究內容提供器

目錄:

7.1 內容提供器簡介

7.2 執行時許可權

7.2.1 Android 許可權機制詳解

7.2.2 在程式執行時中申請許可權

7.3 訪問其他程式中的資料

7.3.1 ContentResolver的基本用法

7.3.2 讀取系統聯絡人

7.4 建立自己的內容提供器

7.4.1 建立內容提供器的步驟

7.4.2 實現跨程式資料共享

7.5 Git 時間——版本控制工具進階

7.5.1 忽略檔案

7.5.2 檢視修改內容

7.5.3 撤銷未提交的修改

7.5.4 檢視提交記錄

7.6 小結與點評


知識點:

7.1 內容提供器簡介

內容提供器(Content Provider)主要用於在不同的應用程式之間實現資料共享的功能,它提供了一套完整的機制,允許一個程式訪問到另一個程式中的資料,同時還能保證被訪問資料的安全性。目前,使用內容提供器是 Android 實現跨程式共享資料的標準方式。

不同於檔案儲存和 SharedPreferences 儲存中的兩種全域性可讀寫操作模式,內容提供器可以選擇只對哪一部分資料進行共享,從而保證程式中的隱私資料不會有洩露的風險。

7.2 執行時許可權

執行時許可權並不是什麼新鮮事物,從系統的第一個版本開始就已經存在了。但其實之前Android的許可權機制在保護使用者安全和隱私等方面起到的作用比較有限,尤其是一些大家都離不開的常用軟體,非常容易“店大欺客”。為此,Android開發團隊在Android6.0 系統中引用了執行時許可權這個功能,從而更好地保護了使用者的安全和隱私。

7.2.1 Android 許可權機制詳解

Android 把所有的許可權歸成兩類:普通許可權和危險許可權。

  • 普通許可權
      不會直接威脅到使用者的安全和隱私的許可權,對於這部分的許可權申請,系統會自動幫我們進行授權。

  • 危險許可權
      可能觸及使用者隱私,或對裝置安全性造成影響的許可權,如獲取聯絡人資訊、地理位置等,對於這部分的許可權申請,必須由使用者手動點選授權才可以,否則程式無法使用相應的功能。

7.2.2 在程式執行時中申請許可權

首先我們需要在AndroidManifest.xml中新增 如下 用於撥打電話

<uses-permission android:name="android.permission.CALL_PHONE" />

然後新建Activity 在onCreate中 直接申請許可權

之後在回撥中進行處理

package com.dak.administrator.firstcode.permission;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ViewConfiguration;
import android.widget.Toast;

import com.dak.administrator.firstcode.R;

public class PermissionActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1);
        } else {
            call();
        }


    }

    @SuppressLint("MissingPermission")
    private void call() {
        Intent intent = new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tel:10085"));
        startActivity(intent);
    }

    @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) {
                    call();
                }else{
                    Toast.makeText(this, "YYou denied the permiss", Toast.LENGTH_SHORT).show();
                }
                break;
        }


    }

    private void setObject(Object object) {
//        (() object)  object.cast
	// 使用者滑動的最小距離
//        ViewConfiguration.get(this).getScaledTouchSlop();
    }
}

7.3 訪問其他程式中的資料

內容提供器的用法有兩種:

  • 使用現有的內容提供器來讀取和操作相應程式中的資料
  • 建立自己的內容提供器給我們程式的資料提供外部訪問介面

7.3.1 ContentResolver的基本用法

對於每一個應用程式來說,如果想要訪問內容提供器中共享的資料,就一定要藉助ContentResolver類,可以通過Context中的getContentResolver方法獲取到該類的例項,從而對資料記性CRUD操作。

要點:

內容 URI 給內容提供器中的資料建立了唯一識別符號,它由 authority(區分不同的應用程式)和 path(區分不同的表)組成,具體的可以到百度瞭解一下。

content://com.example.app.provider/table1

對於CRUD操作:

查詢:

Curson curson = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);

新增操作:

// 將待新增的資料組裝到 ContentValues 中
ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
// 呼叫 insert() 方法新增資料
getContentResolver().insert(uri, values);

更新操作:

ContentValues values = new ContentValues();
// 清空
values.put("column1","");
// 呼叫 update() 方法更新資料
getContentResolver().update(uri, values, "column1 = ? and column2 = ?",new String[]{"text","1"});

刪除操作:

getContentResolver().delete(uri,  "column2 = ?", new String[]{"1"});

7.3.2 讀取系統聯絡人

話不多說,直接上程式碼:

package com.dak.administrator.firstcode.content_resolver;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
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 com.dak.administrator.firstcode.R;

import java.util.ArrayList;
import java.util.List;

import static android.content.pm.PackageManager.PERMISSION_GRANTED;

public class ResolverActivity extends AppCompatActivity {

    ArrayAdapter<String> adapter;
    List<String> contactsList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_resolver);

        ListView contactsView = 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) != 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, @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 {
                    Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                }
                break;



        }
    }
}

Xml檔案 顯示我們查詢到的內容

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context="com.dak.administrator.firstcode.content_resolver.ResolverActivity">

    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/contacts_view"
        >




    </ListView>


</LinearLayout>

7.4 建立自己的內容提供器

7.4.1 建立內容提供器的步驟

相關方法已經註釋

package com.dak.administrator.firstcode.content_resolver;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.io.BufferedReader;

/**
 * Created by Administrator on 2018/10/18.
 */

public class MyProvider extends ContentProvider {
    public static final int TABLE1_DIR = 0;
    public static final int TABLE1_TIME = 1;
    public static final int TABLE2_DIR = 2;
    public static final int TABLE2_TIME = 3;

    private static UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI("com.dak.administrator.firstcode","table1",TABLE1_DIR);
        uriMatcher.addURI("com.dak.administrator.firstcode","table1/#",TABLE1_TIME);
        uriMatcher.addURI("com.dak.administrator.firstcode","table2",TABLE2_DIR);
        uriMatcher.addURI("com.dak.administrator.firstcode","table2/#",TABLE2_TIME);
    }

    // 初始化內容提供器的時候呼叫,返回true表示成功,false失敗
    @Override
    public boolean onCreate() {
        return false;
    }

    // 從內容提供器中查詢資料
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        switch (uriMatcher.match(uri)) {
            case TABLE1_DIR:
                //查詢table1表中的所有資料
                break;
            case TABLE1_TIME:
                //查詢table1表中的單條資料
                break;
            case TABLE2_DIR:
                //查詢table2表中的所有資料
                break;
            case TABLE2_TIME:
                //查詢table2表中的單條資料
                break;
        }

        return null;
    }
    // 根據傳入的內容 URI 來返回 MIME 型別
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        //MIME型別  規定:
        // 1. 必須以vnd 開頭
        // 2. 如果內容URI以路徑結尾,則後接android.cursor.dir/
        //    如果內容URI以id結尾,則後接android.cursor.item/
        // 3. 最後接上vnd.<authority>.<path>
        switch (uriMatcher.match(uri)) {
            case TABLE1_DIR:
                return "vnd.android.cursor.dir/vnd.com.dak.administrator.firstcode.table1";
            case TABLE1_TIME:

                return "vnd.android.cursor.item/vnd.com.dak.administrator.firstcode.table1";
            case TABLE2_DIR:

                return "vnd.android.cursor.dir/vnd.com.dak.administrator.firstcode.table2";
            case TABLE2_TIME:

                return "vnd.android.cursor.item/vnd.com.dak.administrator.firstcode.table2";
        }

        return null;
    }

    // 向內容提供器中新增一條資料
    @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;
    }
}

7.4.2 實現跨程式資料共享

在這裡主要是通過SQLite 的增刪改查方式,來操作。

提供一個主要Resolver,關乎資料庫方面的這塊就不寫了。

public class DataBaseProvider extends ContentProvider {

    public static final int BOOK_DIR = 0; //訪問 book 表中的所有資料
    public static final int BOOK_ITEM = 1;//訪問 book 表中的單條資料
    public static final int CATEGORY_DIR = 3;
    public static final int CATEGORY_ITEM = 4;

    public static final String AUTHORITY = "com.wonderful.myfirstcode.chapter7.provider";

    private static UriMatcher uriMatcher;
    
    private MyDatabaseHelper dbHelper;

    static {
        // 建立 UriMatcher 例項
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        // 呼叫 addURI() 方法,此方法接收3個引數:authority、path、自定義程式碼
        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() {
        // 建立 MyDatabaseHelper 例項
        dbHelper = new MyDatabaseHelper(getContext(),"BookStore.db",null,2);
        // 返回true表示完成了建立或升級資料庫
        return true;
    }

    /**
     * 查詢資料
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                // 查詢 book 表中的所有資料
                cursor = db.query("book",projection,selection,selectionArgs,
                        null,null,sortOrder);
                break;
            
            case BOOK_ITEM:
                // 查詢 book 表中的單條資料
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("book",projection,"id = ?",new String[]{bookId},
                        null,null,sortOrder);
                break;
            
            case CATEGORY_DIR:
                cursor = db.query("category",projection,selection,selectionArgs,
                        null,null,sortOrder);
                break;
            
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("category",projection,"id = ?",new String[]
                        {categoryId}, null,null,sortOrder);
                break;
            
            default:
                break;
        }
        return cursor;
    }

    /**
     * 新增資料
     */
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Uri uriReturn = null;
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("book",null,values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/book" + newBookId);
                break;

            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("category",null,values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/category" +
                        newCategoryId);
                break;

            default:
                break;
        }
        return uriReturn;
    }

    /**
     * 更新資料
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        int updatedRows = 0;
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                updatedRows = db.update("book",values,selection,selectionArgs);
                break;

            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updatedRows = db.update("book",values,"id = ?",new String[]{bookId});
                break;

            case CATEGORY_DIR:
                updatedRows = db.update("category",values,selection,selectionArgs);
                break;

            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updatedRows = db.update("category",values,"id = ?",new String[]
                        {categoryId});
                break;

            default:
                break;
        }
        return updatedRows;
    }

    /**
     * 刪除資料
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        int deletedRows = 0;
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                deletedRows = db.delete("book",selection,selectionArgs);
                break;

            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deletedRows = db.delete("book","id = ?",new String[]{bookId});
                break;

            case CATEGORY_DIR:
                deletedRows = db.delete("category",selection,selectionArgs);
                break;

            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deletedRows = db.delete("category","id = ?",new String[]
                        {categoryId});
                break;

            default:
                break;
        }
        return deletedRows;
    }

    /**
     * 獲取 Uri 物件所對應的 MIME 型別
     */
    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.wonderful.myfirstcode." +
                        "chapter7.provider.book";

            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.wonderful.myfirstcode." +
                        "chapter7.provider.book";

            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.wonderful.myfirstcode." +
                        "chapter7.provider.category";

            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.wonderful.myfirstcode." +
                        "chapter7.provider.category";
        }
        return null;
    }
    
}

 

7.5 Git 時間——版本控制工具進階

7.5.1 忽略檔案

7.5.2 檢視修改內容

7.5.3 撤銷未提交的修改

7.5.4 檢視提交記錄

關於Git 相關的內容請移駕:

https://blog.csdn.net/lhk147852369/article/details/84307580

7.6 小結與點評

郭霖總結:

本章的內容不算多,而且很多時候都是在使用上一章中學習的資料庫只是,所以理解這部分內容對你來說是比較輕鬆地吧。在本章中,我們一開始先了解了Android的許可權機制,並且學會了如何在6.0以上的系統中使用執行時許可權,然然後又重點學習了內容提供器的相關內容,以實現跨程序資料共享的功能。現在你不僅知道了如何去訪問其他程式中的資料,還學會了怎樣建立自己的內容提供器來共享資料,收穫還是挺大的吧。

不過每次在建立內容提供器的時候,你都需要提醒下自己, 我是不是應該這麼做?因為只有真正需要將資料共享出去的時候我們才應該建立內容提供器,僅僅是用於程式內部訪問的資料就沒有必要這麼做,所以千萬別對它進行濫用。

在連續學了幾章系統機制方面的內容之後是不是感覺有些枯燥?那麼下一- 章中我們就來換換口味,學習一下Android多媒體方面的知識吧。

我的總結:

之前在進行工作的過程中,並沒有用到這方面的知識,現在學習了一番,收穫還是非常大的。