【android 9】【input】【9.发送按键事件3——Inputchannel的创建过程】

系列文章

本人系列文章-CSDN博客


目录

系列文章

目录

1.简介

1.1 主要步骤

1.2 时序图

2.源码分析

2.1 WindowManagerImpl的addView

2.2 WindowManagerGlobal的addView

2.3 ViewRootImpl

2.4 getWindowSession

 2.5 WMS中的openSession

 2.6 Session

2.7 class W

2.8 setView

2.9 addToDisplay

2.10 addWindow

2.11 openInputChannel

2.12 Java层openInputChannelPair

2.13 android_view_InputChannel_nativeOpenInputChannelPair

2.14 openInputChannelPair

2.15 transferTo

2.16 WMS向IMS注册并监听socket

2.17 nativeRegisterInputChannel

2.18 android_view_InputChannel_getInputChannel

2.19 android_server_InputWindowHandle_getHandle

2.20 registerInputChannel

2.21 InputDispatcher::registerInputChannel

2.22 Connection

2.23 android_view_InputChannel_setDisposeCallback

2.24 WindowInputEventReceiver

2.25 InputEventReceiver

2.26 nativeInit

2.27 NativeInputEventReceiver

2.28 initialize

2.29 setFdEvents

2.30 Looper::addFd


1.简介

上一篇中,主要介绍了按键事件中inputdispatcher线程的分发流程,最后会通过sokcet对发送按键消息到应用端,那么这个socket对是什么时候创建的呢?是什么时候和IMS建立连接的呢?本文便主要解答一下这部分内容。

1.1 主要步骤

1.首先当Activity启动后,应用程序端会创建一个空的InputChannel对象。

2.然后应用程序端会通过binder调用到WMS服务,WMS服务会通过openInputChannel 方法会创建一对 InputChannel,一个给到IMS,一个会通过binder调用返回给应用端。

3.然后WMS会将其中一个socket注册到IMS服务中,IMS服务通过epoll机制来监听,是否有来自应用端发送的消息,当应用程序端通过sokcet发送消息时,IMS中的handleReceiveCallback回调函数会执行。

4.然后此时应用端到WMS的binder调用函数返回,返回给应用程序端一个socket,应用程序端会创建一个NativeInputEventReceiver对象,同时应用程序端也会通过epoll机制来监听,是否有来自IMS发送的消息,当存在IMS发送事件到应用程序端时,会调用NativeInputEventReceiver的handleEvent函数。

1.2 时序图

 (图片可保存到本地放大观看)

2.源码分析

首先当Activity启动后,最终会调用到WindowManagerImpl.addView()函数,我们便从WindowManagerImpl.addView()函数进行分析。

2.1 WindowManagerImpl的addView

主要作用:

1.调用WindowManagerGlobal对象的addview对象。

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) 
{applyDefaultToken(params);mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//mGlobal就是WindowManagerGlobal对象
}

2.2 WindowManagerGlobal的addView

主要作用:

1.创建ViewRootImpl对象。

2.调用ViewRootImpl的setView函数,此函数会创建空的InputChannel对象,然后传给WMS,WMS会返回一个和IMS连接好的socket给应用程序端。

此时我们仍然在应用程序进程中。

