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系统的交互的接口。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories