Android应用Context详解及源码解析,android游戏开发入门视频

本文详细剖析了Android中Context的实现(ContextImpl)、Activity、Service和Application的继承关系,以及Context的实例化过程。重点讲解了不同组件的Context数量和资源访问的一致性。通过源码解析,揭示了getApplication与getApplicationContext的差异,并总结了关键技术和标签.

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**

  • Proxying implementation of Context that simply delegates all of its calls to

  • another Context. Can be subclassed to modify behavior without changing

  • the original Context.

*/

public class ContextWrapper extends Context {

Context mBase;

public ContextWrapper(Context base) {

mBase = base;

}

/**

  • Set the base context for this ContextWrapper. All calls will then be

  • delegated to the base context. Throws

  • IllegalStateException if a base context has already been set.

  • @param base The new base context for this wrapper.

*/

protected void attachBaseContext(Context base) {

if (mBase != null) {

throw new IllegalStateException(“Base context already set”);

}

mBase = base;

}

}

该类的构造函数包含了一个真正的Context引用(ContextImpl对象),然后就变成了ContextImpl的装饰着模式。

再来看看ContextWrapper的子类ContextThemeWrapper源码注释:

/**

  • A ContextWrapper that allows you to modify the theme from what is in the

  • wrapped context.

*/

public class ContextThemeWrapper extends ContextWrapper {

}

该类内部包含了主题Theme相关的接口,即android:theme属性指定的。

再来看看Activity、Service、Application类的继承关系源码:

public class Activity extends ContextThemeWrapper

implements LayoutInflater.Factory2,

Window.Callback, KeyEvent.Callback,

OnCreateContextMenuListener, ComponentCallbacks2,

Window.OnWindowDismissedCallback {

}

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {

}

public class Application extends ContextWrapper implements ComponentCallbacks2 {

}

看见没有?他们完全符合上面我们绘制的结构图与概述。

2-3 解决应用Context个数疑惑

有了上面的Context继承关系验证与分析之后我们来看下一个应用程序到底有多个Context?

Android应用程序只有四大组件,而其中两大组件都继承自Context,另外每个应用程序还有一个全局的Application对象。所以在我们了解了上面继承关系之后我们就可以计算出来Context总数,如下:

APP Context总数 = Application数(1) + Activity数(Customer) + Service数(Customer);

到此,我们也明确了Context是啥,继承关系是啥样,应用中Context个数是多少的问题。接下来就有必要继续深入分析这些Context都是怎么来的。

【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】

3 各种Context在ActivityThread中实例化过程源码分析


在开始分析之前还是和《Android异步消息处理机制详解及源码分析》的3-1-2小节及《Android应用setContentView与LayoutInflater加载解析机制源码分析》的2-6小节一样直接先给出关于Activity启动的一些概念,后面会写文章分析这一过程。

Context的实现是ContextImpl,Activity与Application和Service的创建都是在ActivityThread中完成的,至于在ActivityThread何时、怎样调运的关系后面会写文章分析,这里先直接给出结论,因为我们分析的重点是Context过程。

3-1 Activity中ContextImpl实例化源码分析

通过startActivity启动一个新的Activity时系统会回调ActivityThread的handleLaunchActivity()方法,该方法内部会调用performLaunchActivity()方法去创建一个Activity实例,然后回调Activity的onCreate()等方法。所以Activity的ContextImpl实例化是在ActivityThread类的performLaunchActivity方法中,如下:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

//已经创建好新的activity实例

if (activity != null) {

//创建一个Context对象

Context appContext = createBaseContextForActivity(r, activity);

//将上面创建的appContext传入到activity的attach方法

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);

}

return activity;

}

看见上面performLaunchActivity的核心代码了吗?通过createBaseContextForActivity(r, activity);创建appContext,然后通过activity.attach设置值。

具体我们先看下createBaseContextForActivity方法源码,如下:

private Context createBaseContextForActivity(ActivityClientRecord r,

final Activity activity) {

//实质就是new一个ContextImpl对象,调运ContextImpl的有参构造初始化一些参数

ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);

//特别特别留意这里!!!

//ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Activity对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Activity)。

appContext.setOuterContext(activity);

//创建返回值并且赋值

Context baseContext = appContext;

//返回ContextImpl对象

return baseContext;

}

再来看看activity.attach,也就是Activity中的attach方法,如下:

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, String referrer, IVoiceInteractor voiceInteractor) {

//特别特别留意这里!!!

//与上面createBaseContextForActivity方法中setOuterContext语句类似,不同的在于:

//通过ContextThemeWrapper类的attachBaseContext方法,将createBaseContextForActivity中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Context的实现类ContextImpl

attachBaseContext(context);

}

通过上面Activity的Context实例化分析再结合上面Context继承关系可以看出:

Activity通过ContextWrapper的成员mBase来引用了一个ContextImpl对象,这样,Activity组件以后就可以通过这个ContextImpl对象来执行一些具体的操作(启动Service等);同时ContextImpl类又通过自己的成员mOuterContext引用了与它关联的Activity,这样ContextImpl类也可以操作Activity。

