天天看點

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?

前言

  • 作為

    Android

    的四大元件之一,

    ContentProvider

    可以說是無處不在了。
  • 但是對于我而言,開發過程中看似

    ContentProvider

    用得很娴熟,卻一直沒能形成一個完整的體系。
  • 也許大家也有着和我類似的煩惱,于是我特地花了幾天的時間,總結了我所知道的知識點,以及面試中可能遇到的問題。将本文分享給大家,希望能幫助大家重新梳理下我們的這個老朋友

    ContentProvider

希望大家閱讀愉快!

文章目錄

  • ContentProvider

    應用程式間非常通用的共享資料的一種方式,也是

    Android

    官方推薦的方式。
  • Android

    中許多系統應用都使用該方式實作資料共享,比如通訊錄、短信等。
Android 的這 13 道 ContentProvider 面試題,你都會了嗎?

1.1 Android 為什麼要設計 ContentProvider 這個元件?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • 很多做

    Android

    開發的人都不怎麼使用它,覺得直接讀取資料庫會更簡單友善。
  • 那麼

    Android

    搞一個内容提供者在資料和應用之間,隻是為了裝高大上,故弄玄虛?我認為其設計用意在于:
  1. 封裝。對資料進行封裝,提供統一的接口,使用者完全不必關心這些資料是在

    DB

    XML

    Preferences

    或者網絡請求來的。當項目需求要改變資料來源時,使用我們的地方完全不需要修改。
  2. 提供一種跨程序資料共享的方式。
  3. 應用程式間的資料共享還有另外的一個重要話題,就是資料更新通知機制了。因為資料是在多個應用程式中共享的,當其中一個應用程式改變了這些共享資料的時候,它有責任通知其它應用程式,讓它們知道共享資料被修改了,這樣它們就可以作相應的處理。

1.2 如何通路自定義 ContentProvider

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • ContentResolver

    接口的

    notifyChange

    函數來通知那些注冊了監控特定 URI的ContentObserver 對象,使得它們可以相應地執行一些處理。
  • ContentObserver 可以通過 registerContentObserver 進行注冊。
  • 通過

    ContentProvider

    Uri

    通路開放的資料。
  1. ContenResolver

    對象通過

    Context

    提供的方法

    getContenResolver()

    來獲得。
  2. ContenResolver

    提供了以下方法來操作:

    insert

    delete

    update

    query

    這些方法分别會調用

    ContenProvider

    中與之對應的方法并得到傳回的結果。

1.3 通過 ContentResolver 擷取 ContentProvider 内容的基本步驟

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  1. 得到

    ContentResolver

    類對象:

    ContentResolver cr = getContentResolver ( )

  2. 定義要查詢的字段

    String

    數組。
  3. 使用

    cr.query()

    ; 傳回一個

    Cursor

    對象。
  4. while

    循環得到

    Cursor

    裡面的内容。

1.4 ContentProvider 是如何實作資料共享的:

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • Android

    中如果想将自己應用的資料 ( 一般多為資料庫中的資料 ) 提供給第三發應用, 那麼我們隻能通過

    ContentProvider

    來實作了。

    ContentProvider

    是應用程式之間共享資料的接口。
  • 使用的時候首先自定義 一個類繼承

    ContentProvider

    , 然後覆寫

    query

    insert

    update

    delete

    等 方法。
  • 因為其是四大元件之一是以必須在

    AndroidManifest

    檔案中進行注冊。
  • 把自己的資料通過

    uri

    的形式共享出去

    android

    系統下 不同程式 資料預設是不能共享通路 需要去實作一個類去繼承

    ContentProvider

public class PersonContentProvider extends ContentProvider{

   public boolean onCreate(){ }
   query(Url, String[], String, String[], String);
   insert(Uri,ContentValues);
   update(Uri,ContentValues,String[]);
   delete(Uri,String,String[]);

}            

1.5 為什麼要用 ContentProvider ?它和 sql 的實作上有什麼差别?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • ContentProvider

    屏蔽了資料存儲的細節 , 内部實作對使用者完全透明 , 使用者隻需要關心操作資料的

    uri

    就可以了,

    ContentProvider

    可以實作不同

    app

    之間 共享。
  • Sql

    也有增删改查的方法, 但是

    sql

    隻能查詢本應用下的資料庫。
  • ContentProvider

    還可以去增删改查本地檔案.

    xml

    檔案的讀取等。

1.6 Uri 介紹

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • 為系統的每一個資源給其一個名字,比方說通話記錄。
  1. 每一個

    ContentProvider

    都擁有一個公共的

    URI

    ,這個

    URI

    用于表示這個

    ContentProvider

    所提供的資料。
  2. Android

    所提供的

    ContentProvider

    都存放在

    android.provider

    包中。
  • 将其分為

    A,B,C,D

    4個部分:
  • A

    :标準字首,用來說明一個

    Content Provider

    控制這些資料,無法改變的;

    "content://"

  • B

    URI

    的辨別,用于唯一辨別這個

    ContentProvider

    ,外部調用者可以根據這個辨別來找到它。它定義了是哪個

    ContentProvider

    提供這些資料。對于第三方應用程式,為了保證

    URI

    辨別的唯一性,它必須是一個完整的、小寫的類名。這個辨別在元素的

    authorities

    屬性中說明:一般是定義該

    ContentProvider

    的包類的名稱;
  • C

    :路徑(

    path

    ),通俗的講就是你要操作的資料庫中表的名字,或者你也可以自己定義,記得在使用的時候保持一緻就可以了;

    "content://com.bing.provider.myprovider/tablename"

  • D

    :如果URI中包含表示需要擷取的記錄的

    ID

    ;則就傳回該id對應的資料,如果沒有

    ID

    ,就表示傳回全部;

    "content://com.bing.provider.myprovider/tablename/#"

    #

    表示資料

    id

1.7 如何通路 asserts 資源目錄下的資料庫?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • 把資料庫

    db

    複制到

    /data/data/packagename/databases/

    目錄下, 然後直接就能通路了。

1.8 多個程序同時調用一個 ContentProvider 的 query 擷取資料,ContentPrvoider 是如何反應的呢?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • 一個

    ContentProvider

    可以接受來自另外一個程序的資料請求。
  • 盡管

    ContentResolver

    ContentProvider

    類隐藏了實作細節,但是

    ContentProvider

    query()

    insert()

    delete()

    update()

    都是在

    ContentProvider

    程序的線程池中被調用執行的,而不是程序的主線程中。
  • 這個線程池是有

    Binder

    建立和維護的,其實使用的就是每個應用程序中的

    Binder

    線程池。

1.9 Android 設計 ContentProvider 的目的是什麼呢?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • 隐藏資料的實作方式,對外提供統一的資料通路接口;
  • 更好的資料通路權限管理。

    ContentProvider

    可以對開發的資料進行權限設定,不同的

    URI

    可以對應不同的權限,隻有符合權限要求的元件才能通路到

    ContentProvider

    的具體操作。
  • ContentProvider

    封裝了跨程序共享的邏輯,我們隻需要

    Uri

    即可通路資料。由系統來管理

    ContentProvider

    的建立、生命周期及通路的線程配置設定,簡化我們在應用間共享資料( 程序間通信 )的方式。我們隻管通過

    ContentResolver

    通路

    ContentProvider

    所提示的資料接口,而不需要擔心它所在程序是啟動還是未啟動。

1.10 運作在主線程的 ContentProvider 為什麼不會影響主線程的UI操作?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • ContentProvider

    onCreate()

    是運作在

    UI

    線程的,而

    query()

    insert()

    delete()

    update()

    是運作線上程池中的工作線程的
  • 是以調用這向個方法并不會阻塞

    ContentProvider

    所在程序的主線程,但可能會阻塞調用者所在的程序的

    UI

    線程!
  • 是以,調用

    ContentProvider

    的操作仍然要放在子線程中去做。
  • 雖然直接的

    CRUD

    的操作是在工作線程的,但系統會讓你的調用線程等待這個異步的操作完成,你才可以繼續線程之前的工作。

1.11 外提供資料共享,那麼如何限制對方的使用呢?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • android:exported

    屬性非常重要。這個屬性用于訓示該服務是否能夠被其他應用程式元件調用或跟它互動。
  • 如果設定為

    true

    ,則能夠被調用或互動,否則不能。
  • 設定為

    false

    時,隻有同一個應用程式的元件或帶有相同使用者

    ID

    的應用程式才能啟動或綁定該服務。
  • 對于需要開放的元件應設定合理的權限,如果隻需要對同一個簽名的其它應用開放

    ContentProvider

    ,則可以設定

    signature

    級别的權限。
  • 大家可以參考一下系統自帶應用的代碼,自定義了

    signature

    級别的

    permission

<permission android:name="com.android.gallery3d.filtershow.permission.READ"
            android:protectionLevel="signature" />

<permission android:name="com.android.gallery3d.filtershow.permission.WRITE"
            android:protectionLevel="signature" />

<provider
    android:name="com.android.gallery3d.filtershow.provider.SharedImageProvider"
    android:authorities="com.android.gallery3d.filtershow.provider.SharedImageProvider"
    android:grantUriPermissions="true"
    android:readPermission="com.android.gallery3d.filtershow.permission.READ"
    android:writePermission="com.android.gallery3d.filtershow.permission.WRITE" />           

