您的位置:首页 > 移动开发 > Android开发

Android应用程序键盘(Keyboard)消息处理机制分析(四)

2011-12-17 10:30 489 查看
上接:Android应用程序键盘(Keyboard)消息处理机制分析(三)

----

4. 应用程序注销键盘消息接收通道的过程分析

当Activity窗口创建时,它会向InputManager注册键盘消息接收通道,而当Activity窗口销毁时,它就会向InputManager注销前面注册的键盘消息接收通道了,本节内容就来看看应用程序注销键盘消息接收通道的过程。

当我们按下键盘上的Back键时,当前激活的Activity窗口就会被失去焦点,但是这时候它还没有被销毁,它的状态被设置为Stopped;当新的Activity窗口即将要显示时,它会通知WindowManagerService,这时候WindowManagerService就会处理当前处理Stopped状态的Activity窗口了,要执行的操作就是销毁它们了,在销毁的时候,就会注销它们之前所注册的键盘消息接收通道。

新的Activity窗口通知WindowManagerService它即将要显示的过程比较复杂,但是它与我们本节要介绍的内容不是很相关,因此,这里就略过大部过程了,我们从ActvitiyRecord的windowsVisible函数开始分析。注意,这里的ActivityRecord是新的Activity窗口在ActivityManangerService的代表,而那些处于Stopped状态的Activity窗口

会放在ActivityStack类的一个等待可见的mWaitingVisibleActivities列表里面,事实于,对于那些Stopped状态的Activity窗口来说,它们是等待销毁,而不是等待可见。

像前面一样,我们先来看一张应用程序注销键盘消息接收通道的过程的序列图,然后根据这个序列图来详细分析互一个步骤:



点击查看大图

Step 1. ActivityRecord.windowsVisible

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityRecord.java文件中:

view
plain

class ActivityRecord extends IApplicationToken.Stub {

......

boolean nowVisible; // is this activity's window visible?

boolean idle; // has the activity gone idle?

......

public void windowsVisible() {

synchronized(service) {

......

if (!nowVisible) {

nowVisible = true;

if (!idle) {

.......

} else {

// If this activity was already idle, then we now need to

// make sure we perform the full stop of any activities

// that are waiting to do so. This is because we won't

// do that while they are still waiting for this one to

// become visible.

final int N = stack.mWaitingVisibleActivities.size();

if (N > 0) {

for (int i=0; i<N; i++) {

ActivityRecord r = (ActivityRecord)

stack.mWaitingVisibleActivities.get(i);

r.waitingVisible = false;

......

}

stack.mWaitingVisibleActivities.clear();

Message msg = Message.obtain();

msg.what = ActivityStack.IDLE_NOW_MSG;

stack.mHandler.sendMessage(msg);

}

}

......

}

}

}

......

}

应用程序中的每一个Activity在ActivityManagerService都有一个代表ActivityRecord,它们以堆栈的形式组织在ActivityManaerService中的ActivityStack中。一个即将要显示,但是还没有显示的Activity,它在ActivityManagerService中的ActivityRecord的成员变量nowVisible为false,而成员变量idle为ture,表示这个即将要显示的Activity窗口处于空闲状态。因此,在上面的这个函数中,会执行下面的语句:

view
plain

final int N = stack.mWaitingVisibleActivities.size();

if (N > 0) {

for (int i=0; i<N; i++) {

ActivityRecord r = (ActivityRecord)

stack.mWaitingVisibleActivities.get(i);

r.waitingVisible = false;

......

}

stack.mWaitingVisibleActivities.clear();

Message msg = Message.obtain();

msg.what = ActivityStack.IDLE_NOW_MSG;

stack.mHandler.sendMessage(msg);

}

前面我们说过,当用户按下键盘上的Back键时,当前激活的Activity记录就被放在ActivityStack对象stack的成员变量mWaitingVisibleActivities中了,这时候就要对它进行处理了。首先是将它们的Activity记录的waitingVisible设置为false,然后就把它们从ActivityStack对象stack的成员变量mWaitingVisibleActivities清空,最后向ActivityStack对象stack发送一个ActivityStack.IDLE_NOW_MSG消息。这个消息最终是由ActivityStack类的activityIdleInternal函数来处理的。

Step 2. ActivityStack.activityIdleInternal

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

view
plain

