您的位置:首页 > 移动开发 > Android开发

Android Context 源码的理解

2016-11-03 16:45 495 查看
Context 是什么

类的剖析
Context

ContextImpl

ContextWrapper

ContextThemeWrapper

Context 创建的时机
Application 的创建

Activity 的启动

Service

Context 的数量

不同 Context 的分析
Application 中的 Context

Activity 中的Context

Service 中的Context

创建 Context 的总结

getApplication 与 getApplicationContext 的区别

Context 的作用域

Context 引起的问题
使用 Application 的问题

内存泄露的问题

Context 是什么?

从接触 Android 开始,就一直被一个既陌生又熟悉的单词所围绕,那就是 Context。

空格无论你是第一次接触 Android 的菜鸟,还是开发中的老鸟,你对 Context 的使用一定不陌生,因为它太常见了,你在加载资源、开启 Activity、创建 View等等都需要 Context。但是 Context 到底是什么呢?一个应用程序中到底有多少个 Context 呢?在什么场景下使用不同的 Context?下面讲一一解答大家的疑惑。

字面意思上理解是上下文,它贯穿了整个应用。

它是一个运行环境,提供了一个应用运行所需要的信息,资源,服务等。

它是一个场景,用户操作和系统交互这一过程就是一个场景,比如 Activity 之间的切换,Service 的启动等。

官方 SDK 中对其描述如下:

Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

根据描述我们可以得到如下信息:

它说 Context 所描述的是一个应用程序环境的信息,即上下文;

这是一个抽象类,Android 系统提供了具体实现类;

可以通过它获取应用的 resources 和 classes,也包括一些应用级别操作,比如:启动 Activity,发送广播,接收 intent 信息等等

这些太抽象了,究竟 Context 是什么?我们平常所说的 Activity、Application、Service其实统统都是一个 Context。

我们去看一下类的继承关系:



发现 Activity、Service、Application都是 Context 的子类。

Context 是一个抽象类,而上述的都是该类的一个实现。Service 和 Application 以及 ContextThemeWrapper 统一继承自 ContextWrapper ,而我们的 Activity 继承自 ContextThemeWrapper,要搞清楚这个类继承的关系。

一张图表示他们的关系



类的剖析

首先要了解一下这几个类是做什么的,查看源代码(基于API 23)下。

Context 类的源代码 3814 行(API 23),贴出来部分给大家看一下:

Context

public abstract class Context{
...
// 启动 Activity
public abstract void startActivity(Intent intent);
// 获取系统服务
public abstract Object getSystemService(String name);
// 发送广播
public abstract void sendBroadcast(Intent intent);
// 开启 Service
public abstract ComponentName startService(Intent service);

public abstract AssetManager getAssets();

public abstract Resources getResources();

public abstract PackageManager getPackageManager();
...
}


源码看到 Context 是一个抽象类,里面定义了各种各样的抽象方法,诸如获取资源,启动 Activity、Service,发送广播,操作数据库,弹出 Toast 等都需要用到 Context 。所以可以理解为 Context 就是抽象出一个 App 应用所有功能的集合,它的具体方法实现是在类 ContextImpl 中实现的。

ContextImpl

class ContextImpl extends Context {
//整个App的主线程
final ActivityThread mMainThread;
//整个App的相关信息
final LoadedApk mPackageInfo;
//资源解析器
private final ResourcesManager mResourcesManager;
//App资源类
private final Resources mResources;
//外部Context的引用
private Context mOuterContext;
//默认主题
private int mThemeResource = 0;
private Resources.Theme mTheme = null;
//包管理器
private PackageManager mPackageManager;
...
//以下是静态区注册系统的各种服务,多达五六十种系统服务,因此每个持有Context引用的对象都可以随时通过getSystemService方法来轻松获取系统服务。
static {
registerService(ACCESSIBILITY_SERVICE, new ServiceFetcher() {
public Object getService(ContextImpl ctx) {
return AccessibilityManager.getInstance(ctx);
}});

registerService(CAPTIONING_SERVICE, new ServiceFetcher() {
public Object getService(ContextImpl ctx) {
return new CaptioningManager(ctx);
}});

registerService(ACCOUNT_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
IAccountManager service = IAccountManager.Stub.asInterface(b);
return new AccountManager(ctx, service);
}});
...

}