SO,由此说明一个Activity就有一个Context,而且生命周期和Activity类相同(记住这句话,写应用就可以避免一些低级的内存泄漏问题)。

3-2 Service中ContextImpl实例化源码分析

写APP时我们通过startService或者bindService方法创建一个新Service时就会回调ActivityThread类的handleCreateService()方法完成相关数据操作(具体关于ActivityThread调运handleCreateService时机等细节分析与上面Activity雷同,后边文章会做分析)。具体handleCreateService方法代码如下:

private void handleCreateService(CreateServiceData data) {

//类似上面Activity的创建,这里创建service对象实例

Service service = null;

try {

java.lang.ClassLoader cl = packageInfo.getClassLoader();

service = (Service) cl.loadClass(data.info.name).newInstance();

} catch (Exception e) {

}

try {

//不做过多解释,创建一个Context对象

ContextImpl context = ContextImpl.createAppContext(this, packageInfo);

//特别特别留意这里!!!

//ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Service对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Service)。

context.setOuterContext(service);

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

//将上面创建的context传入到service的attach方法

service.attach(context, this, data.info.name, data.token, app,

ActivityManagerNative.getDefault());

service.onCreate();

} catch (Exception e) {

}

}

再来看看service.attach,也就是Service中的attach方法,如下:

public final void attach(

Context context,

ActivityThread thread, String className, IBinder token,

Application application, Object activityManager) {

//特别特别留意这里!!!

//与上面handleCreateService方法中setOuterContext语句类似,不同的在于:

//通过ContextWrapper类的attachBaseContext方法,将handleCreateService中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Context的实现类ContextImpl

attachBaseContext(context);

}

可以看出步骤流程和Activity的类似,只是实现细节略有不同而已。

SO,由此说明一个Service就有一个Context,而且生命周期和Service类相同(记住这句话,写应用就可以避免一些低级的内存泄漏问题)。

3-3 Application中ContextImpl实例化源码分析

当我们写好一个APP以后每次重新启动时都会首先创建Application对象(每个APP都有一个唯一的全局Application对象,与整个APP的生命周期相同)。创建Application的过程也在ActivityThread类的handleBindApplication()方法完成相关数据操作(具体关于ActivityThread调运handleBindApplication时机等细节分析与上面Activity雷同,后边文章会做分析)。而ContextImpl的创建是在该方法中调运LoadedApk类的makeApplication方法中实现,LoadedApk类的makeApplication()方法中源代码如下:

public Application makeApplication(boolean forceDefaultAppClass,

Instrumentation instrumentation) {

//只有新创建的APP才会走if代码块之后的剩余逻辑

if (mApplication != null) {

return mApplication;

}

//即将创建的Application对象

Application app = null;

String appClass = mApplicationInfo.className;

if (forceDefaultAppClass || (appClass == null)) {

appClass = “android.app.Application”;

}

try {

java.lang.ClassLoader cl = getClassLoader();

if (!mPackageName.equals(“android”)) {

initializeJavaContextClassLoader();

}

//不做过多解释,创建一个Context对象

ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

//将Context传入Instrumentation类的newApplication方法

app = mActivityThread.mInstrumentation.newApplication(

cl, appClass, appContext);

//特别特别留意这里!!!

//ContextImpl中有一个Context的成员叫mOuterContext,通过这条语句就可将当前新Application对象赋值到创建的ContextImpl的成员mOuterContext(也就是让ContextImpl内部持有Application)。

appContext.setOuterContext(app);

} catch (Exception e) {

}

return app;

}

接着看看Instrumentation.newApplication方法。如下源码:

public Application newApplication(ClassLoader cl, String className, Context context)

throws InstantiationException, IllegalAccessException,

ClassNotFoundException {

return newApplication(cl.loadClass(className), context);

}

继续看重载两个参数的newApplication方法,如下:

static public Application newApplication(Class<?> clazz, Context context)

throws InstantiationException, IllegalAccessException,

ClassNotFoundException {

//继续传递context

app.attach(context);

return app;

}

继续看下Application类的attach方法,如下:

final void attach(Context context) {

//特别特别留意这里!!!

//与上面makeApplication方法中setOuterContext语句类似,不同的在于:

//通过ContextWrapper类的attachBaseContext方法,将makeApplication中实例化的ContextImpl对象传入到ContextWrapper类的mBase变量,这样ContextWrapper(Context子类)类的成员mBase就被实例化为Application的实现类ContextImpl

attachBaseContext(context);

}

可以看出步骤流程和Activity的类似,只是实现细节略有不同而已。

SO,由此说明一个Application就有一个Context,而且生命周期和Application类相同(然而一个App只有一个Application,而且与应用生命周期相同)。

4 应用程序APP各种Context访问资源的唯一性分析


你可能会有疑问,这么多Context都是不同实例,那么我们平时写App时通过context.getResources得到资源是不是就不是同一份呢?下面我们从源码来分析下,如下:

class ContextImpl extends Context {

private final ResourcesManager mResourcesManager;

private final Resources mResources;

@Override

public Resources getResources() {

return mResources;

}

}

看见没,有了上面分析我们可以很确定平时写的App中context.getResources方法获得的Resources对象就是上面ContextImpl的成员变量mResources。那我们追踪可以发现mResources的赋值操作如下:

private ContextImpl(ContextImpl container, ActivityThread mainThread,

LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,

Display display, Configuration overrideConfiguration) {

//单例模式获取ResourcesManager对象

mResourcesManager = ResourcesManager.getInstance();

//packageInfo对于一个APP来说只有一个,所以resources 是同一份

Resources resources = packageInfo.getResources(mainThread);

if (resources != null) {

if (activityToken != null

|| displayId != Display.DEFAULT_DISPLAY

|| overrideConfiguration != null

|| (compatInfo != null && compatInfo.applicationScale

!= resources.getCompatibilityInfo().applicationScale)) {

//mResourcesManager是单例,所以resources是同一份

resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),

packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),

packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,

overrideConfiguration, compatInfo, activityToken);

}

}

//把resources赋值给mResources

mResources = resources;

}

由此可以看出在设备其他因素不变的情况下我们通过不同的Context实例得到的Resources是同一套资源。

PS一句,同样的分析方法也可以发现Context类的packageInfo对于一个应用来说也只有一份。感兴趣可以自行分析。

5 应用程序APP各种Context使用区分源码分析


5-1 先来解决getApplication和getApplicationContext的区别

很多人一直区分不开这两个方法的区别,这里从源码来分析一下,如下:

Android应用源码安卓源码(172个合集),可以做为你的学习参考。 365MobileSecretary v1.0.6(365手机助手AIDL) 888个经典 Logo.rar AdXmpp(Openfire+asmack+spark) .zip AidlDemo(简单aidl的例子) aidl跨进程调用.rar andbatdog电池监控.rar andbatdog监视电池.rar andricoFacebook客户端.rar Android Gamex分析报告.rar Android 英语单词记忆程序源码 AndroidPdfViewerPDF查看器.zip AndroidPlayer(仿酷狗播放器).tar androidtalk_2010_11_17【Sundy系列】全看懂了-加两年经验-语音朗读-语音识别-语音.rar Android下的信息客户端 WhisperSystems-TextSecure.zip Android与js交互.rar Android中监听电话状态.rar Android之Wifi学习教程.rar Android之用PopupWindow实现弹出菜单.rar android在wifi下手机与电脑的socket通信.rar android多线程断点下载.rar Android手机一键Root原理分析.zip Android手机的VoIP客户端 Sipdroid.rar Android有未接来电后处理(判断未接来电).rar Android模仿乐淘的应用程序分析源码.zip Android游戏源码——忍者快跑.rar Android自动发送短信.rar Android自动开关机实现.rar Android视频采集+RTSP完整代码(可用) Android远程登录含有loading登录效.zip Angle v1.0_2D游戏引擎.ZIP BOOK看遍所有UI控件.7z BrewClock闹钟.zip BTAndroidWebViewSelection(webview选择文字) cellmap v2.0 基站查询定位导航系统 .rar DialogShow.rar dialog去除边框代码.rar DocumentViewer(PDF阅读器) douBanList(滚动到底部加载新的,软缓存,懒加载) Droid Wall 手机防火墙.zip FBReader修改epub快速加载.rar FiveChess五子棋.zip Flashlight灯光.zip GetSDTree(简单SD卡文件浏览器) hotel宾馆系统.zip ImageView 图片循环跑马灯的效果.rar ipcamera-for-android 手机变成IP Camera.rar jamendo-开源在线音乐.rar jchat4android手机聊天程序.rar LoginXml.rar MineSweeper由java实现.zip miniTwitter登录界面.rar MyAppWeixin(仿微信界面) MyBrowser(简单网页浏览器) Myjob3(图片剪辑功能).rar OPENG开发的示例代码.rar OpenSudoku一个简单的九宫格数独游戏.zip OssSystem(OA系统图书管理简单版).rar Phonegap+HTML5+CSS3+jQuer简单界面模板示例及源码.rar ProgressBar 几乎全部的用法.rar QQ_UI之分类菜单DEMO.zip QQ的登录界面 源代码 .zip quitesleep手机电话功能软件.rar rokon_src_2-0-3_游戏引擎.zip scientific-calculator-for-android( 功能强大的科学计算器).zip SeeJoPlayer(播放器).7z SipDroid客户端源码.rar sipdroid语音及视频通话.rar tablelogin(登陆界面).rar TankWar坦克大战.zip telecapoland-jamendo-android-6cd07fb(国外开源音乐播放器) TorProxy应用实现了Android手机无线电电传通讯(TOR).rar UI设计之 仿做蘑菇街UI设计 源码.zip ViewPager-实现左右两个屏幕的切换.rar VIEW双缓冲与SurfaceView比较.zip weibo4andriod-2011-01-14.zip WordPress for Android zirco-browser浏览器源码.rar Zirco-browser:超越海豚的开源浏览器.rar zz-doctor中医大夫助
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值