Android 系统源码分析之View(一)

View类两万一千多行,在这里逐步分析。建议这篇blog对照Android View源码观看!!

/*     <tr>
 *         <td><code>{@link #onFinishInflate()}code>td>
 *         <td>Called after a view and all of its children has been inflated
 *         from XML.td>
 *     tr>
 *
 *     <tr>
 *         <td rowspan="3">Layouttd>
 *         <td><code>{@link #onMeasure(int, int)}code>td>
 *         <td>Called to determine the size requirements for this view and all
 *         of its children.
 *         td>
 *     tr>
 *     <tr>
 *         <td><code>{@link #onLayout(boolean, int, int, int, int)}code>td>
 *         <td>Called when this view should assign a size and position to all
 *         of its children.
 *         td>
 *     tr>
 *     <tr>
 *         <td><code>{@link #onSizeChanged(int, int, int, int)}code>td>
 *         <td>Called when the size of this view has changed.
 *         td>
 *     tr>
 *
 *     <tr>
 *         <td>Drawingtd>
 *         <td><code>{@link #onDraw(android.graphics.Canvas)}code>td>
 *         <td>Called when the view should render its content.
 *         td>
 *     tr>
 *
 *     <tr>
 *         <td rowspan="4">Event processingtd>
 *         <td><code>{@link #onKeyDown(int, KeyEvent)}code>td>
 *         <td>Called when a new hardware key event occurs.
 *         td>
 *     tr>
 *     <tr>
 *         <td><code>{@link #onKeyUp(int, KeyEvent)}code>td>
 *         <td>Called when a hardware key up event occurs.
 *         td>
 *     tr>
 *     <tr>
 *         <td><code>{@link #onTrackballEvent(MotionEvent)}code>td>
 *         <td>Called when a trackball motion event occurs.
 *         td>
 *     tr>
 *     <tr>
 *         <td><code>{@link #onTouchEvent(MotionEvent)}code>td>
 *         <td>Called when a touch screen motion event occurs.
 *         td>
 *     tr>
 *
 *     <tr>
 *         <td rowspan="2">Focustd>
 *         <td><code>{@link #onFocusChanged(boolean, int, android.graphics.Rect)}code>td>
 *         <td>Called when the view gains or loses focus.
 *         td>
 *     tr>
 *
 *     <tr>
 *         <td><code>{@link #onWindowFocusChanged(boolean)}code>td>
 *         <td>Called when the window containing the view gains or loses focus.
 *         td>
 *     tr>
 *
 *     <tr>
 *         <td rowspan="3">Attachingtd>
 *         <td><code>{@link #onAttachedToWindow()}code>td>
 *         <td>Called when the view is attached to a window.
 *         td>
 *     tr>
 *
 *     <tr>
 *         <td><code>{@link #onDetachedFromWindow}code>td>
 *         <td>Called when the view is detached from its window.
 *         td>
 *     tr>
 *
 *     <tr>
 *         <td><code>{@link #onWindowVisibilityChanged(int)}code>td>
 *         <td>Called when the visibility of the window containing the view
 *         has changed.
 *         td>
 *     tr>
 */

View通过构造方法创建,onMeasure测量,Layout方法测量需要测量自身和子View,onLayout方法分配子View,onSizeChanged方法当视图大小发生改变时调用,onDraw方法用于Canvas的绘制内容,onTouchEvent触屏时调用,onFocusChanged焦点改变时调用,onWindowFocusChanged同理,onWindowVisibilityChanged视图可见性onDetachedFromWindow取消监听和相关事件,onAttachedToWindow绑定相关事件监听。

/*     android:id="@+id/my_button"
 *     android:layout_width="wrap_content"
 *     android:layout_height="wrap_content"
 *     android:text="@string/my_button_text"
 */

XML引用View控件,并分配ID

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
              .......
        }

