深刻Android系統(十一)AMS-3-Service和ContentProvider管理

Service 管理

官方對Service的描述是:java

Service是一種可在後臺執行長時間運行操做而不提供界面的應用組件。服務可由其餘應用組件啓動,並且即便用戶切換到其餘應用,服務仍將在後臺繼續運行。此外,組件可經過綁定到服務與之進行交互,甚至是執行進程間通訊IPCandroid

Service使用上,應用須要在AndroidManifest.xml文件中經過標籤<service/>來聲明一個Service,標籤的屬性以下:數據庫

<service android:description="string resource" android:directBootAware=["true" | "false"] android:enabled=["true" | "false"] android:exported=["true" | "false"] android:foregroundServiceType=["connectedDevice" | "dataSync" | "location" | "mediaPlayback" | "mediaProjection" | "phoneCall"] android:icon="drawable resource" android:isolatedProcess=["true" | "false"] android:label="string resource" android:name="string" android:permission="string" android:process="string" >
    . . .
</service>
複製代碼

<service/>標籤屬性比較簡單,大部分和前面介紹的<application/>的屬性重複,關於標籤的描述你們能夠參考官網:<service/>標籤數組

咱們重點記錄下特殊的幾個屬性:安全

  • android:directBootAware:服務是否支持直接啓動,即其是否能夠在用戶解鎖設備以前運行
    • 在直接啓動期間,應用中的服務僅可訪問存儲在設備保護存儲區的數據
  • android:process:指定將運行服務的進程的名稱。
    • 正常狀況下,應用的全部組件都會在爲應用建立的默認進程中運行。該名稱與應用軟件包的名稱相同。
    • <application>元素的process屬性可爲全部組件設置不一樣的默認進程名稱
    • 不過,組件可使用本身的process屬性替換默認值,將應用散佈到多個進程中
    • 若是爲此屬性分配的名稱以冒號(:)開頭,則系統會在須要時建立應用專用的新進程,而且服務會在該進程中運行
    • 若是進程名稱以小寫字符開頭,則服務將在使用該名稱的全局進程中運行,前提是它擁有相應的權限。如此一來,不一樣應用中的組件即可共享進程,從而減小資源使用。
  • android:isolatedProcess:若是設置爲true,則此服務將在與系統其他部分隔離的特殊進程下運行。

都是建立新的進程,那麼android:processandroid:isolatedProcess的區別是什麼呢?markdown

  • 當咱們僅僅指定android:process屬性時,服務啓動後進程信息以下:
    u0_a50        7889  3220 1114656  94464 0                   0 S lee.hua.skills_android
        u0_a50        8358  3220 1078100  58700 0                   0 S lee.hua.skills_android:newProcess
    複製代碼
    • 建立了新的進程,不過user是同樣的,都是u0_a50
    • Android在新進程的權限和接口調用方面未作限制
  • 當咱們僅僅指定android:isolatedProcess屬性時,服務啓動後進程信息以下:
    u0_a50        7889  3220 1114656  94464 0                   0 S lee.hua.skills_android
        u0_i0         8358  3220 1078100  58700 0                   0 S lee.hua.skills_android:newProcess
    複製代碼
    • 首先,user不一樣了,查看uid分別是:(u0_a50)10050(u0_i0)99000
    • Android中定義的isolated進程的起始號爲99000
    • Androidisolated進程的接口調用作了一些限制
      • 好比調用bindService()的進程若是是isolated進程的話會拋出安全異常

後面Service的啓動代碼中會涉及到isolated相關的邏輯,咱們先看下Service的生命週期網絡

Service的生命週期

Activity相似,Service也有聲明週期,可是和Activity的聲明週期相比,Service的要簡單許多。數據結構

官方圖示以下:
imageapp

  • 圖片左側顯示的是使用startService()建立的服務的生命週期
  • 圖片右側顯示的是使用bindService()建立的服務的生命週期

Service生命週期(從建立到銷燬)可遵循如下任一路徑:ide

  • startService()

    • 該服務在其餘組件調用startService()時建立,而後無限期運行,且必須經過調用stopSelf()來自行中止運行。
    • 此外,其餘組件也可經過調用stopService() 來中止此服務。服務中止後,系統會將其銷燬。
  • bindService()

    • 該服務在其餘組件(客戶端)調用bindService()時建立。而後,客戶端經過IBinder接口與服務進行通訊。
    • 客戶端可經過調用unbindService() 關閉鏈接。
    • 多個客戶端能夠綁定到相同服務,並且當全部綁定所有取消後,系統即會銷燬該服務。(服務沒必要自行中止運行。)

Service 有兩種運行模式

  • 前臺模式:前臺服務執行一些用戶能注意到的操做,前臺服務必須顯示通知。即便用戶中止與應用的交互,前臺服務仍會繼續運行。
  • 後臺模式:後臺服務執行用戶不會直接注意到的操做,在API 26或更高版本,當應用自己未在前臺運行時,系統會對運行後臺服務施加限制

關於生命週期的回調函數就不細講了,比較簡單,你們能夠參考官網:Service基礎知識

而在ActivityThread的成員變量mServices中保存了應用中全部Service對象,定義以下:

final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
複製代碼

對於Service類來講,重要的成員變量以下:

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
    // 引用 ActivityThread 對象
    private ActivityThread mThread = null;
    // Service 的類名
    private String mClassName = null;
    // service 的惟一標識符
    private IBinder mToken = null;
    // 引用 Application
    private Application mApplication = null; 
}
複製代碼

Service的管理類

AMS 中對於Service的管理是經過ActiveServices類來進行的。ActiveServices類的構造方法以下:

public ActiveServices(ActivityManagerService service) {
        mAm = service;
        int maxBg = 0;
        try {
            maxBg = Integer.parseInt(SystemProperties.get("ro.config.max_starting_bg", "0"));
        } catch(RuntimeException e) {
        }
        mMaxStartingBackground = maxBg > 0
                ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
    }
複製代碼

構造方法很簡單,只是從系統屬性ro.config.max_starting_bg中讀取了容許後臺容許的Service的最大數量。

ActiveServices類中還有一些重要的成員變量,以下所示:

// How long we wait for a service to finish executing.
    static final int SERVICE_TIMEOUT = 20*1000;
    // How long we wait for a service to finish executing.
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
    // How long the startForegroundService() grace period is to get around to
    // calling startForeground() before we ANR + stop it.
    static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;

    final ActivityManagerService mAm;

    // 系統運行運行的最大Service數量
    final int mMaxStartingBackground;

    final SparseArray<ServiceMap> mServiceMap = new SparseArray<>();
    final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>();
    final ArrayList<ServiceRecord> mPendingServices = new ArrayList<>();
    final ArrayList<ServiceRecord> mRestartingServices = new ArrayList<>();
    final ArrayList<ServiceRecord> mDestroyingServices = new ArrayList<>();
複製代碼
  • mServiceMap是一個SparseArray類型的數組,索引是用戶Id
    • 不一樣用戶的Service記錄存放在數組元素中,每一個元素是一個ServiceMap
    • ServiceMap中存儲了某個用戶全部的ServiceRecord對象
    • 應用進程中的ServiceAMS中對應的數據結構就是ServiceRecord
  • mServiceConnections儲存的是全部鏈接記錄ConnectionRecord的對象
    • 鏈接記錄指的是某個進程綁定Service時傳遞的信息
    • mServiceConnections記錄了AMS和使用服務的進程之間的聯繫
  • mPendingServices保存的是正在等待進程啓動的Service
    • 當啓動一個服務時,服務所在的進程可能尚未啓動
    • 這時AMS會先去啓動服務所在的進程,這個時間可能比較長
    • 所以,先把Service的信息保存在mPendingServices
  • mRestartingServices保存的是正在等待從新啓動進程的Service
    • 若是Service所在的進程崩潰了,會把該Service加入到mRestartingServices中,準備從新啓動進程
  • mDestroyingServices保存的是正在等待進程銷燬的Service
    • 銷燬進程也須要一段時間,所以在完成銷燬前,先把Service保存在mDestroyingServices

Service 的啓動過程

Context提供了兩個接口來啓動Service,分別是startService()bindService()。定義以下:

public abstract ComponentName startService(Intent service);
   public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags);
複製代碼

從接口定義能夠看出,bindService()須要傳遞一個回調接口ServiceConnection來接收Binder對象。

這兩個接口最後調用的是AMSstartService()bindService(),兩個方法的實現差很少,bindService()更復雜一些。

後面以bindService()方法爲例進行分析,在此以前,咱們先看下啓動過程的時序圖:
image

AMSbindService()的代碼以下:

public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException {
        // 檢查調用進程是不是isolate Process,若是是拋出安全異常
        enforceNotIsolatedCaller("bindService");
        // 檢查啓動Service的Intent中是否包含文件描述符
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        // 檢查 callingPackage 是否爲空
        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }
        synchronized(this) {
            // 調用 ActiveService 類的 bindServiceLocked 方法
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }
複製代碼

ActiveService.bindServiceLocked

ActiveService類的bindServiceLocked()方法以下:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, String callingPackage, final int userId) throws TransactionTooLargeException {
    ......// 省略一些權限和錯誤檢查
    // 建立 ServiceRecord 對象
    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
                Binder.getCallingUid(), userId, true, callerFg, isBindExternal, allowInstant);
    ......// 省略 res 的異常檢查
    ServiceRecord s = res.record;
    ......
    try {
        // 將 service 從 mRestartingServices 列表中移除
        if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
            ......
        }
        ......
        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            s.lastActivity = SystemClock.uptimeMillis();
            // 經過 bringUpServiceLocked 啓動 Service 所在的進程
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                    permissionsReviewRequired) != null) {
                // 請注意,這裏作了退出操做
                return 0;
            }
        }
        if (s.app != null) {
            ......
            // 調整進程的優先級
            mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
                    || s.app.treatLikeActivity, b.client);
            mAm.updateOomAdjLocked(s.app, true);
        }
        if (s.app != null && b.intent.received) {
            // 若是進程已經啓動而且綁定過了,直接調用 IServiceConnection 的 connected()方法
            // 此方法最終會調用 ServiceConnection 的 onServiceConnected() 方法
            try {
                c.conn.connected(s.name, b.intent.binder, false);
            } catch (Exception e) {
                ......
            }
            if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                // Intent 參數中要求從新綁定的狀況,執行 requestServiceBindingLocked()
                requestServiceBindingLocked(s, b.intent, callerFg, true);
            }
        } else if (!b.intent.requested) {
            // 尚未進行綁定,執行 requestServiceBindingLocked() 方法
            requestServiceBindingLocked(s, b.intent, callerFg, false);
        }
        ......
    }
    return 1;
}
複製代碼

bindServiceLocked()方法中:

  • 若是Service所在的進程尚未啓動,會執行bringUpServiceLocked()來啓動進程
  • 若是所在進程已經啓動,那麼只須要調用requestServiceBindingLocked()方法來綁定服務

ActiveService.bringUpServiceLocked

bringUpServiceLocked()方法的主要做用是啓動Service,核心代碼以下:

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired) throws TransactionTooLargeException {
    ......
    // 判斷是否要在進程隔離下運行
    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    String hostingType = "service";
    ProcessRecord app;
    if (!isolated) {
        // 非 isolated 進程
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                    + " app=" + app);
        if (app != null && app.thread != null) {
            // 若是進程已經啓動,執行 realStartServiceLocked()
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                realStartServiceLocked(r, app, execInFg);
                return null;
            } ...
        }
    } else {
        // isolated 進程,isolatedProc 第一次爲 null
        app = r.isolatedProc;
        ......
    }
    // 若是進程尚未啓動,執行 startProcessLocked() 啓動進程
    if (app == null && !permissionsReviewRequired) {
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                hostingType, r.name, false, isolated, false)) == null) {
            ......
            // 進程啓動失敗,返回異常信息
            return msg;
        }
        if (isolated) {
            // 給 isolatedProc 賦值
            r.isolatedProc = app;
        }
    }
    ......
    if (!mPendingServices.contains(r)) {
        // 將 Service 加入 mPendingServices
        mPendingServices.add(r);
    }
    ......
    return null;
}
複製代碼

調用bringUpServiceLocked()方法時

  • 若是進程已經啓動,並且isolated屬性爲false,那麼調用realStartServiceLocked()進入到下一步
  • 若是進程尚未啓動,調用AMSstartProcessLocked()來啓動進程
  • 最後,爲了進程啓動結束後能繼續運行該Service,將其加入到mPendingServices集合中

新啓動進程在哪裏調用的realStartServiceLocked()來啓動Service的呢?
按照進程的啓動邏輯,啓動完會執行AMSattachApplicationLocked()方法,而後調用ActiveServicesattachApplicationLocked()方法,方法以下:

class ActivityThread{
    public static void main(String[] args) {
        ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        ...
    }
    private void attach(boolean system, long startSeq) {
        ...
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread, startSeq);
        }
        ...
    }
}
class ActivityManagerService{
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            ...
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            ...
        }
    }
    private final boolean attachApplicationLocked(IApplicationThread thread, int pid, int callingUid, long startSeq) {
        ...
        // Find any services that should be running in this process...
        if (!badApp) {
            // 調用 ActiveService 的 attachApplicationLocked 方法
            didSomething |= mServices.attachApplicationLocked(app, processName);
        }
        ...
        return true;
    }
}
class ActiveService{
    boolean attachApplicationLocked(ProcessRecord proc, String processName) throws RemoteException {
        boolean didSomething = false;
        if (mPendingServices.size() > 0) {
            ServiceRecord sr = null;
            for (int i=0; i<mPendingServices.size(); i++) {
                ...
                // 此處再一次執行了 realStartServiceLocked()
                realStartServiceLocked(sr, proc, sr.createdFromFg);
                ...
            }
        }
        ...
        return didSomething;
    }
}
複製代碼

這樣,就和bringUpServiceLocked()的邏輯銜接起來了。

ActiveService.realStartServiceLocked

realStartServiceLocked()方法的調用流程:

private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
        ...
            // 調用 scheduleCreateService 在服務進程中建立 Service 對象
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
        ...
        // 要求服務進程綁定Binder服務
        requestServiceBindingsLocked(r, execInFg);
        ...
    }
    private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) throws TransactionTooLargeException {
        for (int i=r.bindings.size()-1; i>=0; i--) {
            IntentBindRecord ibr = r.bindings.valueAt(i);
            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
                break;
            }
        }
    }
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        ...
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                ...
                // 調用 scheduleBindService 綁定服務
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                ...
            } ...
        }
        return true;
    }
複製代碼

realStartServiceLocked()主要的工做就是調用兩個方法,分別是

  • ApplicationThreadscheduleCreateService()
  • requestServiceBindingsLocked()方法
    • 該方法最終也調用了ApplicationThreadscheduleBindService()方法

前面介紹過,ApplicationThreadschedule*方法其實都是發送相應的處理消息到ActivityThread中,對應的實現方法分別爲handleCreateService()handleBindService()這兩個方法,咱們來看下:

