我們都知道Android的界面是通過xml編寫的 然后通過Activity 加載xml的layout文件進(jìn)行顯示缀遍,那么怎么從xml加載到我們的Activity呢?
下面我們一步步了解view的加載流程 舷蒲,看看到底view是怎么創(chuàng)建出來的
一、Android的界面顯示層次
二、源碼分析-View的加載
1蛤吓、故事要從setContentView開始
在我們的Activity點進(jìn)setContentView方法 我們看到 里面實際上會調(diào)用window的setContentView 方法進(jìn)行l(wèi)ayout的解析
/**
* 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,可以看到碎罚,當(dāng)mContentParent ==null時磅废,調(diào)用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創(chuàng)建好了以后 通過 generateLayout 創(chuàng)建了一個mContentParent
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
generateLayout 中通過我們配置的各種參數(shù)進(jìn)行判斷加載哪個根布局,然后從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在上面已經(jīng)創(chuàng)建完成,而我們的layout就添加到mContentParent上面憔购,最后調(diào)用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方法 我們發(fā)現(xiàn)最終都會來到這個方法:
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 創(chuàng)建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 中經(jīng)過條件判斷 ,最終會調(diào)用createView() 反射創(chuàng)建最終我們得到的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就創(chuàng)建出來了
三玫鸟、源碼分析-資源的加載
1导绷、application的創(chuàng)建
private void handleBindApplication(AppBindData data) {
mInstrumentation = new Instrumentation();
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
}
先創(chuàng)建了個appContext 然后通過mInstrumentation 把application創(chuàng)建出來
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的創(chuàng)建
進(jìn)入createAppContext(), 創(chuàng)建了個ContextImpl屎飘,給它設(shè)置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的創(chuàng)建
進(jìn)入LoadedApk.getResources(), 經(jīng)過條件判斷,通過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;
}
繼續(xù)進(jìn)入getRecourse钦购,通過findOrCreateResourcesImplForKeyLocked方法獲取到資源
private @Nullable Resources createResources(@Nullable IBinder activityToken,@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) synchronized (this) {
ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key);
}
然后判斷空 為空 調(diào)用createResourcesImpl 創(chuàng)建資源
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;
}
進(jìn)入createResourcesImpl 創(chuàng)建了個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;
}
=創(chuàng)建出AssetManager后調(diào)用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();
最后 調(diào)用 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;
}