Context是什么,有什么用
在Android开发中,Context是一个抽象类,它是Android运用程序环境的一部分。它供给了拜访运用程序资源和履行各种操作的接口。能够说,Context是Android运用程序与体系环境进行交互的桥梁。
Context的效果包括:
- 拜访运用程序资源:经过
Context
,能够获取运用程序的资源,如字符串、布局文件、图像等。这些资源能够在运用程序的各个组件中运用,例如Activity
、Service
、BroadcastReceiver
等。 - 发动组件:经过
Context
,能够发动其他组件,如发动Activity
、发动Service
、发送播送等。它供给了拜访体系服务的才能,如发动其他运用程序、发送体系播送等。 - 获取运用程序的上下文:经过
Context
,能够获取运用程序的上下文,如获取ApplicationContext
,用于在整个运用程序中同享数据或履行全局操作。 - 拜访体系服务:经过
Context
,能够拜访各种体系服务,如获取体系级的服务(如传感器服务、位置服务)、拜访设备功用(如摄像头、存储器)、履行网络操作等。 - 拜访运用程序的文件:经过
Context
目标,能够获取运用程序的文件目录,创立、读取、写入和删去文件等操作。 - 处理资源生命周期:经过
Context
,能够办理运用程序资源的生命周期,如创立、毁掉目标、注册和注销监听器等。它供给了一种机制,保证资源的正确运用和释放,避免内存走漏等问题。
public abstract AssetManager getAssets();
/**
* Returns a Resources instance for the application's package.
* <p>
* <strong>Note:</strong> Implementations of this method should return
* a Resources instance that is consistent with the AssetManager instance
* returned by {@link #getAssets()}. For example, they should share the
* same {@link Configuration} object.
*
* @return a Resources instance for the application's package
* @see #getAssets()
*/
public abstract Resources getResources();
/** Return PackageManager instance to find global package information. */
public abstract PackageManager getPackageManager();
/** Return a ContentResolver instance for your application's package. */
public abstract ContentResolver getContentResolver();
/**
* Return the Looper for the main thread of the current process. This is
* the thread used to dispatch calls to application components (activities,
* services, etc).
* <p>
* By definition, this method returns the same result as would be obtained
* by calling {@link Looper#getMainLooper() Looper.getMainLooper()}.
* </p>
*
* @return The main looper.
*/
public abstract Looper getMainLooper();
/**
* Return an {@link Executor} that will run enqueued tasks on the main
* thread associated with this context. This is the thread used to dispatch
* calls to application components (activities, services, etc).
*/
public Executor getMainExecutor() {
// This is pretty inefficient, which is why ContextImpl overrides it
return new HandlerExecutor(new Handler(getMainLooper()));
}
public abstract Context getApplicationContext();
public final CharSequence getText(@StringRes int resId) {
return getResources().getText(resId);
}
/**
* Returns a localized string from the application's package's
* default string table.
*
* @param resId Resource id for the string
* @return The string data associated with the resource, stripped of styled
* text information.
*/
@NonNull
public final String getString(@StringRes int resId) {
return getResources().getString(resId);
}
/**
* Returns a localized formatted string from the application's package's
* default string table, substituting the format arguments as defined in
* {@link java.util.Formatter} and {@link java.lang.String#format}.
*
* @param resId Resource id for the format string
* @param formatArgs The format arguments that will be used for
* substitution.
* @return The string data associated with the resource, formatted and
* stripped of styled text information.
*/
@NonNull
public final String getString(@StringRes int resId, Object... formatArgs) {
return getResources().getString(resId, formatArgs);
}
/**
* Returns a color associated with a particular resource ID and styled for
* the current theme.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return A single color value in the form 0xAARRGGBB.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
@ColorInt
public final int getColor(@ColorRes int id) {
return getResources().getColor(id, getTheme());
}
/**
* Returns a drawable object associated with a particular resource ID and
* styled for the current theme.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return An object that can be used to draw this resource.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
@Nullable
public final Drawable getDrawable(@DrawableRes int id) {
return getResources().getDrawable(id, getTheme());
}
/**
* Returns a color state list associated with a particular resource ID and
* styled for the current theme.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return A color state list.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
@NonNull
public final ColorStateList getColorStateList(@ColorRes int id) {
return getResources().getColorStateList(id, getTheme());
}
/**
* Set the base theme for this context. Note that this should be called
* before any views are instantiated in the Context (for example before
* calling {@link android.app.Activity#setContentView} or
* {@link android.view.LayoutInflater#inflate}).
*
* @param resid The style resource describing the theme.
*/
public abstract void setTheme(@StyleRes int resid);
/** @hide Needed for some internal implementation... not public because
* you can't assume this actually means anything. */
@UnsupportedAppUsage
public int getThemeResId() {
return 0;
}
/**
* Return the Theme object associated with this Context.
*/
@ViewDebug.ExportedProperty(deepExport = true)
public abstract Resources.Theme getTheme();
/**
* Retrieve styled attribute information in this Context's theme. See
* {@link android.content.res.Resources.Theme#obtainStyledAttributes(int[])}
* for more information.
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])
*/
@NonNull
public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(attrs);
}
/**
* Retrieve styled attribute information in this Context's theme. See
* {@link android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])}
* for more information.
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
*/
@NonNull
public final TypedArray obtainStyledAttributes(@StyleRes int resid,
@NonNull @StyleableRes int[] attrs) throws Resources.NotFoundException {
return getTheme().obtainStyledAttributes(resid, attrs);
}
/**
* Retrieve styled attribute information in this Context's theme. See
* {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
* for more information.
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
*/
@NonNull
public final TypedArray obtainStyledAttributes(
@Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
}
/**
* Retrieve styled attribute information in this Context's theme. See
* {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
* for more information.
*
* @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
*/
@NonNull
public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
@NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
return getTheme().obtainStyledAttributes(
set, attrs, defStyleAttr, defStyleRes);
}
总之,Context
在Android开发中具有重要的效果,它供给了拜访运用程序资源、发动组件、拜访体系服务以及处理资源生命周期的才能。开发者能够运用Context
来完结各种运用程序功用和与体系环境的交互。
Context有哪些
Context
自身是一个抽象类,首要完结类为 ContextImpl
,别的有子类 ContextWrapper
和 ContextThemeWrapper
,别的还有其他由上述三个类引申出来的Context
类,Application
/Service
/Activity
,他们的承继联系如下:
ContextImpl
/ContextWrapper
/ContextThemeWrapper
的差异
ContextImpl | ContextWrapper | ContextThemeWrapper |
---|---|---|
ContextImpl 是Context 的首要完结类,它供给了大部分Context 的基本功用和行为。它是Android框架中真实的上下文完结类,用于处理运用程序的资源拜访、组件发动、文件操作和体系服务等操作。 |
ContextWrapper 是一个包装类,用于对现有的Context目标进行包装或修改其功用。它是Context 的一个间接子类,能够经过承继ContextWrapper 类来扩展Context 的功用,例如增加自定义的行为或修改Context 的行为。 |
ContextThemeWrapper :ContextThemeWrapper 是Context 的另一个包装类,它承继自ContextWrapper 类。与ContextWrapper 类似,ContextThemeWrapper 也是用于包装现有的Context 目标,但它还供给了自己的主题资源。经过ContextThemeWrapper ,能够为特定的上下文设置不同的主题,以完结界面的样式和外观的改变。 |
ContextImpl
上文说到,Context
自身是一个抽象类,首要的完结类便是ContextImpl
,即Context
的那些功用都是在ContexImpl
中完结的,即ContextImpl
实践承担着供给运用程序资源拜访、组件发动和体系服务等功用的职责。
public class ContextImpl extends Context {
private Resources mResources;
private Theme mTheme;
void setResources(Resources r) {
if (r instanceof CompatResources) {
((CompatResources) r).setContext(this);
}
mResources = r;
}
@Override
public Resources getResources() {
return mResources;
}
@Override
public void setTheme(int resId) {
synchronized (mSync) {
if (mThemeResource != resId) {
mThemeResource = resId;
initializeTheme();
}
}
}
public Resources.Theme getTheme() {
synchronized (mSync) {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getOuterContext().getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
}
private void initializeTheme() {
if (mTheme == null) {
mTheme = mResources.newTheme();
}
mTheme.applyStyle(mThemeResource, true);
}
// 其他办法的完结省掉...
}
在ContextImpl
,咱们要点重视一下Resource
及Theme
的相关完结,ContextImpl
中供给了getResources/setResources
办法,用于获取Resources
以及设置Resources
,以供给资源的拜访。
在getTheme/setTheme
用于获取Theme
以及设置Theme
,以供给对主题的拜访.
要点看一下getTheme()
办法,该办法,会首要获取mThemeResource
,这儿直接挑选的体系默许主题,体系会依据不同的sdk版别挑选不同的默许主题。
@UnsupportedAppUsage
public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {
return selectSystemTheme(curTheme, targetSdkVersion,
com.android.internal.R.style.Theme,
com.android.internal.R.style.Theme_Holo,
com.android.internal.R.style.Theme_DeviceDefault,
com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}
/** @hide */
public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,
int dark, int deviceDefault) {
if (curTheme != ID_NULL) {
return curTheme;
}
if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
return orig;
}
if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return holo;
}
if (targetSdkVersion < Build.VERSION_CODES.N) {
return dark;
}
return deviceDefault;
}
经过ContextImpl
的实例,运用程序能够获取到Resources
目标和Theme
目标,然后完结对资源和主题的拜访和处理。需求留意的是,这是一个简化的,实践的ContextImpl
源码非常复杂,还涉及到处理上下文的生命周期、体系服务的获取等方面的逻辑。
ContextWrapper
ContextWrapper
是一个包装类,内部包括一个mBase
成员变量,一切的完结都是调用mBase
的办法。
public class ContextWrapper extends Context {
@UnsupportedAppUsage
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
@Override
public void setTheme(int resid) {
mBase.setTheme(resid);
}
/** @hide */
@Override
@UnsupportedAppUsage
public int getThemeResId() {
return mBase.getThemeResId();
}
@Override
public Resources.Theme getTheme() {
return mBase.getTheme();
}
@Override
public ClassLoader getClassLoader() {
return mBase.getClassLoader();
}
@Override
public String getPackageName() {
return mBase.getPackageName();
}
}
ContextThemeWrapper
ContextThemeWrapper
承继自ContextWrapper
,从姓名中能够看出,该类首要是跟主题相关的包装类:
public class ContextThemeWrapper extends ContextWrapper {
...
@Override
public Resources getResources() {
return getResourcesInternal();
}
private Resources getResourcesInternal() {
if (mResources == null) {
if (mOverrideConfiguration == null) {
mResources = super.getResources();
} else {
final Context resContext = createConfigurationContext(mOverrideConfiguration);
mResources = resContext.getResources();
}
}
return mResources;
}
@Override
public void setTheme(int resid) {
if (mThemeResource != resid) {
mThemeResource = resid;
initializeTheme();
}
}
@Override
public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
@UnsupportedAppUsage
private void initializeTheme() {
final boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
final Resources.Theme theme = getBaseContext().getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
}
onApplyThemeResource(mTheme, mThemeResource, first);
}
...
}
和ContextImpl
相比较,ContextThemeWrapper
中获取资源以及主题的代码有所不同,多了一个Configuration
,其他行为大致一致。
别的在AppCompat
中,默许的主题为Theme_AppCompat_Light
,
package androidx.appcompat.view;
public class ContextThemeWrapper extends ContextWrapper {
...
@Override
public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
if (mThemeResource == 0) {
mThemeResource = R.style.Theme_AppCompat_Light;
}
initializeTheme();
return mTheme;
}
}
App中不同Context目标的Theme
咱们在开发中,常常会用到各种Context,常用的有activity/application/applicationContext/baseContext,为了测验不同Context中Theme目标,咱们编写如下代码:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
printLog("baseContext is ${baseContext.themeResId} baseContext is $baseContext")
printLog("application is ${application.themeResId} application is $application")
printLog("applicationContext is ${applicationContext.themeResId} applicationContext is $applicationContext")
printLog("activity is ${this.themeResId}")
}
private fun printLog(msg: String) {
println("MainActivity themeResId in $msg")
}
}
咱们分别获取每个Context
对应的themeResId
,即每个Context
中Theme
对应的resId
:
对代码运转成果咱们有如下结论:
-
getApplication
和getApplicationContext
得到的是同一个Application
实例目标; -
Application
目标中的themeResId
为0 ,Application
其实也有主题的运用,究竟主题样式都是针对UI元素的; - **
Activity
中的主题和getBaseContext
**中的主题是不一样的,详细对应什么主题下文将进行探求 -
getBaseContext
中得到的是ContextThemeWrapper
,这点让我有点意外,之前的了解都是Activity发动时,会新建一个ContextImpl
目标,在attachBaseContext
中赋予Activity
中的mBase
,所以仔细研究一下发现,其实是AppCompatActivity做了替换:
//androidx.appcompat.app.AppCompatActivity
// AppCompatActivity重写了Activity中的attachBaseContext办法
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(getDelegate().attachBaseContext2(newBase));
}
咱们看一下署理类AppCompatDelegateImpl
中attachBaseContext2
的完结:
//androidx.appcompat.app.AppCompatDelegateImpl
@NonNull
@Override
@CallSuper
public Context attachBaseContext2(@NonNull final Context baseContext) {
mBaseContextAttached = true;
final int modeToApply = mapNightMode(baseContext, calculateNightMode());
// If the base context is a ContextThemeWrapper (thus not an Application context)
// and nobody's touched its Resources yet, we can shortcut and directly apply our
// override configuration.
if (sCanApplyOverrideConfiguration
&& baseContext instanceof android.view.ContextThemeWrapper) {
final Configuration config = createOverrideConfigurationForDayNight(
baseContext, modeToApply, null);
ContextThemeWrapperCompatApi17Impl.applyOverrideConfiguration(
(android.view.ContextThemeWrapper) baseContext, config);
return baseContext;
}
// Again, but using the AppCompat version of ContextThemeWrapper.
if (baseContext instanceof ContextThemeWrapper) {
final Configuration config = createOverrideConfigurationForDayNight(
baseContext, modeToApply, null);
((ContextThemeWrapper) baseContext).applyOverrideConfiguration(config);
return baseContext;
}
// We can't apply the configuration directly to the existing base context, so we need to
// wrap it. We can't create a new configuration context since the app may rely on method
// overrides or a specific theme -- neither of which are preserved when creating a
// configuration context. Instead, we'll make a best-effort at wrapping the context and
// rebasing the original theme.
if (!sCanReturnDifferentContext) {
return super.attachBaseContext2(baseContext);
}
Configuration configOverlay = null;
final Configuration config = createOverrideConfigurationForDayNight(
baseContext, modeToApply, configOverlay);
//要点1:新建ContextThemeWrapper目标将传入的baseContext赋值给ContextWrapper中的mBase,
// 而且ContextThemeWrapper中的主题为Theme_AppCompat_Empty
// Next, we'll wrap the base context to ensure any method overrides or themes are left
// intact. Since ThemeOverlay.AppCompat theme is empty, we'll get the base context's theme.
final ContextThemeWrapper wrappedContext = new ContextThemeWrapper(baseContext,
R.style.Theme_AppCompat_Empty);
wrappedContext.applyOverrideConfiguration(config);
// Check whether the base context has an explicit theme or is able to obtain one
// from its outer context. If it throws an NPE because we're at an invalid point in app
// initialization, we don't need to worry about rebasing under the new configuration.
boolean needsThemeRebase;
try {
needsThemeRebase = baseContext.getTheme() != null;
} catch (NullPointerException e) {
needsThemeRebase = false;
}
if (needsThemeRebase) {
// Attempt to rebase the old theme within the new configuration. This will only
// work on SDK 23 and up, but it's unlikely that we're keeping the base theme
// anyway so maybe nobody will notice. Note that calling getTheme() will clone
// the base context's theme into the wrapped context's theme.
ResourcesCompat.ThemeCompat.rebase(wrappedContext.getTheme());
}
return super.attachBaseContext2(wrappedContext);
}
//androidx.appcompat.app.AppCompatDelegate
@NonNull
@CallSuper
public Context attachBaseContext2(@NonNull Context context) {
// 要点2,将上一步包装了baseContext的ContextThemeWrapper目标进一步赋值给Activity的mBase
attachBaseContext(context);
return context;
}
终究AppCompatActivity
中的mBase
是包装了ContextImpl
的ContextThemeWrapper
目标,而且其主题为Theme_AppCompat_Empty
关于第三点,getBaseActivity
和Activity
中的主题到底是哪一个,咱们能够依据resId和resources索引表resource.arsc(直接将apk文件拖到AndroidStudio中就能够看到该文件)找到:
2131755410
和2131755474
对应16进制为0x7f100192
与0x7f1001d2
能够看到,getBaseActivity
和Activity
中的主题分别对应Theme_AppCompat_Empty
与咱们在AndroidManifest.xml
中设置的运用主题Theme.ThemeTest
总结
Context是Android运用程序与体系环境进行交互的桥梁,首要完结类是ContextImpl, 能够拜访运用程序资源/发动组件/拜访体系服务/拜访运用程序的文件等,而Context能够分为三种:ContextImpl
/ContextWrapper
/ContextThemeWrapper
,不同ContextImpl
是Context的首要完结类,ContextWrapper
是简单的包装类,一切的完结都由其内部的mBase
成员完结,ContextThemeWrapper承继自ContextWrapper
,它的首要承继者是Activity
,和其他两个Context
不同的是,他内部对运用资源和主题有不同的行为,在运用中运用跟主题相关的Context
时,最好运用activity
,而不要运用getBaseContext
或者applictaion
.
参考文档
www.cnblogs.com/linhaostudy…