public class ActivityStack {

......

final void activityIdleInternal(IBinder token, boolean fromTimeout,

Configuration config) {

......

ArrayList<ActivityRecord> stops = null;

......

int NS = 0;

......

synchronized (mService) {

......

// Atomically retrieve all of the other things to do.

stops = processStoppingActivitiesLocked(true);

NS = stops != null ? stops.size() : 0;

......

}

int i;

......

// Stop any activities that are scheduled to do so but have been

// waiting for the next one to start.

for (i=0; i<NS; i++) {

ActivityRecord r = (ActivityRecord)stops.get(i);

synchronized (mService) {

if (r.finishing) {

finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);

} else {

......

}

}

}

......

}

......

}

这个函数首先会调用processStoppingActivitiesLocked函数把所有处于Stopped状态的Activity取回来,然后逐个分析它们,如果它们的ActivityRecord中的finishing成员变量为true,就说明这个Activity需要销毁了,于是,就调用finishCurrentActivityLocked函数来销毁它们。

Step 3. ActivityStack.finishCurrentActivityLocked

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

view
plain

public class ActivityStack {

......

private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,

int mode) {

......

return finishCurrentActivityLocked(r, index, mode);

}

private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r,

int index, int mode) {

......

// make sure the record is cleaned out of other places.

mStoppingActivities.remove(r);

mWaitingVisibleActivities.remove(r);

......

final ActivityState prevState = r.state;

r.state = ActivityState.FINISHING;

if (mode == FINISH_IMMEDIATELY

|| prevState == ActivityState.STOPPED

|| prevState == ActivityState.INITIALIZING) {

// If this activity is already stopped, we can just finish

// it right now.

return destroyActivityLocked(r, true) ? null : r;

} else {

......

}

return r;

}

......

}

从上面的Step 2中传进来的参数mode为FINISH_IMMEDIATELY,并且这个即将要被销毁的Activity的状态为Stopped,因此,接下来就会调用destroyActivityLocked函数来销毁它。

Step 4. ActivityStack.destroyActivityLocked

这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

view
plain

public class ActivityStack {

......

final boolean destroyActivityLocked(ActivityRecord r,

boolean removeFromApp) {

......

boolean removedFromHistory = false;

......

final boolean hadApp = r.app != null;

if (hadApp) {

......

try {

......

r.app.thread.scheduleDestroyActivity(r, r.finishing,

r.configChangeFlags);

} catch (Exception e) {

......

}

......

} else {

......

}

......

return removedFromHistory;

}

......

}

在前面一篇文章Android应用程序启动过程源代码分析中,我们说到,每一个应用程序进程在ActivityManagerService中,都ProcessRecord记录与之对应,而每一个Activity,都是运行在一个进程上下文中,因此,在ActivityManagerService中,每一个ActivityRecord的app成员变量都应该指向一个ProcessRecord记录,于是,这里得到的hadApp为true。在ProcessRecord类中,有一个成员变量thread,它的类型为IApplicationThread。在文章Android应用程序启动过程源代码分析中,我们也曾经说过,每一个应用程序在启动的时候,它都会在内部创建一个ActivityThread对象,而在这个ActivityThread对象中,有一个成员变量mAppThread,它的类型为ApplicationThread,这是一个Binder对象,专门用来负责在应用程序和ActivityManagerService之间执行进程间通信工作的。应用程序在启动的时候,就会将这个Binder对象传递给ActivityManagerService,而ActivityManagerService就会把它保存在相应的ProcessRecord记录的thread成员变量中。因此,ProcessRecord记录的thread成员变量其实就是ApplicationThread对象的远程接口,于是,执行下面这个语句的时候:

view
plain

r.app.thread.scheduleDestroyActivity(r, r.finishing,

r.configChangeFlags);

就会进入到ApplicationThread类中的scheduleDestroyActivity函数来。

Step 5. ApplicationThread.scheduleDestroyActivity

这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

view
plain

public final class ActivityThread {

......

private final class ApplicationThread extends ApplicationThreadNative {

......

public final void scheduleDestroyActivity(IBinder token, boolean finishing,

int configChanges) {

queueOrSendMessage(H.DESTROY_ACTIVITY, token, finishing ? 1 : 0,

configChanges);

}

......

}

......

}

这个函数调用外部类ActivityThread的queueOrSendMessage函数来往应用程序的消息队列中发送一个H.DESTROY_ACTIVITY消息,这个消息最终由ActivityThread类的handleDestroyActivity函数来处理。

