Android资源加载流程

Android资源加载流程

从使用到原理

使用

首先来看一个从资源string获取字符串的使用

public class ResourceActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    public void getString(){
        String appName = getResources().getString(R.string.app_name);
    }
}

深入到getResources()分析

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
        TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
    //.....省略
    @Override
    public Resources getResources() {
        if (mResources == null && VectorEnabledTintResources.shouldBeUsed()) {
            mResources = new VectorEnabledTintResources(this, super.getResources());
        }
        return mResources == null ? super.getResources() : mResources;
    }

    //.....省略
}

不管mResources是否为null其实都会调用super.getResources()方法,而super.getResources()对应的父类就是ContextThemeWrapper

public class ContextThemeWrapper extends ContextWrapper {

    @Override
    public Resources getResources() {
        return getResourcesInternal();
    }

    private Resources getResourcesInternal() {
        //首次调用mResources为null,初始化
        if (mResources == null) {
            //如果没有重写了配置,调用父类获取
            //重写配置的条件   
            //(sdk in 21-25 || 黑夜模式切换 )&& Manifest没有处理UI模式 && sdk>=17 && 没有调用attachBaseContext && Activity继承ContextThemeWrapper
            if (mOverrideConfiguration == null) {
                mResources = super.getResources();
            } else {
                //重写了配置,就使用新配置信息获取
                final Context resContext = createConfigurationContext(mOverrideConfiguration);
                mResources = resContext.getResources();
            }
        }
        return mResources;
    }
}


public class ContextWrapper extends Context {
    @Override
    public Resources getResources() {
        //当前的mBase就是ContextImpl
        return mBase.getResources();
    }
}

mBase是什么时候设置的

ActivityThread#performLaunchActivity()中在每启动一个新Activity的时候都会创建对应的ContextImpl

ActivityThread.java
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        
        //···省略非关键代码
        
        //创建activity
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            //获取创建Application
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            //···省略无关代码

            if (activity != null) {
                //···省略无关代码
                appContext.setOuterContext(activity);
                //将appContext(ContextImpl)设置进入Activity
                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, window, r.configCallback,
                        r.assistToken);
            //···省略无关代码
            }
        return activity;
    }

    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        ·····省略无关代码
        //创建Activity的ContextImpl,传入对应的显示id和设备配置
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
        ······省略无关代码
        return appContext;
    }


ContextImpl.java
    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        //真正创建Activity ContextImpl的地方
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader, null);

        ······省略

        //创建资源管理器
        final ResourcesManager resourcesManager = ResourcesManager.getInstance();

        // ResourcesManager类创建Activity的全部配置,并设置给ContextImpl
        context.setResources(resourcesManager.createBaseActivityResources(activityToken,
                packageInfo.getResDir(), //基本资源路径
                splitDirs,//分割的资源路径
                packageInfo.getOverlayDirs(),//覆盖路径
                packageInfo.getApplicationInfo().sharedLibraryFiles,//app库文件
                displayId,
                overrideConfiguration,
                compatInfo,
                classLoader));
        context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
                context.getResources());
        return context;
    }
    
