Context目标是如此常见和传递运用,它可能会很简单发生并不是你预期的景象。加载资源、发动一个新的Activity、获取体系服务、获取内部文件途径以及创立view(其实还远不止这些)通通都需求Context目标来完结。我(原文作者)想做的仅仅给咱们供给一些Context是怎么作业的见地,以及让咱们在运用中更有用的运用Context的技巧。
Context的类型
并不是一切的context实例都是等价的。依据Android运用的组件不同,你拜访的context面向有些纤细的不同。
Application – 是一个运转在你的运用进程中的单例。在Activity或许Service中,它能够经过getApplication()函数取得,或许人和承继于context的目标中,经过getApplicationContext()办法取得。不论你是经过何种办法在哪里取得的,在一个进程内,你总是取得到同一个实例。
Activity/Service – 承继于ContextWrapper,它完成了与context相同API,可是署理这些办法调用到内部躲藏的Context实例,即咱们所知道的根底context。任何时分当体系创立一个新的Activity或许Service实例的时分,它也创立一个新的ContextImpl实例来做一切的深重的作业。每一个Activity和Service以及其对应的根底context,对每个实例来说都是仅有的。
BroadcastReciver – 它自身不是context,也没有context在它里边,可是每逢一个新的播送抵达的时分,结构都传递一个context目标到onReceive()。这个context是一个ReceiverRestrictedContext实例,它有两个首要函数被禁掉:registerReceiver()和bindService()。这两个函数在BroadcastReceiver.onReceive()不允许调用。每次Receiver处理一个播送,传递进来的context都是一个新的实例。
ContentProvider – 它自身也不是一个Context,可是它能够经过getContext()函数给你一个Context目标。假设ContentProvider是在调用者的的本地(例如,在同一个运用进程),getContext()将回来的是Application单例。但是,假设调用这个ContentProvider在不同的进程的时分,它将回来一个新创立的实例代表这个Provider所运转的包。
保存引证
第一个咱们需求处理问题是,在一个目标或许类内部保存一个context引证,而它生命周期却超越其保存引证的目标的生命周期。例如,创立一个自定义的单例,它需求一个context来加载资源或许获取ContentProvider,然后保存一个指向当时Activiy或许Service的引证在单例中。
糟糕的单例
[java]
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是从哪里来的,而且假设保存一个终究指向的是Activity或许Servece的引证是并不安全的。这是一个问题,是由于一个单例在类的内部保持一个仅有的静态引证,这意味着咱们的目标,以及一切其他它所引证的目标,将永久不能被废物收回。假设这个Context是一个Activity,咱们将保存与这个Activity相关的一切的view以及其他大的目标,然后形成内存走漏。
为了处理这个问题,咱们修正单例永久仅仅保存Application context:
改进的单例:
[java
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;
}
}
现在这个比如中,咱们的Context来自哪里都没有联系,由于咱们这儿保存引证是安全的。Application Context 自身便是一个单例,所以咱们再创立别的一个static引证,不会形成任何内存走漏。别的一个很好的比如是,在后台线程或许一个等候的Handler中保存Context的引证,也能够运用这样的办法。
为什么咱们不能总是引证Application context呢?正如前面说的,引证Application context永久不必忧虑内存走漏的问题。问题的答案,就像我在开端的介绍中说的,是由于不同context并不是等价的。
Context的才能
Conext能做的通用操作决定于这个context开始来源于哪里。下表所列的是,在运用中常见的会收到context目标的,以及对应的每种状况,它能够用于哪些地方:
Application | Activity | Service | ContentProvider | BroadcastReceiver | |
---|---|---|---|---|---|
Show a Dialog | NO | YES | NO | NO | NO |
Start an Activity | NO1 | YES | NO1 | NO1 | NO1 |
Layout Inflation | NO2 | YES | NO2 | NO2 | NO2 |
Start a Service | YES | YES | YES | YES | YES |
Bind to a Service | YES | YES | YES | YES | NO |
Send a Broadcast | YES | YES | YES | YES | YES |
Register BroadcastReceiver | YES | YES | YES | YES | NO3 |
Load Resource Values | YES | YES | YES | YES | YES |