第一行代碼學習筆記第七章——探究內容提供器

知識點目錄

知識點回顧

7.1 內容提供器簡介

內容提供器(Content Provider)主要用於在不同的應用程序之間實現數據共享的功能。它提供了一套完整的機制,允許一個程序訪問另一個程序中的數據,同時還能保證被訪問數據的安全性。

內容提供器可以選擇只對哪一部分數據進行共享,從而保證我們程序中的隱私數據不會有泄漏的風險。

7.2 運行權限

Android 6.0引用了運行時權限功能,很好地保護了用戶的安全和隱私。

7.2.1 Android權限機制詳解

Android要求我們在訪問用戶涉及到安全性的時候,需要在AndroidManifest.xml中加入相應的權限。這樣用戶就在如下兩個方面得到了保護:

  • 在低於6.0的系統設備上安裝程序時,就會在安裝界面提醒用戶該apk需要的權限

  • 用戶可以在應用程序管理界面查看任意一個程序的權限申請情況

但很多常用的軟件存在“店大欺客”的情況,濫用很多權限。因此Android研發團隊在6.0時就加入了運行時權限功能。即用戶不需要在安裝軟件的時候一次性授權所有申請的權限,而是可以在軟件的使用過程中再對某一項權限申請進行授權。

當然並不是所有的權限都是在運行時申請。Android將所有的權限分爲了兩類:

  • 普通權限

  • 危險權限

普通權限:是指那些不會威脅到用戶的安全和隱私的權限,系統會自動幫我們進行授權。

危險權限:是指那些可能會觸及用戶隱私或者對設備安全性造成影響的權限,必須要由用戶手動點擊授權才行,否則程序就無法使用相應的功能。

Android中的危險權限主要有如下幾種:

注意:表中每一個權限都屬於一個權限組,在處理運行時權限時使用權限名,如果用戶一旦同意授權了,那麼該權限所對應的權限組中所有的其他權限也會同時被授權。

Android系統中完整的權限列表如下:

點擊進入查看Android系統中的所有權限

7.2.2 在程序運行時申請權限

下面我們來使用CALL_PHONE來演示。

示例代碼:

  1. 定義一個button觸發點

     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
     
         <Button
             android:id="@+id/make_call"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="Make Call"
             android:textAllCaps="false"/>
     
     </LinearLayout>
    
  2. 在AndroidManifest.xml中聲明權限

     <uses-permission android:name="android.permission.CALL_PHONE"/>
    
  3. 運行時權限

     public class MainActivity extends AppCompatActivity {
     
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
             Button makeCall = (Button) findViewById(R.id.make_call);
             makeCall.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     //檢查權限
                     if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)
                             != PackageManager.PERMISSION_GRANTED) {
                         //向用戶申請授權
                         ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, 1);
                     } else {
                         call();
                     }
                 }
             });
         }
     
         private void call() {
             try {
                 Intent intent = new Intent(Intent.ACTION_CALL);
                 intent.setData(Uri.parse("tel:10086"));
                 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                     return;
                 }
                 startActivity(intent);
             } catch (Exception e) {
                 e.printStackTrace();
             }
         }
     
         @Override
         public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
             switch (requestCode) {
                 case 1:
                     //如果授權成功則調用call()方法
                     if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                         call();
                     } else {
                         Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
                     }
                     break;
                 default:
             }
         }
     }
    

解釋說明:

第一步:判斷用戶是不是已經給我們授權過

用ContextCompat.checkSelfPermission()方法去判斷用戶是否授過權。共接收兩個參數:

  • 參數一:上下文

  • 參數二:具體的權限名

第二步:將返回值與PackageManager.PERMISSION_GRANTED做比較,如果相等就說明用戶已經授權,如果不相等就表示用戶沒有授權。

第三步:如果授權了,則直接調用call()方法,如果沒有授權,則調用ActivityCompat.requestPermissions()方法向用戶申請授權。requestPermissions()共接收三個參數:

  • 參數一:Activity實例

  • 參數二:String數組,把要申請的權限名放在數組中

  • 參數三:請求碼,只要確保是唯一值就行

調用完requestPermissions()方法後,系統會彈出一個權限申請的對話框,然後用戶可以選擇同意或拒絕,無論哪種最終都會調用onRequestPermissionsResult()方法,授權結果封裝在grantResults參數中,我們只需要判斷一下最後的授權結果。

效果圖:

備註:可以在Settings—>Apps—>RunntimePermissionTest—>Permissions中去關閉已經授予程序的危險權限。

7.3 訪問其他程序中的數據

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

  • 使用現有的內容提供器來讀取和操作相應程序中的數據

  • 創建自己的內容提供器給我們程序的數據提供外部訪問接口

7.3.1 ContentResolver的基本用法

Android中想要訪問內容提供器中共享的數據,需要藉助ContentResolver類,可以通過Context中的getContentResolver()方法獲取到類的實例。它提供了一系列的方法對數據進行CRUD操作。

ContentResolver不接受表名,是接收內容URI,內容URI給內容提供器中的數據建立了唯一標識符,主要由authority和path組成:

  • authority:是用於對不同的應用程序做區分,一般都會採用程序包名

  • path:是用於對同一個程序中的不同表做區分

不過我們需要在頭部加上協議聲明,因此,內容URI最標準的格式寫法如下:

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

在得到內容URI字符串後,還需要將它解析成Uri對象。

Uri uri = Uri.parse("content://com.example.app.provider/table1");

查詢數據

Uri uri = Uri.parse("content://com.example.app.provider/table1");
Cursor cursor = getContentResolver().query(
        uri,           //指定查詢哪個應用程序下哪張表
        projection,    //指定查詢的列名
        selection,     //指定where的約束條件
        selectionArgs, //爲where中的佔位符提供具體的值
        sortOrder);    //指定查詢結果的排序方式

if (cursor != null) {
    while (cursor.moveToNext()) {
        String column1 = cursor.getString(cursor.getColumnIndex("column1"));
        int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
    }
}

添加數據

ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
getContentResolver().insert(uri, values);

更新數據

ContentValues values = new ContentValues();
values.put("column1","");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[]{"text", "1"});

刪除數據

getContentResolver().delete(uri, "column2 = ?", new String[]{"1"});
7.3.2 讀取系統聯繫人

下面我們來讀取電話簿中的聯繫人。

示例代碼

  1. 寫一個ListView佈局

     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
     
         <ListView
             android:id="@+id/contacts_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent">
     
         </ListView>
     
     </LinearLayout>
    
  2. 讀取聯繫人邏輯

     public class MainActivity extends AppCompatActivity {
     
         List<String> contactsList = new ArrayList<>();
         private ArrayAdapter<String> mAdapter;
     
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
             ListView contactsView = (ListView) findViewById(R.id.contacts_view);
             mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contactsList);
             contactsView.setAdapter(mAdapter);
             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);
                     }
                     mAdapter.notifyDataSetChanged();
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             } finally {
                 if (cursor != null) {
                     cursor.close();
                 }
             }
         }
     
         @Override
         public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull 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:
             }
         }
     }
    
  3. 添加權限

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

效果圖:

7.4 創建自己的內容提供器

7.4.1 創建內容提供器的步驟

新建一個類去繼承ContentProvider的方式來創建一個自己的內容提供器。

ContentProvider類中有6個抽象方法,子類繼承它的時候,需要將這6個方法全部重寫。

public class MyProvider extends ContentProvider {
    @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) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        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;
    }
}

onCreate()

初始化內容提供器時調用。主要完成對數據庫的創建和升級等操作。

返回true表示內容提供器初始化成功;返回false表示失敗。

備註:只有當存在ContentResolver嘗試訪問我們程序中的數據時,內容提供器纔會被初始化。

query()

從內容提供器中查詢數據,查詢的結果存放在Cursor對象中。共接收5個參數:

Uri:確定查詢哪張表

projection:確定查詢哪些列

selection和selectionArgs:約束查詢哪些行

sortOrder:對查詢結果進行排序

insert()

向內容提供器中添加一條數據。共接收2個參數:

Uri:確定要添加到哪張表

values:待添加的數據保存到ContentValues中

返回一個用於表示這條新紀錄的URI。

update()

更新內容提供器中已有的數據。共接收4個參數:

Uri:確定要更新哪張表

values:保存要更新的數據

selection和selectionArgs:約束更新哪些行

受到影響的行數將作爲返回值返回。

delete()

從內容提供器中刪除數據。共接收3個參數:

Uri:確定要刪除哪張表中的數據

selection和selectionArgs:約束刪除哪些行

被刪除的行數將作爲返回值返回。

getType()

根據傳入內容的URI來返回相應的MIME類型

內容URI主要的兩種格式:

格式一:

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

表示訪問com.example.app.provider這個應用的table1表中的數據。

格式二:

content://com.example.app.provider/table2/1

表示訪問com.example.app.provider這個應用的table2表中id爲1的數據

我們可以使用通配符的方式來分別匹配這兩種格式的內容URI,規則如下:

  • *:表示匹配任意長度的任意字符

  • #:表示匹配任意長度的數字

所以,一個能匹配任意表的內容URI格式就可以寫成:

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

一個能匹配table1表中任意一行數據的內容URI格式就可以寫成:

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

緊接着,我們可以藉助UriMatcher這個類實現匹配內容URI的功能,UriMatcher提供了一個addURI()方法,該方法共接收3個參數:

  • 參數一:authority (一般是應用包名+provider)

  • 參數二:path (一般是表名)

  • 參數三:自定義代碼

然後調用UriMatcher的match()方法時,就可以將一個Uri對象傳入,返回值是某個能夠匹配這個Uri對象所對應的自定義代碼,根據這個自定義就可以判斷出調用方期望訪問的是哪張表中的數據。

示例代碼:

public class MyProvider extends ContentProvider {

    public static final int TABLE1_DIR = 0;
    public static final int TABLE1_ITEM = 1;
    public static final int TABLE2_DIR = 2;
    public static final int TABLE2_ITEM = 3;
    private static UriMatcher uriMatcher;
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
        uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_ITEM);
        uriMatcher.addURI("com.example.app.provider", "table2", TABLE2_DIR);
        uriMatcher.addURI("com.example.app.provider", "table2", TABLE2_ITEM);
    }

    @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_ITEM:
                //查詢table1表中的單條數據
                break;
            case TABLE2_DIR:
                //查詢table2表中的所有數據
                break;
            case TABLE2_ITEM:
                //查詢table2表中的單條數據
                break;
            default:
                break;
        }
        return null;
    }

    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        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;
    }
}

上面只演示了query()方法,其他的insert()、update()、delete()方法實現起來也一樣,都通過UriMatcher的match()方法去判斷訪問方期望訪問哪張表。

MIME類型

getType()方法會根據Uri返回一個MIME類型,一個URI所對應的MIME字符串主要由3個部分組成:

  • 必須以vnd開頭

  • 如果內容URI以路徑結尾,則後接android.cursor.dir/;如果內容URI已id結尾,則後接android.cursor.item/。

  • 最後接上vnd..

所以,對於:

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

這個內容URI,它所對應的MIME類型就可以寫成:

vnd.android.cursor.dir/vnd.com.example.app.provider.table1

對於:

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

這個內容URI,它所對應的MIME類型就可以寫成:

vnd.android.cursor.item/vnd.com.example.app.provider.table1

示例代碼:

public String getType(@NonNull Uri uri) {
    switch (uriMatcher.match(uri)) {
        case TABLE1_DIR:
            return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
        case TABLE1_ITEM:
            return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
        case TABLE2_DIR:
            return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
        case TABLE2_ITEM:
            return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
        default:
            break;
    }
    return null;
}
7.4.2 實現跨程序數據共享

在上一章DatabaseTest項目上繼續開發,新建一個內容提供器,右擊com.example.broadcasttest包—>New—>Other—>Content Provider。

自定義內容提供器代碼如下:

public class DatabaseProvider extends ContentProvider {

    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 UriMatcher mUriMatcher;
    public static final String AUTHORITY = "com.example.databasetest.provider";

    static {
        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mUriMatcher.addURI(AUTHORITY,"book",BOOK_DIR);
        mUriMatcher.addURI(AUTHORITY,"book/#",BOOK_ITEM);
        mUriMatcher.addURI(AUTHORITY,"category",CATEGORY_DIR);
        mUriMatcher.addURI(AUTHORITY,"category/#",CATEGORY_ITEM);
    }

    private MyDatabaseHelper mDbHelper;

    public DatabaseProvider() {
    }

