美文网首页
Android View的加载流程(Api 30)

Android View的加载流程(Api 30)

作者: BubbleCat | 来源:发表于2021-11-04 15:40 被阅读0次

    我们都知道Android的界面是通过xml编写的 然后通过Activity 加载xml的layout文件进行显示,那么怎么从xml加载到我们的Activity呢?
    下面我们一步步了解view的加载流程 ,看看到底view是怎么创建出来的

    一、Android的界面显示层次

    image.png

    二、源码分析-View的加载

    1、故事要从setContentView开始

    在我们的Activity点进setContentView方法 我们看到 里面实际上会调用window的setContentView 方法进行layout的解析

        /**
         * Set the activity content from a layout resource.  The resource will be
         * inflated, adding all top-level views to the activity.
         *
         * @param layoutResID Resource ID to be inflated.
         *
         * @see #setContentView(android.view.View)
         * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
         */
        public void setContentView(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
    

    2、我们再看看window的setContentView干了什么

    1.PhoneWindow,可以看到,当mContentParent ==null时,调用installDecor();

     @Override
        public void setContentView(int layoutResID) {
            if (mContentParent == null) {
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (mDecor == null) {
                mDecor = generateDecor(-1);
            } else {
                mDecor.setWindow(this);
            }
    

    generateDecor帮我们new了一个DecorView

        protected DecorView generateDecor(int featureId) {
            return new DecorView(context, featureId, this, getAttributes());
        }
    

    再往下 mDecor创建好了以后 通过 generateLayout 创建了一个mContentParent

     if (mContentParent == null) {
                mContentParent = generateLayout(mDecor);
    }
    

    generateLayout 中通过我们配置的各种参数进行判断加载哪个根布局,然后从ID_ANDROID_CONTENT 找到contentParent 并返回
    而ID_ANDROID_CONTENT 就是android.R.id.content,也就是上图中的最里面一层是一个FrameLayout

        /**
         * The ID that the main layout in the XML layout file should have.
         */
        public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
    
        protected ViewGroup generateLayout(DecorView decor) {
           // ......省略部分代码
           // xxxx
           if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
                layoutResource = R.layout.screen_simple_overlay_action_mode;
            } else {
                layoutResource = R.layout.screen_simple;
            
            }
            mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
            ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT);
            if (contentParent == null) {
                throw new RuntimeException("Window couldn't find content container view");
            }
         }
    

    接着我们看我们传入的layoutId在哪里使用,mContentParent在上面已经创建完成,而我们的layout就添加到mContentParent上面,最后调用LayoutInflater .inflate 添加我们的layout

        @Override
        public void setContentView(int layoutResID) {
          
            if (mContentParent == null) {
                installDecor();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
              //xxx
            } else {
                mLayoutInflater.inflate(layoutResID, mContentParent);
            }
            ...
    

    3.LayoutInflater ,接着来到LayoutInflater 中的inflate方法 我们发现最终都会来到这个方法:

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) 
    

    并且解析属性

    
                final Context inflaterContext = mContext;
                final AttributeSet attrs = Xml.asAttributeSet(parser);
                Context lastContext = (Context) mConstructorArgs[0];
                mConstructorArgs[0] = inflaterContext;
                View result = root;
    

    然后通过createViewFromTag 创建view

      if (TAG_MERGE.equals(name)) {
            rInflate(parser, root, inflaterContext, attrs, false);
       } else {                  
            final View temp = createViewFromTag(root, name, inflaterContext, attrs);
            ViewGroup.LayoutParams params = null;       
       }
    

    createViewFromTag 中经过条件判断 ,最终会调用createView() 反射创建最终我们得到的view

    
        public final View createView(@NonNull Context viewContext, @NonNull String name,
                                     @Nullable String prefix, @Nullable AttributeSet attrs)
                throws ClassNotFoundException, InflateException {
            Constructor<? extends View> constructor = sConstructorMap.get(name);
            Class<? extends View> clazz = null;
            if (constructor == null) {
                      clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                sConstructorMap.put(name, constructor);
            }
            Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = viewContext;
            Object[] args = mConstructorArgs;
            args[1] = attrs;
            try {
                final View view = constructor.newInstance(args);
                return view;
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        }
    

    到这里我们的view就创建出来了

    三、源码分析-资源的加载

    1、application的创建

     private void handleBindApplication(AppBindData data) {
            mInstrumentation = new Instrumentation();
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    }
    

    先创建了个appContext 然后通过mInstrumentation 把application创建出来

     public Application makeApplication(boolean forceDefaultAppClass,
                Instrumentation instrumentation) {
                    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    
                NetworkSecurityConfigProvider.handleNewApplication(appContext);
                app = mActivityThread.mInstrumentation.newApplication(
                        cl, appClass, appContext);
    }
    

    2、context的创建

    进入createAppContext(), 创建了个ContextImpl,给它设置recourse这个recourse就是要加载的资源

     static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
                String opPackageName) {
         ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
                    0, null, opPackageName);
            context.setResources(packageInfo.getResources());
         return context;
    }
    

    3、Resources的创建

    进入LoadedApk.getResources(), 经过条件判断,通过ResourcesManager.getResources()

     public Resources getResources() {
            if (mResources == null) {
                final String[] splitPaths;
                   mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                        splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
                        Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                        getClassLoader(), null);
            }
            return mResources;
        }
    

    继续进入getRecourse,通过findOrCreateResourcesImplForKeyLocked方法获取到资源

    private @Nullable Resources createResources(@Nullable IBinder activityToken,@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) synchronized (this) {
                  ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key);           
        }
    

    然后判断空 为空 调用createResourcesImpl 创建资源

     private @Nullable ResourcesImpl findOrCreateResourcesImplForKeyLocked(
                @NonNull ResourcesKey key) {
            ResourcesImpl impl = findResourcesImplForKeyLocked(key);
            if (impl == null) {
                impl = createResourcesImpl(key);
                if (impl != null) {
                    mResourceImpls.put(key, new WeakReference<>(impl));
                }
            }
            return impl;
        }
    

    进入createResourcesImpl 创建了个AssetManager 资源即是通过AssetManager 获取

      private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
            final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
            daj.setCompatibilityInfo(key.mCompatInfo);
            final AssetManager assets = createAssetManager(key);
            if (assets == null) {
                return null;
            }
            final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
            final Configuration config = generateConfig(key, dm);
            final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
    
            return impl;
        }
    

    =创建出AssetManager后调用addApkAssets 添加不同的资源

     protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
            final AssetManager.Builder builder = new AssetManager.Builder();
            // already.
            if (key.mResDir != null) {
                    builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/,
            }
    
    
           if (key.mSplitResDirs != null) {
                for (final String splitResDir : key.mSplitResDirs) {
                        builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/, false /*overlay*/));              
                }
            }
    
            if (key.mLibDirs != null) {
                for (final String libDir : key.mLibDirs) {        
                            builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/,
                                    false /*overlay*/));              
                }
            }
     return builder.build();
    

    最后 调用 AssetManager.Builder.build把system资源、loader资源、user资源等整合在一起 通过AssetManager.nativeSetApkAssets() 加载资源

        public AssetManager build() {
            final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
            final ArrayList<ApkAssets> loaderApkAssets = new ArrayList<>();
            final ArraySet<ApkAssets> uniqueLoaderApkAssets = new ArraySet<>();
            for (int i = mLoaders.size() - 1; i >= 0; i--) {
                final List<ApkAssets> currentLoaderApkAssets = mLoaders.get(i).getApkAssets();
                for (int j = currentLoaderApkAssets.size() - 1; j >= 0; j--) {
                    final ApkAssets apkAssets = currentLoaderApkAssets.get(j);
                    if (uniqueLoaderApkAssets.add(apkAssets)) {
                        loaderApkAssets.add(0, apkAssets);
                    }
                }
            }
            final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size()
                    + loaderApkAssets.size();
            final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount];
            System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length);
    
            for (int i = 0, n = mUserApkAssets.size(); i < n; i++) {
                apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i);
            }
            for (int i = 0, n = loaderApkAssets.size(); i < n; i++) {
                apkAssets[i + systemApkAssets.length  + mUserApkAssets.size()] =
                        loaderApkAssets.get(i);
            }
            final AssetManager assetManager = new AssetManager(false /*sentinel*/);
            assetManager.mApkAssets = apkAssets;
            AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
    
            return assetManager;
        }
    

    相关文章

      网友评论

          本文标题:Android View的加载流程(Api 30)

          本文链接:https://www.haomeiwen.com/subject/zoopoltx.html