1. 程式人生 > >android 輸入法刪除輸入框裡面字元流程分析

android 輸入法刪除輸入框裡面字元流程分析

我們先看刪除主要呼叫的方法,該方法在InputMethodService的繼承類子類中

getCurrentInputConnection().deleteSurroundingText(1, 0);

此getCurrentInputConnection方法拿到的物件是什麼呢?是InputConnectionWrapper類,進入該方法,【注意點一,後面將解釋getCurrentInputConnection()為什麼拿到的物件是InputConnectionWrapper】

public class InputConnectionWrapper implements InputConnection {

   public boolean deleteSurroundingText(int leftLength, int rightLength) {
    	
    	Log.d(TAG, "InputConnectionWrapper  deleteSurroundingText");
    	
        try {
            mIInputContext.deleteSurroundingText(leftLength, rightLength);
            return true;
        } catch (RemoteException e) {
            return false;
        }
    }

而上面的mIInputContext是IInputContext類物件,這個在哪實現的呢?【注意點二,後續將解釋,mIInputContext是在哪初始化的】

[email protected]:/work/project/a1001eh/frameworks/base$ grep -r "IInputContext.Stub" ./
./core/java/com/android/internal/view/IInputConnectionWrapper.java:public class IInputConnectionWrapper extends IInputContext.Stub {

好吧,我們進入到IInputConnectionWrapper中,進入該方法:

    public void deleteSurroundingText(int leftLength, int rightLength) {
        dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
            leftLength, rightLength));
    }

繼續看此訊息的處理:
          case DO_DELETE_SURROUNDING_TEXT: {
            	Log.w(TAG, "IInputConnectionWrapper class " +  "deleteSurroundingText DO_DELETE_SURROUNDING_TEXT msg");
                InputConnection ic = mInputConnection.get();
                if (ic == null || !isActive()) {
                    Log.w(TAG, "IInputConnectionWrapper class " +  "deleteSurroundingText on inactive InputConnection");
                    return;
                }
                ic.deleteSurroundingText(msg.arg1, msg.arg2);
                return;
            }

那麼這個mInputConnection.get()得到的是什麼呢?其實就是當前EditView的BaseInputConnection【注意點三,後面將解釋,為什麼這裡的mInputConnection.get()得到的是BaseInputConnection物件】,進入該BaseInputConnection類的deleteSurroundingText方法
   public boolean deleteSurroundingText(int leftLength, int rightLength) {
        if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength
                + " / " + rightLength);
        Log.d("PateoInputMethod", "BaseInputConnection deleteSurroundingText ");
        final Editable content = getEditable();
        if (content == null) return false;

        beginBatchEdit();
        
        int a = Selection.getSelectionStart(content);
        int b = Selection.getSelectionEnd(content);

        if (a > b) {
            int tmp = a;
            a = b;
            b = tmp;
        }

        // ignore the composing text.
        int ca = getComposingSpanStart(content);
        int cb = getComposingSpanEnd(content);
        if (cb < ca) {
            int tmp = ca;
            ca = cb;
            cb = tmp;
        }
        if (ca != -1 && cb != -1) {
            if (ca < a) a = ca;
            if (cb > b) b = cb;
        }

        int deleted = 0;

        if (leftLength > 0) {
            int start = a - leftLength;
            if (start < 0) start = 0;
            content.delete(start, a);
            deleted = a - start;
        }

        if (rightLength > 0) {
            b = b - deleted;

            int end = b + rightLength;
            if (end > content.length()) end = content.length();

            content.delete(b, end);
        }
        
        endBatchEdit();
        
        return true;
    }

到這裡,我需要解釋上面的三個注意點了

第三個注意點:

【注意點三,後面將解釋,為什麼這裡的mInputConnection.get()得到的是BaseInputConnection物件】