public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {//view代表添加哪个窗口,此时view是DecorView//params窗口的参数//display显示到那块屏幕上//parentWindow父窗口是谁final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;ViewRootImpl root;View panelParentView = null;synchronized (mLock) {int index = findViewLocked(view, false);//从mViews中查找此view是否已经存在/*if (index >= 0) {if (mDyingViews.contains(view)) {// Don't wait for MSG_DIE to make it's way through root's queue.mRoots.get(index).doDie();} else {throw new IllegalStateException("View " + view+ " has already been added to the window manager.");}// The previous removeView() had not completed executing. Now it has.}*/root = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);mViews.add(view);//将此DecorView保存到mViews容器中mRoots.add(root); //将此ViewRootImpl保存到容器中mParams.add(wparams);//保存参数// do this last because it fires off messages to start doing thingstry {root.setView(view, wparams, panelParentView);} catch (RuntimeException e) {// BadTokenException or InvalidDisplayException, clean up.if (index >= 0) {removeViewLocked(index, true);}throw e;}}}

2.3 ViewRootImpl

主要作用:

1.获取IWindowSession代理类,此类用于应用端和wms进行通信。

2.new W(this);W 继承自 IWindow.Stub,用于wms服务端向应用端通信。在调用本类的setView时会将此W对象传递给WMS。

//ViewRootImpl.Java
public ViewRootImpl(Context context, Display display) {mContext = context;mWindowSession = WindowManagerGlobal.getWindowSession();//获取IWindowSession代理类,此类用于应用和wms进行通信mDisplay = display;//显示到那个display中mDirty = new Rect();mTempRect = new Rect();mVisRect = new Rect();mWinFrame = new Rect();mWindow = new W(this);//w继承自class W extends IWindow.Stub,用于wms服务端向应用端通信mFirst = true; // true代表此view第一次被添加mChoreographer = Choreographer.getInstance();mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
}

2.4 getWindowSession

主要作用:

1.获取和WMS通信用的Session对象。

//WindowManagerGlobal.java
public static IWindowSession getWindowSession() {synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {try {InputMethodManager imm = InputMethodManager.getInstance();//输入法IWindowManager windowManager = getWindowManagerService();//获取wms的binder代理对象sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {//传入了客户端实现的WindowSessionCallback回调类,用于wms通信到应用程序@Overridepublic void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);}},imm.getClient(), imm.getInputContext());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return sWindowSession;}
}
//aidl接口如下
IWindowSession openSession(in IWindowSessionCallback callback, in IInputMethodClient client,in IInputContext inputContext);

 2.5 WMS中的openSession

主要作用:

1.此时会走到WMS中,WMS中会创建一个session对象

//此时会调用到WMS中
public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs 
{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);return session;//}
}

 2.6 Session

主要作用为:
1.此类内部会保存WindowManagerService对象和客户端实现的IWindowSessionCallback类对象

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient 
{public Session(WindowManagerService service, IWindowSessionCallback callback,IInputMethodClient client, IInputContext inputContext) {mService = service;//此时service是WindowManagerService对象mCallback = callback;//callback是客户端实现的IWindowSessionCallback类对象,是一个binder对象mClient = client;//此时是输入法的客户端mUid = Binder.getCallingUid();mPid = Binder.getCallingPid();synchronized (mService.mWindowMap) {if (mService.mInputMethodManager == null && mService.mHaveInputMethods) {IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);mService.mInputMethodManager = IInputMethodManager.Stub.asInterface(b);//获取输入法的binder代理对象}}long ident = Binder.clearCallingIdentity();try {// Note: it is safe to call in to the input method manager// here because we are not holding our lock.if (mService.mInputMethodManager != null) {mService.mInputMethodManager.addClient(client, inputContext,mUid, mPid);//将此addClient添加到输入法中} else {client.setUsingInputMethod(false);}client.asBinder().linkToDeath(this, 0);} catch (RemoteException e) {// The caller has died, so we can just forget about this.try {if (mService.mInputMethodManager != null) {mService.mInputMethodManager.removeClient(client);}} catch (RemoteException ee) {}} finally {Binder.restoreCallingIdentity(ident);}}}

2.7 class W

主要作用:

1.W 继承自 IWindow.Stub,用于wms服务端向应用端通信。在调用本类的setView时会将此W对象传递给WMS

static class W extends IWindow.Stub {private final WeakReference<ViewRootImpl> mViewAncestor;private final IWindowSession mWindowSession;W(ViewRootImpl viewAncestor) {mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);mWindowSession = viewAncestor.mWindowSession;//客户端保存IWindowSession session通信对象}
}

2.8 setView

主要作用为:

1.应用端创建空的InputChannel对象。

2.应用端通过binder调用addToDisplay函数,此时会走到到wms服务中,在wms中会对mInputChannel赋值,并返回。返回的是一个已经和IMS连接的socket。

3.当应用端拿到socket后,会new WindowInputEventReceiver对象,此对象内部最终后调用应用端ui主线程的Looper::addFd函数,通过epoll机制监听此socket,当存在IMS发送事件到应用程序端时,会调用NativeInputEventReceiver的handleEvent函数。

//frmaework/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {//view是view代表添加哪个窗口,此时view是DecorView//params窗口的参数//panelParentView,如果是子窗口,则子窗口存在父窗口,此时是普通窗口,则为nullsynchronized (this) {if (mView == null) {mView = view;mWindowAttributes.copyFrom(attrs);if (mWindowAttributes.packageName == null) {mWindowAttributes.packageName = mBasePackageName;//给mWindowAttributes添加了包名}attrs = mWindowAttributes;if ((mWindowAttributes.inputFeatures& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {mInputChannel = new InputChannel();//客户端创建InputChannel对象,此时的InputChannel是空的对象,并没有赋值的内容}try {//将空的mInputChannel传入其中,应用端通过binder调用到wms中,在wms中会对mInputChannel赋值res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);//mWindow继承自IWindow.Stub,用于wms服务端向应用端通信//mWindowAttributes窗口属性//mInputChannel本质是socket,用于应用程序和ims进行输入事件的通信}if (mInputChannel != null) {mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());}}}}

2.9 addToDisplay

此时会通过binder走到WMS的系统服务中。

主要作用:

1.调用Windowmanagerservice的addWindow函数。

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,Rect outStableInsets, Rect outOutsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {//此时mWindow继承自IWindow.Stub,用于wms服务端向应用端通信//outInputChannel本质是socket,用于应用程序和ims进行输入事件的通信return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);}
}

2.10 addWindow

主要作用:

1.调用WindowState类的openInputChannel函数。

//Windowmanagerservice.java
public int addWindow(Session session, IWindow client, int seq,LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) //此时client客户端的Window,继承自IWindow.Stub,用于wms服务端向应用端通信//outInputChannel本质是socket,用于应用程序和ims进行输入事件的通信
{//仅当窗口的inputFeatures未指定NO_INPUT_CHANNEL选项时才会为此窗口创建InputChannel对boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if (callingUid != SYSTEM_UID){Slog.e(TAG_WM,"App trying to use insecure INPUT_FEATURE_NO_INPUT_CHANNEL flag. Ignoring");openInputChannels = true;}if  (openInputChannels) { win.openInputChannel(outInputChannel);//win1是一个WindowState类对象,调用其类的openInputChannel函数}				
}

2.11 openInputChannel

主要作用为:

1.调用openInputChannelPair创建一个InputChannel对

2.将0号inputChannel调用InputManager.registerInputChannel注册到IMS中

3.将1号inputChannel传递给outInputChannel,即传递给应用端。

// frmaework/base/services/core/java/com/android/server/wm/WindowState.java
void openInputChannel(InputChannel outInputChannel) 
{if (mInputChannel != null) {throw new IllegalStateException("Window already has an input channel.");}String name = getName();InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//会创建一个sokcet对mInputChannel = inputChannels[0];mClientChannel = inputChannels[1];mInputWindowHandle.inputChannel = inputChannels[0];//其中0号inputChannel交给InputWindowHandle保存if (outInputChannel != null) {mClientChannel.transferTo(outInputChannel);//将mClientChannel所持有的1号inputChannel传递给outInputChannelmClientChannel.dispose();mClientChannel = null;} /*else {// If the window died visible, we setup a dummy input channel, so that taps// can still detected by input monitor channel, and we can relaunch the app.// Create dummy event receiver that simply reports all events as handled.mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);}*/mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);//将0号的InputChannel向IMS进行注册//mService定义是com.android.server.wm.WindowManagerService mService//故mInputManager是构造wms时传入的,mInputManager是InputManagerService对象
}

2.12 Java层openInputChannelPair


//路径:frameworks\base\core\java\android\view\InputChannel.java
public static InputChannel[] openInputChannelPair(String name)
{if (name == null) {throw new IllegalArgumentException("name must not be null");}if (DEBUG) {Slog.d(TAG, "Opening input channel pair '" + name + "'");}return nativeOpenInputChannelPair(name);
}

2.13 android_view_InputChannel_nativeOpenInputChannelPair

主要作用为:

1.调用InputChannel::openInputChannelPair创建两个c++类型的InputChannel对象。

2.将c++层的两个InputChannel对象转化为java层的InputChannel对象,并返回到java层。

路径:frameworks/base/core/jni/android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,jclass clazz, jstring nameObj) {const char* nameChars = env->GetStringUTFChars(nameObj, NULL);std::string name = nameChars;env->ReleaseStringUTFChars(nameObj, nameChars);sp<InputChannel> serverChannel;sp<InputChannel> clientChannel;status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);//调用c++层的openInputChannelPairif (result) {String8 message;message.appendFormat("Could not open input channel pair.  status=%d", result);jniThrowRuntimeException(env, message.string());return NULL;}//创建两个存储gInputChannelClassInfo.clazz类型的对象的数组,默认值为nulljobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);if (env->ExceptionCheck()) {return NULL;}//将c++类型的serverChannel对象转化为java类型的serverChannelObjjobject serverChannelObj = android_view_InputChannel_createInputChannel(env,std::make_unique<NativeInputChannel>(serverChannel));if (env->ExceptionCheck()) {return NULL;}jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,std::make_unique<NativeInputChannel>(clientChannel));if (env->ExceptionCheck()) {return NULL;}env->SetObjectArrayElement(channelPair, 0, serverChannelObj);//将java类型的serverChannelObj放入数组env->SetObjectArrayElement(channelPair, 1, clientChannelObj);return channelPair;//返回一个存储java类型的ChannelObj数组
}

2.14 openInputChannelPair

主要作用为:

1.通过socketpair创建一个socket对。

2.设置socket对的发送缓冲区和接受缓冲区的大小

3.用c++层的InputChannel封装socket对,一个封装给IMS,一个封装最终会通过binder调用返回给应用端。

// frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {int sockets[2];if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {//将sockets数组传入,通过socketpair函数创建sockets对,返回值为0代表成功status_t result = -errno;ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",name.c_str(), errno);outServerChannel.clear();outClientChannel.clear();return result;}int bufferSize = SOCKET_BUFFER_SIZE;setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));//设置socket[0]的发送缓冲区大小setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));//设置socket[0]的接收缓冲区大小setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));//设置socket[1]的发送缓冲区大小setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));//设置socket[1]的接收缓冲区大小std::string serverChannelName = name;serverChannelName += " (server)";outServerChannel = new InputChannel(serverChannelName, sockets[0]);//用InputChannel封装sockets[0],给服务端std::string clientChannelName = name;clientChannelName += " (client)";outClientChannel = new InputChannel(clientChannelName, sockets[1]);//用InputChannel封装sockets[1],给客户端return OK;
}

2.15 transferTo

主要作用是:

1.转移socket给输入的参数。

// frameworks\base\core\java\android\view\InputChannel.java
public void transferTo(InputChannel outParameter) {if (outParameter == null) {throw new IllegalArgumentException("outParameter must not be null");}        nativeTransferTo(outParameter);}
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,jobject otherObj) {if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {jniThrowException(env, "java/lang/IllegalStateException","Other object already has a native input channel.");return;}NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, obj);android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);//将调用者mClientChannel对应的NativeInputChannel对象,赋值给outInputChannelandroid_view_InputChannel_setNativeInputChannel(env, obj, NULL);//将调用者mClientChannel的置空
}
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,jobject inputChannelObj) {jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);return reinterpret_cast<NativeInputChannel*>(longPtr);
}

