上一節(jié)中講到 dispatchMotionLocked() 向目標窗口分發(fā)事件苛蒲,接下來繼續(xù)學習目標窗口是如何獲取和綁定的迎罗;窗口的綁定是在 Activity 的啟動流程中(具體可以參考 筆記14 Activity 啟動流程)茉继,這里先簡單介紹一下 Activity 的創(chuàng)建佳鳖。
1. Activity 的創(chuàng)建
其中跨進程通信完成后,使用 ActivityThread 的內(nèi)部類 ApplicationThread 中 scheduleLaunchActivity() 方法進行處理了:其實在這個方法中也很簡單坟岔,就是使用主線程的 handler 發(fā)送了一條 H.LAUNCH_ACTIVITY 的消息豫缨,然后 handle r處理消息時調(diào)用了 handleLaunchActivity(r, null) 方法独令,具體有以下兩步:
(1)創(chuàng)建 activity 第 1 步 activity.attach():
handleLaunchActivity() -> performLaunchActivity() -> activity.attach(),這個 activity.attach() 方法好芭,是對 activity進行綁定燃箭,完成這一步activity才成為四大組件之一,未完成時都只能算一個對象栓撞。
(2)創(chuàng)建 activity 第 2 步 handleResumeActivity():
handleLaunchActivity() -> handleResumeActivity()遍膜,執(zhí)行 onResume() 方法并渲染;
1.1 activity.attach():
// activity 的 attach() 方法:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
// 創(chuàng)建 Window
// 這里的 mWindow 時 PhoneWindow瓤湘,后續(xù)版本的 mWindow 初始化如下,更加直觀:
// mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
// ...
// 在 window 中創(chuàng)建時 mWindowManager 其實是 WindowManagerImpl恩尾,代碼不復雜弛说,可以跟進去看看
mWindow.setWindowManager( // 設置 WindowManager
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager(); // 這里就可以獲取 WindowManager 了
mCurrentConfig = config;
}
在這里記住三個知識點(后續(xù)講setContentView時再詳細分析):
a. Window 類是一個抽象類,它的唯一實現(xiàn)類是 PhoneWindow翰意;
b. PhoneWindow 有一個內(nèi)部類 DecorView木人,DecorView 是 Activity 的根 View信柿;
c. DecorView 繼承自 FramLayout;
關于創(chuàng)建Window對象:
PolicyManager 為策略類醒第,其實現(xiàn)類 Policy 的makeNewWindow內(nèi)部創(chuàng)建了window對象渔嚷;
// mWindow = PolicyManager.makeNewWindow(this):
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static { // 通過反射創(chuàng)建 sPolicy
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
// ...
}
}
private PolicyManager() {}
// 創(chuàng)建PhoneWindow
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
public static LayoutInflater makeNewLayoutInflater(Context context) {
return sPolicy.makeNewLayoutInflater(context);
}
public static WindowManagerPolicy makeNewWindowManager() {
return sPolicy.makeNewWindowManager();
}
public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
return sPolicy.makeNewFallbackEventHandler(context);
}
}
// Policy.java 類中的 makeNewWindow() 方法:
public window makeNewWindow(Context context){
return new PhoneWindow(context);
}
1.2 handleResumeActivity():
// 第二步:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
// 主要作用是調(diào)用 performResumeActivity() 到 activity 的 onResume 狀態(tài),然后獲取
// DecorView稠曼,創(chuàng)建一個關聯(lián)的 ViewRootImpl 對象,用來配合 WindowManagerService 服
// 務來管理該 Activity 組件的窗口狀態(tài)形病,最后 addView
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
// ...
//activity創(chuàng)建成功,window此時為空霞幅,進入此分支漠吻;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
// 一層層的看最終調(diào)用的是:WindowManagerGlobal.java -> addView()
wm.addView(decor, l); // 這里就是測量,擺放司恳,繪制途乃; 也是下面要講的內(nèi)容;
}
} else if (!willBeVisible) {
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r);
// ...
}
}
2. 從 addView() 到 服務端(WMS) InputChannel (也就是 fd ) 的注冊
整個過程如下所示:服務端的注冊即 WMS 中 InputChannel 注冊到 InputDispatcher扔傅;
2.1 從 addView 開始:
// WindowManagerImpl.addView():
// 單例獲取 WindowManagerGlobal
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
// WindowManagerGlobal.addView():
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ... 省略部分代碼:參數(shù)的校驗
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
// ...
ViewRootImpl root;
View panelParentView = null;
//...
// 實例化 ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
// 添加 view 到全局集合中
// 如果想 hook 全部的 view 時耍共,可以通過反射獲取 WindowManagerGlobal -> mViews;
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try { // 將 view 添加到 ViewRootImpl 中去
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// ...
}
}
ViewRootImpl.setView():
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
// 這里先將 mView 保存了 DecorView 的實例猎塞,
// 然后調(diào)用 requestLayout() 方法试读,以完成應用程序用戶界面的初次布局。
if (mView == null) {
mView = view;
// mWindowAttributes 保存了窗口所對應的 LayoutParams
mWindowAttributes.copyFrom(attrs);
/**
* 在添加窗口之前邢享,先通過requestLayout方法在主線程上安排一次“遍歷”鹏往。
* 所謂“遍歷”是指 ViewRootImpl 中的核心方法 performTraversal()。
* 這個方法實現(xiàn)對控件樹進行測量骇塘、布局伊履、向 WMS 申請修改窗口屬性以及重繪的所有工作。
*/
requestLayout();
/*初始化 mInputChannel款违。InputChannel 是窗口接受來自 InputDispatcher 的輸入事件的管
道唐瀑。注意,僅當窗口的屬性 inputFeatures 不含有 INPUT_FEATURE_NO_INPUT_CHANNEL 時
才會創(chuàng)建 InputChannel插爹,否則 mInputChannel 為空哄辣,從而導致此窗口無法接受任何輸入事件 */
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel(); // 見 注釋1
}
try {
// ...
/* 將窗口添加到 WMS 中。完成這個操作之后赠尾,mWindow 已經(jīng)被添加到指定的Display中去
而且 mInputChannel(如果不為空)已經(jīng)準備好接受事件了力穗。只是由于這個窗口沒有進行
過 relayout() ,因此它還沒有有效的 Surface 可以進行繪制 */
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel); // 重點代碼
// 這里的 mWindowSession 是向WMS跨進程請求獲取的
} catch (RemoteException e) {
// ...
} finally {
// ...
}
}
}
(注釋1)InputChannel的構(gòu)造函數(shù):此時 mInputChannel = new InputChannel() 這里還是一個 Java 的對象气嫁;
public final class InputChannel implements Parcelable {
private static final String TAG = "InputChannel";
@SuppressWarnings("unused")
private long mPtr; // used by native code
private static native InputChannel[] nativeOpenInputChannelPair(String name);
private native void nativeDispose(boolean finalized);
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
private native void nativeDup(InputChannel target);
private native String nativeGetName();
// 構(gòu)造函數(shù)中沒有任何操作当窗,此時 mInputChannel = new InputChannel() 只是一個普通的java對象;
// 那么要想具有 c++ 的屬性寸宵,唯一的方法就是持有 c++ 對象的指針崖面,也就是將 mPtr 賦值元咙;
public InputChannel() {
}
// ...
}
2.2 mWindowSession.addToDisplay():重點的開始
(1)首先,mWindowSession 是如何獲取到的:ViewRootImpl 的構(gòu)造函數(shù)中進行初始化的巫员;
// mWindowSession 的初始化:ViewRootImpl 的構(gòu)造函數(shù)中進行初始化的庶香;
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession(); // 通過 WMS 獲取
}
// WindowManagerGlobal 類中的 getWindowSession() 方法
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
// 通過 WMS 獲取 mWindowSession
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
// WMS 中 openSession():
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext); // 參數(shù) this 就是 WMS;
return session;
}
IWindowSession:一個aidl接口简识,它的真的實現(xiàn)類是Session赶掖,是一個 Binder 對象,也是 ViewRootImpl 和 WMS 進行通信的代理财异。在 ViewRootImpl 的 setView() 中最終也是通過它和 WMS 通信完成了 Window 的添加倘零。這個 Session 是應用唯一的,它的創(chuàng)建是在 WindowManagerGloable 中通過 getWindowSession() 獲取的戳寸。
(2)mWindowSession.addToDisplay():就是調(diào)用了 Session 的 addToDisplay() 方法呈驶;
// Session 的 addToDisplay() 方法:又是跨進程通訊
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets,
InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outInputChannel); // 調(diào)用了 WMS 中的 addWindow() 方法
}
該方法的參數(shù) outInputChannel 是 ViewRootImpl.setView() 中 mInputChannel = new InputChannel(),不過此時 InputChannel 中的 c++ 對象指針還未賦值疫鹊,mService 就是 new Session 時傳入的 WMS袖瞻。
(3)WMS 的addWindow() 方法:App 端 InputChannel 賦值與建立 socketpair 雙向通信;
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
WindowState attachedWindow = null;
WindowState win = null; // window 對象的信息
long origId;
final int type = attrs.type;
synchronized(mWindowMap) {
// ...
win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
// ...
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
// 關鍵代碼拆吆,打開一對 InputChannel聋迎,客戶端和服務端
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
// WMS 設置 Channel 為 inputChannels[0]
win.setInputChannel(inputChannels[0]);
// 客戶端設置 Channel 為 inputChannels[1],此時給上面 Java層的 mInputChannel 中 mPtr 賦值
// 經(jīng)過賦值后枣耀,Java 層 mInputChannel 才具有 c++ 的對象
inputChannels[1].transferTo(outInputChannel);
// 將服務端的 socket 注冊到 InputDispatcher 中
// 這里的 win.mInputWindowHandle 是在 win 初始化的時候 new 出來的霉晕;
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
// ...
// 每次添加都會更新
mInputMonitor.updateInputWindowsLw(false /*force*/);
// ...
}
// ...
}
在 WMS 的 addWindow() 方法中有很多的關鍵點,這里列舉一些下面內(nèi)容中需要關注的關鍵點:
- (a)win = new WindowState():創(chuàng)建出 WindowState 來描述 App 端的信息捞奕;
- (b)InputChannel.openInputChannelPair():使用 socketpair() 打開一對 InputChannel牺堰;
- (c)win.setInputChannel(inputChannels[0]):設置服務端 WMS 的 InputChannel 為 inputChannels[0];
- (d)inputChannels[1].transferTo(outInputChannel):客戶端設置 Channel 為 inputChannels[1]颅围,給上面 Java層的 mInputChannel 中 mPtr 賦值伟葫,經(jīng)過賦值后,Java 層 mInputChannel 才具有了 c++ 的對象院促;
- (e)mInputManager.registerInputChannel():將服務端的 InputChannel 注冊到 InputDispatcher 中 筏养;
2.3 雙向通信 socketpair 的建立:承接上面的關鍵點進行分析;
(1)InputChannel.openInputChannelPair():在 InputChannel.java 中很簡單常拓,只是調(diào)用了一個 native 方法 nativeOpenInputChannelPair(name)渐溶,在 frameworks/base/core/jni/android_view_InputChannel.cpp 中:
// android_view_InputChannel.cpp 中:
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
String8 name(nameChars);
env->ReleaseStringUTFChars(nameObj, nameChars);
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
// 第一步(詳見下方代碼):創(chuàng)建一對 socket 通信;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
String8 message;
message.appendFormat("Could not open input channel pair. status=%d", result);
jniThrowRuntimeException(env, message.string());
return NULL;
}
// 第二步:創(chuàng)建 java 數(shù)組對象弄抬;用于保存這對 InputChannel
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
// 第三步(詳見下方代碼):創(chuàng)建 NativeInputChannel掌猛,將 InputChannel 指針保存于 mInputChannel 中;
// 第四步 (詳見下方代碼):設置 Java InputChannel 的 mPtr 成員變量眉睹;
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
// 第一步:創(chuàng)建一對 InputChannel荔茬;(InputChannel.cpp中)
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
// ...
// 封裝成 InputChannel
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
InputChannel::InputChannel(const String8& name, int fd) :
mName(name), mFd(fd) {
// 代碼很簡單,有用的只有一行(將 fd 設置成為非阻塞方式)
int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
}
// 第二步:創(chuàng)建 java 數(shù)組對象竹海;用于保存這對 InputChannel(通過調(diào)用 jni 的方法慕蔚,省略介紹)
// 第三步:創(chuàng)建 NativeInputChannel,將傳入的 InputChannel 指針保存于 mInputChannel 成員變量中斋配;
NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
mInputChannel(inputChannel), mDisposeCallback(NULL) {
}
// 第四步:
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
NativeInputChannel* nativeInputChannel) {
jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
gInputChannelClassInfo.ctor); // 調(diào)用 jni 方法 NewObject 創(chuàng)建一個 Java InputChannel 對象
if (inputChannelObj) {
// 調(diào)用 android_view_InputChannel_setNativeInputChannel 將 nativeInputChannel 對象指針強
// 轉(zhuǎn)為 jlong孔飒,然后將其設置到 Java InputChannel 類的 mPtr 成員變量上;
android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
}
return inputChannelObj;
}
經(jīng)過 InputChannel.cpp 中 openInputChannelPair() 中調(diào)用 socketpair()艰争,并封裝成一對 InputChannel坏瞄,然后轉(zhuǎn)換成 Java 對象返回給 WMS 供其使用(這里注意一下,客戶端的 InputChannel 是 WMS 獲取到之后甩卓,經(jīng)過 Binder 通信發(fā)送給 app 的鸠匀,這一套 socketpair + Binder 的通信方式,實現(xiàn)了任意進程間的雙向通信)逾柿;另外缀棍,socketpair() 在前面的【輸入事件番外篇1 Linux知識點】中有講過。
(2)設置服務端(WMS)和客戶端(App)端的 InputChannel:
// 設置 WMS 的 InputChannel:win.setInputChannel(inputChannels[0])
// WindowState.java 中
void setInputChannel(InputChannel inputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
// 代碼很簡單机错,只需要把 WindowState 中的兩個成員變量的值置為傳入的參數(shù) inputChannels[0]爬范;
mInputChannel = inputChannel;
mInputWindowHandle.inputChannel = inputChannel;
}
// 設置 App 的 InputChannel:inputChannels[1].transferTo(outInputChannel)
// InputChannel.java 中
public void transferTo(InputChannel outParameter) {
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
// 執(zhí)行了native 方法,調(diào)用了 android_view_InputChannel.cpp 中的方法
// 將 InputChannel 內(nèi)部狀態(tài)的所有權(quán)轉(zhuǎn)移到另一個實例弱匪,并使該實例無效青瀑。
nativeTransferTo(outParameter);
}
// android_view_InputChannel.cpp 中
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
jobject otherObj) {
/* otherObj 即對應的入?yún)ⅲ{(diào)用 android_view_InputChannel_getNativeInputChannel 方法可將 Java
InputChannel 對象的 mPtr 成員變量強轉(zhuǎn)為 NativeInputChannel 對象萧诫,由于傳遞過來的 Java 層的
InputChannel 對象中 mPtr 還沒有賦值斥难,是一個個“空殼”,所以強轉(zhuǎn)以后一定為空财搁,否則拋出異常 */
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Other object already has a native input channel.");
return;
}
// 這次調(diào)用是將 obj 轉(zhuǎn)化為 NativeInputChannel 對象蘸炸。
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
// 將obj 轉(zhuǎn)化的 NativeInputChannel 對象(實際是指針)設置到 otherObj mPtr 成員變量上。*/
android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
// 最后將 obj mPtr 成員變量設置為 0尖奔,即表示 NativeInputChannel 對象為 NULL搭儒。
android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}
// 這兩個被調(diào)用的方法,主要調(diào)用 jni 方法 GetLongField 和 SetLongField提茁,實現(xiàn) Native 層操作 java 對象成員變量淹禾。
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
jobject inputChannelObj) {
jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
return reinterpret_cast<NativeInputChannel*>(longPtr);
}
static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
NativeInputChannel* nativeInputChannel) {
env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
reinterpret_cast<jlong>(nativeInputChannel));
}
經(jīng)過上面的賦值操作,WMS 端和 App 端各自持有了一對經(jīng)過 socketpair 創(chuàng)建封裝的 InputChannel茴扁,那么接下來就是看看如何與事件分發(fā)系統(tǒng)聯(lián)系起來了铃岔;
(3)mInputManager.registerInputChannel():將 WMS 端的 InputChannel 注冊到 InputDispatcher 中;這里的 mInputManager 是 IMS,在 WMS 的構(gòu)造函數(shù)中作為參數(shù)傳入毁习;也就是說調(diào)用的是 IMS 的 registerInputChannel() 方法:
// InputManagerService.java 中:
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
// native 方法
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
// 將 ptr 強轉(zhuǎn)為 NativeInputManager 對象 (這里的 ptr 是 IMS 的 c++ 指針智嚷,不是 InputChannel 的)
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
// 將 inputChannelObj 轉(zhuǎn)化為 NativeInputChannel 對象
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
// 將 inputWindowHandleObj 轉(zhuǎn)化為 NativeInputWindowHandle 對象
sp<InputWindowHandle> inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
// 最終調(diào)用 NativeInputManager 類 registerInputChannel() 方法完成實際注冊工作
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
if (status) {
String8 message;
message.appendFormat("Failed to register input channel. status=%d", status);
jniThrowRuntimeException(env, message.string());
return;
}
if (! monitor) {
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
}
// com_android_server_input_InputManagerService.cpp 中:
// 這里是一個過渡的方法,主要是獲取到之前 IMS 啟動時創(chuàng)建的 InputDispatcher纺且,執(zhí)行里面的方法盏道;
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel( // 回到了 InputDispatcher 中了
inputChannel, inputWindowHandle, monitor);
}
// frameworks/native/services/inputflinger/InputDispatcher.cpp 中:
// 在這個方法中,WMS 的 InputChannel 終于注冊到 InputDispatcher 中了载碌,也就是說猜嘱,當有事件分發(fā)時,服
// 務端的 InputChannel 獲取到事件分發(fā)然后通過數(shù)據(jù)通道發(fā)送到 App 端了嫁艇;其主要的工作有以下四步:
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
// 第一步:檢查 InputChannel 是否已經(jīng)注冊過
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
// 第二步:創(chuàng)建 Connection 對象朗伶,Connection 表示客戶端和服務端的一個輸入數(shù)據(jù)通道
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd(); // // 獲取 socketpair 的 fd
// 第三步:以 fd 為 key 將 Connection 添加到容器(mConnectionsByFd)中
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
/* 第四步:將 fd 添加到 Looper 監(jiān)聽列表中。一旦對端的 Socket 寫入數(shù)據(jù)步咪,Looper 就會被喚醒论皆,接著
就會調(diào)用 handleReceiveCallback */
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// 喚醒 Looper,因為一些連接(Connection)已經(jīng)改變
mLooper->wake();
return OK;
}
經(jīng)過這一步歧斟,WMS 的 InputChannel 就和 InputDispatcher 建立了關聯(lián)〈客瑁現(xiàn)在該回到 ViewRootImpl.setView() 方法中具體分析如何關聯(lián)客戶端 InputChannel 了。
3. App 端 InputChannel (也就是 fd ) 的注冊
在上面 WindowManagerGlobal.addView() -> ViewRootImpl.setView() -> Session.addToDisplay -> WMS.addWindow() 最終完成了 WMS 端的 InputChannel 的注冊静袖;接下來分析客戶端 InputDispatcher 的關聯(lián)觉鼻,也是從 ViewRootImpl.setView() 這個方法中開始的:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
// ... 省略部分代碼
// 在add to window manager之前執(zhí)行第一次 requestLayout,以確保在 requestLayout 之前已經(jīng)接收
// 到系統(tǒng)的任何事件队橙;
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
// ...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
} catch (RemoteException e) {
//...
} finally {
//...
}
// ... 省略部分代碼
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
// 將 InputChannel 封裝成 WindowInputEventReceiver坠陈,這里是重點
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper()); // 注意一下,這里使用的是主線程的 Looper捐康;
}
// ... 省略部分代碼
}
}
將 App 端的 InputChannel 封裝成 WindowInputEventReceiver仇矾,這里也沒有執(zhí)行它的任何方法,那就看一下這個 WindowInputEventReceiver 的構(gòu)造函數(shù)解总,看看有沒有執(zhí)行一些方法:
(1)封裝 WindowInputEventReceiver:從類名上來看贮匕,這個類是 App 端窗口的事件接收器;
// WindowInputEventReceiver.java 中:ViewRootImpl 的內(nèi)部類花枫;
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper); // 執(zhí)行了父類中的方法刻盐,繼續(xù)跟進
}
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
// InputEventReceiver.java 中:
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue); // 這里就執(zhí)行到 c++ 代碼中了
mCloseGuard.open("dispose");
}
InputEventReceiver 構(gòu)造器中首先檢查入?yún)?inputChannel 和 looper 是否為 null,接著從 looper 中獲取 MessageQueue劳翰,最后調(diào)用 nativeInit() 進一步初始化敦锌;
(2)JNI 層初始化:nativeInit();
// frameworks/base/core/jni/android_view_InputEventReceiver.cpp
// 主要有以下四個點:
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
//(1)將 Java 層對象 InputChannel 對象轉(zhuǎn)化為 NativeInputChannel 對象
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
//(2)將 Java 層 MessageQueue 對象轉(zhuǎn)化為 NativeMessageQueue 對象
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
//(3)創(chuàng)建 NativeInputEventReceiver 對象
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
//(4)執(zhí)行 NativeInputEventReceiver 對象 initialize() 方法
status_t status = receiver->initialize();
if (status) {
String8 message;
message.appendFormat("Failed to initialize input event receiver. status=%d", status);
jniThrowRuntimeException(env, message.string());
return 0;
}
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // 保留對象的引用
return reinterpret_cast<jlong>(receiver.get());
}
// NativeInputEventReceiver 構(gòu)造函數(shù):將傳遞來的入?yún)⒈4娴綄某蓡T變量中佳簸。
// class NativeInputEventReceiver : public LooperCallback 繼承自 LooperCallback乙墙,
// 并實現(xiàn) LooperCallback 的 handleEvent() 方法;
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
jobject receiverWeak, const sp<InputChannel>& inputChannel,
const sp<MessageQueue>& messageQueue) :
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mInputConsumer(inputChannel), mMessageQueue(messageQueue),
mBatchedInputEventPending(false), mFdEvents(0) {
}
// initialize() 方法:執(zhí)行了 setFdEvents,其中 ALOOPER_EVENT_INPUT = 1 << 0听想,左移0位腥刹,結(jié)果為 1;
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
// setFdEvents() 方法:根據(jù)獲取 MessageQueue->getLooper()哗魂,然后調(diào)用 Looper 的 addFd()
void NativeInputEventReceiver::setFdEvents(int events) { // 傳入的參數(shù)為 1<< 0 為 1
if (mFdEvents != events) { // mFdEvents 初始化的值為 0肛走;
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) { // 所以執(zhí)行此分支
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
從 nativeInit() 開始,經(jīng)過一系列對象轉(zhuǎn)換(將 Java 對象轉(zhuǎn)換成 native 層的 c++ 對象)录别,并創(chuàng)建 NativeInputEventReceiver,然后執(zhí)行了 Looper 的 addFd() 方法邻吞;
(3)Looper.cpp 中:addFd()
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
// // 這里的 callback 是傳入的參數(shù) NativeInputEventReceiver
return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
}
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
if (!callback.get()) {
if (! mAllowNonCallbacks) {
ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
return -1;
}
if (ident < 0) {
ALOGE("Invalid attempt to set NULL callback with ident < 0.");
return -1;
}
} else {
ident = POLL_CALLBACK;
}
int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
{ // acquire lock
AutoMutex _l(mLock);
// 封裝成 Request
Request request;
request.fd = fd;
request.ident = ident;
request.callback = callback;
request.data = data;
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = epollEvents;
eventItem.data.fd = fd;
ssize_t requestIndex = mRequests.indexOfKey(fd); // 查找當前是否保存過
if (requestIndex < 0) { // 如果沒有則保存
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.add(fd, request); // 保存 request
} else { // 如果有則替換
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.replaceValueAt(requestIndex, request);
}
} // 釋放鎖
return 1;
}
在 addFd() 方法中组题,先將參數(shù)封裝成 Request,然后向保存 Request 的容器中查詢是否保存過當前 fd抱冷,沒有保存則保存崔列,反之則替換;通過 epoll_ctl() 添加 fd 描述符后旺遮,當有數(shù)據(jù)從服務端寫入赵讯,就會喚醒 Looper,最終回調(diào)到 NativeInputEventReceiver(繼承自LooperCallback) 的 handleEvent() 方法耿眉。
(4)pollInner() 執(zhí)行回調(diào):
int Looper::pollInner(int timeoutMillis) {
// ...
// Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.ident == POLL_CALLBACK) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
// 執(zhí)行 回調(diào)函數(shù) handleEvent()
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
removeFd(fd);
}
// Clear the callback reference in the response structure promptly because we
// will not clear the response vector itself until the next poll.
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
還記得上面說到傳入的 Looper 是主線程的 Looper 嗎边翼?在 Java 層主線程中,執(zhí)行了 looper.loop() 方法后鸣剪,會循環(huán)執(zhí)行 pollInner()组底,所以這里的回調(diào)執(zhí)行后就會返回給 Java 層處理了;