我們先來看下mInputConnection.get()的初始化,其在IInputConnectionWrapper類中

    public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
        mInputConnection = new WeakReference<InputConnection>(conn);
        mMainLooper = mainLooper;
        mH = new MyHandler(mMainLooper);
    }

再看看IInputConnectionWrapper這個構造方法在哪被呼叫的,我們看到InputMethodManager中有個IInputConnectionWrapper的整合類中呼叫了此方法
   private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
        private final InputMethodManager mParentInputMethodManager;

        public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
                final InputMethodManager inputMethodManager) {
            super(mainLooper, conn);
            mParentInputMethodManager = inputMethodManager;
        }

        @Override
        public boolean isActive() {
            return mParentInputMethodManager.mActive;
        }
    }

那麼上面的super(mainLooper, conn)的呼叫處的ControlledInputConnectionWrapper構造方法又是在哪被呼叫的呢?在InputMethodManager類的startInputInner方法中,到這裡比較清晰了點,程式碼如下:
    boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
            int windowFlags) {
    	
        final View view;
        synchronized (mH) {
            view = mServedView;
            
            // Make sure we have a window token for the served view.
            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: view=" + view);
            if (view == null) {
                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "ABORT input: no served view!");
                return false;
            }
        }
        
        // Now we need to get an input connection from the served view.
        // This is complicated in a couple ways: we can't be holding our lock
        // when calling out to the view, and we need to make sure we call into
        // the view on the same thread that is driving its view hierarchy.
        Handler vh = view.getHandler();
        if (vh == null) {
            // If the view doesn't have a handler, something has changed out
            // from under us, so just bail.
            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "ABORT input: no handler for view!");
            return false;
        }
        if (vh.getLooper() != Looper.myLooper()) {
            // The view is running on a different thread than our own, so
            // we need to reschedule our work for over there.
            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: reschedule to view thread");
            vh.post(new Runnable() {
                public void run() {
                	 Log.d(TAG, "InputMethodManager class startInputInner method");
                    startInputInner(null, 0, 0, 0);
                }
            });
            return false;
        }
        
        // Okay we are now ready to call into the served view and have it
        // do its stuff.
        // Life is good: let's hook everything up!
        EditorInfo tba = new EditorInfo();
        tba.packageName = view.getContext().getPackageName();
        tba.fieldId = view.getId();
        InputConnection ic = view.onCreateInputConnection(tba);
        if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: tba=" + tba + " ic=" + ic);
        
        synchronized (mH) {
            // Now that we are locked again, validate that our state hasn't
            // changed.
            if (mServedView != view || !mServedConnecting) {
                // Something else happened, so abort.
                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  
                        "Starting input: finished by someone else (view="
                        + mServedView + " conn=" + mServedConnecting + ")");
                return false;
            }

            // If we already have a text box, then this view is already
            // connected so we want to restart it.
            if (mCurrentTextBoxAttribute == null) {
                controlFlags |= CONTROL_START_INITIAL;
            }
            
            // Hook 'em up and let 'er rip.
            mCurrentTextBoxAttribute = tba;
            mServedConnecting = false;
            mServedInputConnection = ic;
            IInputContext servedContext;
            if (ic != null) {
                mCursorSelStart = tba.initialSelStart;
                mCursorSelEnd = tba.initialSelEnd;
                mCursorCandStart = -1;
                mCursorCandEnd = -1;
                mCursorRect.setEmpty();
                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
            } else {
                servedContext = null;
            }
            
            try {
                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "START INPUT: " + view + " ic="
                        + ic + " tba=" + tba + " controlFlags=#"
                        + Integer.toHexString(controlFlags));
                InputBindResult res;
                if (windowGainingFocus != null) {
                    res = mService.windowGainedFocus(mClient, windowGainingFocus,
                            controlFlags, softInputMode, windowFlags,
                            tba, servedContext);
                } else {
                    res = mService.startInput(mClient,
                            servedContext, tba, controlFlags);
                }

