首页 > 其他分享 >Android Activity setContentView流程解析

Android Activity setContentView流程解析

时间:2023-09-27 23:06:15浏览次数:52  
标签:ViewGroup setContentView content DecorView Activity Android 方法 id

Activity setContentView流程解析

参考图解:

Android Activity setContentView流程解析_android

自主生码.jpg

1.当MainActivity直接继承自Activity时

此时会执行Activity类的setContentView方法:

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

主要的逻辑在getWindow().setContentView(layoutResID)中,下面将以实现类PhoneWindow的setContentView方法进行讲解 在该方法的开头,首先会对mContentParent进行判空检查,为空时将调用installDecor()进行DecorView的初始化:

if (mContentParent == null) {
        installDecor();
    } 

进入installDecor方法,首先当decorView为空时,会调用generateDecor方法进行DecorView的创建:

if (mDecor == null) {
        //核心方法
        mDecor = generateDecor(-1);
        //......
    } else {
        mDecor.setWindow(this);
    }

进入generateDecor方法,该方法比较简短,在完成context的创建后就创建出一个DecorView并返回:

protected DecorView generateDecor(int featureId) {
        Context context;
        //根据不同情况对context进行初始化
        //......
        return new DecorView(context, featureId, this, getAttributes());
    }

至此完成了对mDecor变量的赋值。 回到installDecor方法,此时将进入contentParent的创建逻辑,核心方法是generateLayout,该方法将对mContentParent进行赋值(这个contentParent就将作为MainActivity布局的父容器):

if (mContentParent == null) {
        //核心方法
        mContentParent = generateLayout(mDecor);
        final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                R.id.decor_content_parent);
        if (decorContentParent != null) {
            mDecorContentParent = decorContentParent;
            //......
        } 
        //...... 
    }    

进入generateLayout方法,

protected ViewGroup generateLayout(DecorView decor) {
        //......
        //上面省略的代码根据Flags、Feature等数据完成了对layoutResource的赋值
        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        //ID_ANDROID_CONTENT值为com.android.internal.R.id.content
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //......
        mDecor.finishChanging();
        return contentParent;    
    }

进入onResourcesLoaded方法,此方法主要的工作就是将layoutResource对应的xml文件解析并添加到decorView中:

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        //......
        mDecorCaptionView = createDecorCaptionView(inflater);
        final View root = inflater.inflate(layoutResource, null);
        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();
    }

此时完成了对decorView的xml布局文件的加载。 回到generateLayout方法,此时将通过findViewById获取到com.android.internal.R.id.content这个ViewGroup,上述decorView加载的xml文件里就有一个控件id与之对应,这个控件就是我们加载并添加MainActivity的布局文件的地方。 generateLayout方法完成了对mContentParent的赋值,也就是说,DecorView中放置MainActivity内容的父容器已经准备完毕。 至此,installDecor方法的核心逻辑介绍完毕,接下来将MainActivity的布局文件加载到decorView的content中,即结束了整个加载流程:

public void setContentView(int layoutResID) {
        //......
        mLayoutInflater.inflate(layoutResID, mContentParent);
        //......
    }

2.当MainActivity直接继承自AppCompatActivity时

首先,进入AppCompatActivity的setContentView方法:

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

此处的getDelegate方法会创建一个AppCompatDelegate对象,由AppCompatDelegateImpl类实现, 进入AppCompatDelegateImpl的setContentView方法:

public void setContentView(int resId) {
        ensureSubDecor();
        //......
    }

首先,进入ensureSubDecor方法:

private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            //核心方法
            mSubDecor = createSubDecor();
           //......
        }
    }

进入createSubDecor方法:

private ViewGroup createSubDecor() {
        //ensureWindow方法完成Delegate与Window的绑定,确保mWindow存在
        ensureWindow();
        mWindow.getDecorView();
        //......
    }

首先,进入PhoneWindow的getDecorView方法:

public final @NonNull View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

可以看到,此处也会与第一种情况一样调到installDecor方法,该方法会将到PhoneWindow内部的DecorView的xml文件的加载解析为止的操作全部完成 回到createSubDecor方法:

private ViewGroup createSubDecor() {
        //......
        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;
        //......
        //完成subDecor的布局解析与加载

        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);

        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);
        }
        mWindow.setContentView(subDecor);
        return subDecor;
    }

windowContentView为android.R.id.content对应的View,contentView为R.id.action_bar_activity_content对应的View,与第一种情况不同的是:它会先将android.R.id.content的子View全部迁移到R.id.action_bar_activity_content上,之后将R.id.action_bar_activity_content对应的View的id替换为android.R.id.content,之后将subDecor添加到PhoneWindow上,进入PhoneWindow的getDecorView方法(此处直接列出了最终会执行到的方法):

public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } 
        //......
        mContentParent.addView(view, params);
        //......
    }

由于之前已经完成了mContentParent的创建,所以不会再执行installDecor,将直接进行addView。 至此,将decorView添加到了PhoneWindow上,回到最初的AppCompatDelegateImpl的setContentView方法完成对MainActivity xml布局文件的加载:

public void setContentView(int resId) {
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        //......
    }

3.流程总结

1.Activity 核心就是PhoneWindow的setContentView方法,其主要干了两件事: 1.完成DecorView的创建与加载 2.将MainActivity的布局加载到DecorView内的一个ViewGroup中

创建DecorView,即installDecor方法,其内部用到了两个核心的方法: 1.generateDecor方法创建出DecorView对象 2.generateLayout方法完成这个DecorView对象的布局加载,并完成了MainActivity的父容器的赋值(即contentParent变量)