ResourcesManager.java
    public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken,//Activity
            @Nullable String resDir,//基本资源路径。 可以为null(仅将加载framwork资源)。
            @Nullable String[] splitResDirs,
            @Nullable String[] overlayDirs,
            @Nullable String[] libDirs,
            int displayId,
            @Nullable Configuration overrideConfig,
            @NonNull CompatibilityInfo compatInfo,
            @Nullable ClassLoader classLoader) {
        try {
           
            //使用当前的路径和设备信息,构建去查询资源的ResourcesKey
            final ResourcesKey key = new ResourcesKey(
                    resDir,
                    splitResDirs,
                    overlayDirs,
                    libDirs,
                    displayId,
                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
                    compatInfo);
            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();

            if (DEBUG) {
                Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
                        + " with key=" + key);
            }

            synchronized (this) {
                // 创建一个ActivityResourcesStruct
                getOrCreateActivityResourcesStructLocked(activityToken);
            }

            // 更新任一存在Activity的Resources引用
            updateResourcesForActivity(activityToken, overrideConfig, displayId,
                    false /* movedToDifferentDisplay */);

            // 这里实际去请求对应的Resources对象
            return getOrCreateResources(activityToken, key, classLoader);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }

    //获取带有与给定键匹配的ResourcesImpl对象的现有Resources对象集,如果不存在则创建一个。
    private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken,
            @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
        synchronized (this) {
            if (activityToken != null) {//Activity对应Resources
                //获取Activity对应的ActivityResources
                final ActivityResources activityResources =
                        getOrCreateActivityResourcesStructLocked(activityToken);

                // 清理所有无效的引用,以免它们堆积。
                ArrayUtils.unstableRemoveIf(activityResources.activityResources,
                        sEmptyReferencePredicate);
                //使用key在缓存中找到对应的ResourcesImpl实现
                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
                if (resourcesImpl != null) {
                    //存在就返回
                    return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
                            resourcesImpl, key.mCompatInfo);
                }
            } else {
                //缓存中获取非Activity对应Resources
                // 不依赖于一个Activity,找到具有合适的ResourcesImpl共享资源
                ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
                if (resourcesImpl != null) {
                    if (DEBUG) {
                        Slog.d(TAG, "- using existing impl=" + resourcesImpl);
                    }
                    return getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
                }
            }

            // 如果我们在这里,我们找不到适合使用的ResourcesImpl,所以现在创建一个。
            // 在这里面会创建对应的AssetManager
            ResourcesImpl resourcesImpl = createResourcesImpl(key);
            if (resourcesImpl == null) {
                return null;
            }

            // 添加ResourcesImpl进入缓存
            mResourceImpls.put(key, new WeakReference<>(resourcesImpl));

            //然后根据是否是Activity创建对应的Resources,因为Service也会有ContextImpl
            final Resources resources;
            if (activityToken != null) {
                resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
                        resourcesImpl, key.mCompatInfo);
            } else {
                resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
            }
            return resources;
        }
    }

    //ResourcesManager是一个单例模式,所有很多地方都会直接调用getResources来查找对应的Resources
     public @Nullable Resources getResources(@Nullable IBinder activityToken,
            @Nullable String resDir,
            @Nullable String[] splitResDirs,
            @Nullable String[] overlayDirs,
            @Nullable String[] libDirs,
            int displayId,
            @Nullable Configuration overrideConfig,
            @NonNull CompatibilityInfo compatInfo,
            @Nullable ClassLoader classLoader) {
        try {
            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
            //构建查找的key
            final ResourcesKey key = new ResourcesKey(
                    resDir,
                    splitResDirs,
                    overlayDirs,
                    libDirs,
                    displayId,
                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
                    compatInfo);
            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
            //获取对应的Resources
            return getOrCreateResources(activityToken, key, classLoader);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }

接下来聚焦到ContextImpl

class ContextImpl extends Context {

    @Override
    public Resources getResources() {
        //实际获取成员变量的mResources,上面讲到的setResources方法设置的
        return mResources;
    }

     void setResources(Resources r) {
        if (r instanceof CompatResources) {
            ((CompatResources) r).setContext(this);
        }
        mResources = r;
    }
}

获取String资源

上面看了怎么设置Resources,接下来将会看如何从strings.xml获取到String


public class Resources {

    public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
        this(null);
        //创建ResourcesImpl对象
        mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
    }


    public String getString(@StringRes int id) throws NotFoundException {
        //调用getText(id)获取
        return getText(id).toString();
    }

    @NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
        //调用mResourcesImpl的getAssets获取到AssetManager
        //然后调用getResourceText获取
        CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
        if (res != null) {
            return res;
        }
        throw new NotFoundException("String resource ID #0x"
                + Integer.toHexString(id));
    }
}

获取String,实际上是在调用AssetManager的getResourceText方法,下面进入getResourceText(id)

public final class AssetManager implements AutoCloseable {
    @UnsupportedAppUsage
    @Nullable CharSequence getResourceText(@StringRes int resId) {
        synchronized (this) {
            //一个动态id的数据容器,用于保存资源值
            final TypedValue outValue = mValue;
            //根据资源resId获取值
            if (getResourceValue(resId, 0, outValue, true)) {
                return outValue.coerceToString();
            }
            return null;
        }
    }
    
    boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
            boolean resolveRefs) {
        Preconditions.checkNotNull(outValue, "outValue");
        synchronized (this) {
            //确保不破坏本机实现。 AssetManager可能已关闭,但是对其的引用仍然存在,因此不会破坏本机实现。
            ensureValidLocked();
            //调用native方法获取资源值
            //mObject是指向本地native实现的指针
            final int cookie = nativeGetResourceValue(
                    mObject, resId, (short) densityDpi, outValue, resolveRefs);
            if (cookie <= 0) {
                return false;
            }

            // Convert the changing configurations flags populated by native code.
            outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
                    outValue.changingConfigurations);
            //如果是字符串类型,给输出string赋值
            if (outValue.type == TypedValue.TYPE_STRING) {
                outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
            }
            return true;
        }
    }
}

通过AssetManager.getResourceText(id),内部实际调用了native的方法去获取。

/frameworks/base/core/jni/android_util_AssetManager.cpp

using ApkAssetsCookie = int32_t;

enum : ApkAssetsCookie {
  kInvalidCookie = -1,
};

static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
                                   jshort density, jobject typed_value,
                                   jboolean resolve_references) {
  //通过ptr获取到对应的AssetManager,ptr会在Java层构造函数里调用nativeCreate获取到ptr
  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
  Res_value value;
  ResTable_config selected_config;
  uint32_t flags;
  //调用jni层assetmanager的GetResource获取资源,传入resid,对应density,输出对象value,配置selected_config
  ApkAssetsCookie cookie =
      assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
                                static_cast<uint16_t>(density), &value, &selected_config, &flags);
  //如果获取失败cookie=-1
  if (cookie == kInvalidCookie) {
    //返回-1
    return ApkAssetsCookieToJavaCookie(kInvalidCookie);
  }

  //转换资源id
  uint32_t ref = static_cast<uint32_t>(resid);
  //解析引用,{@ code false}使其不解析
  if (resolve_references) {
    cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &);
    if (cookie == kInvalidCookie) {
      return ApkAssetsCookieToJavaCookie(kInvalidCookie);
    }
  }
  //CopyValue方法将得到的值,通过env的set方法更改outValue对象的值
  return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
}

/**
  *  通过nativeCreate创建时返回的Long类型指针值,查找对应的AssetManager
  **/
static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
  return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
}

NativeGetResourceValue方法中,实际解析资源方法的方法assetmanager->GetResource

/frameworks/base/libs/androidfw/AssetManager2.cpp

ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
                                           uint16_t density_override, Res_value* o  t_value,
                                             ResTable_config* out_selected_config,
                                             uint32_t* out_flags) const {
    FindEntryResult entry;
    //这个方法有点长,根据resid和density_override是否为0查找最佳适配
    ApkAssetsCookie cookie =
        FindEntry(resid, density_override, false /* stop_at_first_match */, &entry);
    if (cookie == kInvalidCookie) {
      return kInvalidCookie;
    }

     if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
      if (!may_be_bag) {
        LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
        return kInvalidCookie;
      }

       // 创建引用,因为我们不能将此复杂类型表示为Res_value.然后在Java层可以根据out_value中获取到值
      out_value->dataType = Res_value::TYPE_REFERENCE;
      out_value->data = resid;
      *out_selected_config = entry.config;
      *out_flags = entry.type_flags;
      return cookie;
    }

    const Res_value* device_value = reinterpret_cast<const Res_value*>(
        reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
    out_value->copyFrom_dtoh(*device_value);

     // 转换成运行时的包id
    entry.dynamic_ref_table->lookupResourceValue(out_value);

    *out_selected_config = entry.config;
    *out_flags = entry.type_flags;
    return cookie;
}


查找资源的适配过程

ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
                                         bool /*stop_at_first_match*/,
                                         FindEntryResult* out_entry) const {
    // 如果density_override不为0可能使用
    ResTable_config density_override_config;    
    // 选择我们的配置或者生成一个密度重写的配置
    const ResTable_config* desired_config = &configuration_;
    if (density_override != 0 && density_override != configuration_.density) {
      density_override_config = configuration_;
      density_override_config.density = density_override;
      desired_config = &density_override_config;
    }
    //校验16进制前面4位不能为0,
    //前两位-系统01,用户应用7f。而第三方资源包,则是从0x02开始逐个递增
    //第3-4位是资源类型,如anim文件夹下的资源就是01
    //第5-8位是资源每一项对应的id
    if (!is_valid_resid(resid)) {
      LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
      return kInvalidCookie;
    }   
    //获取包id、资源类型id、实体id、以及包id索引
    const uint32_t package_id = get_package_id(resid);
    const uint8_t type_idx = get_type_id(resid) - 1;
    const uint16_t entry_idx = get_entry_id(resid); 
    const uint8_t package_idx = package_ids_[package_id];
    if (package_idx == 0xff) {
      LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",   package_id, resid);
      return kInvalidCookie;
    }   
    //初始化参数
    const PackageGroup& package_group = package_groups_[package_idx];
    const size_t package_count = package_group.packages_.size();    
    ApkAssetsCookie best_cookie = kInvalidCookie;
    const LoadedPackage* best_package = nullptr;
    const ResTable_type* best_type = nullptr;
    const ResTable_config* best_config = nullptr;
    ResTable_config best_config_copy;
    uint32_t best_offset = 0u;
    uint32_t type_flags = 0u;   
    // 如果desirable_config与设置的配置相同,那么我们可以使用过滤列表,并且由于它们已经匹配,因此不需要匹配配置。
    const bool use_fast_path = desired_config == &configuration_;   
    for (size_t pi = 0; pi < package_count; pi++) {
      const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
      const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
      ApkAssetsCookie cookie = package_group.cookies_[pi];  
      // 如果此包中的类型ID偏移,则在搜索类型时需要将其考虑在内。
      const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx);
      if (UNLIKELY(type_spec == nullptr)) {
        continue;
      } 
      uint16_t local_entry_idx = entry_idx; 
      // 如果此软件包提供了IDMAP,请转换条目ID。
      if (type_spec->idmap_entries != nullptr) {
        if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx,     local_entry_idx)) {
         // 没有映射,因此资源并不意味着位于此叠加包中。
          continue;
        }
      } 
      type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);  
      // 如果封装是覆盖层,则甚至必须选择相同的配置。
      const bool package_is_overlay = loaded_package->IsOverlay();  
      const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs  [type_idx];
      //配置相同,已经缓存了
      if (use_fast_path) {
        const std::vector<ResTable_config>& candidate_configs = filtered_group.  configurations;
        const size_t type_count = candidate_configs.size();
        for (uint32_t i = 0; i < type_count; i++) {
          const ResTable_config& this_config = candidate_configs[i];    
          // 我们可以跳过调用ResTable_config::match()的步骤,因为我们知道所有不匹配的候选配置都已被滤除。
          if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
              (package_is_overlay && this_config.compare(*best_config) == 0)) {
            // 配置匹配并且比以前的选择更好。
            // 查找该配置是否存在该输入值。
            const ResTable_type* type_chunk = filtered_group.types[i];
            const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk,   local_entry_idx);
            if (offset == ResTable_type::NO_ENTRY) {
              continue;
            }   
            best_cookie = cookie;
            best_package = loaded_package;
            best_type = type_chunk;
            best_config = &this_config;
            best_offset = offset;
          }
        }
      } else {
        // 这是较慢的路径,它不使用过滤的配置列表。
        //在这里,我们必须从映射的APK中读取ResTable_config,将其转换为主机字节序,并填写编译APK时不存在的任何新字段。
        //此外,在选择配置时,我们不能只记录指向ResTable_config的指针,我们必须将其复制。
        const auto iter_end = type_spec->types + type_spec->type_count;
        for (auto iter = type_spec->types; iter != iter_end; ++iter) {
          ResTable_config this_config;
          this_config.copyFromDtoH((*iter)->config);    
          if (this_config.match(*desired_config)) {
            if ((best_config == nullptr || this_config.isBetterThan(*best_config,   desired_config)) ||
                (package_is_overlay && this_config.compare(*best_config) == 0)) {
              // The configuration matches and is better than the previous selection.
              // Find the entry value if it exists for this configuration.
              const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
              if (offset == ResTable_type::NO_ENTRY) {
                continue;
              } 
              best_cookie = cookie;
              best_package = loaded_package;
              best_type = *iter;
              best_config_copy = this_config;
              best_config = &best_config_copy;
              best_offset = offset;
            }
          }
        }
      }
    }   
    //最好的适配资源没有就返回-1
    if (UNLIKELY(best_cookie == kInvalidCookie)) {
      return kInvalidCookie;
    }   
    //确定已经存在了资源,找到最好的Entry之后,写入out实体中,返回best_cookie
    const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
    if (UNLIKELY(best_entry == nullptr)) {
      return kInvalidCookie;
    }   
    out_entry->entry = best_entry;
    out_entry->config = *best_config;
    out_entry->type_flags = type_flags;
    out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(),   best_type->id - 1);
    out_entry->entry_string_ref =
        StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
    out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
    return best_cookie;
}