上面的servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);中的ic是在該方法中被賦值的,看下:

InputConnection ic = view.onCreateInputConnection(tba);

追蹤其view
    /**
     * This is the view that should currently be served by an input method,
     * regardless of the state of setting that up.
     */
    View mServedView;

跟蹤mServedView的賦值
   private boolean checkFocusNoStartInput(boolean forceNewFocus) {
        // This is called a lot, so short-circuit before locking.
        if (mServedView == mNextServedView && !forceNewFocus) {
            return false;
        }

        InputConnection ic = null;
        synchronized (mH) {
            if (mServedView == mNextServedView && !forceNewFocus) {
                return false;
            }
            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "checkFocus: view=" + mServedView
                    + " next=" + mNextServedView
                    + " forceNewFocus=" + forceNewFocus);

            if (mNextServedView == null) {
                finishInputLocked();
                // In this case, we used to have a focused view on the window,
                // but no longer do.  We should make sure the input method is
                // no longer shown, since it serves no purpose.
                closeCurrentInput();
                return false;
            }
            if (DEBUG) Log.v(TAG,"mServedInputConnection=" + mServedInputConnection);
            ic = mServedInputConnection;

            mServedView = mNextServedView;
            mCurrentTextBoxAttribute = null;
            mCompletions = null;
            mServedConnecting = true;
        }

        if (ic != null) {
            ic.finishComposingText();
        }

        return true;
    }

繼續跟蹤mNextServedView
    void focusInLocked(View view) {
        if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "focusIn: " + view);
        
        if (mCurRootView != view.getRootView()) {
            // This is a request from a window that isn't in the window with
            // IME focus, so ignore it.
            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Not IME target window, ignoring");
            return;
        }
        
        mNextServedView = view;
        scheduleCheckFocusLocked(view);
    }

上面這個focusInLocked(View view) 又是被誰呼叫的呢?
   /**
     * Call this when a view receives focus.
     * @hide
     */
    public void focusIn(View view) {
        synchronized (mH) {
            focusInLocked(view);
        }
    }

上面的focus方法又是在哪被呼叫的呢?是在view中,如下:
    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
        if (gainFocus) {
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        }

        InputMethodManager imm = InputMethodManager.peekInstance();
        if (!gainFocus) {
            if (isPressed()) {
                setPressed(false);
            }
            if (imm != null && mAttachInfo != null
                    && mAttachInfo.mHasWindowFocus) {
              	Log.d(TAG, "View class onFocusChanged method InputMethodManager focusOut");
                imm.focusOut(this);
            }
            onFocusLost();
        } else if (imm != null && mAttachInfo != null
                && mAttachInfo.mHasWindowFocus) {
        	Log.d(TAG, "View class onFocusChanged method InputMethodManager focusIn");
            imm.focusIn(this);
        }

而這個view是什麼呢?而這個this就可能是view的繼承物件比如TextView,或繼承TextView的EditText,而上面的InputConnection ic = view.onCreateInputConnection(tba);實際就是呼叫了它的子類TextView中的onCreateInputConnection方法:
    @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
    	
    	Log.d(TAG, "TextView class onCreateInputConnection method start");
    	
        if (onCheckIsTextEditor() && isEnabled()) {
            if (mInputMethodState == null) {
                mInputMethodState = new InputMethodState();
            }
            outAttrs.inputType = mInputType;
            if (mInputContentType != null) {
                outAttrs.imeOptions = mInputContentType.imeOptions;
                outAttrs.privateImeOptions = mInputContentType.privateImeOptions;
                outAttrs.actionLabel = mInputContentType.imeActionLabel;
                outAttrs.actionId = mInputContentType.imeActionId;
                outAttrs.extras = mInputContentType.extras;
            } else {
                outAttrs.imeOptions = EditorInfo.IME_NULL;
            }
//            if (focusSearch(FOCUS_DOWN) != null) {
//                outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
//            }
//            if (focusSearch(FOCUS_UP) != null) {
//                outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
//            }
//            if ((outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION)
//                    == EditorInfo.IME_ACTION_UNSPECIFIED) {
//                if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0) {
//                    // An action has not been set, but the enter key will move to
//                    // the next focus, so set the action to that.
//                    outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
//                } else {
//                    // An action has not been set, and there is no focus to move
//                    // to, so let's just supply a "done" action.
//                    outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
//                }
//                if (!shouldAdvanceFocusOnEnter()) {
//                    outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
//                }
//            }
//            if (isMultilineInputType(outAttrs.inputType)) {
//                // Multi-line text editors should always show an enter key.
//                outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
//            }
            outAttrs.hintText = mHint;
            if (mText instanceof Editable) {
            	Log.d(TAG, "TextView class onCreateInputConnection return EditableInputConnection");
                InputConnection ic = new EditableInputConnection(this);
                outAttrs.initialSelStart = getSelectionStart();
                outAttrs.initialSelEnd = getSelectionEnd();
                outAttrs.initialCapsMode = ic.getCursorCapsMode(mInputType);
                return ic;
            }
        }
        return null;
    }

上面的注意下:public class EditableInputConnection extends BaseInputConnection 現在你明白了吧:mInputConnection.get()得到的就是BaseInputConnection物件

第一個注意點

注意點一,後面將解釋getCurrentInputConnection()為什麼拿到的物件是InputConnectionWrapper

當游標進入輸入框的時候,會走入初始化DO_START_INPUT訊息,在這裡進行了InputConnectionWrapper物件的生成

            case DO_START_INPUT: {
            	Log.w(TAG, "==================================================>IInputMethodWrapper class " + " DO_START_INPUT msg");
                HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
                IInputContext inputContext = (IInputContext)args.arg1;
                InputConnection ic = inputContext != null
                        ? new InputConnectionWrapper(inputContext) : null;
                EditorInfo info = (EditorInfo)args.arg2;
                info.makeCompatible(mTargetSdkVersion);
                inputMethod.startInput(ic, info);
                return;
            }

在這裡進行了new InputConnectionWrapper(inputContext)其inputContext又是哪來的呢?
    public void startInput(IInputContext inputContext, EditorInfo attribute) {
        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
                inputContext, attribute));
    }