...
//启动Activity的地方
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity " +
" context requires the FLAG_ACTIVITY_NEW_TASK flag." +
" Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity)null, intent, -1, options);
}
...
//启动服务的地方
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, mUser);
}
...
}


ContextImpl 实现了抽象类 Context 里面的所有方法,而又因为 Context 的具体能力是由 ContextImpl 类来实现,也就是说像 Activity、Application 这样的类其实并不会去具体实现 Context 的功能,仅仅只是做了一层接口封装而已,Context 的具体功能都是由 ContextImpl 去完成的。因此绝大多数情况下,Activity、Service、Application 这三种类型的 Context 是可以通用的。但是,有几种情况比较特殊,比如启动 Activity,弹出 Dialog。出于安全原因的考虑, Android 是不允许 Activity 或 Dialog 凭空出现的,例如 Activity 的启动是必须要建立在另一个 Activity 的基础之上,以此形成返回栈。而 Dialog 则必须在一个 Activity上面弹出(除非是System Alert类型的 Dialog),因此在这种情况下,我们只能使用 Activity 类型的 Context,否则就会出错。

ContextWrapper

ContextWrapper 是 Context 的子类,名字上我们可以看到是 Context 的包装,Application、Activity、Service 都是直接或间接继承自 ContextWrapper,点开 ContextWrapper

public class ContextWrapper extends Context{

Context mBase;
// 给mBase赋值
public ContextWrapper(Context base){

mBase = base;
}
// 创建Application、Service、Activity的时候会调用该方法,给mBase赋值,若mBase早已赋值,会抛出状态非法异常
protected void attachBaseContext(Context base) {

if(mBase != null) {
throw new IllegalStateException("mBase context already set");
mBase = base;
}
}
...
public Context getBaseContext() {
return mBase;
}

@Override
public AssetManager getAssets() {
return mBase.getAssets();
}

@Override
public Resources getResources() {
return mBase.getResources();
}

@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}

@Override
public Looper getMainLooper() {
return mBase.getMainLooper();
}

@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}

@Override
public String getPackageName() {
return mBase.getPackageName();
}

@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}

@Override
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}

@Override

1897e
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}

@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
mBase.unregisterReceiver(receiver);
}

@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}

@Override
public boolean stopService(Intent name) {
return mBase.stopService(name);
}

@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
return mBase.bindService(service, conn, flags);
}

@Override
public void unbindService(ServiceConnection conn) {
mBase.unbindService(conn);
}

@Override
public Object getSystemService(String name) {
return mBase.getSystemService(name);
}
...
}


如上。

很多方法都是比较常见的。首先可以看到所有的方法具体的实现都是统一调用 mBase 对象中对应的当前方法名的方法。

那何为 mBase?

注意看 attachBaseContext() 这个方法,此方法传入了一个 base 参数,并将它赋值给 mBase 对象。

而 attachBaseContext() 这个方法其实是由系统来调用的,当我们创建 Activity、Service、Application时就会调用该方法,它会把 ContextImpl 对象作为参数传递到 attachBaseContext() 方法当中,从而赋值给 mBase 对象,之后 ContextWrapper 中的所有方法其实都是通过这种委托的机制交由 ContextImpl 去具体实现的,所以说 ContextImpl 是上下文功能的实现类。

而 getBaseContext() 方法,返回了mBase 对象,其实这个对象就是 ContextImpl 对象。

ContextThemeWrapper

而 ContextThemeWrapper 类,意味着该类内部包含了 Theme 相关的接口,也就是 android:theme 属性所指定的。我们都知道,只有 Activity 才需要主题,Service 是不需要主题的,Activity 在启动的时候系统都会加载一个主题,也就是我们平时在 AndroidManifest.xml 文件里面写的 android:theme=”@style/AppTheme“ 属性。因此 Activity 继承自 ContextThemeWrapper ,Service 和 Application 继承自 ContextWrapper,而 ContextThemeWrapper 也继承自 ContextWrapper。

所以Activity,Application,Service 其实都关联着一个 mBase 变量,而 mBase 变量是 ContextImpl 对象的赋值,也是真正实现抽象类 Context 的地方。虽然 Activity,Application,Service 都统一继承自 Context,但是他们自己本身持有的 Context 对象是不同的。

查看 ContextThemeWrapper 的部分源代码:

