27 個問題突破全部重難點,BroadcastReceiver 、ContentProvider 知多少?「建議收藏」

Android

前言


  • 距離上次更新過去一週多了,打破了以前兩到三天一更的慣例,主要仍是這部份內容太多了。
  • 原先想把 BroadcastReceiverContentProvider 分兩篇來總結,但的確,這兩大組件的使用不像 ActivityService 那麼頻繁,因此仍是決定一次性搞定。
  • 因而這篇近 1.5 W 字的文章就誕生了。能夠說這周幾乎全部時間都花在這上面,本身看了幾遍感受已經極爲全面了。
  • 祝你們閱讀愉快。

最後,但願你們都能有所收穫,歡迎食用!java

文章目錄


文章目錄

方便你們學習,我在 GitHub 上創建個 倉庫


1、BroadcastReceiver


  • BroadcastReceiver,顧名思義就是「廣播接收者」的意思,它是Android四大基本組件之一。
  • 這種組件本質上是一種全局的監聽器,用於監聽系統全局的廣播消息。
  • 它能夠接收來自系統和應用的的廣播。

BroadcastReceiver

1.1 什麼是 BroadcastReceiver

什麼是 BroadcastReceiver

  • 是四大組件之一, 主要用於接收 app 發送的廣播
  • 內部通訊實現機制:經過 android 系統的 Binder 機制.

1.2 廣播分爲兩種

廣播分爲兩種

1.2.1 無序廣播

無序廣播

  • 也叫標準廣播,是一種徹底異步執行的廣播。
  • 在廣播發出以後,全部廣播接收器幾乎都會在同一時刻接收到這條廣播消息,它們之間沒有任何前後順序,廣播的效率較高。
  • 優勢: 徹底異步, 邏輯上可被任何接受者收到廣播,效率高
  • 缺點: 接受者不能將處理結果交給下一個接受者, 且沒法終止廣播.

1.2.2 有序廣播

有序廣播

  • 是一種同步執行的廣播。
  • 在廣播發出以後,同一時刻只有一個廣播接收器可以收到這條廣播消息,當其邏輯執行完後該廣播接收器纔會繼續傳遞。
  • 調用 SendOrderedBroadcast() 方法來發送廣播,同時也可調用 abortBroadcast() 方法攔截該廣播。可經過 <intent-filter> 標籤中設置 android:property 屬性來設置優先級,未設置時按照註冊的順序接收廣播。
  • 有序廣播接受器間能夠互傳數據。
  • 當廣播接收器收到廣播後,當前廣播也可使用 setResultData 方法將數據傳給下一個接收器。
  • 使用 getStringExtra 函數獲取廣播的原始數據,經過 getResultData 方法取得上個廣播接收器本身添加的數據,並可用 abortBroadcast 方法丟棄該廣播,使該廣播再也不被別的接收器接收到。

總結

  • 總結
  1. 按被接收者的優先級循序傳播 A > B > C ,
  2. 每一個都有權終止廣播, 下一個就得不到
  3. 每個均可進行修改操做, 下一個就獲得上一個修改後的結果.

1.2.3 最終廣播者

最終廣播者

  • Context.sendOrderedBroadcast ( intent , receiverPermission , resultReceiver , scheduler , initialCode , initialData , initialExtras ) 時咱們能夠指定 resultReceiver 爲最終廣播接收者.
  • 若是比他優先級高的接受者不終止廣播, 那麼他的 onReceive 會執行兩次
  • 第一次是正常的接收
  • 第二次是最終的接收
  • 若是優先級高的那個終止廣播, 那麼他仍是會收到一次最終的廣播

1.2.4 常見的廣播接收者運用場景

廣播接收者運用場景

  • 開機啓動, sd 卡掛載, 低電量, 外撥電話, 鎖屏等
  • 好比根據產品經理要求, 設計播放音樂時, 鎖屏是否決定暫停音樂.

1.3 BroadcastReceiver 的種類

1.3.1 廣播做爲 Android 組件間的通訊方式,以下使用場景:

對前一部分 「 請描述一下 BroadcastReceiver 」 進行展開補充github