最終跟蹤到InputMethodManagerService類中
            case MSG_START_INPUT:
            	Slog.d(TAG,"InputMethodManagerService class MSG_START_INPUT msg ");
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    SessionState session = (SessionState)args.arg1;
                    setEnabledSessionInMainThread(session);
                    session.method.startInput((IInputContext)args.arg2,
                            (EditorInfo)args.arg3);
                } catch (RemoteException e) {
                }
                return true;

    InputBindResult attachNewInputLocked(boolean initial) {
    	if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" +  "attachNewInputLocked method coming...,mBoundToMethod=" + mBoundToMethod + " ,initial=" + initial);
        if (!mBoundToMethod) {
            executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                    MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
            mBoundToMethod = true;
        }
        final SessionState session = mCurClient.curSession;
        if (initial) {
            executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
                    MSG_START_INPUT, session, mCurInputContext, mCurAttribute));

  InputBindResult startInputUncheckedLocked(ClientState cs,
            IInputContext inputContext, EditorInfo attribute, int controlFlags) {
        // If no method is currently selected, do nothing.
        if (mCurMethodId == null) {
            return mNoBinding;
        }

        if (mCurClient != cs) {
            // If the client is changing, we need to switch over to the new
            // one.
            unbindCurrentClientLocked();
            if (DEBUG) Slog.v(TAG, "InputMethodManagerService class" +  "switching to client: client = "
                    + cs.client.asBinder());

            // If the screen is on, inform the new client it is active
            if (mScreenOn) {
                try {
                    cs.client.setActive(mScreenOn);
                } catch (RemoteException e) {
                    Slog.w(TAG, "InputMethodManagerService class" +  "Got RemoteException sending setActive notification to pid "
                            + cs.pid + " uid " + cs.uid);
                }
            }
        }

        // Bump up the sequence for this client and attach it.
        mCurSeq++;
        if (mCurSeq <= 0) mCurSeq = 1;
        mCurClient = cs;
        mCurInputContext = inputContext;

  InputBindResult startInputLocked(IInputMethodClient client,
            IInputContext inputContext, EditorInfo attribute, int controlFlags) {

    @Override
    public InputBindResult startInput(IInputMethodClient client,
            IInputContext inputContext, EditorInfo attribute, int controlFlags) {
        synchronized (mMethodMap) {
            final long ident = Binder.clearCallingIdentity();
            try {
                return startInputLocked(client, inputContext, attribute, controlFlags);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
            int windowFlags) {
    	
        final View view;
        synchronized (mH) {
            view = mServedView;
            
            // Make sure we have a window token for the served view.
            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: view=" + view);
            if (view == null) {
                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "ABORT input: no served view!");
                return false;
            }
        }
        
        // Now we need to get an input connection from the served view.
        // This is complicated in a couple ways: we can't be holding our lock
        // when calling out to the view, and we need to make sure we call into
        // the view on the same thread that is driving its view hierarchy.
        Handler vh = view.getHandler();
        if (vh == null) {
            // If the view doesn't have a handler, something has changed out
            // from under us, so just bail.
            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "ABORT input: no handler for view!");
            return false;
        }
        if (vh.getLooper() != Looper.myLooper()) {
            // The view is running on a different thread than our own, so
            // we need to reschedule our work for over there.
            if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: reschedule to view thread");
            vh.post(new Runnable() {
                public void run() {
                	 Log.d(TAG, "InputMethodManager class startInputInner method");
                    startInputInner(null, 0, 0, 0);
                }
            });
            return false;
        }
        
        // Okay we are now ready to call into the served view and have it
        // do its stuff.
        // Life is good: let's hook everything up!
        EditorInfo tba = new EditorInfo();
        tba.packageName = view.getContext().getPackageName();
        tba.fieldId = view.getId();
        InputConnection ic = view.onCreateInputConnection(tba);
        if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "Starting input: tba=" + tba + " ic=" + ic);
        
        synchronized (mH) {
            // Now that we are locked again, validate that our state hasn't
            // changed.
            if (mServedView != view || !mServedConnecting) {
                // Something else happened, so abort.
                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  
                        "Starting input: finished by someone else (view="
                        + mServedView + " conn=" + mServedConnecting + ")");
                return false;
            }

            // If we already have a text box, then this view is already
            // connected so we want to restart it.
            if (mCurrentTextBoxAttribute == null) {
                controlFlags |= CONTROL_START_INITIAL;
            }
            
            // Hook 'em up and let 'er rip.
            mCurrentTextBoxAttribute = tba;
            mServedConnecting = false;
            mServedInputConnection = ic;
            IInputContext servedContext;
            if (ic != null) {
                mCursorSelStart = tba.initialSelStart;
                mCursorSelEnd = tba.initialSelEnd;
                mCursorCandStart = -1;
                mCursorCandEnd = -1;
                mCursorRect.setEmpty();
                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
            } else {
                servedContext = null;
            }
            
            try {
                if (DEBUG) Log.v(TAG,"InputMethodManager class " +  "START INPUT: " + view + " ic="
                        + ic + " tba=" + tba + " controlFlags=#"
                        + Integer.toHexString(controlFlags));
                InputBindResult res;
                if (windowGainingFocus != null) {
                    res = mService.windowGainedFocus(mClient, windowGainingFocus,
                            controlFlags, softInputMode, windowFlags,
                            tba, servedContext);
                } else {
                    res = mService.startInput(mClient,
                            servedContext, tba, controlFlags);
主要:
       InputConnection ic = view.onCreateInputConnection(tba);

上面還有一句

servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);

即把mServedInputConnection = ic放入了mInputConnection = new WeakReference<InputConnection>(conn);中方便後續get
    private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
        private final InputMethodManager mParentInputMethodManager;

        public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
                final InputMethodManager inputMethodManager) {
            super(mainLooper, conn);
            mParentInputMethodManager = inputMethodManager;
        }

        @Override
        public boolean isActive() {
            return mParentInputMethodManager.mActive;
        }
    }

    public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
        mInputConnection = new WeakReference<InputConnection>(conn);
        mMainLooper = mainLooper;
        mH = new MyHandler(mMainLooper);
    }


