邯山区建设局网站,合肥网站建设开发,龙岩天宫山缆车开放时间,青浦网站制作公司作者#xff1a;两日的blog Context是什么#xff0c;有什么用
在Android开发中#xff0c;Context是一个抽象类#xff0c;它是Android应用程序环境的一部分。它提供了访问应用程序资源和执行各种操作的接口。可以说#xff0c;Context是Android应用程序与系统环境进行交… 作者两日的blog 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 applications package.* p* strongNote:/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 applications 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 applications 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 itreturn 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 applications packages* 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 applications packages* 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 cant 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 Contexts 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 Contexts 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 Contexts 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 Contexts 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的区别
ContextImplContextWrapperContextThemeWrapperContextImpl是Context的主要实现类它提供了大部分Context的基本功能和行为。它是Android框架中真正的上下文实现类用于处理应用程序的资源访问、组件启动、文件操作和系统服务等操作。ContextWrapper是一个包装类用于对现有的Context对象进行包装或修改其功能。它是Context的一个间接子类可以通过继承ContextWrapper类来扩展Context的功能例如添加自定义的行为或修改Context的行为。ContextThemeWrapperContextThemeWrapper是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;}Overridepublic Resources getResources() {return mResources;}Overridepublic 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 {UnsupportedAppUsageContext mBase;public ContextWrapper(Context base) {mBase base;}Overridepublic void setTheme(int resid) {mBase.setTheme(resid);}/** hide */OverrideUnsupportedAppUsagepublic int getThemeResId() {return mBase.getThemeResId();}Overridepublic Resources.Theme getTheme() {return mBase.getTheme();}Overridepublic ClassLoader getClassLoader() {return mBase.getClassLoader();}Overridepublic String getPackageName() {return mBase.getPackageName();}}ContextThemeWrapper
ContextThemeWrapper继承自ContextWrapper从名字中可以看出该类主要是跟主题相关的包装类
public class ContextThemeWrapper extends ContextWrapper {...Overridepublic 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;}Overridepublic void setTheme(int resid) {if (mThemeResource ! resid) {mThemeResource resid;initializeTheme();}}Overridepublic Resources.Theme getTheme() {if (mTheme ! null) {return mTheme;}mThemeResource Resources.selectDefaultTheme(mThemeResource,getApplicationInfo().targetSdkVersion);initializeTheme();return mTheme;}UnsupportedAppUsageprivate 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 {... Overridepublic 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.AppCompatDelegateImplNonNull
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 nobodys 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 cant apply the configuration directly to the existing base context, so we need to// wrap it. We cant 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, well 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, well wrap the base context to ensure any method overrides or themes are left// intact. Since ThemeOverlay.AppCompat theme is empty, well get the base contexts 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 were at an invalid point in app// initialization, we dont 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 its unlikely that were keeping the base theme// anyway so maybe nobody will notice. Note that calling getTheme() will clone// the base contexts theme into the wrapped contexts 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的mBaseattachBaseContext(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.
Android 学习笔录
Android 性能优化篇https://qr18.cn/FVlo89 Android 车载篇https://qr18.cn/F05ZCM Android 逆向安全学习笔记https://qr18.cn/CQ5TcL Android Framework底层原理篇https://qr18.cn/AQpN4J Android 音视频篇https://qr18.cn/Ei3VPD Jetpack全家桶篇内含Composehttps://qr18.cn/A0gajp Kotlin 篇https://qr18.cn/CdjtAF Gradle 篇https://qr18.cn/DzrmMB OkHttp 源码解析笔记https://qr18.cn/Cw0pBD Flutter 篇https://qr18.cn/DIvKma Android 八大知识体https://qr18.cn/CyxarU Android 核心笔记https://qr21.cn/CaZQLo Android 往年面试题锦https://qr18.cn/CKV8OZ 2023年最新Android 面试题集https://qr18.cn/CgxrRy Android 车载开发岗位面试习题https://qr18.cn/FTlyCJ 音视频面试题锦https://qr18.cn/AcV6Ap