BroadcastReceiver 使用場景

  • APP 內部的消息通訊。
  • 不一樣 APP 之間的消息通訊。算法

  • Android 系統在特定狀況下與 APP 之間的消息通訊。sql

  • 廣播使用了觀察者模式,基於消息的發佈 / 訂閱事件模型。廣播將廣播的發送者和接受者極大程度上解耦,使得系統可以方便集成,更易擴展。數據庫

  • BroadcastReceiver 本質是一個全局監聽器,用於監聽系統全局的廣播消息,方便實現系統中不一樣組件間的通訊。設計模式

  • 自定義廣播接收器須要繼承基類 BroadcastReceiver ,並實現抽象方法 onReceive ( context, intent ) 。默認狀況下,廣播接收器也是運行在主線程,所以 onReceiver() 中不能執行太耗時的操做( 不超過 10s ),不然將會產生 ANR 問題。onReceiver() 方法中涉及與其餘組件之間的交互時,可使用發送 Notification 、啓動 Service 等方式,最好不要啓動 Activity數組

1.3.2 系統廣播

系統廣播

  • Android 系統內置了多個系統廣播,只要涉及手機的基本操做,基本上都會發出相應的系統廣播,如開機啓動、網絡狀態改變、拍照、屏幕關閉與開啓、電量不足等。在系統內部當特定時間發生時,系統廣播由系統自動發出。緩存

  • 常見系統廣播 Intent 中的 Action 爲以下值:

常見系統廣播 Intent 中的 Action

  1. 短信提醒:android.provider.Telephony.SMS_RECEIVED
  2. 電量太低:ACTION_BATIERY_LOW
  3. 電量發生改變:ACTION_BATTERY_CHANGED
  4. 鏈接電源:ACTION_POWER_CO             
  • Android 7.0 開始,系統不會再發送廣播 ACTION_NEW_PICTUREACTION_NEW_VIDEO ,對於廣播 CONNECTIVITY_ACTION 必須在代碼中使用 registerReceiver 方法註冊接收器,在 AndroidManifest 文件中聲明接收器不起做用。
  • Android 8.0 開始,對於大多數隱式廣播,不能在 AndroidManifest 文件中聲明接收器。

1.3.3 局部廣播

局部廣播

  • 局部廣播的發送者和接受者都同屬於一個 APP
  • 相比於全局廣播具備如下優勢:
  1. 其餘的 APP 不會受到局部廣播,不用擔憂數據泄露的問題。
  2. 其餘 APP 不可能向當前的 APP 發送局部廣播,不用擔憂有安全漏洞被其餘 APP 利用。
  3. 局部廣播比經過系統傳遞的全局廣播的傳遞效率更高。
  • Android v4 包中提供了 LocalBroadcastManager 類,用於統一處理 APP 局部廣播,使用方式與全局廣播幾乎相同,只是調用註冊 / 取消註冊廣播接收器和發送廣播偶讀方法時,須要經過 LocalBroadcastManager 類的 getInstance() 方法獲取的實例調用。

1.4 BroadcastReceiver 註冊方式

BroadcastReceiver 註冊方式

1.4.1 靜態註冊

AndroidManifest.xml 文件中配置。

<receiver android:name=".MyReceiver" android:exported="true">
    <intent-filter>
        <!-- 指定該 BroadcastReceiver 所響應的 Intent 的 Action -->
        <action android:name="android.intent.action.INPUT_METHOD_CHANGED"
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
  • 兩個重要屬性須要關注:

兩個重要屬性須要關注

  1. android: exported
    其做用是設置此 BroadcastReceiver 可否接受其餘 APP 發出的廣播 ,當設爲 false 時,只能接受同一應用的的組件或具備相同 user ID 的應用發送的消息。這個屬性的默認值是由 BroadcastReceiver 中有無 Intent-filter 決定的,若是有 Intent-filter ,默認值爲 true ,不然爲 false
  2. android: permission
    若是設置此屬性,具備相應權限的廣播發送方發送的廣播才能被此 BroadcastReceiver 所接受;若是沒有設置,這個值賦予整個應用所申請的權限。

1.4.2 動態註冊

  • 調用 ContextregisterReceiver ( BroadcastReceiver receiver , IntentFilter filter ) 方法指定。

1.5 在 Mainfest 和代碼如何註冊和使用 BroadcastReceiver ? ( 一個 action 是重點 )

Mainfest 和代碼如何註冊和使用 BroadcastReceiver