private void handleCreateService(CreateServiceData data) {
    ...
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        // 建立 Service 對象
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } catch (Exception e) {
        ...
    }
    try {
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        ...
        // 調用 Service 的 onCreate() 方法
        service.onCreate();
        mServices.put(data.token, service);
        ...
    } catch (Exception e) {
        ...
    }
}
private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            data.intent.prepareToEnterProcess();
            try {
                if (!data.rebind) {
                    // 調用 Service 的 onBind() 方法
                    IBinder binder = s.onBind(data.intent);
                    // 調用 AMS 的 publishService 來執行 onServiceConnected() 回調通知
                    ActivityManager.getService().publishService(
                            data.token, data.intent, binder);
                } else {
                    // 調用 Service 的 onRebind() 方法
                    s.onRebind(data.intent);
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
                ensureJitEnabled();
            } catch (RemoteException ex) {
                ...
            }
        } catch (Exception e) {
            ...
        }
    }
}
複製代碼

上面的兩個handle*方法把Service啓動的聲明周期函數都調用完成了。

須要注意的是AMSpublishService()方法,該方法會調用ActiveServicespublishServiceLocked()方法,實現以下:

class ActivityManagerService{
    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        ...
        ConnectionRecord c = clist.get(i);
        // 執行 IServiceConnection 的 connected() 方法
        c.conn.connected(r.name, service, false);
        ...
    }
}
final class ConnectionRecord {
    ...
    // IServiceConnection.aidl 的服務類的實如今 LoadedApk 中
    final IServiceConnection conn;
}
class LoadedApk{
    private static class InnerConnection extends IServiceConnection.Stub {
        final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
        InnerConnection(LoadedApk.ServiceDispatcher sd) {
            mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
        }
        public void connected(ComponentName name, IBinder service, boolean dead) throws RemoteException {
                // 此處省略一些中間調用過程
                doConnected();
        }
    }
    public void doConnected(ComponentName name, IBinder service, boolean dead) {
        ...
        // 調用 onServiceConnected 方法通知 caller
        mConnection.onServiceConnected(name, service);
        ...
    }
}
複製代碼

到這裏,bindService()的過程就結束了,整體而言,Service部分的啓動仍是比較簡潔的,畢竟對Service來講只有啓動和中止兩種操做。

ContentProvider 管理

ContentProvider並無生命週期,也沒有狀態的變化。

AMS中經過ContentProviderRecord類來記錄ContentProvider信息,用ContentProviderConnection類來記錄進程中鏈接ContentProvider的信息。

AMS的成員變量mProviderMap保存了系統中全部ContentProvider的記錄。

同時在ProcessRecord中也經過成員變量pubProviders來記錄進程中全部建立的ContentProvider,使用成員變量conProviders來記錄進程中全部使用的ContentProvider。定義以下:

// class (String) -> ContentProviderRecord
    final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
    // All ContentProviderRecord process is using
    final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
複製代碼

理解ContentProvider

ContentProvider用來提供數據的統一訪問方式,若是某個應用中有數據須要提供給其餘應用訪問,不管數據是存儲在數據庫中,仍是儲存在文件中,甚至是網絡中,均可以經過ContentProvider提供的接口來訪問。官方傳送門:ContentProvider

ContentProvider的使用

建立一個ContentProvider組件只須要繼承ContentProvider類便可。子類必須實現父類定義的用於數據增、刪、改、查的抽象接口,分別是:

  • 插入接口:根據Uri插入ContentValues對象中的數據。原型以下:
    public abstract Uri insert (Uri uri, ContentValues values) 複製代碼
  • 刪除接口:根據Uri刪除selection條件所匹配的所有記錄。原型以下:
    public abstract int delete (Uri uri, String selection, String[] selectionArgs) 複製代碼
  • 更新接口:根據Uri修改selection條件所匹配的所有記錄。原型以下:
    public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs) 複製代碼
  • 查詢接口:根據Uri查詢selection條件所匹配的所有記錄,並能夠指定排序方式。原型以下:
    public abstract Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 複製代碼

上面的接口中都使用了Uri來表示數據,Uri前面已經介紹了,但ContentProvider不能接收任意的Uri,它指定了Uri的格式。

以下爲一個簡單示例:

content://android.launcher.settings.provider/app/3
複製代碼

這個Uri分爲4部分:

  • 前綴:必須是content://
  • authorityandroid.launcher.settings.provider用來標識一個惟一的ContentProvider
    • 官方建議:若是您的Android軟件包名稱爲com.example.<appname>ContentProvider的受權應該設置爲com.example.<appname>.provider
  • pathapp表示數據的類型,由ContentProvider內部定義
    • content://com.example.app.provider/table1:匹配一個名爲table1的表
    • content://com.example.app.provider/table2/dataset1:匹配一個名爲dataset1的表
    • 也可使用content://com.example.app.provider/*匹配ContentProvider中的任何內容
  • id:最後的代號3表示數據的第幾項
    • content://com.example.app.provider/table3/6,會匹配table3中對應6所標識行的內容
    • 也可使用通配符#,如content://com.example.app.provider/table3/#用來匹配任意id

