现在的位置: 首页 > 综合 > 正文

Context.openOrCreateDatabase与SQLiteDatabase.openOrCreateDatabase的区别

2016年12月14日 ⁄ 综合 ⁄ 共 9462字 ⁄ 字号 评论关闭

咋看标题好像我在暗示两者会有一定的区别,其实我只是卖了个关子吸引下大家眼球,这两者没有什么区别,其本质都是调用了SQLiteDatabase.openDatabase方法来创建数据库:

public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags, DatabaseErrorHandler errorHandler) {
    SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
    db.open();
    return db;
}

很多童鞋在追踪API源码的时候都会发现Activity类下的openOrCreateDatabase方法会指向ContextWrapper类下的openOrCreateDatabase方法:

@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
    return mBase.openOrCreateDatabase(name, mode, factory);
}

但是进一步追踪发现直接就去了Context.openOrCreateDatabase方法中了:

public abstract SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory);

没办法看到其实现过程……这里的原因有二:其一是因为Android确实没在API Source中提供该部分的有关源码;其二是因为我们并没有搞清楚ContextWrapper在构造函数中传入的Context引用究竟是什么。

大家知道Context作为Android的上下文环境引用抽象类对我们来说既熟悉又陌生,熟悉在于我们经常会使用到它,Android呢也有很多它的扩展类,陌生在于我们不知道本质是什么或者说其到底是个什么东西?关于Context的相关知识大家可以百度谷歌下,这方面的相关文档很多,在这里就不作过多介绍了,阅读本文你不需要知道过多的Context知识。

为了搞清楚ContextWrapper在构造函数中传入的Context引用我们回到Activity类中,Activity是ContextThemeWrapper扩展类,而ContextThemeWrapper类又扩展至ContextWrapper,在ContextThemeWrapper类中同样有一个名为mBase的Context成员变量,其被赋值的地方有两处:

第一处:构造函数

public ContextThemeWrapper(Context base, int themeres) {
    super(base);
    mBase = base;
    mThemeResource = themeres;
}

第二处:attachBaseContext方法

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(newBase);
    mBase = newBase;
}

在Activity类中并没有调用父类ContextThemeWrapper的相关构造方法,因此我们先不管,但是在Activity.attach方法中调用了ContextThemeWrapper.attachBaseContext并传入了一个Context对象:

final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config) {
	attachBaseContext(context);
	// 省去无关代码……………………
}

那么attach方法又是在哪儿被调用的呢?传入attach方法的Context引用又是什么玩意呢?这里我们先留一个疑问!Mark,合影留念,茄子!

我们知道一个Android应用会有一个Activity作为程序启动时显示的主界面,而这个Activity我们会在AndroidManifest.xml文件中通过intent-filter来定义:

<activity
    android:name="com.aigestudio.MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

当该Activity启动后则会依次去走其生命周期方法onCreate----->onStart----->onResume----->等等,对于一些刚接触Android的童鞋来说,会认为MAIN定义的Activity的onCreate方法就是应用的入口,因为几乎我们所接触的所有东西都会从这里开始(当然静态代码块等其他的奇葩在这就不多争论了),是不是这样的呢?事实上,一个Android应用的真正入口应该是在ActivityThread(frameworks\base\core\java\android\app\ActivityThread.java),在该类中的Main方法里,其对Activity做了大量的初始化工作:

public final class ActivityThread {
	// 省略无数代码……………………
    public static void main(String[] args) {
        SamplingProfilerIntegration.start();

        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        EventLogger.setReporter(new EventLoggingReporter());

        Security.addProvider(new AndroidKeyStoreProvider());

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        <strong>ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }</strong>

        AsyncTask.init();

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

看到这个方法是不是有些似曾相似的感脚?我想即便是第一天学Java的童鞋都不会陌生。我们主要来看看这个方法中的一小部分:

ActivityThread thread = new ActivityThread();
// 省略一行代码……………………

if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
}

这里实例化了本类获得了一个ActivityThread对象,sMainThreadHandler成员变量初始值为null:

static Handler sMainThreadHandler;

也就是说每当ActivityThread类在被卸载后重新加载时都会执行

sMainThreadHandler = thread.getHandler();

这条语句。ActivityThread类的getHandler()方法呢,返回的是内部类H的实例:

final Handler getHandler() {
    return mH;
}

mH为成员变量并在类初始化时被赋值:

final H mH = new H();

H类是Handler类的扩展类,作用很简单,就是处理ApplicationThread发送到消息队列的消息,而ApplicationThread则通过Binder与AMS进行通信并将其调用通过H类的实例对象将消息发送至消息队列,这一过程比较复杂与本文的关系不大这里就先不详解了,有空写篇关于Activity运行机制的文章再剖析,这里我们直接看H类中启动Activity的消息处理:

private class H extends Handler {
	// 省去大量代码……………………
    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;

                r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            // 省去大量代码……………………
        }
        // 省去一行代码……………………
    }
    // 省去大量代码……………………
}

对我们来说这段消息处理的重点其实就是handleLaunchActivity(r, null)方法,该方法用来处理Activity启动的相关工作,具体实现如下:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // 省去一些代码……………………
    Activity a = performLaunchActivity(r, customIntent);

    // 省去一群代码……………………
}

同样,对于本文我们也不需要关注其他的代码,重点就一行:Activity a = performLaunchActivity(r, customIntent);在performLaunchActivity方法中,有大量有关Activity的赋值和初始化操作并创建Activity的实例返回:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

	ActivityInfo aInfo = r.activityInfo;
	if (r.packageInfo == null) {
		r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE);
	}

	ComponentName component = r.intent.getComponent();
	if (component == null) {
		component = r.intent.resolveActivity(mInitialApplication.getPackageManager());
		r.intent.setComponent(component);
	}

	if (r.activityInfo.targetActivity != null) {
		component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity);
	}

	Activity activity = null;
	try {
		java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
		activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
		StrictMode.incrementExpectedActivityCount(activity.getClass());
		r.intent.setExtrasClassLoader(cl);
		if (r.state != null) {
			r.state.setClassLoader(cl);
		}
	} catch (Exception e) {
		if (!mInstrumentation.onException(activity, e)) {
			throw new RuntimeException("Unable to instantiate activity " + component + ": " + e.toString(), e);
		}
	}

	try {
		Application app = r.packageInfo.makeApplication(false, mInstrumentation);

		if (localLOGV)
			Slog.v(TAG, "Performing launch of " + r);
		if (localLOGV)
			Slog.v(TAG, r + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + r.packageInfo.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() + ", dir=" + r.packageInfo.getAppDir());

		if (activity != null) {
			Context appContext = createBaseContextForActivity(r, activity);
			CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
			Configuration config = new Configuration(mCompatConfiguration);
			if (DEBUG_CONFIGURATION)
				Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config);
			activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config);

			if (customIntent != null) {
				activity.mIntent = customIntent;
			}
			r.lastNonConfigurationInstances = null;
			activity.mStartedActivity = false;
			int theme = r.activityInfo.getThemeResource();
			if (theme != 0) {
				activity.setTheme(theme);
			}

			activity.mCalled = false;
			mInstrumentation.callActivityOnCreate(activity, r.state);
			if (!activity.mCalled) {
				throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()");
			}
			r.activity = activity;
			r.stopped = true;
			if (!r.activity.mFinished) {
				activity.performStart();
				r.stopped = false;
			}
			if (!r.activity.mFinished) {
				if (r.state != null) {
					mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
				}
			}
			if (!r.activity.mFinished) {
				activity.mCalled = false;
				mInstrumentation.callActivityOnPostCreate(activity, r.state);
				if (!activity.mCalled) {
					throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPostCreate()");
				}
			}
		}
		r.paused = true;

		mActivities.put(r.token, r);

	} catch (SuperNotCalledException e) {
		throw e;

	} catch (Exception e) {
		if (!mInstrumentation.onException(activity, e)) {
			throw new RuntimeException("Unable to start activity " + component + ": " + e.toString(), e);
		}
	}

	return activity;
}

可以看到该方法有大量的赋值和初始化操作,什么ActivityInfo啊、ComponentName啊、Application啊、Configuration啊等等等等……………………当然还有我们的Activity的初始化:

activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);

这里只是给大家提一下,我们的重点不在Activity我就不看这个方法了。重中之重呢就是其中Context的初始化和参数的传递:

Context appContext = createBaseContextForActivity(r, activity);
// 省去若干代码
activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config);

注意到木有?这里再初始化了一个Context实例对象后直接将其引用传给了activity的attach方法!原来attach方法是在这里被调用并传入的Context!那么这个Context是什么呢?进去createBaseContextForActivity方法看看呗~

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
	ContextImpl appContext = new ContextImpl();
	appContext.init(r.packageInfo, r.token, this);
	appContext.setOuterContext(activity);

	Context baseContext = appContext;
	// 省略一段代码……………………
	return baseContext;
}

索谍撕呢!原来传入attach方法的Context是ContextImpl(frameworks\base\core\java\android\app\ContextImpl.java)的一个实例!那ContextImpl当然也就是个Context的扩展类啦~哈哈哈哈,那我们看看能不能在其中找到openOrCreateDatabase的实现呢?答案是肯定的!:

@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) {
	File f = validateFilePath(name, true);
	int flags = SQLiteDatabase.CREATE_IF_NECESSARY;
	if ((mode & MODE_ENABLE_WRITE_AHEAD_LOGGING) != 0) {
		flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING;
	}
	SQLiteDatabase db = SQLiteDatabase.openDatabase(f.getPath(), factory, flags, errorHandler);
	setFilePermissionsFromMode(f.getPath(), mode, 0);
	return db;
}

从上面的代码可以看到,其最终还是调用了SQLiteDatabase.openDatabase方法来创建数据库,而从Activity传入ContextThemeWrapper真正的Context则是Context的扩展类ContextImpl。

关于ContextImpl的内容很丰富,其内部代码就有两千行之巨,但是它依然是个轻量级的类,因为其大多数操作都是直接调用PackageInfo的方法来进行的,有机会可以对其再做更深的探讨,这篇文章呢就到此为止!虽说篇幅很长问题其实很简单,但是追求真理的道路我们不能停啊~同时……药不能停……Fuck!

抱歉!评论已关闭.