public class ContextThemeWrapper extends ContextWrapper{

private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
private Configuration mOverrideConfiguration;
private Resources mResources;

public ContextThemeWrapper() {
super(null);
}

public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}

public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
}

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

}


Context 创建的时机

知道了 Context 的继承关系后,自然而然的就想到什么时候会创建 Context 对象呢?

应用程序创建 Context 时的情况有这么几种情况:

1. 创建 Application 时

2. 创建 Service 时

3. 创建 Activity 时

Application 的创建

每个应用程序第一次启动的时候,都会首先创建 Application 对象。创建 Application 的时机在创建 handleBindApplication() 方法中,位于 ActivityThread 中,如下:

//创建Application时同时创建的ContextIml实例
private final void handleBindApplication(AppBindData data){
...
///创建Application对象
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
...
}

public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
...
try {
java.lang.ClassLoader cl = getClassLoader();
//创建一个ContextImpl对象实例
ContextImpl appContext = new ContextImpl();
//初始化该ContextIml实例的相关属性
appContext.init(this, null, mActivityThread);
///新建一个Application对象
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
//将该Application实例传递给该ContextImpl实例
appContext.setOuterContext(app);
}
...
}


通过 makeApplication 方法创建一个 Application 对象,在 makeApplicatioin 方法中,创建一个 ContextImpl 对象的实例并初始化相关属性,然后通过 ActivityThread 的 Instrumentation 对象调用 newApplication 方法得到该 Application 对象。

Activity 的启动

当我们每次启动 Activity 的时候,看源代码:

Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}


可以看到,都会走到 startActivityForResult 中去,而查阅 startActivityForResult 的源码:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received.  Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}

cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}


通过 mInstrumentation.execStartActivity() 方法最终调用了 ActivityManagerNative.getDefault().startActivity() 方法。

而 ActivityManagerNative.getDefault() 实际是通过 Binder 机制调到了另一个进程中的 AMS.startActivity 方法,然后 ActivityStackSupervisor.realStartActivityLocked 等等等等一大堆,最终走到了 ActivityThread.handleLaunchActivity 方法,如下:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;

if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}

// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);

if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);

// Initialize before creating the activity
WindowManagerGlobal.initialize();

// 在这里启动一个 Activity
Activity a = performLaunchActivity(r, customIntent);

if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out paused, because it
// needs to be visible but isn't in the foreground. We accomplish this by going
// through the normal startup (because activities expect to go through onResume()
// the first time they run, before their window is displayed), and then pausing it.
// However, in this case we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just retain the current
// state it has.
performPauseActivityIfNeeded(r, reason);

// We need to keep around the original state, in case we need to be created again.
// But we only do this for pre-Honeycomb apps, which always save their state when
// pausing, so we can not have them save their state when restarting from a paused
// state. For HC and later, we want to (and can) let the state be saved as the
// normal part of stopping the activity.
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}


而 handleLaunchActivity 内部又调用了 performLaunchActivity() 方法,然后去创建了一个 Activity 实例,并且回调 onCreate() , onStart()方法等。

/**
主要做了以下几件事
1. 从ActivityClientRecord中获取待启动act的组件信息;
2. 通过Instrumentation.newActivity方法使用ClassLoader创建act对象;
3. 通过LoadedApk.makeApplication方法创建Application对象,注意如果已经有Application对象的话是不会再次创建的;
4. 创建ComtextImpl对象,并调用Activity.attach方法完成一些重要数据的初始化操作;
5. 最终调用Activity.onCreate()方法;
*/
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

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);
r.intent.prepareToEnterProcess();
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 (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window);

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;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
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.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
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;
}


可以看到在创建 Activity 实例的同时还创建了 ContextImpl 的实例

if(activity != null) {
// 创建一个 Activity
ContextImpl appContext = new ContextImpl();
// 初始化该 ContextImpl 实例的相关属性
appContext.init(r.packageInfo,r.token,this);
// 将该 Activity 信息传递给该 ContextImpl 实例
appContext.setOuterContext(activity);
}


Service

通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法,

完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类,如下:

//创建一个Service实例时同时创建ContextIml实例
private final void handleCreateService(CreateServiceData data){
...
//创建一个Service实例
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
}
...
ContextImpl context = new ContextImpl(); //创建一个ContextImpl对象实例
context.init(packageInfo, null, this);   //初始化该ContextIml实例的相关属性
//获得我们之前创建的Application对象信息
Application app = packageInfo.makeApplication(false, mInstrumentation);
//将该Service信息传递给该ContextImpl实例
context.setOuterContext(service);
...
}


Context 的数量

由上述创建 Context 的时机来分析,一个应用程序 App 共有的 Context 类型为三种,分别是:Application、Activity、Service。因此 Context 的数量的计算公式为:

Context 的数量 =  Activity 数量 + Service 数量 + 1


1代表的是 Application ,因为一个 App 里可以有 N 个 Activity 和 Service,但是却只能有一个 Application。

当然,如果你细心的话,可能会疑问,所谓的四大组件,为什么在这里只有 Activity 和 Service 持有 Context,那BroadcastReceiver 以及 ContentProvider 呢?

其实 BroadcastReceiver 和 ContentProvider 并不是 Context 的子类,他们所持有的 Context 是其他地方传过去的,并不能算是 Context。

不同 Context 的分析

Application 中的 Context

ActivityThread 源码

应用入口

public static void main(String[] args) {
...
//初始化Looper
Looper.prepareMainLooper();
//创建一个APP主线程ActivityThread对象
ActivityThread thread = new ActivityThread();
//初始化App应用信息
thread.attach(false);
//获得主线程也就是UI线程的handler对象
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//Android4.1版本之后添加了这么一个方法,目的就是为了能让AsyncTask能在子线程创建,
//在之前的版本是不能在子线程中创建初始化AsyncTask的。
AsyncTask.init();
//启动Looper循环,进入消息循环。
Looper.loop();
}


main 方法主要创建程序主线程 ActivityThread 并初始化,并调用 attach 来初始化

private void attach(boolean system) {
//整个应用的Application对象
Application mInitialApplication;
//整个应用的后台管家
Instrumentation mInstrumentation;
...
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
//利用ContextImpl创建整个应用的Application对象
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
//调用Application对象的onCreate方法
mInitialApplication.onCreate();
}
...
}


context.mPackageInfo.makeApplication(true, null); 创建了应用的 Application 对象。跟进去看看

public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
//第一次进来mApplication==null条件不满足,之后创建Activity的时候条件满足直接返回当前Application对象
if (mApplication != null) {
return mApplication;
}

Application app = null;

try {

//为Appliaction创建ContextImpl对象
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
//调用Instrumentation类中的newApplication方法创建Application
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
//给ContextImpl设置外部引用
appContext.setOuterContext(app);
}
...
return app;
}


通过 Instrumentation 的 newApplication 方法来创建 Application 对象。

查看 newApplication 方法

public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}

static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}


通过类加载器来创建 Application ,并且调用 attach 来初始化。进入 Application 查看 attach

final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}


Application 类是继承自 ContextWrapper 类,在 attach 方法中调 ContextWrapper 中的 attachBaseContext 方法来对 ContextWrapper 的成员变量 mBase 赋值,而 mBase 就是 ContextImpl,通过委托机制让 ContextImpl 真正实现了 Context 类中的所有抽象方法。

Activity 中的Context

当 Application 创建完成后,AMS 会通过 Binder 机制通知 ActivityThread 去创建需要的 Activity。最终转到 performLaunchActivity 方法来创建 Activity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...

Activity activity = null;
try {
//通过Instrumentation类的newActivity方法来创建一个Activity对象
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
}
try {
//获取当前应用的Application对象,该对象的唯一作用就是作为参数传递到Activity里,
然后在Activity类中可以获得调用getApplication方法来获取Application对象
Application app = r.packageInfo.makeApplication(false, mInstrumentation);

...
if (activity != null) {
//为Activity创建ContextImpl对象
Context appContext = createBaseContextForActivity(r, activity);
//为Activity赋值初始化
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.voiceInteractor);
...
//获取当前应用的主题资源
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
//设置主题
activity.setTheme(theme);
}

activity.mCalled = false;
if (r.isPersistable()) {
//辗转到Activity,调用Activity的生命周期onCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
//调用Activity的生命周期onStart方法
activity.performStart();
r.stopped = false;
}
...

return activity;
}


由上可见,是通过 createBaseContextForActivity 来创建 ContextImpl 对象,而又将该对象传递到 attach 方法中来对 Activity 进行初始化操作。