關於Uri的詳細部分能夠參考官網:內容Uri模式

爲了讓其餘應用能夠訪問ContentProvider,必須在AndroidManifest文件中使用<provider/>標籤聲明。<provider/>標籤訂義以下:

<provider android:authorities="list" android:directBootAware=["true" | "false"] android:enabled=["true" | "false"] android:exported=["true" | "false"] android:grantUriPermissions=["true" | "false"] android:icon="drawable resource" android:initOrder="integer" android:label="string resource" android:multiprocess=["true" | "false"] android:name="string" android:permission="string" android:process="string" android:readPermission="string" android:syncable=["true" | "false"] android:writePermission="string" >
        . . .
</provider>
複製代碼

官網的使用指南在這裏

<provider/>標籤的大部分屬性和<application/>標籤的屬性相同,咱們這裏只簡單介紹下<provider/>標籤特有的一些屬性:

  • authorities:鑑權字符串列表。每種鑑權字符串表示一種ContentProvider提供的數據。多個鑑權字符串之間用分號分割。
    • 爲避免衝突,受權方名稱應遵循Java 樣式的命名慣例(如com.example.provider.cartoonprovider)。
    • 一般,它是實現提供程序的ContentProvider子類的名稱。
    • 沒有默認值。必須至少指定一個鑑權字符串。
  • grantUriPermissions:用來設置可否容許原本沒有權限訪問本組件數據的其餘應用經過受權的方式訪問。例如:
    • Email應用收到一個帶有附件的郵件時,應用可能須要調用其餘應用來瀏覽附件,經過受權的方式可讓沒有權限的應用讀取附件的數據
    • 受權是經過在啓動組件的Intent中添加標記FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION實現
    • 若是但願打開受權功能,或者把這個屬性設置爲true,或者在<grant-uri-permission/>中定義相關權限
  • initOrder:指定ContentProvider的初始化順序,數據類型爲整型,數值越大越早建立
  • multiprocess:用來設置該ContentProvider可否在其餘應用中建立。默認值爲false
    • 若是設爲true,則每一個應用進程都有本身的內容提供程序對象。這樣能夠減小進程間通訊的開銷
    • 若是設爲false,則應用的進程僅共享一個內容提供程序對象。

ContentProvider的定義

ContentProvider類的定義很是簡單,除了幾個保存屬性值的成員變量外,最重要的就是下面代碼中的mTransport變量:

public abstract class ContentProvider implements ComponentCallbacks2 {
    ...
    private Transport mTransport = new Transport();
    ...
}
複製代碼

mTransport變量的類型是Transport,它是定義在ContentProvider中的一個內部類,繼承關係圖以下:
image

從繼承關係就能夠看出Transport實際上是一個Binder服務類,它實現了IContentProvider接口。而其餘組件對ContentProvider的操做就是經過Transport提供的接口來完成的

ActivityThread中管理ContentProvider的成員變量有4個,分別是:

final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
        = new ArrayMap<ProviderKey, ProviderClientRecord>();
    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
        = new ArrayMap<IBinder, ProviderRefCount>();
    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
        = new ArrayMap<IBinder, ProviderClientRecord>();
    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
            = new ArrayMap<ComponentName, ProviderClientRecord>();
複製代碼

這些變量的類型都是ArrayMap,它們儲存的都是ProviderClientRecord的引用,對象ProviderClientRecord中保存了ContentProvider的信息

  • mProviderMap保存的是包括本地對象和引用對象在內的全部ContentProvider對象
  • mProviderRefCountMap中保存的是對其餘進程中的ContentProvider對象的引用計數對象,其中保存着被引用對象的IContentProvider對象
  • mLocalProviders只保存本地的ContentProvider對象
  • mLocalProvidersByName保存的和mLocalProviders中的同樣,只是能夠經過ComponentName來查找ContentProvider對象

獲取ContentProvider

一般應用中先經過ContextgetContentResolver()方法獲得應用中的ContentResolver對象,而後再調用它的acquireProvider()方法來獲取IContentProvider

acquireProvider()方法對於應用來講是隱藏的,對於普通應用來講可使用acquireContentProviderClient()方法來獲取ContentResolver對象,不過,最後都會調用到acquireProvider()方法。

ContentResolver是一個抽象類,真正的實現是ApplicationContentResolver類,這個類在ContextImpl.java中定義,acquireProvider()方法的實現也很簡單,以下所示:

private static final class ApplicationContentResolver extends ContentResolver {
        private final ActivityThread mMainThread;
        ...
        @Override
        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }
        ...
    }
複製代碼

acquireProvider()方法只是調用了ActivityThreadacquireProvider()方法:

public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) {
        // 先查詢已建立的 ContentProvider 集合
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider; // 若是已存在,直接返回
        }
        ContentProviderHolder holder = null;
        try {
            synchronized (getGetProviderLock(auth, userId)) {
                // 調用 AMS 的 getContentProvider 獲得一個 ContentProviderHolder 對象
                holder = ActivityManager.getService().getContentProvider(
                        getApplicationThread(), auth, userId, stable);
            }
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        if (holder == null) {
            return null; // 獲取失敗直接返回
        }
        // 安裝得到的 ContentProvider 對象
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }
複製代碼

ActivityThreadacquireProvider()方法:

  • 先調用acquireExistingProvider(),這個方法將檢查mProviderMap中是否已經建立了相應的ContentProvider對象
  • 若是集合中已經存在,直接返回該對象
  • 不然調用AMSgetContentProvider()方法來獲得一個ContentProviderHolder對象
  • 最後,若是ContentProviderHolder對象不爲null,執行installProvider()安裝這個ContentProvider

咱們看下AMSgetContentProvider()方法:

public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) {
        // 確保不是 isolated 進程
        enforceNotIsolatedCaller("getContentProvider");
        if (caller == null) {
            String msg = "null IApplicationThread when getting content provider "
                    + name;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }
        return getContentProviderImpl(caller, name, null, stable, userId);
    }
複製代碼

AMSgetContentProvider()方法在簡單檢查參數後,調用了內部的getContentProviderImpl()方法:

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;
        boolean providerRunning = false;
        synchronized(this) {
            long startTime = SystemClock.uptimeMillis();
            ProcessRecord r = null;
            if (caller != null) {
                r = getRecordForAppLocked(caller);
                if (r == null) {
                    // 若是調用者的進程不存在,拋出異常
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                          + " (pid=" + Binder.getCallingPid()
                          + ") when getting content provider " + name);
                }
            }
            ...
            // 檢查 ContentProvider 是否已經發布
            cpr = mProviderMap.getProviderByName(name, userId);
            ...
            if (providerRunning) {
                // ContentProvider 已經發布的狀況
                cpi = cpr.info;
                String msg;
                // 權限檢查
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
                        != null) {
                    throw new SecurityException(msg);
                }
                if (r != null && cpr.canRunHere(r)) {
                    // ContentProvider 所在的進程已經存在,而且設置了能夠在調用者的進程建立
                    // 建立ContentProviderHolder對象
                    ContentProviderHolder holder = cpr.newHolder(null);
                    // 並設置一個空的 provider,這樣客戶進程會從新建立一個 ContentProvider
                    holder.provider = null;
                    return holder;
                }
                ...
                // ContentProvider 已經存在,增長引用計數
                conn = incProviderCountLocked(r, cpr, token, stable);
                ... // 省略進程優先級調整部分
            }
            if (!providerRunning) {
                // ContentProvider 沒有發佈的狀況
                try {
                    // 從 PMS 中獲取 ContentProvider 信息
                    cpi = AppGlobals.getPackageManager().
                        resolveContentProvider(name,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
                } catch (RemoteException ex) {
                }
                if (cpi == null) {
                    return null;
                }
                ...// 省略一些參數的處理
                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                // 先從 mProviderMap 根據 ComponentName 查找 ContentProvider
                cpr = mProviderMap.getProviderByClass(comp, userId);
                // 此時若是 ContentProvider 爲 null,能夠理解爲第一次啓動的狀況
                final boolean firstClass = cpr == null;
                if (firstClass) {
                    ...
                    try {
                        // 從 PMS 獲取 ContentProvider 所在應用的信息
                        ApplicationInfo ai =
                            AppGlobals.getPackageManager().
                                getApplicationInfo(
                                        cpi.applicationInfo.packageName,
                                        STOCK_PM_FLAGS, userId);
                        if (ai == null) {
                            Slog.w(TAG, "No package info for content provider "
                                    + cpi.name);
                            return null;
                        }
                        ai = getAppInfoForUser(ai, userId);
                        // 建立一個新的 ContentProviderRecord 對象
                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                    } ...
                }
                if (r != null && cpr.canRunHere(r)) {
                    // 若是 ContentProviderRecord 能安裝在調用者進程中,返回一個空的 ContentProvider
                    return cpr.newHolder(null);
                }
                // 檢測請求的 ContentProvider 是否在 mLaunchingProviders集合中
                final int N = mLaunchingProviders.size();
                int i;
                for (i = 0; i < N; i++) {
                    if (mLaunchingProviders.get(i) == cpr) {
                        break;
                    }
                }
                // 若是請求的 ContentProvider 不在 mLaunchingProviders 集合中,啓動它
                if (i >= N) {
                    try {
                        ...
                        // 獲取 ContentProvider 所在進程的 ProcessRecord
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null && !proc.killed) {
                            // 若是對應的進程已經存在
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                // 將 ContentProvider 保存到 pubProviders 中
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    // 調用進程的方法來安裝 ContentProvider
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            // 若是對應的進程沒有啓動,先啓動進程
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false);
                            if (proc == null) {
                                Slog.w(TAG, "Unable to launch app "
                                        + cpi.applicationInfo.packageName + "/"
                                        + cpi.applicationInfo.uid + " for provider "
                                        + name + ": process is bad");
                                return null;
                            }
                        }
                        cpr.launchingApp = proc;
                        // 將 ContentProvider 添加到 mLaunchingProviders 集合中
                        mLaunchingProviders.add(cpr);
                    } finally {
                        Binder.restoreCallingIdentity(origId);
                    }
                }
                if (firstClass) {
                    // 第一次啓動,保存到 mProviderMap 中,key 爲 ComponentName
                    mProviderMap.putProviderByClass(comp, cpr);
                }
                // 添加到 mProviderMap 中,key 爲 String
                mProviderMap.putProviderByName(name, cpr);
                // 增長 ContentProvider 的引用計數
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null) {
                    // 設置等待狀態爲 true
                    conn.waiting = true;
                }
            }
            ...
        }
        // 掛起調用者線程,直到 ContentProvider 安裝完成
        final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
        synchronized (cpr) {
            while (cpr.provider == null) {
                if (cpr.launchingApp == null) {
                    ...
                    return null;
                }
                try {
                    ...
                    if (conn != null) {
                        conn.waiting = true;
                    }
                    // 掛起等待
                    cpr.wait(wait);
                    if (cpr.provider == null) {
                        ...
                        return null;
                    }
                } catch (InterruptedException ex) {
                } finally {
                    if (conn != null) {
                        conn.waiting = false;
                    }
                }
            }
        }
        return cpr != null ? cpr.newHolder(conn) : null;
    }
複製代碼

getContentProviderImpl()方法雖然有點長,但邏輯仍是比較容易理解的,方法中分兩種狀況進行了處理:

  • 第一種是若是請求的ContentProvider已經發布了
    • 先是經過cpr.canRunHere(r)判斷這個ContentProvider是否能夠運行在請求者的進程中
      • 若是容許,那麼不會將已發佈的ContentProvider返回,而是將返回的ContentProviderHolder對象的provider對象會設置爲null
      • 不容許多進程的狀況程序會繼續執行
    • 接下來就是作一些進程優先級的調整和ContentProvider計數等數據的處理
    • 最後,返回cpr.newHolder(conn)

canRunHere()方法判斷的條件之一就是provider是否設置了multiprocess屬性並且兩個進程有相同的uid

  • 第二種狀況就是ContentProvider沒有發佈
    • 首先檢查這個ContentProvider可否在調用者的進程中建立
    • 接下來判斷若是進程尚未建立出來,調用startProcessLocked()方法來啓動進程
    • 若是進程已經啓動了,則調用應用進程的scheduleInstallProvider()方法來安裝ContentProvider
      • scheduleInstallProvider()方法僅僅是發送一個INSTALL_PROVIDER的消息
    • 而後將ContentProvider添加到mLaunchingProvidersmProviderMap集合中
      • 此時ContentProvider還未安裝完成
    • 最後,循環等待ContentProvider安裝完成

AMS中能夠用多種參數從mProviderMap中查找ContentProvider。所以,在儲存數據時會調用putProviderByName()putProviderByClass()等方法把ContentProvider對象加入,方便之後查找

安裝ContentProvider

前面提到ActivityThreadscheduleInstallProvider()方法會發送一個INSTALL_PROVIDER的消息來開始ContentProvider的安裝過程。

消息的處理方法是handleInstallProvider()方法,這個方法又會調用installContentProviders()方法,代碼以下:

private void installContentProviders( Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();
        // 循環調用 installProvider() 方法來建立應用全部的 ContentProvider
        for (ProviderInfo cpi : providers) {
            ...
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }
        try {
            // 調用 AMS 的 publishContentProviders 方法來進行註冊
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
複製代碼

installContentProviders()方法主要作了兩件事情:

  • 根據傳遞進來的參數循環調用installProvider()方法來安裝ContentProvider
  • 將安裝好的ContentProvider對象註冊到AMS

咱們分別來看下

ActivityThread.installProvider

installProvider()方法以下:

private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        if (holder == null || holder.provider == null) {
            // holder.provider 爲空的狀況,說明要新建相關的 ContentProvider
            // 先建立 provider 對象
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            ...// 省略大量的 Context 獲取邏輯
            try {
                // 採用反射的方式獲取建立 ContentProvider 
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    // System startup case.
                    packageInfo = getSystemContext().mPackageInfo;
                }
                // instantiateProvider 方法其實就是執行了 cl.loadClass(className).newInstance() 操做
                // 若是 class 當前進程中不存在,localProvider 就爲空了
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                // 獲得 ContentProvider 的 Binder 對象
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    // Binder 對象獲取失敗
                    return null;
                }
                // localProvider 初始化後執行此方法,最後會執行 ContentProvider 的 onCreate 方法
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                ...
                return null;
            }
        } else {
            // 不爲空,無需新建,直接使用
            provider = holder.provider;
        }
        ContentProviderHolder retHolder;
        synchronized (mProviderMap) {
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
                // localProvider 不爲空的狀況
                ComponentName cname = new ComponentName(info.packageName, info.name);
                // 根據名稱查找 ContentProvider 是否已經存在
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                    // pr 不爲空
                    // 說明已經有人搶先一步建立了,直接使用
                    provider = pr.mProvider;
                } else {
                    // 不然新建一個 ContentProviderHolder 對象
                    holder = new ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    // 經過 installProviderAuthoritiesLocked 方法新建 ProviderClientRecord 對象
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    // 並將pr添加到 mLocalProviders 集合中
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
                // localProvider 爲空說明安裝的是對其餘進程中的 ContentProvider 的引用
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                if (prc != null) {
                    // 引用對象已經存在的狀況
                    // noReleaseNeeded=true 表示能夠永久安裝
                    // android 經過noReleaseNeeded 和 stable 組合來肯定引用是否從新建立
                    // 感受此處可先忽略,重點了解流程
                    if (!noReleaseNeeded) {
                        // 增長引用計數
                        incProviderRefLocked(prc, stable);
                        try {
                            // 在 AMS 中 移除舊的
                            ActivityManager.getService().removeContentProvider(
                                    holder.connection, stable);
                        } catch (RemoteException e) {
                            //do nothing content provider object is dead any way
                        }
                    }
                } else {
                    // 引用對象不存在,建立一個新的引用對象,並添加到集合中
                    ProviderClientRecord client = installProviderAuthoritiesLocked(
                            provider, localProvider, holder);
                    ...
                    mProviderRefCountMap.put(jBinder, prc);
                }
                retHolder = prc.holder;
            }
        }
        return retHolder;
    }
複製代碼

須要注意installProvider()方法的返回值類型,ContentProviderHolder類的主要包含了ContentProviderIContentProvider對象以及ProviderInfo對象,簡要結構以下:

public class ContentProviderHolder implements Parcelable {
        public final ProviderInfo info;
        public IContentProvider provider;
        ...
    }
複製代碼

而對於installProvider()方法的調用來講,源碼中只有兩個調用鏈:

  • 一個是*()->installContentProviders()->installProvider()

    • 前面講到的scheduleInstallProvider()方法就是走的這個
    • 這個調用過程當中的ContentProviderHolder參數是空的
    • 這會致使localProvider不爲空
    • 所以會走新建ProviderClientRecord對象的分支
  • 另外一個是*()->acquireProvider()->installProvider()

    • 通常狀況ContentProviderHolder參數不爲空

    • 這會致使localProvider爲空

    • 所以會執行建立ProviderRefCount引用對象的分支

AMS.publishContentProviders()

方法以下:

public final void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
        if (providers == null) {
            // 空集合檢查
            return;
        }
        // 對 isolated 標記的進程進行限制
        enforceNotIsolatedCaller("publishContentProviders");
        synchronized (this) {
            // 獲得發佈 ContentProvider 的進程的 ProcessRecord 對象
            final ProcessRecord r = getRecordForAppLocked(caller);
            if (r == null) {
                throw new SecurityException(...;
            }
            ...
            final int N = providers.size();
            for (int i = 0; i < N; i++) {
                ContentProviderHolder src = providers.get(i);
                if (src == null || src.info == null || src.provider == null) {
                    // 對要發佈的 ContentProvider 進行檢查,異常狀況直接跳過
                    continue;
                }
                // 查看要發佈ContentProvider進程的 pubProviders 集合
                ContentProviderRecord dst = r.pubProviders.get(src.info.name);
                if (dst != null) {
                    // 若是集合中存在該 ContentProvider 的相關信息
                    // 將其添加到 AMS 的 mProviderMap 中,共兩個接口
                    ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                    // 接口1:putProviderByClass
                    mProviderMap.putProviderByClass(comp, dst);
                    String names[] = dst.info.authority.split(";");
                    for (int j = 0; j < names.length; j++) {
                        // 接口2:putProviderByName
                        // 這個主要是按照 authorities 屬性的內容進行存放
                        mProviderMap.putProviderByName(names[j], dst);
                    }
                    int launchingCount = mLaunchingProviders.size();
                    int j;
                    boolean wasInLaunchingProviders = false;
                    for (j = 0; j < launchingCount; j++) {
                        // 將 ContentProvider 從 mLaunchingProviders中移除
                        if (mLaunchingProviders.get(j) == dst) {
                            mLaunchingProviders.remove(j);
                            wasInLaunchingProviders = true;
                            j--;
                            launchingCount--;
                        }
                    }
                    if (wasInLaunchingProviders) {
                        // 結束超時檢測機制
                        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                    }
                    synchronized (dst) {
                        dst.provider = src.provider;
                        dst.proc = r;
                        // 還記得 getContentProviderImpl() 方法中最後的 wait() 方法麼
                        // 在這裏就會通知喚醒等待的線程
                        dst.notifyAll();
                    }
                    ...
                }
            }
        }
    }
複製代碼

publishContentProviders()會先在調用者的進程中查找ContentProvider的信息,而後進一步完善ContentProviderRecord數據

  • 前面的方法中已經填充了部分數據,這裏主要的是對authorities屬性進行關聯
  • 同時也經過putProviderByClass()增長ComponentName映射

前面的getContentProviderImpl()方法中,在檢測到ContentProviderRecordprovider對象爲空時會執行ContentProviderRecord對象的wait()方法掛起線程

publishContentProviders()方法在數據完善後,會經過notifyAll()來喚醒對應ContentProviderRecord對象所在的全部線程(PS:同步作的不錯,就是繞的圈有點大哇)

相關文章
相關標籤/搜索