    @Override
    public boolean onCreate() {
        mDbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        //查詢數據
        SQLiteDatabase db = mDbHelper.getReadableDatabase();
        Cursor cursor = null;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                //getPathSegments()會將內容URI權限之後的部分以"/"符號進行分割,分割後的結果放入到一個
                //字符串列表中,列表的第0個位置存放的是路徑,第1個位置存放的是id
                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 = mDbHelper.getWritableDatabase();
        Uri uriReturn = null;
        switch (mUriMatcher.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 = mDbHelper.getWritableDatabase();
        int updateRows = 0;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                updateRows = db.update("Book", values, selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updateRows = db.update("Book", values, "id = ?", new String[]{bookId});
                break;
            case CATEGORY_DIR:
                updateRows = db.update("Category", values, selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updateRows = db.update("Category", values, "id = ?", new String[]{categoryId});
                break;
            default:
                break;
        }
        return updateRows;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // 刪除數據
        SQLiteDatabase db = mDbHelper.getWritableDatabase();
        int deleteRows = 0;
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                deleteRows = db.delete("Book", selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deleteRows = db.delete("Book", "id = ?", new String[]{bookId});
                break;
            case CATEGORY_DIR:
                deleteRows = db.delete("Category", selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deleteRows = db.delete("Category", "id = ?", new String[]{categoryId});
                break;
            default:
                break;
        }
        return deleteRows;
    }

    @Override
    public String getType(Uri uri) {
        switch (mUriMatcher.match(uri)) {
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
            default:
                break;
        }
        return null;
    }
}

DatabaseTest項目的完整代碼我已上傳我到gitHub,有需要的朋友可以進入查看:

點擊查看DatabaseTest項目

訪問內容提供器中的數據:

public class MainActivity extends AppCompatActivity {

    private String mNewId;

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

        //添加數據
        Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
                ContentValues values = new ContentValues();
                values.put("name", "A Clash of kings");
                values.put("author", "George Martin");
                values.put("pages", 1040);
                values.put("price",22.85);
                Uri newUri = getContentResolver().insert(uri, values);
                mNewId = newUri.getPathSegments().get(1);
            }
        });

        //查詢數據
        Button queryData = (Button) findViewById(R.id.query_data);
        queryData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
                Cursor cursor = getContentResolver().query(uri, null, null, null, null);
                if (cursor != null) {
                    while (cursor.moveToNext()) {
                        String name = cursor.getString(cursor.getColumnIndex("name"));
                        String author = cursor.getString(cursor.getColumnIndex("author"));
                        int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                        double price = cursor.getDouble(cursor.getColumnIndex("price"));
                        Log.d("MainActivity", "book name is " + name);
                        Log.d("MainActivity", "book author is " + author);
                        Log.d("MainActivity", "book pages is " + pages);
                        Log.d("MainActivity", "book price is " + price);
                    }
                }
            }
        });

        //更新數據
        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + mNewId);
                ContentValues values = new ContentValues();
                values.put("name", "A Storm of Swords");
                values.put("pages", 1216);
                values.put("price", 24.05);
                getContentResolver().update(uri, values, null, null);
            }
        });

        //刪除數據
        Button deleteData = (Button) findViewById(R.id.delete_data);
        deleteData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + mNewId);
                getContentResolver().delete(uri, null, null);
            }
        });

        //清空表中的數據
        Button clearData = (Button) findViewById(R.id.clear_data);
        clearData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book/");
                int delete = getContentResolver().delete(uri, null, null);
                Log.d("MainActivity", "delete = " + delete);
            }
        });
    }
}

ProvideTest程序是去訪問DatabaseTest中的數據,完整代碼我已上傳我到gitHub,有需要的朋友可以進入查看:

點擊查看ProvideTest項目

7.5 Git時間

這裏推薦一個Git詳細教程。

Git使用教程

7.6 小結點評

本章瞭解了Android的權限機制,並且學會了如何在6.0以上的系統中使用運行時的權限,然後又重點學習了內容提供器的相關內容,以實現跨程序共享的功能。

非常感謝您的耐心閱讀,希望我的文章對您有幫助。歡迎點評、轉發或分享給您的朋友或技術羣。
發佈了46 篇原創文章 · 獲贊 72 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章