private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
// 让 ContextImpl 持有 Activity 对象的引用
appContext.setOuterContext(activity);
Context baseContext = appContext;
...
return baseContext;
}


appContext.setOuterContext(activity);

是为了让 ContextImpl 持有 Activity 的引用,目的是在 ContextImpl 类中注册一些服务,设置主题等。而做这些都需要 Activity 对象的引用。

再来看看 Activity 的 attach 方法

public 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, IVoiceInteractor voiceInteractor) {
//调用父类方法对mBase变量赋值
attachBaseContext(context);
//创建一个Activity的窗口
mWindow = PolicyManager.makeNewWindow(this);
//给Window窗口设置回调事件
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
//设置键盘弹出状态
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();

mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
//此处注意,将整个应用的Application对象赋值给Activity的mApplication成员变量。
//目的是为了能在Activity中通过getApplication方法来直接获取Application对象
mApplication = application;
...
}
//在Activity中返回当前应用的Application对象
/** Return the application that owns this activity. */
public final Application getApplication() {
return mApplication;
}
}


attach 方法进来就调用了父类的 attachBaseContext 方法。

attachBaseContext 里面的代码以及剩下的要做的事在前面的环节中已经分析过了,此处就不再分析了。

结论与上述一样,之所以 Activity 可以调用 Context 里面的任何方法,是因为 mBase ,而 mBase 就是一个 ComtextImpl 对象,车轱辘话我就不来回说了。

Service 中的Context

与 Activity 类似,Service 也是由 AMS 通过 Binder 机制通知 ActivityThread 去创建一个 Service ,最后转到 handleCreateService 方法。

private void handleCreateService(CreateServiceData data) {

LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
//通过类加载器创建Service服务
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
}
...
try {
...
//此处为Service创建一个ContextImpl对象
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
//同样为ContextImpl类设置外部对象,目的还是让ContextImpl持有外部类的引用
//在ContextImpl类中的许多方法需要使用到外部Context对象引用
context.setOuterContext(service);
...
//获得当前应用的Applicaton对象,该对象在整个应用中只有一份,是共享的。
Application app = packageInfo.makeApplication(false, mInstrumentation);
//将ContextImpl对象和Application对象作为attach方法参数来初始化Service
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//Service初始化完成之后系统自动调用onCreate生命周期方法
service.onCreate();
...
}


与上述 Application 和 Activity 类似,也是通过类加载器来创建 Service 对象,然后再创建 ContextImpl 对象,而且也是为 ContextImpl 设置 Service 对象 Context 的引用,其目的也是 ContextImpl 类中的许多方法需要使用外部 Context 引用。

接下来的事情都类似了,跟 Activity 差不多,看一下 attach 方法

public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
//调用父类方法去注册ContextImpl对象
attachBaseContext(context);
mThread = thread;           // NOTE:  unused - remove?
mClassName = className;
mToken = token;
//将整个应用的Applicaton对象赋值给Service类的成员变量mApplication
mApplication = application;
mActivityManager = (IActivityManager)activityManager;

}


不做累述,分析与前文相似。

创建 Context 的总结

上述分析了这么多,看了这么多源码,其实有关创建 Context 对象的地方其实都是在 ActivityThread 类中,而 ActivityThread 就是整个应用的入口,也是整个应用的主线程。

每个应用首先会创建一个 Application 对象,且只有一个,然后会根据你的代码和需求创建相应的 Activity 和 Service。并且在创建 Activity 或者 Service 的时候都会持有当前应用的 Application 对象,通过 getApplication 方法就可以拿到。

Context 定义了 N 多方法,都是抽象的。需要子类 Context 的子类去实现,Activity 、Application 、Service 创建的时候都会去创建一个 ContextImpl 对象,最终他们都会通过调用 ContextWrapper 里面的 attachBaseContext 方法给 mBase 赋值,也就是 ContextImpl 对象,所以真正的实现体是 ContextImpl。

getApplication 与 getApplicationContext 的区别

有时候会发现 getApplicationContext 可以做的事 getApplication 也能做,他们两个有什么区别呢?

查看 getApplicationContext 的源代码

public class ContextWrapper extends Context {
...
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
...
}


发现是调用了 mBase 的 getApplicationContext 方法,也就是在 ContextImpl 里面实现,追过去看一下