Step 6. ActivityThread.handleDestroyActivity

这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

view
plain

public final class ActivityThread {

......

private final void handleDestroyActivity(IBinder token, boolean finishing,

int configChanges, boolean getNonConfigInstance) {

......

ActivityClientRecord r = performDestroyActivity(token, finishing,

configChanges, getNonConfigInstance);

if (r != null) {

WindowManager wm = r.activity.getWindowManager();

View v = r.activity.mDecor;

if (v != null) {

......

if (r.activity.mWindowAdded) {

wm.removeViewImmediate(v);

}

......

}

......

}

......

}

......

}

这里首先调用performDestroyActivity来执行一些销毁Activity的操作,期间就会调用Activity的onDestroy函数让Activity本身有机会执行一些销毁前的工作了。这里通过r.activity.getWindowManager函数返回的是一个LocalWindowManager对象,而通过r.activity.mDecor得到的是一个DecorView对象,这些都是在Activity启动的时候设置好的。函数最后调用LocalWindowManager对象wm的removeViewImmediate函员来从LocalWindowManager移除这个DecorView对象。

Step 7. LocalWindowManager.removeViewImmediate

这个函数定义在frameworks/base/core/java/android/view/Window.java文件中:

view
plain

public abstract class Window {

......

private class LocalWindowManager implements WindowManager {

......

public final void removeViewImmediate(View view) {

mWindowManager.removeViewImmediate(view);

}

......

private final WindowManager mWindowManager;

}

......

}

LocalWindowManager类的成员变量mWindowManager是一个WndowManagerImpl对象,这个函数只是简单地调用WndowManagerImpl类的removeViewImmediate来进一步处理。

Step 8. WndowManagerImpl.removeViewImmediate

这个函数定义在frameworks/base/core/java/android/view/WindowManagerImpl.java文件中:

view
plain

public class WindowManagerImpl implements WindowManager {

......

public void removeViewImmediate(View view) {

synchronized (this) {

int index = findViewLocked(view, true);

ViewRoot root = mRoots[index];

......

root.die(true);

......

}

}

......

}

这个函数首先是找到这个view所属的ViewRoot对象root,然后调用这个root对象的die函数来销毁它。

Step 9. ViewRoot.die

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

view
plain

public final class ViewRoot extends Handler implements ViewParent,

View.AttachInfo.Callbacks {

......

public void die(boolean immediate) {

if (immediate) {

doDie();

} else {

......

}

}

......

}

上面Step 8传进来的immediate参数为true,因此,这里直接调用doDie函数来进一步处理。

Step 10. ViewRoot.doDie

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

view
plain

public final class ViewRoot extends Handler implements ViewParent,

View.AttachInfo.Callbacks {

......

void doDie() {

......

synchronized (this) {

......

if (mAdded) {

mAdded = false;

dispatchDetachedFromWindow();

}

}

}

......

}

当我们把Activity窗口中的View添加到一个ViewRoot对象时,就会把它的成员变量mAdded设置为true,这样就表示这个ViewRoot中有View存在,于是,这里就会调用dispatchDetachedFromWindow函数来进一步处理。

Step 11. ViewRoot.ispatchDetachedFromWindow

这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:

view
plain

public final class ViewRoot extends Handler implements ViewParent,

View.AttachInfo.Callbacks {

......

void dispatchDetachedFromWindow() {

......

if (mInputChannel != null) {

if (mInputQueueCallback != null) {

......

} else {

InputQueue.unregisterInputChannel(mInputChannel);

}

}

try {

sWindowSession.remove(mWindow);

} catch (RemoteException e) {

}

......

}

......

}

前面在介绍应用程序注册键盘消息接收通道的过程时,在Step 18,我们说到,ViewRoot类中的mInputQueueCallback为null,表示由这个ViewRoot自己来管理键盘输入事件,因此,这里首先会调用InputQueue的unregisterInputChannel函数来注销注册在应用程序这一侧的Client端InputChannel,然后再调用sWindowSession的remove函数来注销注册在InputManager这一侧的Server端InputChannel,这个逻辑是和前面介绍应用程序注册键盘消息接收通道的逻辑相对应的,前面分别注册了这两个InputChannel,现在Activity要销毁了,当然就要把它们注销了。

我们先来看注销注册在应用程序这一侧的Client端InputChannel,然后再回过头来分析注销注册在InputManager这一侧的Server端InputChannel。

Step 12. InputQueue.unregisterInputChannel

这个函数定义在frameworks/base/core/java/android/view/InputQueue.java文件中:

view
plain

public final class InputQueue {

......

public static void unregisterInputChannel(InputChannel inputChannel) {

......

synchronized (sLock) {

......

nativeUnregisterInputChannel(inputChannel);

}

}

......

}

这个函数只是简单地调用本地方法nativeUnregisterInputChannel来执行具体的操作。

Step 13. InputQueue.nativeUnregisterInputChannel

这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

view
plain

static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,

jobject inputChannelObj) {

status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj);

......

}

这里调用NativeInputQueue的成员函数unregisterInputChannel来进一步处理。

Step 14. NativeInputQueue.unregisterInputChannel

这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:

view
plain

status_t NativeInputQueue::unregisterInputChannel(JNIEnv* env, jobject inputChannelObj) {

sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,

inputChannelObj);

......

{ // acquire lock

AutoMutex _l(mLock);

ssize_t connectionIndex = getConnectionIndex(inputChannel);

......

sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);

mConnectionsByReceiveFd.removeItemsAt(connectionIndex);

connection->status = Connection::STATUS_ZOMBIE;

connection->looper->removeFd(inputChannel->getReceivePipeFd());

env->DeleteGlobalRef(connection->inputHandlerObjGlobal);

connection->inputHandlerObjGlobal = NULL;

......

} // release lock

......

return OK;

}

真正的注销工作就是这里实现的了,读者可以对照前面介绍应用程序注册键盘消息接收通道过程中的Step 21(NativeInputQueue.registerInputChannel)来分析,它首先是将在之前创建的Connection对象从NativeInputQueue中的mConnectionByReceiveFd向量中删除:

view
plain

ssize_t connectionIndex = getConnectionIndex(inputChannel);

......

sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);

mConnectionsByReceiveFd.removeItemsAt(connectionIndex);

然后再把这个Client端InputChannel的前向管道的读端文件描述符从应用程序主线程中的Looper对象中删除:

view
plain

connection->looper->removeFd(inputChannel->getReceivePipeFd());

这样,这个Activity窗口以后就不会接收到键盘事件了。

最后将Connection对象中的回调对象inputHandlerOjbGlobal对象删除:

view
plain

env->DeleteGlobalRef(connection->inputHandlerObjGlobal);

connection->inputHandlerObjGlobal = NULL;

回忆一下前面我们在分析InputManager分发键盘消息给应用程序处理时,曾经说到,每当有键盘事件发生时,InputManager首先就会调用NativeInputQueue类的handleReceiveCallback函数。在这个handleReceiveCallback函数里面,NativeInputQueue会找到相应的Connection对象,然后把它里面的内部对象inputHandlerOjbGlobal作为参数来调用Java层的InputQueue类的dispatchKeyEvent函数来通知应用程序,有键盘事件发生了。在InputQueue类的dispatchKeyEvent函数里面,就是通过这个inputHandlerOjbGlobal对象来直正通知到当前激活的Activity窗口来处理这个键盘事件的。

注册在应用程序这一侧的Client端InputChannel被注销以后,回到前面的Step 11中,我们继续分析注销注册在InputManager这一侧的Server端InputChannel。

Step 15. WindowManagerService.Session.remove

这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

view
plain

public class WindowManagerService extends IWindowManager.Stub

implements Watchdog.Monitor {

......

private final class Session extends IWindowSession.Stub

implements IBinder.DeathRecipient {

......

public void remove(IWindow window) {

removeWindow(this, window);

}

......

}

......

}

这个函数只是简单地调用其外部类WindowManagerService的removeWindow函数来进一步执行操作。

Step 16. WindowManagerService.removeWindow

这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

view
plain

public class WindowManagerService extends IWindowManager.Stub

implements Watchdog.Monitor {

......

public void removeWindow(Session session, IWindow client) {

synchronized(mWindowMap) {

WindowState win = windowForClientLocked(session, client, false);

if (win == null) {

return;

}

removeWindowLocked(session, win);

}

}

......

}

回忆一下前面我们在分析应用程序注册键盘消息管道的过程时,在Step 11(WindowManagerService.addWindow)中,WindowManagerService为这个即将要激活的Activity窗口创建了一个WindowState对象win,创建的时候,使用了从ViewRoot中传过来的两个参数,分别是一个Session对象session和一个IWindow对象client。