这里实现的三个接口,先来看Drawable.Callback

 /**
     * Implement this interface if you want to create an animated drawable that
     * extends {@link android.graphics.drawable.Drawable Drawable}.
     * Upon retrieving a drawable, use
     * {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)}
     * to supply your implementation of the interface to the drawable; it uses
     * this interface to schedule and execute animation changes.
     */
    public static interface Callback {
        /**
         * Called when the drawable needs to be redrawn.  A view at this point
         * should invalidate itself (or at least the part of itself where the
         * drawable appears).
         *
         * @param who The drawable that is requesting the update.
         */
        public void invalidateDrawable(Drawable who);

        /**
         * A Drawable can call this to schedule the next frame of its
         * animation.  An implementation can generally simply call
         * {@link android.os.Handler#postAtTime(Runnable, Object, long)} with
         * the parameters (what, who, when) to perform the
         * scheduling.
         *
         * @param who The drawable being scheduled.
         * @param what The action to execute.
         * @param when The time (in milliseconds) to run.  The timebase is
         *             {@link android.os.SystemClock#uptimeMillis}
         */
        public void scheduleDrawable(Drawable who, Runnable what, long when);

        /**
         * A Drawable can call this to unschedule an action previously
         * scheduled with {@link #scheduleDrawable}.  An implementation can
         * generally simply call
         * {@link android.os.Handler#removeCallbacks(Runnable, Object)} with
         * the parameters (what, who) to unschedule the drawable.
         *
         * @param who The drawable being unscheduled.
         * @param what The action being unscheduled.
         */
        public void unscheduleDrawable(Drawable who, Runnable what);
    }

View内部对外公开两个方法setCallback 、getCallback

 /**
     * Bind a {@link Callback} object to this Drawable.  Required for clients
     * that want to support animated drawables.
     *
     * @param cb The client's Callback implementation.
     *
     * @see #getCallback()
     */
    public final void setCallback(Callback cb) {
        mCallback = new WeakReference(cb);
    }

    /**
     * Return the current {@link Callback} implementation attached to this
     * Drawable.
     *
     * @return A {@link Callback} instance or null if no callback was set.
     *
     * @see #setCallback(android.graphics.drawable.Drawable.Callback)
     */
    public Callback getCallback() {
        if (mCallback != null) {
            return mCallback.get();
        }
        return null;
    }

如果你想扩展View动画,可以实现Drawable.Callback,然后调用setCallback传入Callback的实现类即可invalidateDrawable()方法,在drawable重画时触发,展示Drawable部分将不可用。scheduleDrawable()用于控制动画的下一帧,用户可以通过mHandler.postAtTime(Runnable,Object)调用,unscheduleDrawable()方法则是取消scheduleDrawable()该方法定义的下一帧动画。这里的setCallback并不是我们常用的this.mCallback=mCallBack而是通过弱引用(引用类型:强引用 、软引用、弱引用、虚引用)引用,以便于GC回收,避免了内存泄露。getCallback()方法通过软引用调用抽象父类的get方法间接的调用native方法getReferent()。下面再来看KeyEvent.Callback接口定义。

 public interface Callback {
        /**
         * Called when a key down event has occurred.  If you return true,
         * you can first call {@link KeyEvent#startTracking()
         * KeyEvent.startTracking()} to have the framework track the event
         * through its {@link #onKeyUp(int, KeyEvent)} and also call your
         * {@link #onKeyLongPress(int, KeyEvent)} if it occurs.
         *
         * @param keyCode The value in event.getKeyCode().
         * @param event Description of the key event.
         *
         * @return If you handled the event, return true.  If you want to allow
         *         the event to be handled by the next receiver, return false.
         */
        boolean onKeyDown(int keyCode, KeyEvent event);

        /**
         * Called when a long press has occurred.  If you return true,
         * the final key up will have {@link KeyEvent#FLAG_CANCELED} and
         * {@link KeyEvent#FLAG_CANCELED_LONG_PRESS} set.  Note that in
         * order to receive this callback, someone in the event change
         * must return true from {@link #onKeyDown} and
         * call {@link KeyEvent#startTracking()} on the event.
         *
         * @param keyCode The value in event.getKeyCode().
         * @param event Description of the key event.
         *
         * @return If you handled the event, return true.  If you want to allow
         *         the event to be handled by the next receiver, return false.
         */
        boolean onKeyLongPress(int keyCode, KeyEvent event);

        /**
         * Called when a key up event has occurred.
         *
         * @param keyCode The value in event.getKeyCode().
         * @param event Description of the key event.
         *
         * @return If you handled the event, return true.  If you want to allow
         *         the event to be handled by the next receiver, return false.
         */
        boolean onKeyUp(int keyCode, KeyEvent event);

        /**
         * Called when multiple down/up pairs of the same key have occurred
         * in a row.
         *
         * @param keyCode The value in event.getKeyCode().
         * @param count Number of pairs as returned by event.getRepeatCount().
         * @param event Description of the key event.
         *
         * @return If you handled the event, return true.  If you want to allow
         *         the event to be handled by the next receiver, return false.
         */
        boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
    }

KeyEnvent类继承自InputEvent,该类定义了许多keyCode值,例如:

 public static final int KEYCODE_SOFT_LEFT       = 1;
    /** Key code constant: Soft Right key.
     * Usually situated below the display on phones and used as a multi-function
     * feature key for selecting a software defined function shown on the bottom right
     * of the display. */
    public static final int KEYCODE_SOFT_RIGHT      = 2;
    /** Key code constant: Home key.
     * This key is handled by the framework and is never delivered to applications. */
    public static final int KEYCODE_HOME            = 3;
    /** Key code constant: Back key. */
    public static final int KEYCODE_BACK            = 4;
    /** Key code constant: Call key. */
    public static final int KEYCODE_CALL            = 5;

用户操作是只执行相应的回调函数,例如按住返回键,那么就会执行KeyEvent.Callback 的onKeyDown(KEYCODE_BACK,KeyEvent),在我们开发时,判断KeyEvent.getAction判断Action类型和keyCode值选择执行相应函数,return boolean 如果返回值为true代表处理该事件不在向下分发,false则不处理该事件。(Action是在KeyEvent构造参数时传入)Action在KeyEvent的定义如下:

 /**
     * {@link #getAction} value: the key has been pressed down.
     */
    public static final int ACTION_DOWN             = 0;
    /**
     * {@link #getAction} value: the key has been released.
     */
    public static final int ACTION_UP               = 1;
    /**
     * {@link #getAction} value: multiple duplicate key events have
     * occurred in a row, or a complex string is being delivered.  If the
     * key code is not {#link {@link #KEYCODE_UNKNOWN} then the
     * {#link {@link #getRepeatCount()} method returns the number of times
     * the given key code should be executed.
     * Otherwise, if the key code is {@link #KEYCODE_UNKNOWN}, then
     * this is a sequence of characters as returned by {@link #getCharacters}.
     */
    public static final int ACTION_MULTIPLE         = 2;
    //..........................此处略....................................

KeyEvent.Callback的onKeyUp方法也onKeyDown差别不大,区别在于事件的响应时间,一个是按下一个是抬起。View和Activity同时重写onKeyDown onkeyUp方法会先执行View的。


/**
 * This interface is implemented by classes source of {@link AccessibilityEvent}s.
 *
 * 
*

Developer Guides

*

For more information about making applications accessible, read the * Accessibility * developer guide.

*
*/
public interface AccessibilityEventSource { /** * Handles the request for sending an {@link AccessibilityEvent} given * the event type. The method must first check if accessibility is on * via calling {@link AccessibilityManager#isEnabled() AccessibilityManager.isEnabled()}, * obtain an {@link AccessibilityEvent} from the event pool through calling * {@link AccessibilityEvent#obtain(int) AccessibilityEvent.obtain(int)}, populate the * event, and send it for dispatch via calling * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent) * AccessibilityManager.sendAccessibilityEvent(AccessibilityEvent)}. * * @see AccessibilityEvent * @see AccessibilityManager * * @param eventType The event type. */ public void sendAccessibilityEvent(int eventType); /** * Handles the request for sending an {@link AccessibilityEvent}. The * method does not guarantee to check if accessibility is on before * sending the event for dispatch. It is responsibility of the caller * to do the check via calling {@link AccessibilityManager#isEnabled() * AccessibilityManager.isEnabled()}. * * @see AccessibilityEvent * @see AccessibilityManager * * @param event The event. */ public void sendAccessibilityEventUnchecked(AccessibilityEvent event); }

sendAccessibilityEventUnchecked() (API级别4)当调用代码需要直接控制检查辅助功能设备的启用时,调用该方法。 sendAccessibilityEvent()(API级别4)当用户在一个视图操作时调用此方法。事件是按照用户操作类型分类,如TYPE_VIEW_CLICKED。你通常不需要实现该方法,除非你是创建一个自定义视图。

  private static final boolean DBG = false;

定义全局final变量DBG,如果是调试模式,在调用某些方法调试时输出日志,例如:

 protected boolean setFrame(int left, int top, int right, int bottom) {
        boolean changed = false;

        if (DBG) {
            Log.d("View", this + " View.setFrame(" + left + "," + top + ","
                    + right + "," + bottom + ")");
        }
        //.......................此处略.......................
  }

Log.i/e/w(TAG,”“)方法中TAG定义:

 /**
     * The logging tag used by this class with android.util.Log.
     */
    protected static final String VIEW_LOG_TAG = "View";

下面的再来看一组变量定义:

   /**
     * When set to true, apps will draw debugging information about their layouts.
     *
     * @hide
     */
    public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout";


   /**
     * Show where the margins, bounds and layout bounds are for each view.
     */
    boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);

SystemProperties类位置在framework层,查看请点击SystemProperties,这里面的内容简单的封装了set get 方法,set get方法内部分别调用了native 方法代码如下:


/**
 * Gives access to the system properties store.  The system properties
 * store contains a list of string key-value pairs.
 *
 * {@hide}
 */
public class SystemProperties
{
    public static final int PROP_NAME_MAX = 31;
    public static final int PROP_VALUE_MAX = 91;

    private static final ArrayList sChangeCallbacks = new ArrayList();

    private static native String native_get(String key);
    private static native String native_get(String key, String def);
    private static native int native_get_int(String key, int def);
    private static native long native_get_long(String key, long def);
    private static native boolean native_get_boolean(String key, boolean def);
    private static native void native_set(String key, String def);
    private static native void native_add_change_callback();

      /**
     * Get the value for the given key, returned as a boolean.
     * Values 'n', 'no', '0', 'false' or 'off' are considered false.
     * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
     * (case sensitive).
     * If the key does not exist, or has any other value, then the default
     * result is returned.
     * @param key the key to lookup
     * @param def a default value to return
     * @return the key parsed as a boolean, or def if the key isn't found or is
     *         not able to be parsed as a boolean.
     * @throws IllegalArgumentException if the key exceeds 32 characters
     */
    public static boolean getBoolean(String key, boolean def) {
        if (key.length() > PROP_NAME_MAX) {
            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
        }
        return native_get_boolean(key, def);
    }
    //.......................其他略...............................
}

该类的作用是获取设置系统属性,进行系统属性设置的程序也必须有system或root权限,
如何将android程序的权限提升到system权限?方法是这样的:
1.在AndroidManifest.xml中,在manifest加入android:sharedUserId=”android.uid.system”。
2.在Android.mk中,將LOCAL_CERTIFICATE := XXX修改成LOCAL_CERTIFICATE :=platform。
该类参考资料:http://blog.csdn.net/ameyume/article/details/8056492

 /**
     * Used to mark a View that has no ID.
     */
    public static final int NO_ID = -1;

NO_ID变量让属性的初始值引用,代码实例如下:

public View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    //.............此处略.......................
     case com.android.internal.R.styleable.View_id:
       mID = a.getResourceId(attr, NO_ID);
       break;
     case com.android.internal.R.styleable.View_tag:
       mTag = a.getText(attr);
       break;
}

再来看一下三个变量定义:sCompatibilityDone 、sUseBrokenMakeMeasureSpec 、sIgnoreMeasureCache

   /**
     * Signals that compatibility booleans have been initialized according to
     * target SDK versions.
     */
    private static boolean sCompatibilityDone = false;
     /**
     * Use the old (broken) way of building MeasureSpecs.
     */
    private static boolean sUseBrokenMakeMeasureSpec = false;

    /**
     * Ignore any optimizations using the measure cache.
     */
    private static boolean sIgnoreMeasureCache = false;

这三个参数在构造方法里面进行了初始化赋值

 public View(Context context) {

  //..............此处略..................

  if (!sCompatibilityDone && context != null) {      
    final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
     // Older apps may need this compatibility hack for measurement.
     sUseBrokenMakeMeasureSpec = targetSdkVersion <= JELLY_BEAN_MR1;

     // Older apps expect onMeasure() to always be called on a layout pass, regardless
     // of whether a layout was requested on that View.
     sIgnoreMeasureCache = targetSdkVersion < KITKAT;
     sCompatibilityDone = true;
     }
  }