上述查找的步骤分为:
1.先校验density_override值是否有(即不为0),然后更新配置
2.校验资源id前两位和三四位类型是否存在
3.循环所有资源的包,然后找到类型规格TypeSpec,TypeSpec中包含了所有的entity的索引id。没找到就继续循环下一个包;如果找到了
4.获取到对应包的不同分辨率的信息,循环选择最佳的资源
5.找到后,写入输出实体entry,返回好的cookie标识

layout.xml资源的加载

从LayoutInflater说起,因为setContentView和在fragment中解析layout.xml都是LayoutInflater.inflate()方法

public abstract class LayoutInflater {

     public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
        return inflate(resource, root, root != null);
    }

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        //获取到当前的Resources对象
        final Resources res = getContext().getResources();
        
        //尝试从预编译里加载View
        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
        if (view != null) {
            return view;
        }
        //从Resources获取Layout的资源解析器
        XmlResourceParser parser = res.getLayout(resource);
        try {
            //层层解析子view
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            
            final Context inflaterContext = mContext;
            //使用外观模式将parser包装成AttributeSet,里面提供了解析各种类型数据的方法
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                //提前解析第一个标签
                advanceToRootNode(parser);
                //返回标签的名称
                final String name = parser.getName();
                //解析merge标签
                if (TAG_MERGE.equals(name)) {
                    //merge标签--root必须不为空,attachToRoot为true
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }
                    //递归降低xml层次结构和实例化View,实例化其子级,然后调用onFinishInflate()
                    //注意 :默认可见性,因此BridgeInflater可以覆盖它。
                
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // 创建临时的rootView
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        // 创建与根匹配的布局参数(如果提供)
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            // 如果我们不附加,请为temp设置布局参数。 
                            //(如果是的话,我们在下面使用addView)
                            temp.setLayoutParams(params);
                        }
                    }

                    // 解析所有的子View
                    rInflateChildren(parser, temp, attrs, true);

                    // 如果需要attachToRoot,则添加进root中
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    // 确定是返回传入的根还是在xml中找到的顶视图.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(
                        getParserStateDescription(inflaterContext, attrs)
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                // 清除引用
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            return result;
        }
    }

     private @Nullable
    View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
        boolean attachToRoot) {
        if (!mUseCompiledView) {
            return null;
        }

        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)");

        // 尝试通过resId获取包名
        String pkg = res.getResourcePackageName(resource);
        // 尝试通过resId获取layout
        String layout = res.getResourceEntryName(resource);
        //尝试通过反射创建拿到已经编译好的View
        try {
            Class clazz = Class.forName("" + pkg + ".CompiledView", false, mPrecompiledClassLoader);
            Method inflater = clazz.getMethod(layout, Context.class, int.class);
            View view = (View) inflater.invoke(null, mContext, resource);

            if (view != null && root != null) {
                // We were able to use the precompiled inflater, but now we need to do some work to
                // attach the view to the root correctly.
                XmlResourceParser parser = res.getLayout(resource);
                try {
                    AttributeSet attrs = Xml.asAttributeSet(parser);
                    advanceToRootNode(parser);
                    ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);

                    if (attachToRoot) {
                        root.addView(view, params);
                    } else {
                        view.setLayoutParams(params);
                    }
                } finally {
                    parser.close();
                }
            }

            return view;
        } catch (Throwable e) {
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        return null;
    }
}