2.AppCompatActivity 核心就是AppCompatDelegateImpl的setContentView方法,它主要干了两件事: 1.准备好subDecor 2.将MainActivity的布局加载到subDecor内的一个ViewGroup中

准备subDecor,即ensureSubDecor方法,用于确保subDecor已经完成创建,内部的核心逻辑在createSubDecor方法中

createSubDecor主要干了几件事: 1.确保Delegate获取到了MainActivity的PhoneWindow实例(ensureWindow方法) 2.完成PhoneWindow内部的DecorView的创建与准备(也就是第一种情况的installDecor方法) 3.创建subDecorView,并让subDecor接管R.id.content(原先属于PhoneWindow内部的DecorView) 4.清空PhoneWindow内部的DecorView的内容,并将subDecor添加到该DecorView中 5.把MaiActivity的布局加载到subDecor中(即R.id.content)

4.差异梳理

第一种情况直接走了Activity的setContentView方法,加载用户布局也是直接用的Activity的PhoneWindow里的DecorView 第二种情况走了AppCompatDelegateImpl的setContentView方法,其PhoneWindow是从MainActivity获取的。使用了一个中介的subDecorView,PhoneWindow自身也有一个DecorView,并且完成了至DecorView的xml文件加载为止的所有操作,之后,subDecorView将被添加到该DecorView中,而原先往R.id.content的布局内容添加将全部由subDecorView进行接管,至此,方完成第二种情况正式加载MainActivity资源操作前的全部工作,而第一种情况则在PhoneWindow的DecorView xml文件加载工作完成时就已告结束。 综上所述,两者最显著的区别就是第二种情况多了一层subDecorView的添加与替换接管的操作。

标签:ViewGroup,setContentView,content,DecorView,Activity,Android,方法,id
From: https://blog.51cto.com/u_16175630/7629714

相关文章

  • 音视频?车载?这两个哪个才是Android 真的技术风口?
    前言都在说Android开发的工作不好找,行业内卷严重。21年大家都说音视频是未来的技术风口,现在又说新能源车载是未来的技术风口,那这两个到底哪个才是真正的风口?其实音视频和车载都是风口为什么这么说呢音视频现在短视频、直播占据了我们大部分的休闲时间,而AR特效,贴纸,美颜也是人手必备......
  • android签名检测和绕过
    PackageManagerService获取签名正常APP中获取PackageInfo中的签名信息是通过Binder通讯向PackageManagerService发送TRANSACTION_getPackageInfo请求,同时设置请求的参数的flag为GET_SIGNATURES。当PackageManagerService判断是TRANSACTION_getPackageInfo请求后会去调用PackageMa......
  • Android 基于共享内存跨进程实时传输大量图片
     aidl传输文件有大小1M限制,单次传输不适合传递大数据,可以使用aidl传递共享内存引用ParcelFileDescriptor方式传递图片信息。具体实现如下一、service端1.aidl文件IIpcService.aidl定义,这里主要用到pfd参数interfaceIIpcService{/***Demonstratessomebasictypes......
  • Android上层WatchDog学习笔记_2
    一、简述1.了解WatchDog的原理,可以更好的理解系统服务的运行机制。二、WatchDog实现1.代码实现位置//frameworks/base/services/core/java/com/android/server/Watchdog.javapublicclassWatchdogextendsThread{...}可见Watchdog是一个线程。2.WatchDo......
  • 手机直播源码,Android 简单的弹框
    手机直播源码,Android简单的弹框   privatestaticString[]items=newString[]{      "拍照",      "从相册中选择",  }; AlertDialog.Builderbuilder=newAlertDialog.Builder(MainActivity.this)        .setTitle(......
  • 直播app开发搭建,Android studio 图片压缩
    直播app开发搭建,Androidstudio图片压缩获取图片目录 Filefile=Environment.getExternalStorageDirectory();//获取根路径storage/emulated/0Stringpath1=file.getPath()+"/Pictures/1655215651628.jpg";//Pictures文件夹下面的1655215651628.jpg图片名 ​例   ......
  • Android Sample 之 Tab 和 Navigation
    Sample中,Tab在上,Navigation在下,后者有图标。不理解为什么用不同的名称。之前没有区分,混淆模糊。 搜索发现有人在stackoverflow问。有人答曰:区别在于 Tab是同一主题,而 Navigation可用于不相关的主题。 Tabsareconsideredtoberelatedtoeachotherwhere......
  • 【Android面试】2023最新面试专题二:ArrayList篇
    1.4 请说一说ArrayList如何保证线程安全,除了加关键字的方式?这道题想考察什么?1、ArrayList的底层原理?考察的知识点ArrayList底层的源码的理解考生如何回答ArrayList如何保证线程安全继承Arraylist,然后重写或按需求编写自己的方法,这些方法要写成synchronized,在这些synchronized的......
  • Android动画-1——Android三种动画详解
    一、前言1.Android的三种动画,即:ViewAnimation(视图动画)DrawableAnimation(帧动画)PropertyAnimation(属性动画) 二、ViewAnimation(视图动画)1.View动画的概述及种类视图动画的作用对象是View,支持四种动画效果,分别是平移、缩放、旋转、透明度。譬如,我们可以对TextView设......
  • android adb调试
    1、无线方式一cd/dD:\Android\sdk\platform-toolstcpip5555(需手机线下连接执行)adbconnect192.168.0.1402、无线方式二cd/dD:\Android\sdk\platform-toolsadbpair192.168.0.140:42319adbconnect192.168.0.140:40717 ......