1. 问题
运用启动时ContentProvider的onCreate 办法 和Application的onCreate 办法那个先调用?
答案: ContentProvider 的onCreate 办法先被调用。Application::attachBaseContext
-> ContentProvider::onCreate
-> Application::onCreate
.
这个问题涉及到一个很重要的问题,那便是在ContentProvider的onCreate 办法中要防止运用到相似 Application.mApplication
的这种用法。 由于有时候 ContentProvider需要用到Context 去加载一些资源,一些同学为了省劲或许便是运用 Application.mApplication
,从而导致空指针。
为了能随时随地的运用Context 目标,绝大多数开发者都会在 Application::onCreate
办法中获取Application
目标,将该目标存在一个类中或许放入一个东西类的静态属性中。 如下这样运用:
//办法1 直接用 MyApplication.mApplication
public class MyApplication extends Application {
public static Application mApplication;
@Override
public void onCreate(){
mApplication = this;
}
}
//办法2 ContextUtil.getContext()
public class ContextUtil {
private static Context appContext;
public static void setContext(Application application) {
appContext = application;
}
public static Context getContext(){
return appContext;
}
}
public class MyProvider extends ContentProvider {
@Override
public void onCreate() {
MyInit.init(ContextUtil.getContext());
}
}
假设在此处进行一个初始化操作,需要用到一个 context目标 进行一些数据处理(比如获取数据等),此时明显就会导致运用Context办法时出现空指针反常。(从前就傻傻的这样做过,然后运用就溃散了)
这就导致一个问题,如果在 ContentProvider
的 onCreate
办法中需要运用Application.mApplication
目标时,就会出现空指针问题。处理该问题的办法有两种:
(1) Application.mApplication
办法的赋值在Appliaction#attachBaseContext
办法中进行。该办法能确保在绝大多处运用Application.mApplication
不会出现为空状况。(Application::attachBaseContext
办法在 ContentProvider::onCreate
之前调用,调用流程将在后边代码剖析)
(2) ContentProvider
中onCreate
中办法中运用getContext
. 其实 ContentProvider
中getContext
获取的目标便是Application
目标(详细原因后边代码剖析)
2. 剖析
详细原因且从Application的创立说起,此处省掉内容为 点击桌面A运用的图标后->Launcher3 经过startActivity办法去启动 A运用 的根Activity->调用到了AMS(ActivityManagerService)-> AMS发现运用未启动->AMS经过socket向Zygote进程发起创立新运用进程的恳求->Zygote fork了本身 并早RuntimeInit::invokeStaticMain
办法内经过反射调用了ActivityThread
的main
办法。
进入到ActivityThread办法中。
frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
// Call per-process mainline module initialization.
initializeMainlineModules();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);//代码1
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
main 办法中会创立ActivityThread
目标.并在代码1处调用attach
办法。
@UnsupportedAppUsage
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);//代码1
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
// Watch for getting close to heap limit.
BinderInternal.addGcWatcher(new Runnable() {
@Override public void run() {
if (!mSomeActivitiesChanged) {
return;
}
Runtime runtime = Runtime.getRuntime();
long dalvikMax = runtime.maxMemory();
long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
if (dalvikUsed > ((3*dalvikMax)/4)) {
if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
+ " total=" + (runtime.totalMemory()/1024)
+ " used=" + (dalvikUsed/1024));
mSomeActivitiesChanged = false;
try {
ActivityTaskManager.getService().releaseSomeActivities(mAppThread);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
});
} else {
// 关于体系运用的处理
}
// 体系配置监听,如反正状况、字体大小等
ViewRootImpl.ConfigChangedCallback configChangedCallback
= (Configuration globalConfig) -> {
synchronized (mResourcesManager) {
// TODO (b/135719017): Temporary log for debugging IME service.
if (Build.IS_DEBUGGABLE && mHasImeComponent) {
Log.d(TAG, "ViewRootImpl.ConfigChangedCallback for IME, "
+ "config=" + globalConfig);
}
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
null /* compat */)) {
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
mResourcesManager.getConfiguration().getLocales());
// This actually changed the resources! Tell everyone about it.
if (mPendingConfiguration == null
|| mPendingConfiguration.isOtherSeqNewer(globalConfig)) {
mPendingConfiguration = globalConfig;
sendMessage(H.CONFIGURATION_CHANGED, globalConfig);
}
}
}
};
ViewRootImpl.addConfigCallback(configChangedCallback);
}
在代码1处,运用经过调用AMS的 attachApplication
办法,将mAppThread(ApplicationThread
类型)binder 目标传递给AMS.
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
if (thread == null) {
throw new SecurityException("Invalid application interface");
}
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid, callingUid, startSeq);//代码1
Binder.restoreCallingIdentity(origId);
}
}
@GuardedBy("this")
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
// ...
// 代码2
thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions,
app.mDisabledCompatChanges);
// ...
}
从代码1处进入代码2处,attachApplicationLocked
办法完结比较长,此处只关注其调用到了thread.binApplication
.thread
是IApplicationThread
类型的目标。而IApplicationThread
是一个aidl文件,前面说的ActivityThread::ApplicationThread
内部类便是一个Binder目标。此处的thread
便是前面attachApplication
传入的ApplicationThread
Binder目标的署理类。
frameworks/base/core/java/android/app/ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
//...
@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
ProviderInfoList providerList, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {
//省掉
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providerList.getList();
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
data.autofillOptions = autofillOptions;
data.contentCaptureOptions = contentCaptureOptions;
data.disabledCompatChanges = disabledCompatChanges;
sendMessage(H.BIND_APPLICATION, data);//代码1
}
//...
}
class H extends Handler {
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION://代码2
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
//省掉
}
}
}
}
}
private void handleBindApplication(AppBindData data) {
//...
Application app;
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
app = data.info.makeApplication(data.restrictedBackupMode, null);//代码3
// Propagate autofill compat state
app.setAutofillOptions(data.autofillOptions);
// Propagate Content Capture options
app.setContentCaptureOptions(data.contentCaptureOptions);
mInitialApplication = app;//初始化App,进程的第一个App
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);//代码4
}
}
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);/
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);//代码5
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
} finally {
// If the app targets < O-MR1, or doesn't change the thread policy
// during startup, clobber the policy to maintain behavior of b/36951662
if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
|| StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
StrictMode.setThreadPolicy(savedPolicy);
}
}
//...
}
代码1处经过Handler 传递消息调用handleBindApplication
,在主线程处理bindApplication
的操作.进入handleBindApplication
办法。
在代码3处调用了 data.info.makeApplication
办法创立了Application. 其中 data是 AppBindData
目标,info
是LoadedApk
目标。
frameworks/base/core/java/android/app/LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
final java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"initializeJavaContextClassLoader");
initializeJavaContextClassLoader();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
// Rewrite the R 'constants' for all library apks.
SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
false, false);
for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
final int id = packageIdentifiers.keyAt(i);
if (id == 0x01 || id == 0x7f) {
continue;
}
rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//代码1
// The network security config needs to be aware of multiple
// applications in the same process to handle discrepancies
NetworkSecurityConfigProvider.handleNewApplication(appContext);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);//代码2
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Unable to instantiate application " + appClass
+ ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);// 代码3
mApplication = app;
if (instrumentation != null) {//代码4
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return app;
}
代码1处,调用ContextImpl.createAppContext
办法创立了运用的上下文目标。
代码2处,mInstrumentation 是Instrumentation
目标,调用handleNewApplication
目标。并调用了Application::attach
办法,而Application::attach
中调用了Application::attachBaseContext
办法,此处稍后介绍。
代码3处,将创立的Application目标放入了 mAllApplications
列表中。关于mApplications
为什么是个数组,暂时还没太搞懂,等以后搞懂了再写篇文章(虽然搜到一些介绍,是多个运用跑到一个进程时用到的,但是详细还没搞太懂)。
代码4处,依据前面ActivityThread::handleBindApplication
调用传入可知instrumentation
传入为null。所以不会走到 Instrumentation::callApplicationOnCreate
办法。(Instrumentation::callApplicationOnCreate
办法中调用了Application::onCreate
)
从代码2处,咱们能够看到Application已经被创立了。接下来看下Instrumentation::handleNewApplication
办法。
frameworks/base/core/java/android/app/Instrumentation.java
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);//代码1
return app;
}
frameworks/base/core/java/android/app/Application.java
@UnsupportedAppUsage
/* package */ final void attach(Context context) {
attachBaseContext(context);//代码2
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
如上,从代码1走到代码2处,调用了Application::attachBaseContext
.
接下来咱们回到,ActivityThread::handleBindApplication
办法代码4处(往上翻下哈),调用了installContentProviders
办法。该办法的进入前提是运用没有运行在 restrictedBackupMode 模式下。
frameworks/base/core/java/android/app/ActivityThread.java
@UnsupportedAppUsage
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) {
if (DEBUG_PROVIDER) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
}
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
@UnsupportedAppUsage
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) {
if (DEBUG_PROVIDER || noisy) {
Slog.d(TAG, "Loading provider " + info.authority + ": "
+ info.name);
}
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if (context.getPackageName().equals(ai.packageName)) {//代码1 包名相同,传入的context依据追溯联系是Application目标
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {//
c = mInitialApplication;
} else {
try {
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
if (c == null) {
Slog.w(TAG, "Unable to get context for package " +
ai.packageName +
" while loading content provider " +
info.name);
return null;
}
if (info.splitName != null) {
try {
c = c.createContextForSplit(info.splitName);
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
}
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);//代码 2
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);//代码3
} catch (java.lang.Exception e) {
if (!mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ": " + e.toString(), e);
}
return null;
}
} else {
provider = holder.provider;
if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
+ info.name);
}
// ...
return retHolder;
}
代码1处依据传入的context调用联系追溯,是 Application
目标.接下c
属性将在代码3处被传入。
代码2处 getAppFactory
获取的是AppComponentFactory
目标,AppComponentFactory::instantiateProvider
办法也很简单,只是创立了ContentProvider
实例
frameworks/base/core/java/android/app/AppComponentFactory.java
public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (ContentProvider) cl.loadClass(className).newInstance();
}
ActivityThread::installProvider
办法中 代码4处,调用了ContentProvider::attachInfo
办法,传入了Context
目标c
和ProviderInfo
目标info
.
frameworks/base/core/java/android/content/ContentProvider.java
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);//代码1
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
mCallingPackage = new ThreadLocal<>();
/*
* Only allow it to be set once, so after the content service gives
* this to us clients can't change it.
*/
if (mContext == null) {
mContext = context;
if (context != null && mTransport != 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);
}
if (Build.IS_DEBUGGABLE) {
setTransportLoggingEnabled(Log.isLoggable(getClass().getSimpleName(),
Log.VERBOSE));
}
ContentProvider.this.onCreate();//代码2
}
}
从代码1处走到了代码2处,调用了ContentProvider::onCreate
办法。此处确认了ContentProvider::onCraete
落后于Application::attachBaseContext
.
接下来再回到ActivityThread::handleBindApplication
代码5(能够从头开始翻)处,调用了Instrumentation::callApplicationOnCreate
办法。
frameworks/base/core/java/android/app/Instrumentation.java
public void callApplicationOnCreate(Application app) {
app.onCreate();
}
由此,总算是调用到了Application::onCreate
处。
3 定论
Application::attachBaseContext
-> ContentProvider::onCreate
-> Application::onCreate
.这个调用顺序确认完毕了。
ContentProvider::getContext
获取的目标是 Application
.
4 扩展 (关于利用ContentProvider 调用顺序的一些骚操作)
ContentProvider 的 onCreate() 的调用时机介于 Application 的 attachBaseContext() 和 onCreate() 之间,因此能够将一些需要在Application
进行初始化的办法放到ContentProvider
中进行,这种办法能够将一些外部库需要在Application
时进行的初始化操作放入库本身,不需要开发者调用初始化办法。(当然,这种办法具有必定的局限性,在 restrictedBackupMode 模式下库就不行了)。LeakCanary2.0便是运用的这种办法,详细请看这篇文章
ContentProvider妙用 — 完结application初始化