1.5.1 使用文件註冊 ( 靜態廣播 )

  • 只要 app 還在運行,那麼會一直收到廣播消息

使用文件註冊 ( 靜態廣播 )

  • 演示:
  1. 一個 app 裏: 自定義一個類繼承 BroadcastReceiver 而後要求重寫 onReveiver 方法
public class MyBroadCastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("MyBroadCastReceiver", "收到信息,內容是 : " + intent.getStringExtra("info") + "");
    }
}
  1. 清單文件註冊,並設置 Action , 就那麼簡單完成接收準備工做
<receiver android:name=".MyBroadCastReceiver">
    <intent-filter>
        <action android:name="myBroadcast.action.call"/>
    </intent-filter>
</receiver>

1.5.2 代碼註冊 ( 動態廣播 )

  • 當註冊的 Activity 或者 Service 銷燬了那麼就會接收不到廣播.

代碼註冊 ( 動態廣播 )

  • 演示:
  1. 在和廣播接受者相同的 app 裏的 MainActivity 添加一個註冊按鈕 , 用來註冊廣播接收者
  2. 設置意圖過濾,添加 Action
//onCreate建立廣播接收者對象
mReceiver = new MyBroadCastReceiver();              

//註冊按鈕
public void click(View view) {
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("myBroadcast.action.call");
    registerReceiver(mReceiver, intentFilter);
}
  1. 銷燬的時候取消註冊
@Override
protected void onDestroy() {
    unregisterReceiver(mReceiver);
    super.onDestroy();
}

1.5.3 在另外一個 app , 定義一個按鈕, 設置意圖, 意圖添加消息內容, 意圖設置 action( ... ) 要匹配 , 而後發送廣播便可.

代碼註冊 ( 動態廣播 )

public void click(View view) {
    Intent intent = new Intent();
    intent.putExtra("info", "消息內容");
    intent.setAction("myBroadcast.action.call");
    sendBroadcast(intent);
}
  • 運行兩個 app 以後:
  1. 靜態註冊的方法: 另外一 app 直接發廣播就收到了
  2. 動態註冊的方法: 本身的 app 先代碼註冊,而後另外一個 app 直接發廣播便可.-

1.6 BroadcastReceiver 的實現原理是什麼?

  • Android 中的廣播使用了設計模式中的觀察者模式:基於消息的發佈 / 訂閱事件模型。

BroadcastReceiver 的實現原理

  • 模型中主要有 3 個角色:
  1. 消息訂閱者( 廣播接收者 )
  2. 消息發佈者( 廣播發布者 )
  3. 消息中心( AMS,即 Activity Manager Service

1.6.1 原理:

原理

  • 廣播接收者經過 Binder 機制在 AMSActivity Manager Service ) 註冊;

  • 廣播發送者經過 Binder 機制向 AMS 發送廣播;

  • AMS 根據廣播發送者要求,在已註冊列表中,尋找合適的 BroadcastReceiver ( 尋找依據:IntentFilter / Permission );

  • AMS 將廣播發送到 BroadcastReceiver 相應的消息循環隊列中;

  • 廣播接收者經過消息循環拿到此廣播,並回調 onReceive() 方法。

  • 須要注意的是:廣播的發送和接受是異步的,發送者不會關心有無接收者或者什麼時候收到。

  

1.7 本地廣播

本地廣播

  • 本地廣播機制使得發出的廣播只可以在應用程序的內部進行傳遞,而且廣播接收器也只能接受來自本應用程序發出的廣播,則安全性獲得了提升。
  • 本地廣播主要是使用了一個 LocalBroadcastManager 來對廣播進行管理,並提供了發送廣播和註冊廣播接收器的方法。
  • 開發者只要實現本身的 BroadcastReceiver 子類,並重寫 onReceive ( Context context, Intetn intent ) 方法便可。
  • 當其餘組件經過 sendBroadcast()sendStickyBroadcast()sendOrderBroadcast() 方法發送廣播消息時,若是該 BroadcastReceiver 也對該消息「感興趣」,BroadcastReceiveronReceive ( Context context, Intetn intent ) 方法將會被觸發。

  • 使用步驟:

  1. 調用 LocalBroadcastManager.getInstance() 得到實例
  2. 調用 registerReceiver() 方法註冊廣播
  3. 調用 sendBroadcast() 方法發送廣播
  4. 調用 unregisterReceiver() 方法取消註冊

