您的位置:首页 > 移动开发 > Android开发

Android疑难点解答

2016-05-10 11:02 429 查看

一.概述

这篇文章主要解答一些在平时学习工作过程中遇到的一些比较值得研究的问题。

二.问题

1.DecorView是何时被创建的

是在PhoneWindow的generateDecor中被创建的

protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}


2.ViewRootImpl是何时被创建的

是在WindowManagerGlobal的addView方法中创建的

public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//省去大量代码

ViewRootImpl root;
View panelParentView = null;
//省去部分代码

root = new ViewRootImpl(view.getContext(), display);
......
}


3.Activity加载布局的流程

在Activity中调用setContentView加载布局,setContentView最终调用PhoneWindow的setContentView方法,在此方法中将根布局xml文件解析成View添加到DecorView中,然后从根布局文件中找到id为contente的父容器,将我们在setContentView中指定的布局文件通过inflate的方式添加进去

//将我们的布局文件添加到mContentParent中,这个mContentParent是根布局中id为content的一个FrameLayout
mLayoutInflater.inflate(layoutResID, mContentParent);


4.Window对象何时被创建

在Activity的attach方法中

final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config) {
attachBaseContext(context);

mFragments.attachActivity(this, mContainer, null);
//创建window对象
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
.....
}


5.Application对象何时被创建

在Instrumentation的newApplication方法中

public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}


6.View绘制的起点在何处

当启动Activity的时候,其中有一步是执行ActivityThread的handleResumeActivity.

final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
.......
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
.......


上面首先调用a的getWindowManager方法,其中a是Activity对象,getWindowManager获得是WindowManager对象,此处时期返回的是它的实现类WindowManagerImpl,.

然后调用了WindowManagerImpl的addView方法

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}


可以看出,WMI只是一个代理类,真正的实现实在WindowManagerGlobal中

public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
......

ViewRootImpl root;
View panelParentView = null;

.....
//创建ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
}

// do this last because it fires off messages to start doing things
try {
//这句话很重要,将DecorView添加到ViewRootImpl中
root.setView(view, wparams, panelParentView);


然后我们看砍setView方法,

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
.....
requestLayout();
......


这里调用了requestLayout方法,这就是View开始绘制的起点

@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}


void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}


mTraversalRunnable是一个Runnable对象,此时会执行它的run方法

final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();


void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}

performTraversals();

if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}


ViewRootImpl的performTraversals方法

private void performTraversals() {
//省去海量代码
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//省去部分代码
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
//省去大量代码
performDraw();
.....
}


7.DecorView何时被添加到Window中

在Activity的makeVisible方法中

void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}


8.影响图片占用内存大小的因素有哪些?

(1).图片尺寸

(2)色彩细节

在Bitmap.Config中有如下定义

//8位ALPHA图
public static final Bitmap.Config ALPHA_8
//16位ARGB图,占2KB
public static final Bitmap.Config ARGB_4444
//32位ARGB图,占4KB大小
public static final Bitmap.Config ARGB_8888
//16位RGB图
public static final Bitmap.Config RGB_565


9.measure方法的两个参数代表什么意思?

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {


widthMeasureSpec封装了视图的宽度大小和规格

heightMeasureSpec封装了视图的高度大小和规格

这两个参数是父视图经过计算传递下来的

我们可以通过下面的方式取出视图的大小和规格

int mode = MeasureSpec.getMode(widthMeasureSpec);
int size = MeasureSpec.getSize(widthMeasureSpec);


10.父视图的widthMeasureSpec和heightMeasureSpec从哪里来?

View的绘制是从ViewRootImpl的performTraversals开始的,下面的代码定义了widthMeasureSpec和heightMeasureSpec

int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);


我们看看getRootMeasureSpec这个方法

/**
* Figures out the measure spec for the root view in a window based on it's
* layout params.
*
* @param windowSize
*            The available width or height of the window
*
* @param rootDimension
*            The layout params for one dimension (width or height) of the
*            window.
*
* @return The measure spec to use to measure the root view.
*/
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {

case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}


这个方法接收两个参数,第一个是Window的尺寸,第二个是代表WIndow尺寸的布局参数。那么看门看看这两个参数的定义

if (mWidth != frame.width() || mHeight != frame.height()) {
mWidth = frame.width();
mHeight = frame.height();
}


WindowManager.LayoutParams lp = mWindowAttributes;
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();


从源码我们可以看出,根布局的widthMeasureSpec中的宽度为Window的宽度,heightMeasureSpec中的高度为Window的高度,规格都是MeasureSpec.EXACTLY,也就是精确的。

11.DecorView是何时变得可见的

在ActivityThread的handleResumeActivity方法中我们可以找到答案。

if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//获取DecorView
View decor = r.window.getDecorView();
//让DecorView可见               decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//将DecorView添加到Window中
wm.addView(decor, l);
}


12.View的post方法是如何做到在onCreate中获取到View的尺寸的。

public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}

// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}


这里的重点在于attachInfo 何时不为空,答案是在ViewRootImpl被创建的时候,ViewRootImpl实在WindowManagerGlobal的addView方法中被创建的,而这个addView方法是在ActivityThread的handleResumeActivity中调用的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: