[컴][안드로이드] action bar 의 menu item 선택시 소스 경로, flow

action bar menu item click 시 routine / 관련 소스 분석

  • com.grabeco 는 application 의 activity 이다.
  • source version : 4.2.2 r1 source

Stack trace

onOptionsItemSelected():106, MainActivity {com.grabeco}
onMenuItemSelected():2548, Activity {android.app}
onMenuItemSelected():366, FragmentActivity {android.support.v4.app}
superOnMenuItemSelected():232, ActionBarActivity {android.support.v7.app}
onMenuItemSelected():159, ActionBarActivityDelegateICS {android.support.v7.app}
onMenuItemSelected():130, ActionBarActivity {android.support.v7.app}
onMenuItemSelected():295, ActionBarActivityDelegateICS$WindowCallbackWrapper {android.support.v7.app}
onMenuItemSelected():980, PhoneWindow {com.android.internal.policy.impl}
dispatchMenuItemSelected():735, MenuBuilder {com.android.internal.view.menu}
invoke():149, MenuItemImpl {com.android.internal.view.menu}
performItemAction():874, MenuBuilder {com.android.internal.view.menu}
onItemClick():156, MenuPopupHelper {com.android.internal.view.menu}
performItemClick():298, AdapterView {android.widget}
performItemClick():1100, AbsListView {android.widget}
run():2749, AbsListView$PerformClick {android.widget}
run():3423, AbsListView$1 {android.widget}
handleCallback():725, Handler {android.os}
dispatchMessage():92, Handler {android.os}
loop():137, Looper {android.os}
main():5041, ActivityThread {android.app}



Flow with Code snippet

public static void main(String[] args) {
  ...
  Looper.prepareMainLooper();
  ..
  Looper.loop();


public static void loop() {
  final Looper me = myLooper();
  ...
  final MessageQueue queue = me.mQueue;
  ...
  for (;;) {
    Message msg = queue.next(); // might block
    ...
    msg.target.dispatchMessage(msg);
    ...
    msg.recycle();
  }


public void dispatchMessage(Message msg) {
     if (msg.callback != null) {
         handleCallback(msg);
     } else {
         if (mCallback != null) {
             if (mCallback.handleMessage(msg)) {
                 return;
             }
         }
         handleMessage(msg);
    }
}



private static void handleCallback(Message message) {
  message.callback.run();
}


android.widget.AbsListView
public boolean onTouchEvent(MotionEvent ev) {
  ...
  case MotionEvent.ACTION_UP: {
    switch (mTouchMode) {
      case TOUCH_MODE_DOWN:
      case TOUCH_MODE_TAP:
      case TOUCH_MODE_DONE_WAITING:
       if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
           ...
           if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
             mTouchModeReset = new Runnable() {
                 @Override
                 public void run() {
                     mTouchMode = TOUCH_MODE_REST;
                     child.setPressed(false);
                     setPressed(false);
                     if (!mDataChanged) {
                         performClick.run();
                     }
                 }
             };

             postDelayed(mTouchModeReset,
                     ViewConfiguration.getPressedStateDuration());
           }
             ...

}


private class PerformClick extends WindowRunnnable implements Runnable {
  public void run() {
    ...
    if (adapter != null && mItemCount > 0 &&
      motionPosition != INVALID_POSITION &&
      motionPosition < adapter.getCount() && sameWindow()) {
        final View view = getChildAt(motionPosition - mFirstPosition);
        if (view != null) {
            performItemClick(view, motionPosition, adapter.getItemId(motionPosition));
        }
    }
  }
  ...
}


public boolean performItemClick(View view, int position, long id) {
  ...
  boolean dispatchItemClick = true;
  ...
  if (dispatchItemClick) {
      handled |= super.performItemClick(view, position, id);
  }
  return handled;
}



public boolean performItemClick(View view, int position, long id) {
    if (mOnItemClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        if (view != null) {
            view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        }
        mOnItemClickListener.onItemClick(this, view, position, id);
        return true;
    }
    return false;
}


public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  MenuAdapter adapter = mAdapter;
  adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
}

public boolean performItemAction(MenuItem item, int flags) {
  MenuItemImpl itemImpl = (MenuItemImpl) item;
  
  if (itemImpl == null || !itemImpl.isEnabled()) {
      return false;
  }
  boolean invoked = itemImpl.invoke();
  ...
}



public boolean invoke() {
  if (mClickListener != null &&
      mClickListener.onMenuItemClick(this)) {
      return true;
  }
  if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
      return true;
  }
  ...
}

boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
  return mCallback != null && mCallback.onMenuItemSelected(menu, item);
}

public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
  final Callback cb = getCallback();
  if (cb != null && !isDestroyed()) {
      final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
      if (panel != null) {
          return cb.onMenuItemSelected(panel.featureId, item);
      }
  }
  return false;
}

...
android.app.Activity
public boolean onMenuItemSelected(int featureId, MenuItem item) {
    switch (featureId) {
        case Window.FEATURE_OPTIONS_PANEL:
            // Put event logging here so it gets called even if subclass
            // doesn't call through to superclass's implmeentation of each
            // of these methods below
            EventLog.writeEvent(50000, 0, item.getTitleCondensed());
            if (onOptionsItemSelected(item)) {
                return true;
            }
    ...
}


Looper 와 message queue

ref.1 의 그림을 보면, looper 와 message queue 의 관계를 좀 더 잘 이해할 수 있다. 대략적으로 이해한 바를 적어본다면,

  • looper 는 일 처리를 하는 곳이고,
  • message queue 는 event 가 발생할 때 event 를 looper 에 전달하기 위한 통로이다.

정도가 되겠다.


References

  1. 안드로이드의 Handler #1. 설명 with Looper+MessageQueue



댓글 없음:

댓글 쓰기