1.7.1 注意事項:

注意事項

  1. 本地廣播沒法經過靜態註冊方式來接受,相比起系統全局廣播更加高效。
  2. 在廣播中啓動 Activity 時,須要爲 Intent 加入 FLAG_ACTIVITY_NEW_TASK 標記,不然會報錯,由於須要一個棧來存放新打開的 Activity
  3. 廣播中彈出 Alertdialog 時,須要設置對話框的類型爲 TYPE_SYSTEM_ALERT ,不然沒法彈出。
  4. 不要在 onReceiver() 方法中添加過多的邏輯或者進行任何的耗時操做,由於在廣播接收器中是不容許開啓線程的,當 onReceiver() 方法運行了較長時間而沒有結束時,程序就會報錯。

1.8 Sticky Broadcast 粘性廣播

Sticky Broadcast 粘性廣播

  • 若是發送者發送了某個廣播,而接收者在這個廣播發送後才註冊本身的 Receiver ,這時接收者便沒法接收到剛纔的廣播
  • 爲此 Android 引入了 StickyBroadcast ,在廣播發送結束後會保存剛剛發送的廣播( Intent ),這樣當接收者註冊完 Receiver 後就能夠繼續使用剛纔的廣播。
  • 若是在接收者註冊完成前發送了多條相同 Action 的粘性廣播,註冊完成後只會收到一條該 Action 的廣播,而且消息內容是最後一次廣播內容。

  • 系統網絡狀態的改變發送的廣播就是粘性廣播。
  1. 粘性廣播經過 ContextsendStickyBroadcast ( Intent ) 接口發送,須要添加權限
  2. uses-permission android:name=」android.permission.BROADCAST_STICKY」
  3. 也能夠經過 ContextremoveStickyBroadcast ( Intent intent ) 接口移除緩存的粘性廣播

1.9 LocalBroadcastManager 詳解

1.9.1 特色:

LocalBroadcastManager 特色

  1. 使用它發送的廣播將只在自身APP內傳播,所以你沒必要擔憂泄漏隱私數據;

  2. 其餘 APP 沒法對你的 APP 發送該廣播,由於你的APP根本就不可能接收到非自身應用發送的該廣播,所以你沒必要擔憂有安全漏洞能夠利用;

  3. 比系統的全局廣播更加高效。

1.9.2 源碼分析 :

LocalBroadcastManager 源碼分析

  1. LocalBroadcastManager 內部協做主要是靠這兩個 Map 集合:MReceiversMActions ,固然還有一個 List 集合 MPendingBroadcasts ,這個主要就是存儲待接收的廣播對象。

  2. LocalBroadcastManager 高效的緣由主要是由於它內部是經過 Handler 實現的,它的 sendBroadcast() 方法含義並不是和咱們平時所用的同樣,它的 sendBroadcast() 方法實際上是經過 handler 發送一個 Message 實現的;

  3. 既然它內部是經過 Handler 來實現廣播的發送的,那麼相比於系統廣播經過 Binder 實現那確定是更高效了,同時使用 Handler 來實現,別的應用沒法向咱們的應用發送該廣播,而咱們應用內發送的廣播也不會離開咱們的應用;

1.9.3 BroadcastReceiver 安全問題

BroadcastReceiver 安全問題

  • BroadcastReceiver 設計的初衷是從全局考慮能夠方便應用程序和系統、應用程序之間、應用程序內的通訊,因此對單個應用程序而言BroadcastReceiver 是存在安全性問題的 ( 惡意程序腳本不斷的去發送你所接收的廣播 ) 。爲了解決這個問題 LocalBroadcastManager 就應運而生了。

  • LocalBroadcastManagerAndroid Support 包提供了一個工具,用於在同一個應用內的不一樣組件間發送 BroadcastLocalBroadcastManager 也稱爲局部通知管理器,這種通知的好處是安全性高,效率也高,適合局部通訊,能夠用來代替 Handler 更新 UI

1.9.4 廣播的安全性

  • Android 系統中的廣播能夠跨進程直接通訊,會產生如下兩個問題:
  1. 其餘 APP 能夠接收到當前 APP 發送的廣播,致使數據外泄。
  2. 其餘 APP 能夠向當前 APP 放廣播消息,致使 APP 被非法控制。

