android 输入法删除输入框里面字符流程分析
2014-04-29 16:33
411 查看
我们先看删除主要调用的方法,该方法在InputMethodService的继承类子类中
此getCurrentInputConnection方法拿到的对象是什么呢?是InputConnectionWrapper类,进入该方法,【注意点一,后面将解释getCurrentInputConnection()为什么拿到的对象是InputConnectionWrapper】
而上面的mIInputContext是IInputContext类对象,这个在哪实现的呢?【注意点二,后续将解释,mIInputContext是在哪初始化的】
pateo@pateo-B86N53X:/work/project/a1001eh/frameworks/base$ grep -r "IInputContext.Stub" ./
./core/java/com/android/internal/view/IInputConnectionWrapper.java:public class IInputConnectionWrapper extends IInputContext.Stub {
好吧,我们进入到IInputConnectionWrapper中,进入该方法:
继续看此消息的处理:
那么这个mInputConnection.get()得到的是什么呢?其实就是当前EditView的BaseInputConnection【注意点三,后面将解释,为什么这里的mInputConnection.get()得到的是BaseInputConnection对象】,进入该BaseInputConnection类的deleteSurroundingText方法
到这里,我需要解释上面的三个注意点了
第三个注意点:
【注意点三,后面将解释,为什么这里的mInputConnection.get()得到的是BaseInputConnection对象】
我们先来看下mInputConnection.get()的初始化,其在IInputConnectionWrapper类中
再看看IInputConnectionWrapper这个构造方法在哪被调用的,我们看到InputMethodManager中有个IInputConnectionWrapper的集成类中调用了此方法
那么上面的super(mainLooper, conn)的调用处的ControlledInputConnectionWrapper构造方法又是在哪被调用的呢?在InputMethodManager类的startInputInner方法中,到这里比较清晰了点,代码如下:
上面的servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);中的ic是在该方法中被赋值的,看下:
追踪其view
跟踪mServedView的赋值
继续跟踪mNextServedView
上面这个focusInLocked(View view) 又是被谁调用的呢?
上面的focus方法又是在哪被调用的呢?是在view中,如下:
而这个view是什么呢?而这个this就可能是view的继承对象比如TextView,或继承TextView的EditText,而上面的InputConnection ic = view.onCreateInputConnection(tba);实际就是调用了它的子类TextView中的onCreateInputConnection方法:
上面的注意下:public class EditableInputConnection extends BaseInputConnection 现在你明白了吧:mInputConnection.get()得到的就是BaseInputConnection对象
第一个注意点
注意点一,后面将解释getCurrentInputConnection()为什么拿到的对象是InputConnectionWrapper
当光标进入输入框的时候,会走入初始化DO_START_INPUT消息,在这里进行了InputConnectionWrapper对象的生成
在这里进行了new InputConnectionWrapper(inputContext)其inputContext又是哪来的呢?
最终跟踪到InputMethodManagerService类中
上面还有一句
即把mServedInputConnection = ic放入了mInputConnection = new WeakReference<InputConnection>(conn);中方便后续get
上面基本说清楚了,但是如果我们再扩展下有关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方法,看下下面
pateo@pateo-B86N53X:/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));
}
再上面看到了又回调了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) {
}
}
}
}
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是在哪初始化的】
pateo@pateo-B86N53X:/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方法,看下下面
pateo@pateo-B86N53X:/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 点击输入框调出输入法流程分析
- Android InputMethod 源码分析,显示输入法流程
- android 【点击输入框调出输入法前的】输入框获取焦点和输入法的初始化分析
- Android调用系统软键盘删除键 以达到删除输入框里面emoji表情的功能
- android back键后的输入法的流程分析
- [Android][KK][SMS]Frameworks学习——接收短信流程分析
- Android 7.0 ActivityManagerService(9) 进程管理相关流程分析(3) computeOomAdjLocked
- Android待机流程分析
- Android GSM驱动模块(rild)详细分析(二)request流程
- Android APN开发流程分析(二、三、四)
- Android中bindService的细节之一:从进程的角度分析绑定Service的流程【Service所在进程首次启动】
- Android应用层View绘制流程与源码分析
- Android中ICS4.0源码Launcher启动流程分析【android源码Launcher系列一】
- Android之MTP框架和流程分析(一)
- Android系统启动流程分析之安装应用
- 关于动态存储分配函数的调用,在已经过排序的数组中查找及删除内容的操作,余数的分析,删除字符数组中的空格,对链表的逆置,在源字符串中查找子字符串的个数,函数指针以及函数的调用,循环赋值带来的问题以及插入
- Android入门-MMS-短信/彩信发送流程+代码分析
- Android中View绘制流程以及invalidate()等相关方法分析
- android ota 升级包制作分析 (3 打包流程)