上面基本說清楚了,但是如果我們再擴充套件下有關IInputContextCallback.Stub,則會看到如下一些內容

   public CharSequence getSelectedText(int flags) {
        CharSequence value = null;
        try {
            InputContextCallback callback = InputContextCallback.getInstance();
            mIInputContext.getSelectedText(flags, callback.mSeq, callback);
            synchronized (callback) {
                callback.waitForResultLocked();
                if (callback.mHaveValue) {
                    value = callback.mSelectedText;
                }
            }
            callback.dispose();
        } catch (RemoteException e) {
            return null;
        }
        return value;
    }

我們看到callback傳入了mIInputContext.getSelectedText方法,看下下面

[email protected]:/work/project/a1001eh/frameworks/base$ grep -r "IInputContext.Stub" ./
./core/java/com/android/internal/view/IInputConnectionWrapper.java:public class IInputConnectionWrapper extends IInputContext.Stub {

    public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
        dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
    }

           case DO_GET_SELECTED_TEXT: {
                SomeArgs args = (SomeArgs)msg.obj;
                try {
                    InputConnection ic = mInputConnection.get();
                    if (ic == null || !isActive()) {
                        Log.w(TAG, "IInputConnectionWrapper class " +  "getSelectedText on inactive InputConnection");
                        args.callback.setSelectedText(null, args.seq);
                        return;
                    }
                    args.callback.setSelectedText(ic.getSelectedText(
                            msg.arg1), args.seq);
                } catch (RemoteException e) {
                    Log.w(TAG, "IInputConnectionWrapper class " +  "Got RemoteException calling setSelectedText", e);
                }
                return;
            }

再上面看到了又回調了setSelectedText方法
 args.callback.setSelectedText(null, args.seq);

args.callback.setSelectedText(ic.getSelectedText(
                            msg.arg1), args.seq);
