我们都知道Android的界面是通过xml编写的 然后通过Activity 加载xml的layout文件进行显示,那么怎么从xml加载到我们的Activity呢?
下面我们一步步了解view的加载流程 ,看看到底view是怎么创建出来的
一、Android的界面显示层次
二、源码分析-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;
}