廣播的安全性

  • 發送廣播
  1. 發送廣播時,增長相應的 permission ,用於權限驗證。
  2. Android 4.0 及以上系統中發送廣播時,可使用 setPackage() 方法設置接受廣播的包名。
  3. 使用局部廣播。
  • 接受廣播
  1. 註冊廣播接收器時,增長相應的 permission ,用於權限驗證。
  2. 註冊廣播接收器時,設置 android:exported 的值爲false。
  • 使用局部廣播。
  1. 發送廣播時,若是增長了 permission
  2. 那接受廣播的 APP 必須申請相應權限,這樣才能收到對應的廣播,反之亦然。

1.9.5 使用 BroadcastReceiver 的好處

BroadcastReceiver 的好處

  1. 因廣播數據在本應用範圍內傳播,你不用擔憂隱私數據泄露的問題。

  2. 不用擔憂別的應用僞造廣播,形成安全隱患。

  3. 相比在系統內發送全局廣播,它更高效。

1.10 如何讓本身的廣播只讓指定的 app 接收?

讓本身的廣播只讓指定的 app 接收

  • 在發送廣播的 app 端,自定義定義權限, 那麼想要接收的另外 app 端必須聲明權限才能收到.
  1. 權限, 保護層級是普通正常.
  2. 用戶權限
<permission android:name="broad.ok.receiver" android:protectionLevel="normal"/>
<uses-permission android:name="broad.ok.receiver" />
  1. 發送廣播的時候加上權限字符串
public void click(View view) {
    Intent intent = new Intent();
    intent.putExtra("info", "消息內容");
    intent.setAction("myBroadcast.action.call");
    sendBroadcast(intent, "broad.ok.receiver");
    //sendOrderedBroadcast(intent,"broad.ok.receiver");
}
  1. 其餘app接收者想好獲取廣播,必須聲明在清單文件權限
<uses-permission android:name="broad.ok.receiver"/>

1.11 廣播的優先級對無序廣播生效嗎?

廣播的優先級對無序廣播生效

  • 優先級對無序也生效.

1.12 動態註冊的廣播優先級誰高?

動態註冊的廣播優先級誰高

  • 誰先註冊,誰就高

1.13 如何判斷當前的 BrodcastReceiver 接收到的是有序仍是無序的廣播?

判斷當前的 BrodcastReceiver 接收到的是有序仍是無序

  • onReceiver 方法裏,直接調用判斷方法得返回值
public void onReceive(Context context, Intent intent) {
    Log.d("MyBroadCastReceiver", "收到信息,內容是 : " + intent.getStringExtra("info") + "");
    boolean isOrderBroadcast = isOrderedBroadcast();
}

1.14 BroadcastReceiver 不能執行耗時操做

BroadcastReceiver 不能執行耗時操做

  • 一方面
  1. BroadcastReceiver 通常處於主線程。
  2. 耗時操做會致使 ANR
  • 另外一方面
  1. BroadcastReceiver 啓動時間較短。
  2. 若是一個進程裏面只存在一個 BroadcastReceiver 組件。而且在其中開啓子線程執行耗時任務。
  3. 系統會認爲該進程是優先級最低的空進程。很容易將其殺死。

2、ContentProvider


  • ContentProvider 應用程序間很是通用的共享數據的一種方式,也是 Android 官方推薦的方式。
  • Android 中許多系統應用都使用該方式實現數據共享,好比通信錄、短信等。

ContentProvider

2.1 Android 爲何要設計 ContentProvider 這個組件?

爲何要設計 ContentProvider

  • 不少作 Android 開發的人都不怎麼使用它,以爲直接讀取數據庫會更簡單方便。
  • 那麼 Android 搞一個內容提供者在數據和應用之間,只是爲了裝高大上,故弄玄虛?我認爲其設計用意在於:
  1. 封裝。對數據進行封裝,提供統一的接口,使用者徹底沒必要關心這些數據是在 DBXMLPreferences 或者網絡請求來的。當項目需求要改變數據來源時,使用咱們的地方徹底不須要修改。
  2. 提供一種跨進程數據共享的方式。
  3. 應用程序間的數據共享還有另外的一個重要話題,就是數據更新通知機制了。由於數據是在多個應用程序中共享的,當其中一個應用程序改變了這些共享數據的時候,它有責任通知其它應用程序,讓它們知道共享數據被修改了,這樣它們就能夠做相應的處理。