1.11.1 如果我們隻需要開放部份的

URI

給其他的應用通路呢?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • 可以參考

    Provider

    URI

    權限設定,隻允許通路部份

    URI

    ,可以參考原生

    ContactsProvider2

    的相關代碼( 注意

    path-permission

    這個選項 ):
<provider android:name="ContactsProvider2"
    android:authorities="contacts;com.android.contacts"
    android:label="@string/provider_label"
    android:multiprocess="false"
    android:exported="true"
    android:grantUriPermissions="true"
    android:readPermission="android.permission.READ_CONTACTS"
    android:writePermission="android.permission.WRITE_CONTACTS">
    <path-permission
            android:pathPrefix="/search_suggest_query"
            android:readPermission="android.permission.GLOBAL_SEARCH" />
    <path-permission
            android:pathPrefix="/search_suggest_shortcut"
            android:readPermission="android.permission.GLOBAL_SEARCH" />
    <path-permission
            android:pathPattern="/contacts/.*/photo"
            android:readPermission="android.permission.GLOBAL_SEARCH" />
    <grant-uri-permission android:pathPattern=".*" />
</provider>           

1.12 ContentProvider 接口方法運作在哪個線程中呢?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • ContentProvider

    可以在

    AndroidManifest.xml

    中配置一個叫做

    android:multiprocess

    的屬性,預設值是 false ,表示 ContentProvider 是單例的
  • 無論哪個用戶端應用的通路都将是同一個

    ContentProvider

    對象,如果設為

    true

    ,系統會為每一個通路該

    ContentProvider

    的程序建立一個執行個體。

1.12.1 這點還是比較好了解的,那如果我要問每個 ContentProvider 的操作是在哪個線程中運作的呢?( 其實我們關心的是 UI 線程和工作線程 )

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • 比如我們在UI線程調用getContentResolver().query查詢資料,而當資料量很大時(或者需要進行較長時間的計算)會不會阻塞UI線程呢?
  • 要分兩種情況回答這個問題:
  1. ContentProvider

    和調用者在同一個程序,

    ContentProvider

    的方法(

    query/insert/update/delete

    等 )和調用者在同一線程中;
  2. ContentProvider

    和調用者在不同的程序,

    ContentProvider

    的方法會運作在它自身所在程序的一個 Binder 線程中。
    但是,注意這兩種方式在 `ContentProvider` 的方法沒有執行完成前都會 `blocked` 調用者。是以你應該知道這個上面這個問題的答案了吧。           
  3. 也可以看看

    CursorLoader

    這個類的源碼,看

    Google

    自己是怎麼使用

    getContentResolver().query

    的。

1.13 ContentProvider 是如何在不同應用程式之間傳輸資料的?

Android 的這 13 道 ContentProvider 面試題,你都會了嗎?
  • 一個應用程序有

    16

    Binder

    線程去和遠端服務進行互動,而每個線程可占用的緩存空間是

    128KB

    這樣,超過會報異常。
  • ContentResolver

    雖然是通過

    Binder

    程序間通信機制打通了應用程式之間共享資料的通道,但

    ContentProvider

    元件在不同應用程式之間傳輸資料是基于匿名共享記憶體機制來實作的。
  • 有興趣的可以檢視一下老羅的文章 Android系統匿名共享記憶體Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
Android 的這 13 道 ContentProvider 面試題,你都會了嗎?

總結

  1. 在這篇文章中,我對我所知道的

    ContentProvider

    知識總進行了詳細的總結,希望大家通過本次閱讀都能有所收獲。
  2. 重點

    :學

    Android

    有一段時間了,我打算好好的梳理一下所學知識,到現在為止,我才總結完

    Activity

    Service

    BroadcastRecevier

    等,有關 事件分發、滑動沖突、新能優化等重要子產品,後面也将詳盡的總結,歡迎大家關注,友善及時接收更新 。
  3. 如果有可以補充的知識點,歡迎大家在評論區指出。

最後

今天分享的面試題就到這裡,還是那句話,有些東西你不僅要懂,而且要能夠很好地表達出來,能夠讓面試官認可你的了解,例如Handler機制,這個是面試必問之題。有些晦澀的點,或許它隻活在面試當中,實際工作當中你壓根不會用到它,但是你要知道它是什麼東西。

不管怎麼樣,不論是什麼樣的大小面試,要想不被面試官虐的不要不要的,隻有刷爆面試題題做好全面的準備,當然除了這個還需要在平時把自己的基礎打紮實,這樣不論面試官怎麼樣一個知識點裡往死裡鑿,你也能應付如流啊~