class ContextImpl extends Context {
...
@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
}
...
}


返回值为mPackageInfo.getApplication() 或者 mMainThread.getApplication();

类型是 Context 。

再来看看 getApplication

public final Application getApplication() {
return mApplication;
}


返回的是当前应用的 Application 对象

所以:

getApplication 和 getApplicationContext 方法返回的对象都是指向当前应用的Application对象,是同一个 Application 对象,仅仅是返回值类型不同而已。

getApplication 方法是在 Activity,Service 类中实现的;

getApplicationContext 方法是在 ContextWrapper 类中实现的。

也就是 getApplication 方法是在 ContextWrapper 子类中实现的,而 getApplicationContext 是在父类中实现的,所以两个方法的使用范围是不一样的。比如说你可以

context.getApplicationContext();

却不能

context.getApplication();

除非你这个 Context 是 Activity 或者 Service。

Context 的作用域

该段参考 : https://possiblemobile.com/2013/06/context/

虽然Context神通广大,但并不是随便拿到一个Context实例就可以为所欲为,它的使用还是有一些规则限制的。由于Context的具体实例是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。



上图中的 No 上面有些数字1,2,意思如下:

数字1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。

数字2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。

数字3:在receiver为null时允许,在4.2或以上的版本中,用于获取黏性广播的当前值。(可以无视)

注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因为在其内部方法中都有一个 context 用于使用。

Context 引起的问题

使用 Application 的问题

虽然每个项目里面都有 Application ,而且用法虽然简单,但是常在河边走哪有不湿鞋?有些地方需要注意一下,比如说,我们都知道 Context 定义了很多方法,诸如getString(),getPackageName(),startActivity()等等等等,既然 Application 是 Context 的子类,那我就可以调用getPackageName(),快速测试一下

package com.xy.bbs;

import android.app.Application;

/**
* Created by mesmerize on 2016/11/1.
*/

public class TestApplication extends Application {

public TestApplication() {

String packageName = this.getPackageName();

System.out.println("Constructor packageName = " + packageName);
}


很简单的一段代码,就是在在构造函数中打印包名,然而运行之后,就崩溃了,log 如下:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.content.ContextWrapper.getPackageName(ContextWrapper.java:132)
at com.xy.bbs.TestApplication.<init>(TestApplication.java:13)
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:430)
at com.android.tools.fd.runtime.BootstrapApplication.createRealApplication(BootstrapApplication.java:215)
at com.android.tools.fd.runtime.BootstrapApplication.attachBaseContext(BootstrapApplication.java:239)
at android.app.Application.attach(Application.java:189)
...


NullPointerException,哎!?怎么还空指针了?

这点稍后说,先把代码改成这样再试试:

package com.xy.bbs;

import android.app.Application;

/**
* Created by mesmerize on 2016/11/1.
*/
public class TestApplication extends Application {

@Override
public void onCreate() {

String packageName = this.getPackageName();

System.out.println("onCreate() packageName = " + packageName);
}
}


然后一切正常,正确打印包名。

这是为什么呢?

之前在谈 ContextWrapper 的时候讲过给 Context 赋值是在 attachBaseContext() 这个方法中赋值,赋值给 mBase ,并且里面所定义的所有方法全是通过 mBase 这个对象来调用的,试想一下,如果 mBase 为 null,调用其中定义的方法时会怎样?肯定就空指针了啊!

所以,这也就引出了 Applicatioin 方法的执行顺序

先走 constructor => 然后 attachBaseContext() => 最后 onCreate()

因此我们在 onCreate() 里面调用 ContextWrapper 定义的方法就不会有问题,而在构造函数中调用就会空指针。

这是一个问题。当然你也可以重写 attachBaseContext() 这个方法,但是要注意要在 super.attachBaseContext(base); 之后调用,不能在之前。

还有一种空指针的情况,虽说 Google 不建议我们自定义 Application,更推崇使用单例,但是身边的人几乎都是在用自定义 Application,不谈这个。

有些人写单例习惯了,有时候可能写出这样的代码:

public class TestApplication extends Application {

private static TestApplication applicatioin;

public static TestApplication getInstance() {
if (applicatioin == null) {
applicatioin = new TestApplication();
}
return applicatioin;
}

}


有问题吗?

肯定是有问题的!

