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

android笔记01-findViewById()源码解析

2016-07-19 10:49 579 查看
Activity源码:


public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {
..........
private Window mWindow;
public Window getWindow() {
return mWindow;
}

public View findViewById(int id) {
return getWindow().findViewById(id);
}

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);

mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(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();

mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;

mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}

}


从面源码可以看到 Activity 里面有一个Window窗口对象,findViewById()方法是通过getWindow()获得 Window对象,并调用Window方法里面的findViewById方法,而点进Window类发现,他是一个抽象类,抽象类肯定是有子类来实现他的方法,那么子类是哪个类呢,接下来往下看。

在Activity里面的attach()方法里面,对window对象进行了初始化:window=PolicyManager.makeNewWindow(this);

PolicyManager是干嘛用的?接下来看看PolicyManager的源码:

public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";

private static final IPolicy sPolicy;

static {
// Pull in the actual implementation of the policy at run-time
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
} catch (InstantiationException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
}
}

......

// The static methods to spawn new policy-specific objects
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}

......
}


public class Policy implements IPolicy {
......

public PhoneWindow makeNewWindow(Context context) {
return new PhoneWindow(context);
}

......
}


PolicyManager是一个窗口管理策略类,它在第一次使用的时候,就会创建一个Policy类实例,并且保存在静态成员变量sPolicy中,以后PolicyManager类的窗口管理策略就是通过Policy类实例实现的,例如PolicyManager类的静态成员函数makeNewWindow就是通过调用Policy类实例的成员函数makeNewWindow来创建一个具体的应用程序窗口的。

所以其实Activity里面的Window对象就是PhoneWindow对象,那么PhoneWindow又是什么类呢?看源码

public class PhoneWindow extends Window implements MenuBuilder.Callback {
......

// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;

// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
......

4000
private LayoutInflater mLayoutInflater;
......

public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
.....
}
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
......
}


PhoneWindow是Window的子类,在PhoneWindow的初始化中有mLayoutInflater,这货不是经常用来加载layout文件生成视图对象的吗,DecorView用来做什么的,下文会讲到。到此就先看到这里,我们跳回到Activity类

public View findViewById(int id) {
return getWindow().findViewById(id);
}


点进Window对象的findViewById()方法,

public abstract class Window {
......
private final Context mContext;
......
public Window(Context context) {
mContext = context;
}
public View findViewById(int id) {
return getDecorView().findViewById(id);
}
public abstract View getDecorView();
......
}


在Window中getDecorView()是一个抽象方法,返回的是一个View对象,这个DecorView是什么呢?刚才上文说到Activity中的Window对象是PhoneWindow,PhoneWindow里面有一个DecorView对象,getDecorView方法的返回值正是DecorVIew这个对象。

PhoneWindow类通过成员变量mLayoutInflater来创建应用程序窗口的视图,这个视图类型为DecorView的成员变量mDecor来描述。PhoneWindow类还有另外一个类型为ViewGroup的成员变量mContentParent,用来描述一个视图容器,这个容器存放的成员变量mDecor所描述的视图的内容,不过这个容器也有可能指向的是mDecor本身。

知道Activity查找的是在DecorView里面查找控件,那我们就必须找到递归或者遍历的方法。DecorView是一个View,看看View的源码:

public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public final View findViewById(int id) {
if (id < 0) {
return null;
}
return findViewTraversal(id);
}
protected View findViewTraversal(int id) {
if (id == mID) {
return this;
}
return null;
}
}


发现View都一系列方法里面并没有递归或者循环遍历,在这里懵逼了一下,看DecorView是继承 FrameLayout的,而FrameLayout是继承ViewGroup的,在ViewGroup里面查找代码,原来是被ViewGroup给重写了。那这就对了,

@Override
protected View findViewTraversal(int id) {
if (id == mID) {
return this;
}

final View[] where = mChildren;
final int len = mChildrenCount;

for (int i = 0; i < len; i++) {
View v = where[i];

if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
v = v.findViewById(id);

if (v != null) {
return v;
}
}
}

return null;
}


这个是重写的方法:可以非常清楚的看到,findViewById的原理,是从头开始找,遇到有子控件的,就递归接着找。先不考虑具体细节,到这里,开始的猜想得到了证实。其中v.findViewById又调用View中的方法,而View的findViewById调用的findViewTraversal()。这样实现了一个递归。

小结:

Activity:Activity中包含一个Window对象,Activity的findViewById是由window.findViewById()来查找,该Window在Activity的attach方法中通过调用PolicyManager.makeNewWindow来创建。

View:最近基本的UI组件,表示屏幕上的一个矩形区域。

DecorView:这个类型为PhoneWindow的应用程序窗口内部有一个类型为DecorView的视图对象,这个视图对象才是真正用来描述一个Activity组件的UI的。

Window:表示顶层窗口,界面管理的显示和事件的响应,每个Activity都会创建一个PhoneWindow对象,是Activity和整个View系统的交互的接口。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 源码