View中的Context是哪里来的

本文深入解析了Android中View的Context生成过程,从Activity的setContentView()方法入手,逐步剖析了DecorView的创建流程及View的初始化过程,最终揭示了View中的Context如何被创建。

前言

在自定义View中我们经常通过getContext()方法来使用Context获取资源、设置样式等。那么大家有没有想过View中getContext()获取的Context是来自哪里的?接下来就分析View中Context的生成过程。

View的Context

我们都知道在View中有个getContext()方法供开发者使用。

   /**
     * Returns the context the view is running in, through which it can
     * access the current theme, resources, etc.
     *
     * @return The view's Context.
     */
    @ViewDebug.CapturedViewProperty
    public final Context getContext() {
        return mContext;
    }

在上面的注释中可以看出,通过View中的Context是View正在运行的上下文环境中,并且可以通过Context获取资源和主题等。根据这些信息我们可以判断View中的Context是一个ContextThemeWrapper类型的Context。

View的Context初始化

在获取到上面的信息后,我们可以知道View的Context是一个ContextThemeWrapper类型的Context,并且在View的构造方法中也可以看到Context被赋值。根据这些信息我们可以判断View在初始化的时候Context被赋值,所以接下来从Activity的setContentView()开始分析。

在Activity的setContentView()方法中View会被创建以及绘制。

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

可以看到在Activity的setContentView()方法中调用了Window的setContentView()方法。我们知道这个Window就是PhoneWindow,接下来直接在PhoneWindow中分析setContentView()方法。

public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor(); //首先初始化DecorView
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    //.........
}

可以看到再说上面的代码中首先初始化了DecorView也就是顶级View。接下来看下DecorView是如何初始化的。

private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1); //创建DecorView
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor); //生成布局
     }
    //........
}

protected DecorView generateDecor(int featureId) {
    Context context;
    if (mUseDecorContext) {
        Context applicationContext = getContext().getApplicationContext();
        if (applicationContext == null) {
            context = getContext();
        } else {
            context = new DecorContext(applicationContext, getContext().getResources());
            if (mTheme != -1) {
                context.setTheme(mTheme);
            }
        }
    } else {
        context = getContext();
    }
    return new DecorView(context, featureId, this, getAttributes());
}

从上面的代码中可以看到,在创建DecorView的过程中,首先会创建一个DecorContext。这个类型的Context就是DecorView中要使用的Context,可以看到这个Context是可以操作主题的。如果Context是空,就使用Window的Context,而Window的Context来自于Activity。

在DecorView创建完成后,接下来就是生成布局。这里就开始了View的初始化过程

protected ViewGroup generateLayout(DecorView decor) {
      //......
    if (mIsFloating) {
        setLayout(WRAP_CONTENT, WRAP_CONTENT);
        setFlags(0, flagsToUpdate);
    } else {
        setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
    }
    //......
    mDecor.startChanging();
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);  //生成布局
    //......
    mDecor.finishChanging();
	return contentParent;
}

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
    mDecorCaptionView = createDecorCaptionView(inflater);
    final View root = inflater.inflate(layoutResource, null);  //初始化View
    if (mDecorCaptionView != null) {
        if (mDecorCaptionView.getParent() == null) {
            addView(mDecorCaptionView,
                 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mDecorCaptionView.addView(root,
            new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
    } else {
        addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
    mContentRoot = (ViewGroup) root;
    initializeElevation();
}

在上面的代码中可以看出,布局是从LayoutInflater的inflate开始的。

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            //......
           // Inflate all children under temp against its context.
           rInflateChildren(parser, temp, attrs, true);
            //......
            return result;
        }
    }

void rInflate(XmlPullParser parser, View parent, Context context,
            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

        final int depth = parser.getDepth();
        int type;
        boolean pendingRequestFocus = false;

        while (((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
            //......
            final String name = parser.getName();
            if (TAG_REQUEST_FOCUS.equals(name)) {
                pendingRequestFocus = true;
                consumeChildElements(parser);
            } else if (TAG_TAG.equals(name)) {
                //通过tag创建
                parseViewTag(parser, parent, attrs);
            } else if (TAG_INCLUDE.equals(name)) {
                if (parser.getDepth() == 0) {
                    throw new InflateException("<include /> cannot be the root element");
                }
                // include 标签
                parseInclude(parser, context, parent, attrs);
            } else if (TAG_MERGE.equals(name)) {
                throw new InflateException("<merge /> must be the root element");
            } else {
                //创建View
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }
        }
    }

一路跟代码下去,我们可以看到在rInflate()方法中根据布局XML文件开始递归的创建View树。在这个过程中每个View都会被创建。可以看到在createViewFromTag(parent, name, context, attrs)方法中使用了Context的参数,接下来继续分析。

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }

        // Apply a theme wrapper, if allowed and one is specified.
        if (!ignoreThemeAttr) {
            //这里为View创建了一个ContextThemeWrapper类型的Context
            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
            final int themeResId = ta.getResourceId(0, 0);
            if (themeResId != 0) {
                context = new ContextThemeWrapper(context, themeResId);
            }
            ta.recycle();
        }

        if (name.equals(TAG_1995)) {
            // Let's party like it's 1995!
            return new BlinkLayout(context, attrs);
        }
        try {
            View view;
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }

            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }
            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } 
    }

