通過Anroid onClick函數理解JAVA回調機制

本文通過介紹Android事件監聽機制來了解下Java的回調機制,即當你點擊一個button的時候發生了什么,點擊之后是如何調到自己寫的onClick函數的。
寫過Android小程序的同學肯定都寫過如下的代碼:

        button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                ...
            }
        });

我們都知道這是在給一個Button注冊監聽事件,當點擊這個button的時候會調到里面寫的onClick函數。但是,這是怎么發生的呢?onClick函數是怎么被調用的?
其實背后運行的是JAVA的回調函數機制。也就是說onClick函數是一個回調函數,當你點擊button的時候,系統監聽到你的點擊然后回調了這個函數。
我們先簡單介紹一下JAVA的回調機制。

JAVA回調機制

回調機制

上圖形象的展示了java回調的原理思想:

  • 類A的a()方法調用類B的b()方法
  • 類B的b()方法執行完畢主動調用類A的callback()方法
    onClick函數其實就跟這里的callback() 一樣,就是回調函數。回調機制文末有鏈接可詳細了解,下面介紹一下是如何實現onClick函數的回調的。

onClick函數的調用實現

onClick函數是實現OnClickListener接口,然后通過new出一個OnclickListener的對象作為參數傳給setOnClickListener函數。
我們去 setOnClickListener 函數看一看
于是跳到了 View.java,這個方法是寫在 View中的:

public void setOnClickListener(@Nullable OnClickListener l) {
    if (!isClickable()) {
        setClickable(true);
    }
    getListenerInfo().mOnClickListener = l;
}

setOnClickListener 方法就如同我們調用時的那樣,傳入一個 OnClickListener 對象作為參數,其實它就是個接口,我們來看一看 OnClickListener 長什么樣子。

public interface OnClickListener {
    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    void onClick(View v);
}

我們應重點關注setOnClickListener 方法中的這一行
getListenerInfo().mOnClickListener = l;
它把我們傳進來的OnClickListener 對象賦給了另一個值。我們在OnclickListener對象中實現了onClick函數,所以mOnClickListener便擁有了我們onClick函數的實現。我們來看看mOnClickListener是什么。這得先看看getListenerInfo()方法

 ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

getListenerInfo()返回一個ListenerInfo,如果mListenerInfo已經存在,就返回,如果不存在,就new一個這個對象返回,這是單例模式(感興趣自行搜索,不做贅述),目的是保證只有一個ListenerInfo對象。
然后我們來看看ListenerInfo是什么

static class ListenerInfo {
        protected OnFocusChangeListener mOnFocusChangeListener;
        private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
        protected OnScrollChangeListener mOnScrollChangeListener;
        private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
        public OnClickListener mOnClickListener;
         protected OnLongClickListener mOnLongClickListener;
        protected OnContextClickListener mOnContextClickListener;
        protected OnCreateContextMenuListener mOnCreateContextMenuListener;
        private OnKeyListener mOnKeyListener;
        private OnTouchListener mOnTouchListener;
        private OnHoverListener mOnHoverListener;
        private OnGenericMotionListener mOnGenericMotionListener;
        private OnDragListener mOnDragListener;
        private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
        OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
    }

原來是一個內部靜態類,成員包括各種事件的監聽接口,其中包括
public OnClickListener mOnClickListener;
誒喲,和我們傳入的一樣的一個OnClickListener接口引用,于是繞了這么一大圈(我們先不管為啥繞),我們傳入的持有我們實現的onClick(View view)方法的OnClickListener接口對象(還記得嗎?),被賦值到了View中的mListenerInfo中的mOnClickListener對象,也就是,我們實現的onCLick(View view) 方法,被mListenerInfo.mOnClickListener持有了。
這下我們知道了我們所實現的onClick函數存到了哪里,但是他是什么時候被調用的呢?

其實這是一個異步處理機制,想象一下,系統一直在監聽著屏幕的點擊事件,在我們觸摸到屏幕的時候進行響應,這是一個線程操作,因為如果這個放在主線程,那在事件被響應之前,我們的線程都是阻塞的,因為屏幕的資源被占用了,無法進行其他操作,而在子線程中,系統監聽著屏幕的活動,然后在我們觸摸(click)時,其實是調用performClick()方法實現了點擊,并且調用了onClick(View view)方法實現了點擊事件的回調,我們就可以恰恰剛好在點擊時間觸發的時候,進行我們想要的操作,也就是我們實現的onClick方法。
我們來看下performClick()函數。

View.java
public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this); // 啦啦啦,回調!!!
            result = true;
        } else {
            result = false;
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

這個函數中通過調用li.mOnClickListener.onClick(this);實現回調我們的onClick函數。

總結

以前寫事件監聽,書上告訴怎么寫自己就怎么寫,并不知道是如何實現的,其實IDE也會自動補全這部分代碼,但是讀完這篇文章大家應該會在自動補全的時候想一想這個原理,知其然也知其所以然,了解背后的原理可能并不會加快你寫代碼的速度,但是遇到問題時應該還是會有點用的。

最后, 同學點個贊吧!!! 加個關注好么

參考文章

Java回調機制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容