第九章-四大組件的工作過程(ContentProvider的工作過程-基於Android8.1源碼)

一、使用ContentProvider示例:
1、在一個單獨的app(B)中定義個MyContentProvider

package com.example.provider;

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

public class MyContentProvider extends ContentProvider {
	private static final String TAG = "MyContentProvider";
	@Override
	public boolean onCreate() {
		Log.d(TAG,"onCreate");

		return false;
	}

	@Override
	public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
		Log.d(TAG,"query , thread name = " + Thread.currentThread().getName());//僅打印個文本,說明調用到了
		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;
	}
}

2、在app(B)的AndroidManifest中註冊

	<provider
		android:authorities="com.example.provider"
		android:name="com.example.provider.MyContentProvider"
		android:exported="true"
		>
	</provider>

3、在app(A)中的activity中使用。

	模擬了不同應用間獲取數據的場景
	Uri uri = Uri.parse("content://com.example.provider");//和XML中的authorities對應
	getContentResolver().query(uri,null,null,null,null);
打印如下
2020-04-11 16:48:00.435 30490-30490/? D/MyContentProvider: onCreate
2020-04-11 16:48:11.051 30490-30503/com.example.test D/MyContentProvider: query , thread name = Binder:30490_1

可以看到app(B)中的方法是執行在Binder線程池中的。

注意:
1、當啓動app(B)就會調用ContentProvider的onCreate方法。
2、當沒有啓動app(B)時,在app(A)中調用就會出現異常。提示找不到provider

E/ActivityThread: Failed to find provider info for com.example.provider

所以只有啓動app(B)的進程,才能正常訪問ContentProvider

二、ContentProvider啓動過程

在這裏插入圖片描述
在這裏插入圖片描述
通過Binder調用到AMS(和之前邏輯一樣),再IPC調用到ActivityThread#handleBindApplication

ActivityThread#handleBindApplication 
	            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);//這個裏面就是完成了Provider的創建
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

            ...
            catch (Exception e) {
                throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
            try {
                mInstrumentation.callApplicationOnCreate(app);//這裏會調用Application的onCreate

ActivityThread#installContentProviders
    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        ...
        ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
       
		...
    }
	
	
ActivityThread#installProvider
		final java.lang.ClassLoader cl = c.getClassLoader();
		localProvider = (ContentProvider)cl.
			loadClass(info.name).newInstance();//看到了吧,通過類加載器加載了Provider的實例
		provider = localProvider.getIContentProvider();
		...
		localProvider.attachInfo(c, info);//接着調用了attachInfo方法

ContentProvider#attachInfo
    public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }
	
	private void attachInfo(Context context, ProviderInfo info, boolean testing) {
		mNoPerms = testing;

		/*
		 * Only allow it to be set once, so after the content service gives
		 * this to us clients can't change it.
		 */
		if (mContext == null) {//默認第一次是null,所以這個只會調用一次
			mContext = context;
			if (context != null) {
				mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
						Context.APP_OPS_SERVICE);
			}
			mMyUid = Process.myUid();
			if (info != null) {
				setReadPermission(info.readPermission);
				setWritePermission(info.writePermission);
				setPathPermissions(info.pathPermissions);
				mExported = info.exported;
				mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
				setAuthorities(info.authority);
			}
			ContentProvider.this.onCreate();//最終調用到了onCreate方法了。
		}
    }

ContentProvider.this 就是當前的ContentProvider對象

到這裏ContentProvider就隨着app的啓動,加載了。

========上面的代碼是自己分析的。下面是書上總結了ActivityThread#handleBindApplication方法的4個步驟
1.創建ContextImpl和instrumentation

	ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

	try {
		java.lang.ClassLoader cl = instrContext.getClassLoader();
		mInstrumentation = (Instrumentation)
			cl.loadClass(data.instrumentationName.getClassName()).newInstance();
	} catch (Exception e) {
		throw new RuntimeException(
			"Unable to instantiate instrumentation "
			+ data.instrumentationName + ": " + e.toString(), e);
	}

	mInstrumentation.init(this, instrContext, appContext,
		   new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
		   data.instrumentationUiAutomationConnection);

2.創建Application對象

     Application app = data.info.makeApplication(data.restrictedBackupMode, null);
     mInitialApplication = app; 

3.啓動當前進程的ContentProvider並調用onCreate方法

如何調用到onCreate方法上面已經分析了,這裏不再贅述

4.調用Application的onCreate方法

	try {
		mInstrumentation.callApplicationOnCreate(app);
	} catch (Exception e) {
		if (!mInstrumentation.onException(app, e)) {
			throw new RuntimeException(
				"Unable to create application " + app.getClass().getName()
				+ ": " + e.toString(), e);
		}
	}

========到這個ContentProvider就啓動好了。如果有其它應用請求這個ContentProvider,AMS就可以直接返回這個對象了。

三、獲取ContentProvider數據過程

ContentProvider的query方法,首先獲取的IContentProvider對象,不管是通過acquireUnstableProvider方法還是直接通過acquireProvider方法,它們的本質都是一樣的,最終都是通過acquireProvider方法來獲取ContentProvider。下面是ApplicationContentResolver的acquireProvider具體的實現方式:
代碼看看是如何獲取到IContentProvider對象的

	ContextWrapper#getContentResolver
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();//前面分析過了mBase就是ContextImpl
    }
	
	ContextImpl#getContentResolver
	public ContentResolver getContentResolver() {
        return mContentResolver;//private final ApplicationContentResolver mContentResolver;
    }
	
	從上面可以知道我們在activity中getContentResolver()拿到的就是一個ApplicationContentResolver對象
	
	ContentResolver#acquireUnstableProvider
	public final IContentProvider acquireUnstableProvider(Uri uri) {
        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
            return null;
        }
        String auth = uri.getAuthority();
        if (auth != null) {
            return acquireUnstableProvider(mContext, uri.getAuthority());//參數個數不一樣,不同的方法。
        }
        return null;
    }
	
	ContextImpl.ApplicationContentResolver#acquireUnstableProvider
	@Override
	protected IContentProvider acquireUnstableProvider(Context c, String auth) {
		return mMainThread.acquireProvider(c,
				ContentProvider.getAuthorityWithoutUserId(auth),
				resolveUserIdFromAuthority(auth), false);
	}
	
	
	ApplicationContentResolver的acquireProvider方法並沒有處理任何邏輯,它直接調用了ActivityThread的acquireProvider方法
	ActivityThread#acquireProvider
	public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;//找到了直接返回
        }

        ContentProviderHolder holder = null;
        try {
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), auth, userId, stable);//看到了,如果ActivityThread中沒找到,又通過Binder調用到了AMS中的getContentProvider方法了
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        if (holder == null) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
            return null;
        }

        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

到這裏就已經可以獲取到IContentProvider這個對象了。

具體的啓動上面已經分析過了(並不知道後面會分析到,之前是自己看源碼分析的。和書上分析一樣。看來有進步了)。

在這裏插入圖片描述

	@Override
	public Cursor query(String callingPkg, Uri uri, String[] projection,
			String selection, String[] selectionArgs, String sortOrder,
			ICancellationSignal cancellationSignal) {
		validateIncomingUri(uri);
		uri = getUriWithoutUserId(uri);
		if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
			return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
					CancellationSignal.fromTransport(cancellationSignal));
		}
		final String original = setCallingPackage(callingPkg);
		try {
			return ContentProvider.this.query(
					uri, projection, selection, selectionArgs, sortOrder,
					CancellationSignal.fromTransport(cancellationSignal));
		} finally {
			setCallingPackage(original);
		}
	}

在這裏插入圖片描述

到這裏我們4大組件的啓動過程就分析完成了。總結下流程就是ActivityThread 通過ApplicationThread這個Binder類和AMS進行交互的過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章