2.16 WMS向IMS注册并监听socket

//InputManagerService.java
//注册输入通道,以便将其用作输入事件目标。
//@param inputChannel要注册的输入通道。
//@param inputWindowHandle与输入通道关联的输入窗口句柄,如果没有,则为null。
public void registerInputChannel(InputChannel inputChannel,InputWindowHandle inputWindowHandle) {if (inputChannel == null) {throw new IllegalArgumentException("inputChannel must not be null.");}nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);//mPtr是nativeInit返回的值,是指向NativeInputManager的指针。//inputChannel输入通道//inputWindowHandle窗口的句柄
}

2.17 nativeRegisterInputChannel

主要作用为:

1.将java层获取的inputChannel对象转化为c++层的inputChannel。

2.将java层的窗口句柄inputWindowHandleObj对象转化为c++层的inputWindowHandle。

3.向IMS注册此inputChannel对象和对应的窗口句柄inputWindowHandle

4.设置一个回调函数handleInputChannelDisposed,此回调函数会在调用java层的InputChannel对象完成或者销毁时触发,其会调用unregisterInputChannel取消IMS中已经保存和监听的socket

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);//从java层获取native层的inputChannel对象if (inputChannel == NULL) {throwInputChannelNotInitialized(env);return;}sp<InputWindowHandle> inputWindowHandle =android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);//获取此窗口在input中的句柄status_t status = im->registerInputChannel(env, inputChannel, inputWindowHandle, monitor);//向ims注册监听此socketif (status) {std::string message;message += StringPrintf("Failed to register input channel.  status=%d", status);jniThrowRuntimeException(env, message.c_str());return;}if (! monitor) {//此时是falseandroid_view_InputChannel_setDisposeCallback(env, inputChannelObj,handleInputChannelDisposed, im);//设置一个回调函数,此回调函数会在调用java层的InputChannel对象完成或者销毁时,触发//主要是调用unregisterInputChannel取消IMS中已经保存和监听的socket}
}