Layout的步骤上述有以下几步:
1、从context->ContextImpl中获取获取到Resources对象
2、尝试从预编译View里加载View
3、从Resources获取Layout的XmlResourceParser资源解析器,其中包含资源id查找到xml文件的过程,将在下面分析
4、使用XmlResourceParser第一个标签,如果是Merge标签,就替换,层层解析出子View。否则,先创建一个临时rootView,然后添加子View,root不为空且attachToRoot=true的话才添加进rootView

下面来看资源id绑定xml的过程

Resources.java
public class Resources {

    public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
        return loadXmlResourceParser(id, "layout");
    }

    XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
            throws NotFoundException {
        final TypedValue value = obtainTempTypedValue();
        try {
            final ResourcesImpl impl = mResourcesImpl;
            //ResourcesImpl获取到值,实际上也是调用的AssetManager#getResourceValue方法
            impl.getValue(id, value, true);
            if (value.type == TypedValue.TYPE_STRING) {
                return impl.loadXmlResourceParser(value.string.toString(), id,
                        value.assetCookie, type);
            }
            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
                    + " type #0x" + Integer.toHexString(value.type) + " is not valid");
        } finally {
            releaseTempTypedValue(value);
        }
    }
}

ResourcesImpl.java
public class ResourcesImpl {

    @UnsupportedAppUsage
    void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
            throws NotFoundException {
        //实际还是调用AssetManager#getResourceValue方法获取,上面已经讲过
        boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
        if (found) {
            return;
        }
        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
    }
}

assets目录的资源加载

从Resources中获取到AssetManager管理器getAssets()

Resources.java
public class Resources {
    /**
     * 获取这些资源的基础AssetManager存储。
     */
    public final AssetManager getAssets() {
        return mResourcesImpl.getAssets();
    }
}

ResourcesImpl.java
public class ResourcesImpl {
    public AssetManager getAssets() {
        return mAssets;
    }
}

AssetManager.java
public final class AssetManager implements AutoCloseable {
    public @NonNull InputStream open(@NonNull String fileName) throws IOException {
        //打开流权限
        return open(fileName, ACCESS_STREAMING);
    }

    public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
        Preconditions.checkNotNull(fileName, "fileName");
        synchronized (this) {
            //确认流已经打开
            ensureOpenLocked();
            //本地打开mObject(本地Assetmanager地址),打开文件名,权限
            final long asset = nativeOpenAsset(mObject, fileName, accessMode);
            //没找到文件抛出异常
            if (asset == 0) {
                throw new FileNotFoundException("Asset file: " + fileName);
            }
            //传入资源对应指针,构建AssetInputStream输入流,来去除文件
            final AssetInputStream assetInputStream = new AssetInputStream(asset);
            //增加索引
            incRefsLocked(assetInputStream.hashCode());
            return assetInputStream;
        }
    }

    /**
      *  调用c层打开asset
      **/
    private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);

}

java层流程:
Resources.getAssets -> ResourcesImpl.getAssets -> AssetManager.open() -> android_util_AssetManager.nativeOpenAsset()

接下来深入jni层查看具体获取

/frameworks/base/core/java/android/content/res/AssetManager.java
static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
                             jint access_mode) {
    //Assets目录下路径
    ScopedUtfChars asset_path_utf8(env, asset_path);
    //如果路径为空,排除空指针
    if (asset_path_utf8.c_str() == nullptr) {
      // This will throw NPE.
      return 0;
    }
    
    ATRACE_NAME(base::StringPrintf("AssetManager::OpenAsset(%s)", asset_path_utf8.c_str()).c_str());    
    //权限校验
    if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
        access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
      jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
      return 0;
    }   
    //通过指针获取到对应的AssetManager
    ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
    //通过路径打开Assets下对应资源,能找到就返回
    std::unique_ptr<Asset> asset =
        assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
    //如果asset没有就温控
    if (!asset) {
      jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
      return 0;
    }
    //返回正确标识
    return reinterpret_cast<jlong>(asset.release());
}