在这个函数中,ViewRoot传过来的两个参数session和client和上面说的两个参数是一致的,因此,这个函数首先通过参数session和client得到一个WindowState对象win,然后调用removeWindowLocked来把它从WindowManagerService删除。

Step 17. WindowManagerService.removeWindowLocked

这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

view
plain

public class WindowManagerService extends IWindowManager.Stub

implements Watchdog.Monitor {

......

public void removeWindowLocked(Session session, WindowState win) {

......

win.disposeInputChannel();

......

}

......

}

我们忽略了这个函数的其它逻辑,只关注注销之前注册的Server端InputChannel的逻辑,这里,注销的操作就是调用win的disposeInputChannel进行的了。

Step 18. WindowState.disposeInputChannel

这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:

view
plain

public class WindowManagerService extends IWindowManager.Stub

implements Watchdog.Monitor {

......

private final class WindowState implements WindowManagerPolicy.WindowState {

......

void disposeInputChannel() {

if (mInputChannel != null) {

mInputManager.unregisterInputChannel(mInputChannel);

mInputChannel.dispose();

mInputChannel = null;

}

}

......

}

......

}

上面说到,在前面分析应用程序注册键盘消息管道的过程时,在Step 11(WindowManagerService.addWindow)中,为当前这个Activity窗口创建了一个WindowState对象,接着创建了一个输入管道后,把Server端的InputChannel保存了在这个WindowState对象的成员变量mInputChannel中,因此,这里,就可以把它取回来,然后调用mInputManager对象的unregisterInputChannel函数来把它注销掉了。

Step 19. InputManager.unregisterInputChannel

这个函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中:

view
plain

public class InputManager {

......

public void unregisterInputChannel(InputChannel inputChannel) {

......

nativeUnregisterInputChannel(inputChannel);

}

......

}

这个函数很简单,它调用本地方法nativeUnregisterInputChannel来进一步处理。

Step 20. InputManager.nativeUnregisterInputChannel

这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中:

view
plain

static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,

jobject inputChannelObj) {

......

sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,

inputChannelObj);

......

status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);

......

}

这个函数首先调用android_view_InputChannel_getInputChannel函数根据Java层的InputChannel对象找到C++层的InputChannel对象,然后调用NativeInputManager的unregisterInputChannel函数来执行注销的操作。

Step 21. NativeInputManager.unregisterInputChannel

这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中:

view
plain

status_t NativeInputManager::unregisterInputChannel(JNIEnv* env,

const sp<InputChannel>& inputChannel) {

......

return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);

}

这个函数与前面分析应用程序注册键盘消息通道的Step 17(NativeInputManager.registerInputChannel)相对应,主要是调用InputDispatcher对象的unregisterInputChannel函数来执行真正注销的操作。

Step 22. InputDispatcher.unregisterInputChannel

这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:

view
plain

status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {

......

{ // acquire lock

AutoMutex _l(mLock);

ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);

......

sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);

mConnectionsByReceiveFd.removeItemsAt(connectionIndex);

......

mLooper->removeFd(inputChannel->getReceivePipeFd());

.....

} // release lock

......

return OK;

}

这一步与前面的Step 14注销应用程序一侧的Client端InputChannel是差不多的,只不过这里是从InputDispatcher中把Server端的InputChannel注销掉。首先是根据传进来的参数inputChannel找到它在InputDispatcher中对应的Connection对象在mConnectionsByReceiveFd中的索引,然后把它从mConnectionsByReceiveFd中删除:

view
plain

ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);

......

sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);

mConnectionsByReceiveFd.removeItemsAt(connectionIndex);

最后,还需要把这个InputChannel中的反向管道读端文件描述符从InputDispatcher的内部对象mLooper中删除,因为这个文件描述符是在前面注册Server端的InputChannel时加入到mLooper对象去的,具体可以参考上面分析应用程序注册键盘消息接收通道的过程中的Step
18(InputDispatcher.registerInputChannel)。

这样, 应用程序注销键盘消息接收通道的过程就分析完成了,整个应用程序键盘消息处理机制也分析完成了,这是一个比较复杂的过程,要完全理解它还需要花费一些努力和时间,不过,理解了这个过程之后,对Android应用程序框架层的理解就更进一步了。

转自:老罗的Android应用程序键盘(Keyboard)消息处理机制分析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息