2.18 android_view_InputChannel_getInputChannel

主要作用为:

1.通过java层的InputChannel对象获取指向c++层的InputChannel对象的指针。

sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) 
{NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
}static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,jobject inputChannelObj) 
{jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);return reinterpret_cast<NativeInputChannel*>(longPtr);
}inline sp<InputChannel> getInputChannel() { return mInputChannel; }

2.19 android_server_InputWindowHandle_getHandle

主要作用为:

1.获取inputWindowHandle类对象,这是WMS的一个窗口的句柄。

//base/services/core/jni/com_android_server_input_InputWindowHandle.cpp
sp<NativeInputWindowHandle> android_server_InputWindowHandle_getHandle(JNIEnv* env, jobject inputWindowHandleObj) {//inputWindowHandleObj是java层传入的inputWindowHandle类对象if (!inputWindowHandleObj) {//如果为空,返回return NULL;}AutoMutex _l(gHandleMutex);jlong ptr = env->GetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr);NativeInputWindowHandle* handle;if (ptr) {handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);} else {jobject inputApplicationHandleObj = env->GetObjectField(inputWindowHandleObj,gInputWindowHandleClassInfo.inputApplicationHandle);sp<InputApplicationHandle> inputApplicationHandle =android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);env->DeleteLocalRef(inputApplicationHandleObj);jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);handle = new NativeInputWindowHandle(inputApplicationHandle, objWeak);handle->incStrong((void*)android_server_InputWindowHandle_getHandle);env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,reinterpret_cast<jlong>(handle));}return handle;
}

2.20 registerInputChannel

主要作用为:

1.调用InputDispatcher的registerInputChannel函数,将窗口的inputChannel和其对应的窗口句柄inputWindowHandle注册到IMS中保存。