進入back這個類,看看有哪些方法:
   static class InputContextCallback extends IInputContextCallback.Stub {
        
        public int mSeq;
        public boolean mHaveValue;
        public CharSequence mTextBeforeCursor;
        public CharSequence mTextAfterCursor;
        public CharSequence mSelectedText;
        public ExtractedText mExtractedText;
        public int mCursorCapsMode;
        
        // A 'pool' of one InputContextCallback.  Each ICW request will attempt to gain
        // exclusive access to this object.
        private static InputContextCallback sInstance = new InputContextCallback();
        private static int sSequenceNumber = 1;
        
        /**
         * Returns an InputContextCallback object that is guaranteed not to be in use by
         * any other thread.  The returned object's 'have value' flag is cleared and its expected
         * sequence number is set to a new integer.  We use a sequence number so that replies that
         * occur after a timeout has expired are not interpreted as replies to a later request.
         */
        private static InputContextCallback getInstance() {
            synchronized (InputContextCallback.class) {
                // Return sInstance if it's non-null, otherwise construct a new callback
                InputContextCallback callback;
                if (sInstance != null) {
                    callback = sInstance;
                    sInstance = null;
                    
                    // Reset the callback
                    callback.mHaveValue = false;
                } else {
                    callback = new InputContextCallback();
                }
                
                // Set the sequence number
                callback.mSeq = sSequenceNumber++;
                return callback;
            }
        }
        
        /**
         * Makes the given InputContextCallback available for use in the future.
         */
        private void dispose() {
            synchronized (InputContextCallback.class) {
                // If sInstance is non-null, just let this object be garbage-collected
                if (sInstance == null) {
                    // Allow any objects being held to be gc'ed
                    mTextAfterCursor = null;
                    mTextBeforeCursor = null;
                    mExtractedText = null;
                    sInstance = this;
                }
            }
        }
        
        public void setTextBeforeCursor(CharSequence textBeforeCursor, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
                    mTextBeforeCursor = textBeforeCursor;
                    mHaveValue = true;
                    notifyAll();
                } else {
                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
                            + ") in setTextBeforeCursor, ignoring.");
                }
            }
        }

        public void setTextAfterCursor(CharSequence textAfterCursor, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
                    mTextAfterCursor = textAfterCursor;
                    mHaveValue = true;
                    notifyAll();
                } else {
                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
                            + ") in setTextAfterCursor, ignoring.");
                }
            }
        }

        public void setSelectedText(CharSequence selectedText, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
                    mSelectedText = selectedText;
                    mHaveValue = true;
                    notifyAll();
                } else {
                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
                            + ") in setSelectedText, ignoring.");
                }
            }
        }

        public void setCursorCapsMode(int capsMode, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
                    mCursorCapsMode = capsMode; 
                    mHaveValue = true;  
                    notifyAll();
                } else {
                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
                            + ") in setCursorCapsMode, ignoring.");
                }
            }
        }

        public void setExtractedText(ExtractedText extractedText, int seq) {
            synchronized (this) {
                if (seq == mSeq) {
                    mExtractedText = extractedText;
                    mHaveValue = true;
                    notifyAll();
                } else {
                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
                            + ") in setExtractedText, ignoring.");
                }
            }
        }
        
        /**
         * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
         * 
         * <p>The caller must be synchronized on this callback object.
         */
        void waitForResultLocked() {
            long startTime = SystemClock.uptimeMillis();
            long endTime = startTime + MAX_WAIT_TIME_MILLIS;

            while (!mHaveValue) {
                long remainingTime = endTime - SystemClock.uptimeMillis();
                if (remainingTime <= 0) {
                    Log.w(TAG, "Timed out waiting on IInputContextCallback");
                    return;
                }
                try {
                    wait(remainingTime);
                } catch (InterruptedException e) {
                }
            }
        }
    }



相關推薦

android 輸入法刪除輸入裡面字元流程分析

我們先看刪除主要呼叫的方法,該方法在InputMethodService的繼承類子類中 getCurrentInputConnection().deleteSurroundingText(1, 0);此getCurrentInputConnection方法拿到的物件是

Android 最多輸入30個字元就不能輸入,彈出提示提醒

android顯示edittext最多輸入字元,Android 中的EditText最大可輸入字元數可以通過xml檔案中為EditText設定maxLength屬性或者在程式碼中為EditText設定L

移動端輸入法擋住輸入

load lin onload ice spa red init event -c 問題:在移動端火狐瀏覽下,輸入框鍵盤遮擋住 解決方案: ? element.scrollIntoView():讓元素滾動到可視區域? 參數:true 對象的頂端與當前窗口的頂部對齊???

處理安卓手機輸入法遮擋輸入問題

 // 處理安卓手機輸入法遮擋輸入框問題     if ((/Android/gi).test(navigator.userAgent)) {         window.addEventListener('resize', f

Android四方形輸入、密碼

Android四方形輸入框、密碼框 一、自定義view繼承EditText,程式碼中控制輸入框的個數和是否為可見密碼格式 package com.example.testcostumview.inputbox; import android.content

react實現刪除輸入內容