sCompatibilityDone 有何意义我还没发现,待后续研究,sUseBrokenMakeMeasureSpec 变量用于判断使用哪一种方式(老版本的方法还是新版本的方法)构建MeasureSpec,MeasureSpec是测量相关,稍后再提。

 public static int makeMeasureSpec(int size, int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

sIgnoreMeasureCache 忽略任何优化使用缓存,在measure测量方法里会用到该参数,稍后再细说measure方法。接着来看一组焦点相关的变量定义:

  /**
     * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
     * calling setFlags.
     */
    private static final int NOT_FOCUSABLE = 0x00000000;

    /**
     * This view wants keystrokes. Use with TAKES_FOCUS_MASK when calling
     * setFlags.
     */
    private static final int FOCUSABLE = 0x00000001;

    /**
     * Mask for use with setFlags indicating bits used for focus.
     */
    private static final int FOCUSABLE_MASK = 0x00000001;

在我们平时控制View的焦点响应方法setFocusable(boolean focusable)其本质就是在根据传入参数获取一个int 型 flag值,随后调用了setFlags隐藏方法;

 /**
     * Set whether this view can receive the focus.
     *
     * Setting this to false will also ensure that this view is not focusable
     * in touch mode.
     *
     * @param focusable If true, this view can receive the focus.
     *
     * @see #setFocusableInTouchMode(boolean)
     * @attr ref android.R.styleable#View_focusable
     */
    public void setFocusable(boolean focusable) {
        if (!focusable) {
            setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
        }
        setFlags(focusable ? FOCUSABLE : NOT_FOCUSABLE, FOCUSABLE_MASK);
    }

View.setVisibility也是如此(同时还判断选择调用Drawale的setVisibilty,通过回调接口Drawable.Callback的 invalidateDrawable(this)方法控制重绘)

 /**
     * Set the enabled state of this view.
     *
     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
     * @attr ref android.R.styleable#View_visibility
     */
    @RemotableViewMethod
    public void setVisibility(@Visibility int visibility) {
        setFlags(visibility, VISIBILITY_MASK);
        if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false);
    }

隐藏方法setFlag对我来说无疑是神秘的,位运算搞得我头大,查了一番资料,马马虎虎明白其原理,位运算相关知识链接点击这里,该方法的具体实现如下:

 /**
     * Set flags controlling behavior of this view.
     *
     * @param flags Constant indicating the value which should be set
     * @param mask Constant indicating the bit range that should be changed
     */
    void setFlags(int flags, int mask) {
        final boolean accessibilityEnabled =
                AccessibilityManager.getInstance(mContext).isEnabled();
        final boolean oldIncludeForAccessibility = accessibilityEnabled && includeForAccessibility();
        //old保存mViewFlags之前的值, (mViewFlags在构造方法里初始化了)     
        int old = mViewFlags;
        mViewFlags = (mViewFlags & ~mask) | (flags & mask);
        //old和mViewFlags异或后得到一个新的changed 凡是为1 说明发送了变化, 就需要View系统进行调整了!
        int changed = mViewFlags ^ old;
        if (changed == 0) {




         /*********
            什么时候能有中文版注释的系统源码,偶滴神啊,头大了
            flags = 0x0000000100 
            int old = mViewFlags; //旧的flag  = 0x00000000  
            mViewFlags = (mViewFlags & ~mask) | (flags & mask); //新的flag   
            0x00000000  & ~(00000001100) | & 00000001100   =   0x0000000100 

            int changed = mViewFlags ^ old;  = 0x0000000100 
            if (changed == 0) { 
            return; //hey ! 没有变化! 直接return!

           ************/



            return;
        }

        //...............此处略..................

        /* Check if the GONE bit has changed */
        if ((changed & GONE) != 0) {
            needGlobalAttributesUpdate(false);
            requestLayout();

            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {

                //清除view的cache
                if (hasFocus()) clearFocus();
                clearAccessibilityFocus();
                destroyDrawingCache();

                if (mParent instanceof View) {
                    // GONE views noop invalidation, so invalidate the parent
                    //重绘
                    ((View) mParent).invalidate(true);
                }
                // Mark the view drawn to ensure that it gets invalidated properly the next
                // time it is visible and gets invalidated
                mPrivateFlags |= PFLAG_DRAWN;
            }
            if (mAttachInfo != null) {
                mAttachInfo.mViewVisibilityChanged = true;
            }
        }

          //........此处略....方法说明requestLayout():重新布局;invalidate(true):重绘..........
    }

半天多时间已去,第一篇View blog也出来了,我也先学先写,差了一些资料发现了很多以前不懂的知识,向下等看完View的所有源代码,对自己会有一个很大的提升,逗比的一天接近尾声,看会儿爱哥的设计模式去,坐等下班。

你可能感兴趣的:(Android)