实际通过AssetManager2的Open方法打开流

/frameworks/base/libs/androidfw/AssetManager2.cpp
std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename,
                                           Asset::AccessMode mode) const {
  //拼上对应路径,打开资源
  const std::string new_path = "assets/" + filename;
  return OpenNonAsset(new_path, mode);
}

std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
                                                   Asset::AccessMode mode,
                                                   ApkAssetsCookie* out_cookie) const {
  //遍历ApkAssets资源列表,调用ApkAssets->Open(filename, mode)打开文件
  for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
    std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
    //找到值就返回
    if (asset) {
      if (out_cookie != nullptr) {
        *out_cookie = i;
      }
      return asset;
    }
  }

  if (out_cookie != nullptr) {
    *out_cookie = kInvalidCookie;
  }
  return {};
}

来看ApkAssets的Open(filename, mode)

/frameworks/base/libs/androidfw/ApkAssets.cpp
std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
    CHECK(zip_handle_ != nullptr);

    ::ZipString name(path.c_str());
    ::ZipEntry entry;
    //通过zipName字符串找到对应的ZipEntry实体
    int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
    //有就直接返回
    if (result != 0) {
      return {};
    }
    //如果是压缩文件
    if (entry.method == kCompressDeflated) {
        std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
        //找到对应file文件映射,如果创建映射失败,抛出异常
        if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
                        entry.compressed_length, true /*readOnly*/)) {
            LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
            return {};
        }

        //解压文件,创建资源对象asset
        std::unique_ptr<Asset> asset =
            Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
        //解压失败,返回空
        if (asset == nullptr) {
            LOG(ERROR) << "Failed to decompress '" << path << "'.";
            return {};
        }
        return asset;
    } else {
        std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
        if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
                        entry.uncompressed_length, true /*readOnly*/)) {
            LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
            return {};
        }
        //在对应的apk中映射到文件
        std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
        if (asset == nullptr) {
            LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
            return {};
        }
        //返回资源对应的指针
        return asset;
    }
}

这一步找的是zip包中的压缩文件,而不是资源数据中的资源Entry,这样也能找到apk包里资源目录下的layout文件

JNI部分的流程大概是:
1、asset路径校验、权限校验
2、通过ptr指针获取到AssetManager对象,调用Open()方法打开资源。
3、拼接上”assets/“前缀路径,接下来遍历ApkAssets列表zip包
4、在apk压缩包里找到最适合的layout的返回

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 开机动画的加载流程如下: 1. 开机自检:开机后,首先进行硬件自检。如果自检通过,将会启动 bootloader。 2. Bootloader:Bootloader 负责初始化硬件设备,加载 kernel,并且将 kernel 加载到内存中。 3. Kernel:Kernel 是 Android 操作系统的核心,负责初始化硬件设备,启动驱动程序,加载文件系统等。 4. Init 进程:Init 进程是 Android 系统中的第一个用户进程,它负责启动系统中所有的服务,并且加载所有的配置文件和属性。 5. SurfaceFlinger:SurfaceFlinger 是 Android 系统中用于显示图形的核心服务,它在启动后会创建一个显示屏幕,显示屏幕上的内容就是从应用程序中传递过来的。 6. Zygote 进程:Zygote 进程是 Android 系统中的一个特殊进程,它负责预加载常用的类和资源,以提高应用程序的启动速度。当应用程序需要启动时,Zygote 进程会 fork 出一个新的进程,该进程会继承 Zygote 进程的状态,从而加速应用程序的启动。 7. 开机动画:在上述进程启动后,系统会加载开机动画。开机动画通常是一个视频或者一组图片,这些图片或视频会被 SurfaceFlinger 显示在屏幕上。 8. 启动屏幕:当开机动画结束后,系统会显示一个启动屏幕,表示 Android 系统已经启动完毕。此时,用户就可以开始使用 Android 设备了。 总的来说,Android 开机动画加载流程比较复杂,其中涉及到多个进程和服务,需要相互配合才能完成整个过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值