react中實現刪除輸入框中的內容 import React,{Component} from 'react' class Clear extends Component{ constructor(props){ super(props) this.state={ data:

Android 支付密碼輸入,自定義EditText實現密碼輸入功能;

剛擼出來的密碼輸入框,註釋和邏輯看著挺清晰的,一些屬性還沒有新增,下個部落格把屬性新增上去; 看一下圖: 直接看程式碼吧! import android.content.Context; import android.graphics.Canvas; import android.

android 讓EditText輸入失去焦點(親測可用)

在顯示一些記錄的時候,不希望EditText有游標,也就是失去焦點,可以通過呼叫edittext.clearFocus()來實現,但clearFocus()的會將焦點移到Activity的第一個View。如果不幸你的EditText就是第一個View,那麼單純地呼叫clear

Android開發之輸入EditText介紹

這篇文章主要為大家詳細介紹了Android佈局之輸入框EditText設計,具有一定的參考價值,感興趣的小夥伴們可以參考一下 現在先簡單介紹一下技術點: 1.如何使用圓角輸入框和按鈕背景 2.如何實現“手機號”、“密碼”後面的豎線 3.如何巢狀輸入框的佈局 4.

Android自定義輸入

public class InputCustomDialog extends Dialog { public InputCustomDialog(Context context, int theme) { super(context, theme);

Android 按鈕以及輸入的樣式

<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="recta

Android自定義輸入樣式

資料來自:菜鳥教程自行編寫一個ShapeDrawable的資原始檔!然後TextView將blackgroung 設定為這個drawable資源即可!shapeDrawable資原始檔的幾個節點以及屬性

jQuery實現動態新增刪除輸入

在一個專案中,一個實體需要動態新增另外的實體多個,即在新增或者編輯這個實體的時候,需要動態新增輸入框。利用jQuery簡單實現了一下,小有成就感。 <%if(isAdd){%>   <div class="am-g am-form-group am-mar

ios textView 輸入時,輸入裡面的文字上下浮動Bug

在寫一個類似微信聊天框的功能時,遇到了這個bug 最後的解決是在無意中發現的 頁面中 有一個tableView 和 一個 view 組成,view 上面有個聊天框 就是textView view 應該是在上面的  也是無心  在把檢視加到view上面時 [self.view

JS學習筆記 - fgm練習 - 限制輸入字元型別 正則 和 || 或運算子的運用 i++和++i

  <script> window.onload = function(){ var aInp = document.getElementsByTagName('input'); var oSum = document.getElemen

對頁面不能為空的檢驗,在輸入裡面提示不能為空的封裝

                <form action="">                     <div class="group">                         <label for="">留言內容:<

iOS 限制輸入字元長度

目的達到長度可以輸入,但是顯示和獲取的資料永遠都是限定的長度 1、對textfield進行監聽 [TextFieldaddTarget:selfaction:@selector(textFie

解決pycharm/Webstorm中文輸入法游標/輸入不能跟隨問題

系統環境:win10 64Bit IDE:Pycharm 2018.1.1(Professional Edition) 輸入法:搜狗智慧輸入法 原因其實還是Pycharm自帶JVM執行環境的鍋,大家沒有更改Pycharm自帶jvm之前紅框內應該是,下面圖片

輸入裡面的值通過正則匹配改變導致的游標問題(坑!!)

前言需求是這樣的,輸入加油卡號,每隔輸入4位自動加上一個橫槓,如圖:這個游標問題是個坑,,加班到10點還沒解決好。。。解決方法首先,這裡我使用的方法是監聽輸入,使用正則匹配。 <p className="addcard_tip">卡號:</p> &l

UITextField輸入中是否有個叉號(用於一次性刪除輸入中的內容)

//輸入框中是否有個叉號,在什麼時候顯示,用於一次性刪除輸入框中的內容   text.clearButtonMode = UITextFieldViewModeAlways; typedef enum {     UITextFieldViewModeNever,