2.2 如何訪問自定義 ContentProvider

如何訪問自定義 ContentProvider

  • ContentResolver 接口的 notifyChange 函數來通知那些註冊了監控特定 URI的ContentObserver 對象,使得它們能夠相應地執行一些處理。
  • ContentObserver 能夠經過 registerContentObserver 進行註冊。
  • 經過 ContentProviderUri 訪問開放的數據。
  1. ContenResolver 對象經過 Context 提供的方法 getContenResolver() 來得到。
  2. ContenResolver 提供瞭如下方法來操做:insert delete update query 這些方法分別會調用 ContenProvider 中與之對應的方法並獲得返回的結果。

2.3 經過 ContentResolver 獲取 ContentProvider 內容的基本步驟

ContentResolver 獲取 ContentProvider 內容的基本步驟

  1. 獲得 ContentResolver 類對象:ContentResolver cr = getContentResolver ( )
  2. 定義要查詢的字段 String 數組。
  3. 使用 cr.query() ; 返回一個 Cursor 對象。
  4. 使用 while 循環獲得 Cursor 裏面的內容。

2.4 ContentProvider 是如何實現數據共享的:

ContentProvider 是如何實現數據共享的

  • Android 中若是想將本身應用的數據 ( 通常多爲數據庫中的數據 ) 提供給第三發應用, 那麼咱們只能經過 ContentProvider 來實現了。 ContentProvider 是應用程序之間共享數據的接口。
  • 使用的時候首先自定義 一個類繼承 ContentProvider , 而後覆寫 queryinsertupdatedelete 等 方法。
  • 由於其是四大組件之一所以必須在 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[]);
   
}

2.5 爲何要用 ContentProvider ?它和 sql 的實現上有什麼差異?

爲何要用 ContentProvider ?它和 sql 的實現上有什麼差異

  • ContentProvider 屏蔽了數據存儲的細節 , 內部實現對用戶徹底透明 , 用戶只須要關心操做數據的 uri 就能夠了, ContentProvider 能夠實現不一樣 app之間 共享。
  • Sql 也有增刪改查的方法, 可是 sql 只能查詢本應用下的數據庫。
  • ContentProvider 還能夠去增刪改查本地文件. xml 文件的讀取等。

2.6 Uri 介紹

Uri 介紹

  • 爲系統的每個資源給其一個名字,比方說通話記錄。
  1. 每個 ContentProvider 都擁有一個公共的 URI ,這個 URI 用於表示這個 ContentProvider 所提供的數據。
  2. Android 所提供的 ContentProvider 都存放在 android.provider 包中。
  • 將其分爲 A,B,C,D 4個部分:
  • A:標準前綴,用來講明一個 Content Provider 控制這些數據,沒法改變的;"content://"
  • BURI 的標識,用於惟一標識這個 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

2.7 如何訪問 asserts 資源目錄下的數據庫?

訪問 asserts 資源目錄下的數據庫

  • 把數據庫 db 複製到 /data/data/packagename/databases/ 目錄下, 而後直接就能訪問了。

2.8 多個進程同時調用一個 ContentProvider 的 query 獲取數據,ContentPrvoider 是如何反應的呢?

調用一個 ContentProvider 的 query 獲取數據,ContentPrvoider 是如何反應的

  • 一個 ContentProvider 能夠接受來自另一個進程的數據請求。
  • 儘管 ContentResolverContentProvider 類隱藏了實現細節,可是 ContentProvider 所提供的 query()insert()delete()update() 都是在 ContentProvider 進程的線程池中被調用執行的,而不是進程的主線程中。
  • 這個線程池是有 Binder 建立和維護的,其實使用的就是每一個應用進程中的 Binder 線程池。

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

設計 ContentProvider 的目的

  • 隱藏數據的實現方式,對外提供統一的數據訪問接口;
  • 更好的數據訪問權限管理。ContentProvider 能夠對開發的數據進行權限設置,不一樣的 URI 能夠對應不一樣的權限,只有符合權限要求的組件才能訪問到 ContentProvider 的具體操做。
  • ContentProvider 封裝了跨進程共享的邏輯,咱們只須要 Uri 便可訪問數據。由系統來管理 ContentProvider 的建立、生命週期及訪問的線程分配,簡化咱們在應用間共享數據( 進程間通訊 )的方式。咱們只管經過 ContentResolver 訪問 ContentProvider 所提示的數據接口,而不須要擔憂它所在進程是啓動仍是未啓動。

2.10 運行在主線程的 ContentProvider 爲何不會影響主線程的UI操做?

ContentProvider 爲何不會影響主線程的UI操做

  • ContentProvideronCreate() 是運行在 UI 線程的,而 query()insert()delete()update() 是運行在線程池中的工做線程的
  • 因此調用這向個方法並不會阻塞 ContentProvider 所在進程的主線程,但可能會阻塞調用者所在的進程的 UI 線程!
  • 因此,調用 ContentProvider 的操做仍然要放在子線程中去作。
  • 雖然直接的 CRUD 的操做是在工做線程的,但系統會讓你的調用線程等待這個異步的操做完成,你才能夠繼續線程以前的工做。

2.11 外提供數據共享,那麼如何限制對方的使用呢?

如何限制對方的使用

  • 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" />

2.11.1 若是咱們只須要開放部份的 URI 給其餘的應用訪問呢?

若是咱們只須要開放部份的 URI 給其餘的應用訪問呢

  • 能夠參考 ProviderURI 權限設置,只容許訪問部份 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>

2.12 ContentProvider 接口方法運行在哪一個線程中呢?

ContentProvider 接口方法運行在哪一個線程中

  • ContentProvider 能夠在 AndroidManifest.xml 中配置一個叫作 android:multiprocess 的屬性,默認值是 false ,表示 ContentProvider 是單例的
  • 不管哪一個客戶端應用的訪問都將是同一個 ContentProvider 對象,若是設爲 true ,系統會爲每個訪問該 ContentProvider 的進程建立一個實例。

2.12.1 這點仍是比較好理解的,那若是我要問每一個 ContentProvider 的操做是在哪一個線程中運行的呢?( 其實咱們關心的是 UI 線程和工做線程 )

每一個 ContentProvider 的操做是在哪一個線程中運行的

  • 好比咱們在UI線程調用getContentResolver().query查詢數據,而當數據量很大時(或者須要進行較長時間的計算)會不會阻塞UI線程呢?

  • 要分兩種狀況回答這個問題:

  1. ContentProvider 和調用者在同一個進程,ContentProvider 的方法( query/insert/update/delete 等 )和調用者在同一線程中;
  2. ContentProvider 和調用者在不一樣的進程,ContentProvider 的方法會運行在它自身所在進程的一個 Binder 線程中。
    可是,注意這兩種方式在 ContentProvider 的方法沒有執行完成前都會 blocked 調用者。因此你應該知道這個上面這個問題的答案了吧。
  3. 也能夠看看 CursorLoader 這個類的源碼,看 Google 本身是怎麼使用 getContentResolver().query 的。

2.13 ContentProvider 是如何在不一樣應用程序之間傳輸數據的?

ContentProvider 是如何在不一樣應用程序之間傳輸數據

  • 一個應用進程有 16Binder 線程去和遠程服務進行交互,而每一個線程可佔用的緩存空間是 128KB 這樣,超過會報異常。
  • ContentResolver 雖然是經過 Binder 進程間通訊機制打通了應用程序之間共享數據的通道,但 ContentProvider 組件在不一樣應用程序之間傳輸數據是基於匿名共享內存機制來實現的。

圖解 ContentProvider

總結


  1. 本文應該是全網最全面的 BroadcastReceiverContentProvider 知識總結了,前先後後投入了大量時間來完成。但願你們經過本次閱讀都能有所收穫。
  2. 重點:關於 Android 的四大組件,到如今爲止我才總結完 ActivityServiceBroadcastRecevierContentProvider等,以及事件分發、滑動衝突、新能優化等重要模塊,進行全面總結,歡迎你們關注 _yuanhao 的 博客園 ,方便及時接收更新

碼字不易,你的點贊是我總結的最大動力!


Android

相關文章
相關標籤/搜索