//com_android_server_input_InputManagerService.cpp
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,const sp<InputChannel>& inputChannel,const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {ATRACE_CALL();return mInputManager->getDispatcher()->registerInputChannel(inputChannel, inputWindowHandle, monitor);//mInputManager是InputManager.cpp类的对象,//所以,mInputManager->getDispatcher()是一个InputDispatcher.cpp类的对象//inputChannel是IMS服务端的socket,用于接收来自应用端的消息//inputWindowHandle是对应的窗口的句柄。一个窗口对应一个socket对。}

2.21 InputDispatcher::registerInputChannel

主要作用为:

1.new了一个Connection对象,此对象里面保存了inputChannel和其对应的窗口句柄inputWindowHandle

2.将此socket的fd和此Connection对象,保存到一个容器中。当input事件发生时,会根据找到的目标窗口句柄取出对应的inputChannel,从中获取socket的fd,然后IMS会通过此socket将消息发送到应用端。

3.通过 mLooper->addFd,监听此socket的fd,从上文我们知道,WMS会创建两个已经连接好的shocket对,其中一个给到应用程序端,一个给到IMS,此处的作用便是监听来自应用程序端发送给IMS的消息,当应用程序端通过sokcet发送消息时,IMS中的handleReceiveCallback回调函数会执行。

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATIONALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(),toString(monitor));
#endif{ // acquire lockAutoMutex _l(mLock);if (getConnectionIndexLocked(inputChannel) >= 0) {//调用getConnectionIndexLocked方法,//根据inputChannel的fd值,查找mConnectionsByFd,看看是否此input channel已经注册ALOGW("Attempted to register already registered input channel '%s'",inputChannel->getName().c_str());return BAD_VALUE;}sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);//new了一个Connection对象,//此对象里面保存了inputChannel和其对应的窗口句柄inputWindowHandleint fd = inputChannel->getFd();mConnectionsByFd.add(fd, connection);//放入mConnectionsByFd中,表示已经注册过的inputChannelif (monitor) {//此时是falsemMonitoringChannels.push(inputChannel);}mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);//监听Inputchannel的可读性。//mLooper的pollOnce()本质上就是epoll_wait(),因此Looper对象具有监听文件描述符可读性事件的能力,在此注册Inputchannel可读性事件,//并在事件到来时通过handleReceiveCallback()回调进行处理} // release lock// Wake the looper because some connections have changed.mLooper->wake();//唤醒InputDispatcher线程return OK;
}
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());if (connectionIndex >= 0) {sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);if (connection->inputChannel.get() == inputChannel.get()) {return connectionIndex;}}return -1;
}

2.22 Connection

Connection类描述了从ImputDispatcher到目标窗口中的一个连接,其中保存了向窗口发送的事件的状态信息。
在 Connection中,重要的成员有:
1.mlnputPublisher,InputPublisher类的一个对象,它封装InputChannel并直接对其进行写入和读取。另外,它也负责ImputMessage结构体的封装与解析。
2.outboundQueue,用于保存等待通过此Connection进行发送的事件队列。
3.waitQueue,用于保存已经通过此Connection将事件发送给窗口,正在等待窗口反馈的事件队列。

InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),monitor(monitor),inputPublisher(inputChannel), inputPublisherBlocked(false) {
}
class Connection : public RefBase {protected:virtual ~Connection();public:enum Status {// 连接状态正常STATUS_NORMAL,// 发生了不可恢复的通信错误STATUS_BROKEN,// input channel已注销。STATUS_ZOMBIE};Status status;sp<InputChannel> inputChannel; //永不为空sp<InputWindowHandle> inputWindowHandle; // 可能为空bool monitor;InputPublisher inputPublisher;InputState inputState;//如果套接字已满,并且在应用程序使用某些输入之前无法发送其他事件,则为True。bool inputPublisherBlocked;// 事件队列需要发送到ConnectionQueue<DispatchEntry> outboundQueue;//已发送到connection但尚未收到应用程序“完成”响应的事件队列。Queue<DispatchEntry> waitQueue;explicit Connection(const sp<InputChannel>& inputChannel,const sp<InputWindowHandle>& inputWindowHandle, bool monitor);inline const std::string getInputChannelName() const { return inputChannel->getName(); }const std::string getWindowName() const;const char* getStatusLabel() const;DispatchEntry* findWaitQueueEntry(uint32_t seq);
};

2.23 android_view_InputChannel_setDisposeCallback

主要作用为:

1.设置一个回调函数,此回调函数会在调用java层的InputChannel对象完成或者销毁时触发,主要是调用unregisterInputChannel取消IMS中已经保存和监听的socket,并清空此InputChannel对象对应的Connection对象的outboundQueue和waitQueue队列。

void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,InputChannelObjDisposeCallback callback, void* data) {NativeInputChannel* nativeInputChannel =android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);//根据java层保存的native层的指针的值,获取nativeInputChannel指针ALOGW("Cannot set dispose callback because input channel object has not been initialized.");} else {nativeInputChannel->setDisposeCallback(callback, data);//此时callback是handleInputChannelDisposed//data是NativeInputManager}
}void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {mDisposeCallback = callback;//此时callback是handleInputChannelDisposedmDisposeData = data;//data是NativeInputManager
}

 handleInputChannelDisposed主要作用是取消注册inputchannel。

static void handleInputChannelDisposed(JNIEnv* env,jobject /* inputChannelObj */, const sp<InputChannel>& inputChannel, void* data) {NativeInputManager* im = static_cast<NativeInputManager*>(data);ALOGW("Input channel object '%s' was disposed without first being unregistered with ""the input manager!", inputChannel->getName().c_str());im->unregisterInputChannel(env, inputChannel);
}

那么这个回调函数什么时候会被调用呢?

在Object类里面,有一个方法finalize()。
当VM的垃圾收集器检测到这个对象不可达的时候,也就是说这个对象为垃圾可以被回收的时候,这个对象的finalize ()方法就会被执行,默认情况下,它不做任何处理,我们可以重写这个方法来进行资源的释放。当回收分配的Object对象的内存之前垃圾收集器会调用对象的finalize()方法。

//InputChannel.java    
protected void finalize() throws Throwable {try {nativeDispose(true);} finally {super.finalize();}}

查看对应关系

{ "nativeDispose", "(Z)V",(void*)android_view_InputChannel_nativeDispose },
static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {NativeInputChannel* nativeInputChannel =android_view_InputChannel_getNativeInputChannel(env, obj);if (nativeInputChannel) {if (finalized) {ALOGW("Input channel object '%s' was finalized without being disposed!",nativeInputChannel->getInputChannel()->getName().c_str());}nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);android_view_InputChannel_setNativeInputChannel(env, obj, NULL);delete nativeInputChannel;}
}
static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {NativeInputChannel* nativeInputChannel =android_view_InputChannel_getNativeInputChannel(env, obj);//根据java层的InputChannel获取native层的NativeInputChannelif (nativeInputChannel) {if (finalized) {ALOGW("Input channel object '%s' was finalized without being disposed!",nativeInputChannel->getInputChannel()->getName().c_str());}nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);android_view_InputChannel_setNativeInputChannel(env, obj, NULL);//设置Java曾保存的指向native层的NativeInputChannel的指针为空delete nativeInputChannel;//delete}
}
void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) {if (mDisposeCallback) {mDisposeCallback(env, obj, mInputChannel, mDisposeData);//会执行handleInputChannelDisposed回调函数mDisposeCallback = NULL;mDisposeData = NULL;}
}

  handleInputChannelDisposed主要作用是取消注册inputchannel。

static void handleInputChannelDisposed(JNIEnv* env,jobject /* inputChannelObj */, const sp<InputChannel>& inputChannel, void* data) {NativeInputManager* im = static_cast<NativeInputManager*>(data);ALOGW("Input channel object '%s' was disposed without first being unregistered with ""the input manager!", inputChannel->getName().c_str());im->unregisterInputChannel(env, inputChannel);
}

查看unregisterInputChannel

status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,const sp<InputChannel>& inputChannel) {ATRACE_CALL();return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel);
}

查看InputDispatcher的unregisterInputChannel函数。

status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {{ // acquire lockAutoMutex _l(mLock);status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);if (status) {return status;}} // release lock// Wake the poll loop because removing the connection may have changed the current// synchronization state.mLooper->wake();return OK;
}

 主要作用为:

1.从容器中删除此Connection对象

2.从epoll_wait中取消此socket的监听

3.清空此connection的等待发送队列outboundQueue和发送成功等待回应的消息队列

status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,bool notify) {ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);//从保存所有inputChannel的容器中找到当前connection的索引if (connectionIndex < 0) {ALOGW("Attempted to unregister already unregistered input channel '%s'",inputChannel->getName().c_str());return BAD_VALUE;}sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);mConnectionsByFd.removeItemsAt(connectionIndex);//从容器中删除此Connection对象,Connection中保存了inputChannel和其对应的窗口句柄if (connection->monitor) {removeMonitorChannelLocked(inputChannel);}mLooper->removeFd(inputChannel->getFd());//mLooper本质是epoll_wait,从epoll_wait中取消此socket的监听,即不再监听来自应用程序端的消息nsecs_t currentTime = now();abortBrokenDispatchCycleLocked(currentTime, connection, notify);//清空此connection的等待发送队列outboundQueue和发送成功等待回应的消息队列connection->status = Connection::STATUS_ZOMBIE;return OK;
}
void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection, bool notify) {// Clear the dispatch queues.drainDispatchQueueLocked(&connection->outboundQueue);traceOutboundQueueLengthLocked(connection);drainDispatchQueueLocked(&connection->waitQueue);traceWaitQueueLengthLocked(connection);if (connection->status == Connection::STATUS_NORMAL) {connection->status = Connection::STATUS_BROKEN;/*此时是falseif (notify) {// Notify other system components.onDispatchCycleBrokenLocked(currentTime, connection);}*/}
}

2.24 WindowInputEventReceiver

此时我们已经知道了WMS将其中一个sokcet注册给了IMS。我们接下来看看第二个socket,应用端是如何处理的?

在上文的ViewRootImpl的setView最后,应用端会new一个WindowInputEventReceiver,然后会调用InputEventReceiver构造函数。

//ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());//mInputChannel是客户端的socket[1],Looper.myLooper主线程的looper
}final class WindowInputEventReceiver extends InputEventReceiver {public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {super(inputChannel, looper);//调用InputEventReceiver的构造方法}}

2.25 InputEventReceiver

主要作用:

1.保存java层的inputChannel对象

2.获取UI主线程,此处主要是需要将客户端的socket添加到Looper中监听,其实Looper的底层也是epoll_wait

3.调用nativeInit函数,此函数内部会new一个NativeInputEventReceiver对象,然后调用此对象的初始化函数,初始化函数的内部会将此应用端的socket添加到Looper中监听。当存在IMS发送事件到应用程序端时,会调用NativeInputEventReceiver的handleEvent函数。

public InputEventReceiver(InputChannel inputChannel, Looper looper) {mInputChannel = inputChannel;//socket[1]mMessageQueue = looper.getQueue();//UI 线程消息队列mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),inputChannel, mMessageQueue);
}

2.26 nativeInit

主要作用为:

1.new了一个NativeInputEventReceiver对象。

2.调用NativeInputEventReceiver的initialize函数。初始化函数的内部会将此应用端的socket添加到Looper中监听。当存在IMS发送事件到应用程序端时,会调用NativeInputEventReceiver的handleEvent函数。

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,inputChannelObj);//获取c++层的inputChannelif (inputChannel == NULL) {jniThrowRuntimeException(env, "InputChannel is not initialized.");return 0;}sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);//获取消息队列if (messageQueue == NULL) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,//new了一个NativeInputEventReceiver对象receiverWeak, inputChannel, messageQueue);status_t status = receiver->initialize();//初始化NativeInputEventReceiver对象/*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); // retain a reference for the objectreturn reinterpret_cast<jlong>(receiver.get());
}

2.27 NativeInputEventReceiver

里面创建了一个InputConsumer类对象,用来保存c++层的inputChannel

NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,jobject receiverWeak, const sp<InputChannel>& inputChannel,const sp<MessageQueue>& messageQueue) :mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),mInputConsumer(inputChannel), mMessageQueue(messageQueue),//创建了一个InputConsumer类对象,用来保存c++层的inputChannelmBatchedInputEventPending(false), mFdEvents(0) 
{/*if (kDebugDispatchCycle) {//默认falseALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());}*/
}
InputConsumer::InputConsumer(const sp<InputChannel>& channel) :mResampleTouch(isTouchResamplingEnabled()),//是否触摸重新采样mChannel(channel), mMsgDeferred(false) 
{
}

2.28 initialize

主要作用:

1.调用setFdEvents函数。

status_t NativeInputEventReceiver::initialize() 
{setFdEvents(ALOOPER_EVENT_INPUT);//ALOOPER_EVENT_INPUT值是1return OK;
}

2.29 setFdEvents

主要作用:

1.调用了Looper的addfd函数,用于监听此fd,并传入了NativeInputEventReceiver对象,当InputManagerService发送消息到应用程序时,会调用NativeInputEventReceiver的handleEvent函数。

void NativeInputEventReceiver::setFdEvents(int events) {if (mFdEvents != events) {//默认是0,此时为1mFdEvents = events;//设置为ALOOPER_EVENT_INPUTint fd = mInputConsumer.getChannel()->getFd();//获取socket[0]的fdif (events) {mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);//调用了Looper的addfd函数,用于监听此fd,并传入了NativeInputEventReceiver对象} else {mMessageQueue->getLooper()->removeFd(fd);}}
}

2.30 Looper::addFd

主要作用为:

1.调用epoll_ctl监听此应用的socket,即监听来自IMS的输入事件。

int Looper::addFd(int fd, int ident, int events,const sp<LooperCallback>& callback, void* data) {{AutoMutex _l(mLock);Request request;request.fd = fd;request.ident = ident;request.events = events;request.seq = mNextRequestSeq++;request.callback = callback; // 是指 NativeInputEventReceiverrequest.data = data;if (mNextRequestSeq == -1) mNextRequestSeq = 0;struct epoll_event eventItem;request.initEventItem(&eventItem);ssize_t requestIndex = mRequests.indexOfKey(fd);if (requestIndex < 0) {// 通过 epoll 监听 fdint epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);......mRequests.add(fd, request); // 该fd 的 request 加入到 mRequests 队列} else {int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);......mRequests.replaceValueAt(requestIndex, request);}} return 1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/36069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java周测总结(3)

1、什么是I0流&#xff1f; 是一串流动的字符,从先进先出的方式要求信息的通道。 2、什么是序列化&#xff1f;什么是反序列化&#xff1f; 序例化是将对象的状态存储到特定的存储介质中的过程反序例化是将特定的有合者公质中数据重新构建对象的过程。 3、Java中线程在哪个包下…

Ingress Controller介绍及部署实践

Ingress Controller介绍及部署实践 1. 概念 1.1 Ingress Ingress 提供从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源所定义的规则来控制。 下面是 Ingress 的一个简单示例&#xff0c;可将所有流量都发送到同一 Service&#xff1a; 通过配置&am…

11.常见的Transforms(二)

常见的Transforms&#xff08;二&#xff09; 1.Resize() 的使用 1.1 作用 resize可以把输入的图片按照输入的参数值重新设定大小。 1.2 所需参数 需要输入想要重新设定的图片大小。 输入的参数类型可以为包含长和宽数值的一个序列&#xff08;h,w&#xff09;或者一个整…

vue-cli的搭建过程

一、创建一个vue2的项目 二、创建成功后删除这三个文件 三、新建一个App.vue文件 四、在文件中添加这一段话 五、打开命令框输入指令下载router路由 六、新建一个router目录&#xff0c;新建index.js文件 七、导入你的路由&#xff0c;进行配置 打开命令行工具&#xff0c;进入…

【LeetCode】一、数组相关:双指针算法 + 置换

文章目录 1、算法复杂度1.1 时间复杂度1.2 空间复杂度 2、数组3、leetcode485&#xff1a;最大连续1的个数4、leetcode283&#xff1a;移动05、leetcode27&#xff1a;移除元素 1、算法复杂度 1.1 时间复杂度 算法的执行时间与输入值之间的关系&#xff08;看代码实际总行数的…

hive零基础入门

1、hive简介 hive&#xff1a;由facebook开源用于解决海量结构化数据的统计工具。 hive是基于Hadoop的数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供sql查询功能。 2、hive本质 hive的本质是HQL&#xff08;HiveSQL&#xff09;转化成MapR…

java 统计xmind的结点数(测试用例case数)

mac电脑解压出来的xmind的数据主要在content.json上 开头结尾有[],里面是json import org.json.JSONArray; import org.json.JSONObject; import java.io.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream;public class XMindLeafCounter2 {public stat…

PlatformIO开发环境

PlatformIO是一个开源的生态系统&#xff0c;用于构建物联网应用&#xff0c;它支持多种微控制器&#xff08;MCU&#xff09;和硬件开发板&#xff0c;并且与各种IDE集成良好&#xff0c;如VSCode, Atom等&#xff0c;使得跨平台的固件开发变得更加简单和高效。 ### 平台介绍…

数据库自动备份到gitee上,实现数据自动化备份

本人有个不太好的习惯&#xff0c;每次项目的数据库都是在线上创建&#xff0c;Navicat 连接线上数据库进行处理&#xff0c;最近有一个项目需要二次升级&#xff0c;发现老项目部署的服务器到期了&#xff0c;完蛋&#xff0c;数据库咩了&#xff01;&#xff01;&#xff01;…

一篇文章教会你【elementUI搭建使用】

Element&#xff0c;一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组 件库. 安装 ElementUI npm i element-ui -S 在 main.js 中写入以下内容&#xff1a; import ElementUI from element-ui; import element-ui/lib/theme-chalk/index.css; Vue.use(Eleme…

【漏洞复现】金和OA 任意文件上传

【产品介绍】 金和OA协同办公管理系统C6软件&#xff08;简称金和OA&#xff09;&#xff0c;本着简单、适用、高效的原则&#xff0c;贴合企事业单位的实际需求&#xff0c;实行通用化、标准化、智能化、人性化的产品设计&#xff0c;充分体现企事业单位规范管理、提高办公效…

老生常谈问题之什么是缓存穿透、缓存击穿、缓存雪崩?举个例子你就彻底懂了!!

老生常谈问题之什么是缓存穿透、缓存击穿、缓存雪崩&#xff1f;举个例子你就彻底懂了&#xff01;&#xff01; 缓存穿透发生场景解决方案 缓存击穿解决方案 缓存雪崩发生场景解决方案 总结三者区分三者原因三者解决方案 想象一下&#xff0c;你开了一家便利店&#xff0c;店里…

Unity3D Text使用超链接跳转事件

系列文章目录 Unity工具 文章目录 系列文章目录&#x1f449;前言&#x1f449;一、第一种使用TextMeshPro加入超链接&#x1f449;二、继承Text组件,重载OnPopulateMesh方法&#x1f449;三.壁纸分享&#x1f449;总结 &#x1f449;前言 有时候会用到跳转的问题,所以添加一…

【Python机器学习实战】 | 基于支持向量机(Support Vector Machine, SVM)进行分类和回归任务分析

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

一个AI图片生成工具导航网站

上周末上线了一个AI图片生成工具导航网站&#xff0c;主要是面向AI图片工具这个垂直领域。 https://chatgpt-image-generator.com/ 目标是通过收集当下的一些工具&#xff0c;然后进行分类管理&#xff0c;一方面方便大家发现新的工具&#xff0c;另一方面能够更加有针对性、…

华为OceanStor磁盘阵列存储恢复出厂设置命令 LUN不处于在线状态,不能执行此操作解决方案

环境 OceanStor S2600T V2老版本 客户现场有一台Oceanstor 2600 V2的存储&#xff0c;因和另一台磁盘扩展框做了跨设备LUN需要进行配置清除&#xff0c;配置结束后需要重新划分存储空间并对接服务器&#xff0c;保证业务能够正常上线&#xff01;在清除配置回退的过程中&#…

G8 - ACGAN

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 模型结构 模型结构 之前几期打卡中&#xff0c;已经介绍过GAN CGAN SGAN&#xff0c;而ACGAN属于上述几种GAN的缝合怪&#xff0c;其模型的结构图如下&a…

Python 中的抽象语法树

Abstract Syntax Trees in Python 注&#xff1a;机翻&#xff0c;未校对。 Requirement: All examples are compatible with at least Python v3.6, except for using ast.dump() with the attribute indent which has been added in Python v3.9. 要求&#xff1a;所有示例至…

第二十课,认识列表与定义列表

一&#xff0c;列表的作用 思考一个问题&#xff1a;如果我想要在程序中&#xff0c;记录5名学生的信息&#xff0c;如姓名。 如何做呢&#xff1f; 这就是列表的作用&#xff0c;能帮助我们更加高效的存储各种数据 思考&#xff1a;如果一个班级100位学生&#xff0c;每个人…

利用SHAP算法解释BERT模型的输出

1 何为SHAP? 传统的 feature importance 只告诉哪个特征重要&#xff0c;但并不清楚该特征如何影响预测结果。SHAP 算法的最大优势是能反应每一个样本中特征的影响力&#xff0c;且可表现出影响的正负性。SHAP算法的主要思想为&#xff1a;控制变量法&#xff0c;如果某个特征…