从上面的代码中我们就找到了答案,View的Context在这里被创建。

总结

在上面的分析中,我们可以知道View中的Context是ContextThemeWrapper类型的。在View被绘制之前的初始化过程中被创建。当然根据Android的特性View的Context也得是一个ContextThemeWrapper类型的,因为View中也涉及到了Theme的操作。

<think>首先,用户的问题是:“我想了解在安卓中ViewContext的具体含义”。用户引用了三个站内引用,我需要参考这些引用来回答问题。 回顾用户提供的引用: - 引用[1]:解释了ContextAndroid中是一个抽象类,有子类如Activity、Service、Application等。 - 引用[2]:提到在Activity中创建的ViewModel,可以通过Activity Context在其他地方获取。 - 引用[3]:描述了AndroidView的事件分发机制,涉及dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。 用户的问题聚焦在“ViewContext的具体含义”,所以我需要解释在ViewContext的含义。 关键点: - 在Android中,Context是应用程序环境的接口。它提供了访问应用资源、启动组件、获取系统服务等的方法。 - 对于View,每个View对象都有一个Context成员变量。这个Context通常是创建View时传递的Context。 - 在View的构造函数中,通常会有一个Context参数。例如,在自定义View时,你需要调用`super(context)`来传递Context给父类。 - 这个Context允许View访问应用资源(如字符串、颜色、尺寸)、启动Activity、访问系统服务(如布局inflater)等。 根据引用[1],Context是一个抽象类,子类包括Activity、Service、Application等。所以,对于ViewContext通常是Activity,因为View通常在Activity中创建。 引用[2]提到ViewModel可以通过Activity Context获取,这间接支持Context的重要性,因为它允许在不同组件间共享数据。 引用[3]是关于事件分发机制的,没有直接提到Context,但可以联系起来:事件分发从Activity开始(Activity的dispatchTouchEvent),Activity本身就是Context。 所以,回答应该结构清晰: 1. **定义Context**:解释Context是什么。 2. **View中的Context**:解释在ViewContext的具体含义,包括如何获取和使用。 3. **实际应用**:给出例子或场景。 4. **注意事项**:比如Context的生命周期管理等。 系统指令: - 使用中文回答。 - 生成相关问题。 - 所有行内数学表达式用$...$,但这里可能没有数学内容,所以忽略。 - 独立公式用$$...$$,也没有。 - LaTeX语法正确,但可能不涉及。 - 引用的段落末尾添加引用标识,如[^1]。 用户提到了“参考站内引用”,所以我应该在回答中引用这些引用。 组织回答结构: - 先直接回答问题。 - 然后解释细节。 - 最后生成相关问题。 最终回答: - **Title或开头**:解释ViewContext。 - **段落**:清晰、步骤化。 - **结尾**:生成相关问题。 具体内容: 1. 在Android中,Context代表应用上下文,提供访问资源、启动组件等接口。[^1] 2. 对于ViewContext是创建View时传递的上下文对象。通常在Activity中创建View时,Context就是Activity本身。View通过Context访问应用资源(如`getResources()`)、inflate布局等。 3. 例如,在自定义View时,构造函数需要Context参数:`public MyView(Context context) { super(context); }`。这样,View就能使用Context的功能。 4. 注意:Context的生命周期很重要。如果View持有Activity Context可能导致内存泄漏,特别是当Context被销毁时。[^2] 在使用ViewModel时,可以利用Activity Context来共享数据。[^3] 生成相关问题: - 类似于示例,生成3个相关问题。 例如: §§相关问题§§ 1. 如何在View中正确使用Context避免内存泄漏? 2. ContextAndroid中的主要功能有哪些? 3. View的事件分发机制如何与Context交互? [基于引用[3]] 确保语言自然、流畅。 </think>### AndroidViewContext的具体含义 在Android开发中,View的`Context`是一个核心概念,它代表应用程序的上下文环境,为View提供访问系统服务和资源的接口。下面我将逐步解释其具体含义、作用以及实际应用场景,确保回答清晰可靠。 #### 1. **Context的基本定义** - Context是一个抽象类(来自`android.content.Context`),它定义了应用程序环境的基本接口。[^1] 它可以访问应用资源(如字符串、布局文件)、启动组件(如Activity或Service)、获取系统服务(如布局inflater或窗口管理器)等。 - 在Android中,Context有多种具体实现子类: - Activity:代表一个屏幕界面。 - Service:用于后台任务。 - Application:代表整个应用的生命周期。 - 对于View而言,Context通常是创建该View的Activity上下文(因为View通常在Activity中实例化)。[^1] #### 2. **View中的Context具体含义** - **创建View时的Context传递**: - Android中每个View对象(如Button、TextView或自定义View)都持有一个Context实例。这个Context是在View构造函数中传入的。例如,自定义View时,标准代码如下: ```java public class CustomView extends View { public CustomView(Context context) { super(context); // 传递Context给父类 // 此时,this.getContext() 返回传入的Context对象 } } ``` - 这里传入的Context通常是Activity(如`new CustomView(this)`在Activity中被调用),它为View提供运行环境。[^1] - **ContextView中的作用**: - **资源访问**:View通过Context获取应用资源,例如使用`context.getResources().getString(R.string.app_name)`获取字符串资源。 - **布局inflater**:View加载布局文件时需Context,如`LayoutInflater.from(context).inflate(R.layout.my_layout, null)`。 - **系统服务集成**:View可以调用`context.getSystemService(Context.WINDOW_SERVICE)`获取窗口管理器等服务。 - **事件分发基础**:在View的事件分发机制中(如`dispatchTouchEvent`或`onTouchEvent`),事件传递的起点是Activity的Context(Activity本身就是Context),事件从Activity传递到PhoneWindow、DecorView,最终到ViewGroup或View。[^3] - **ViewModel共享**:Context允许在不同的UI组件(如Activity、Fragment或View)中共享ViewModel实例。例如,在Activity中创建ViewModel后,任何持有该Activity ContextView都可以访问它。[^2] - **生命周期注意事项**: - ViewContext通常绑定到其宿主Activity的生命周期。如果View持有Context引用过长(如静态引用),可能导致内存泄漏(当Activity销毁时Context无法回收)。因此,推荐避免在View中持有静态Context。 - 在自定义View时,Context应仅用于短期操作(如inflate布局),或使用Application Context(通过`context.getApplicationContext()`)来避免生命周期问题。[^1][^2] #### 3. **实际应用示例** - **场景**:在一个Activity中创建Button View。 - Context传递:Activity的`onCreate()`方法中调用`Button button = new Button(this)`,这里的`this`就是Activity Context。 - 资源使用:Button通过`getContext().getResources()`加载文本或颜色。 - **代码片段**: ```java public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 创建Button,传入Activity Context Button button = new Button(this); button.setText("Click Me"); // 通过Context获取资源 String text = getResources().getString(R.string.button_text); } } ``` - 如果Button需要在自定义View中使用,Context同样通过构造函数传递,确保View能独立访问环境资源。 #### 总结 ViewContext是其运行时环境的“门户”,它使View能够与Android系统和应用互动。核心是:ContextView提供资源和服务,通常是创建View的Activity实例。正确使用Context能提升性能和稳定性。[^1][^2][^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值