Application 是系统的组件,系统组件就要让系统自己去创建,这样new出来,就只是一个普通的 java 对象,并不具备 Context 的能力,拿这样一个对象去调用 Context 的方法,还是空指针。

正确写法如下:

public class TestApplication extends Application {

private static TestApplication application;

public static TestApplication getInstance() {
return application;
}

@Override
public void onCreate() {
super.onCreate();
application = this;
}

}


浅显易懂,我就不多做解释了。



内存泄露的问题

该段部分参考:https://possiblemobile.com/2013/06/context/

首先看一段代码:

public class CustomManager {
private static CustomManager sInstance;

public static CustomManager getInstance(Context context) {
if (sInstance == null) {
sInstance = new CustomManager(context);
}

return sInstance;
}

private Context mContext;

private CustomManager(Context context) {
mContext = context;
}
}


这就是一个很普通的单例,我们在项目中经常会编写一些工具类等,可能会写成单例的模式,比如上述代码。这就可能造成一些问题。

可以看到上述代码中需要一个 Context,由于工具类大多需要访问资源,所以需要 Context ,这无可厚非,但是有一种可能,就是 Context 的类型。如果是在 Activity 中或者是 Service 中呢?

sInstance 可是一个静态的强引用的啊!而我们都知道 static 的生命周期是伴随整个项目的生命周期而存活的,静态变量在整个应用的内存里只保存一份,一旦创建就不会释放该变量的内存,直到整个应用都销毁才会释放static静态变量的内存。如果你用 Activity 作为 Context传了进来。那也就是说,此 Activity 会一直活下去,直到项目退出。否则的话,这个 Activity 是没办法被回收的。而 Activity 生命周期肯定没这么长,所以就造成了内存泄露了!

所以,遇到这种问题的时候,尽可能改成跟整个应用的生命周期一样的 Context,比如 ApplicationContext。

改动代码如下:

public class CustomManager {
private static CustomManager sInstance;

public static CustomManager getInstance(Context context) {
if (sInstance == null) {
//Always pass in the Application Context
sInstance = new CustomManager(context.getApplicationContext());
}

return sInstance;
}

private Context mContext;

private CustomManager(Context context) {
mContext = context;
}
}


即可。

再来看看另一种 View 持有引用导致的内存泄露

public class CustomResource {
//静态变量drawable
private static Drawable mDrawable;
private View view;

public CustomResource(Context context) {
Resources resources = context.getResources();
mDrawable = resources.getDrawable(R.drawable.ic_launcher);
view = new View(context);
view.setBackgroundDrawable(mDrawable);
}
}


这段代码也会内存泄露,查阅 setBackgroundDrawable() 的源码可以看到:

public void setBackgroundDrawable(Drawable background) {
...
/**此处的this就是当前View对象*/
// Set callback last, since the view may still be initializing.
background.setCallback(this);
...
}


而View对象哪来的?是通过Context对象获得的。

因此,变量 background 持有View对象的引用,View持有Context的引用,所有background间接持有Context对象的引用了。这也就意味着,如果该 Context 对应的 Activity 退出 finish 掉的时候,该 Activity 是不能完全释放的,因为 static 的 drawable 持有该 Activity 的 Context 对象的间接引用,从而导致该 Activity 内存无法回收,导致内存泄露隐患。

但是起因是由于 static drawable 持有 View 对象的引用导致内存泄露的,并不是由于 context.getResource 导致的,因此并不能用 context.getApplicationContext 来解决。Android 3.0之后修改了 setBackgroundDrawable 方法的 setCallback(this)方法,里面实现使用了弱引用来持有 View 对象的引用,从而避免了内存泄露隐患。

所以,尽量避免使用静态资源,或者直接使用弱引用来解决也是可以的。

一般而言,由 Context 所造成的内存泄露,几乎都是当 Context 销毁的时候,却由于被引用而导致销毁失败,所以我们应该记住正确使用 Context 的姿势:

当 Application 的 Context 能搞定的情况下,并且生命周期长的对象,优先使用 Application 的Context。

不要让生命周期长于 Activity 的对象持有 Activity 的引用。

尽量不要在 Activity 中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,而静态内部类不会持有外部类的引用,外部类会以正常的方式回收。如果静态内部类想使用外部类的属性或方法,可以将外部实例引用作为弱引用持有。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: