当前位置: 首页 > news >正文

Android 14输入系统架构分析:图解源码从驱动层到应用层的完整传递链路

一、资料快车

1、深入了解Android输入系统:https://blog.csdn.net/innost/article/details/47660387

2、书籍 - Android系统源代码情景分析

二、Perface

1、参考:

2、系统程序分析方法

1)加入log,并跟着log一步步分析 -logcat;

2)利用ChatGPT提供基础概念解析 & 代码解析 & 设计原理;

3、目标

1)提供查阅代码的线索、思路;

2)能够根据日志进行快读的代码分析;

3)区分代码层次,为定制系统提供思路;

4)站在前人的肩膀上进一步探究;

5)熟悉架构后,我们应能 如何添加自定义按键、拦截keyevent并添加自己的处理逻辑;

6)Android源代码兼容多种设备,比如一开始Android针对手机设计(因此很多代码都有手机的身影),后面适应不同的设备,不同设备的处理流程不同,注意分辨;

4、action

务必根据本文提供的线索 去看源代码。

带着疑问去了解

1、kl/kcm作用是什么?在哪里会被读取?

2、定义CVTE快捷键,应该怎么做?

3、如何跟中控和TVAPI联合起来?

三、代码目录

所在层次名称代码路径
Application应用(调用系统库,实现用户逻辑)实现android中对应的类及方法,进行inputevent处理
frameworkandroid (此部分链接到应用)/android/frameworks/base/services/java/com/android/server/SystemServer.java
/android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
/android/frameworks/base/core/java/*
/android/frameworks/base/core/java/com/android/internal/policy/DecorView.java
/android/frameworks/base/core/java/android/app/Activity.java
/android/frameworks/base/core/java/android/view/ViewRootImpl.java
/android/frameworks/base/core/java/android/view/View.java
/android/frameworks/base/core/java/android/view/KeyEvent.java
/android/frameworks/base/core/java/android/view/InputChannel.java
service (系统服务,服务端集中管理)Framework java基础服务
/android/frameworks/base/services/java/com/android/server/SystemServer.java
Framework android核心服务
/android/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
JNI(过渡到native)一、libservices.core - 静态库模块
/android/frameworks/base/services/core/jni/
/android/frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
二、libandroid_runtime - 静态库模块
/android/frameworks/base/core/jni/

/android/frameworks/base/core/jni/android_view_InputEventReceiver.cpp
NDK一、libinput - 静态库模块
/android/frameworks/native/libs/input/
/android/frameworks/native/libs/input/InputTransport.cpp
二、libinputflinger/libinputflinger_base - 动态库模块
/android/frameworks/native/services/inputflinger/

/android/frameworks/native/services/inputflinger/InputManager.cpp
三、lib
/android/system/core/libutils/*
/android/system/core/libutils/Looper.cpp
cvte/android/frameworks/base/core/java/com/cvte/key/*
kernelinput子系统

四、系统框架流程

1、粗略框图 - 来自深入了解Android

在这里插入图片描述

2、代码层级
在这里插入图片描述

3、framework部分
在这里插入图片描述

4、下面针对各个部分进行拆解分析

五、基础概念

1、APP界面构成关系

1)Application 、Activity、Windows 、Decro(ration) 、View

在这里插入图片描述

一个Application有多个Activity,Activity中的界面部分为Windows(PhoneWindow),是我们所看到的当前整个界面,windows界面由Decro(样式) 来描述,Decro由各种view组件(TextView、button)组成。

2)从代码角度看Decro与View的树状关系
在这里插入图片描述

3)小结:

android里:
1个application, 有1个或多个activity
1个activity, 有1个PhoneWindow
1个window, 有1个decor
1个decor, 有多个viewgroup/layout
viewgroup/layout中, 有多个view

2、Activity组件与ViewRoot对象的关系

在这里插入图片描述

Android设计思想 - 抽象分工

一个应用程序窗口是由一个Activity来描述(描述控件组成、布局等等)- 策略者;

每一个Activity组件都有一个关联的ViewRoot对象(除了绘制具体的窗口,还负责分发键盘事件) - 具体实施者;

Activity和ViewRoot通过DecorView和PhoneWindow对象来关联起来;

需要跟踪代码才能掌握理解以上关系

Android-Activity与Window与View之间的关系:https://github.com/jeanboydev/Android-ReadTheFuckingSourceCode/blob/master/article/android/framework/Android-Activity%E4%B8%8EWindow%E4%B8%8EView%E4%B9%8B%E9%97%B4%E7%9A%84%E5%85%B3%E7%B3%BB.md

2、输入事件

输入事件有多种类型(触摸、拖动、鼠标、按键等等)

3、key相关概念

1)*.kl : keylayout,linux的scancode对应的Android按键映射;

2)*.kcm:key code map,Android按键对应的字符映射;

3)*.idc : input device configutation 输入设备配置文件;

以上文件在板卡都可以找到

4、socketpair 与 binder

socketpair 与 binder 为Android的跨进程通信技术模块,两者结合可以实现双向的跨进程通信。

5、inotify和epoll

inotify模块:用于监测某目录下的文件变化

epoll模块:用于监测文件内容变化

六、Linux kernel

1、input输入子系统

2、IR驱动

https://blog.csdn.net/STCNXPARM/article/details/134235394

七、Andorid系统部分

1、WMS

2、InputManagerService(java)

java层的InputManagerService对native层的InputManager进一步封装,很多实现都在native层。

1)Reader(CPP)

1、Reader角色
Reader主要工作
1)从设备里读取输入事件;
2)根据kcm/kl文件,进行按键映射处理;
3)将RawEvent传递给dispatcher处理;
这块代码比较固定,也很少去适配,了解下即可android\frameworks\native\services\inputflinger\*1、InputReader对象启动线程
android\frameworks\native\services\inputflinger\reader\InputReader.cpp
status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}2、InputThread对,提供一个线程模板
android\frameworks\native\services\inputflinger\InputThread.cpp
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake): mName(name), mThreadWake(wake) {mThread = sp<InputThreadImpl>::make(loop);mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}3、读取输入事件
//主要使用inotify监测/dev/input,使用epoll监测有无数据
void InputReader::loopOnce() { //作为函数参数传给InputThread
std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis); //读取event设备节点
...
mQueuedListener.flush(); //flush队列,通知dispatcher线程
...
}
2、InputReader和EventHub的分工
1)InputReader(上层角色)

在这里插入图片描述

1)如果设备刚接入,则先addDeviceLocked() 添加设备

2)如果设备已存在且有输入事件产生,则processEventLocked()

3)处理事件对象,这里以KeyboardInputMapper为例

2)EventHub(底层角色)

EventHub读取设备节点,获取输入事件RawEvent
在这里插入图片描述

1)读取输入设备驱动信息(底层原理是使用inotify/epoll机制进行监测),加载idc/kcm/kl文件(驱动设备提供路径);

2)最终转换成RawEvent,交给InputReader继续处理;

EventHub是linux device交互的功能类

1、openDeviceLocked
android\frameworks\native\services\inputflinger\reader\EventHub.cpp
void EventHub::openDeviceLocked(const std::string& devicePath) {...//打开设备int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);if (fd < 0) {ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));return;}...//ioctl得到信号// Get device name.if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));} else {buffer[sizeof(buffer) - 1] = '\0';identifier.name = buffer;}...//加载idc//kl/kcmif (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK |InputDeviceClass::SENSOR)) {// Load the keymap for the device.keyMapStatus = device->loadKeyMapLocked();}
}
3)InputReader和EventHub的分工介绍(分层思想)

在这里插入图片描述

3、复杂的底层数据结构介绍

1)RawEvent/input_event/Device/InputDevice

1、android\frameworks\native\services\inputflinger\reader\include\EventHub.h
struct RawEvent { //// Time when the event happenednsecs_t when;// Time when the event was read by EventHub. Only populated for input events.// For other events (device added/removed/etc), this value is undefined and should not be read.nsecs_t readTime;int32_t deviceId;int32_t type;int32_t code;int32_t value;
};2、include/linux/input.h
struct input_event{struct timeval time;__u16 type;__u16 code;__s32 value;
};3、Device //注意不是linux device,不要混淆
android\frameworks\native\services\inputflinger\reader\include\EventHub.h
struct Device {int fd; // may be -1 if device is closedconst int32_t id;const std::string path;const InputDeviceIdentifier identifier; //输入设备的基本信息...std::string configurationFile; //对应idc配置文件std::unique_ptr<PropertyMap> configuration;std::unique_ptr<VirtualKeyMap> virtualKeyMap;KeyMap keyMap; //对应两种配置文件,kcm->kl,先找kcm映射,找不到再找kl映射
}
关于Device信息,Android上可以敲dumpsys input查看所有的input设备信息定义
F:\1.code\MTK9256_AN14\android\frameworks\native\services\inputflinger\reader\include\EventHub.h
std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices;4、
F:\1.code\MTK9256_AN14\android\frameworks\native\services\inputflinger\reader\include\InputDevice.hclass InputDevice { //绑定EventHub的Device,目的是封装Device的复杂操作,给上层提供更方便的操作
private:InputReaderContext* mContext;int32_t mId;int32_t mGeneration;int32_t mControllerNumber;InputDeviceIdentifier mIdentifier;std::unordered_map<int32_t, DevicePair> mDevices;
}
//定义
F:\1.code\MTK9256_AN14\android\frameworks\native\services\inputflinger\reader\include\InputReader.h
std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices GUARDED_BY(mLock);5、
/* Types of input device configuration files. */
enum class InputDeviceConfigurationFileType : int32_t {CONFIGURATION = 0,     /* .idc file */KEY_LAYOUT = 1,        /* .kl file */KEY_CHARACTER_MAP = 2, /* .kcm file */
};

2)KeyMap-KeyLayoutMap-KeyCharacterMap

1、
F:\1.code\MTK9256_AN14\android\frameworks\native\include\input\Keyboard.h
class KeyMap {
public:std::string keyLayoutFile;std::shared_ptr<KeyLayoutMap> keyLayoutMap;std::string keyCharacterMapFile;std::shared_ptr<KeyCharacterMap> keyCharacterMap;
}2、
class KeyLayoutMap {
private:struct Key {int32_t keyCode;uint32_t flags;};
}
kl文件格式举例
key 305   BUTTON_B   // key <linux key> <Android key>3、
class KeyCharacterMap {
private:struct Behavior {/* The meta key modifiers for this behavior. */int32_t metaState = 0;/* The character to insert. */char16_t character = 0;/* The fallback keycode if the key is not handled. */int32_t fallbackKeyCode = 0;/* The replacement keycode if the key has to be replaced outright. */int32_t replacementKeyCode = 0;};struct Key {/* The single character label printed on the key, or 0 if none. */char16_t label = 0;/* The number or symbol character generated by the key, or 0 if none. */char16_t number = 0;/* The list of key behaviors sorted from most specific to least specific* meta key binding. */std::list<Behavior> behaviors;};class Parser {} //加载kcm文件时用此class来解析
}kcm文件格式举例:
1、
key B {label:                              'B'  # 印在按键上的文字                       base:                               'b'  # 如果没有其他按键(shift, ctrl等)同时按下,此按键对应的字符是'b'                     shift, capslock:                    'B'  # 组合按键,如shift+base='B', capslock+base='B'
}
2、
key SPACE {label:                              ' 'base:                               ' 'alt, meta:                          fallback SEARCH   #组合按键ctrl:                               fallback LANGUAGE_SWITCH   #组件按键
}解析组合按键构造三个Behavior
Behavior.metaState=alt
Behavior.fallbackKeyCode=AKEYCODE_SEARCHBehavior.metaState=meta
Behavior.fallbackKeyCode=AKEYCODE_SEARCHBehavior.metaState=ctrl
Behavior.fallbackKeyCode=AKEYCODE_LANGUAGE_SWITCH放到链表std::list<Behavior> behaviors

3)涉及的数据结构总览
在这里插入图片描述

2)Dispatcher(CPP)

1、Dispatcher的工作

在这里插入图片描述

要点解析

1)输入事件之按键分类:
Global key(一键启动 - 快捷键)
system key(静音、音量、home键)
user key(普通输入按键 )2)inboundQueue/outboundQueue(双端队列 - 用于储存输入事件);
出队:mInboundQueue.pop_front();
入队:mInboundQueue.push_front();3)mCommandQueue(用于储存Command):  
出队:mCommandQueue.pop_front();
入队:mCommandQueue.push_back(command);
2、代码时序图

输入事件有多种类型(触摸、拖动、鼠标等等),这里以输入按键为例(从底层->上层)
在这里插入图片描述

3、关键点分析
1)reader与dispatcher的交互实现

1、总体框图

在这里插入图片描述

InputFlinger进程

android\frameworks\native\services\inputflinger\host\main.cpp

2、InputManager的构造函数中建立联系,通过mQueuedListener,Reader获得Dispatcher的指针;

android\frameworks\native\services\inputflinger\InputManager.cpp
/*** The event flow is via the "InputListener" interface, as follows:* InputReader -> UnwantedInteractionBlocker -> InputProcessor -> InputDispatcher*/
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,InputDispatcherPolicyInterface& dispatcherPolicy) {mDispatcher = createInputDispatcher(dispatcherPolicy);mProcessor = std::make_unique<InputProcessor>(*mDispatcher);mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mProcessor);mReader = createInputReader(readerPolicy, *mBlocker);
}

3、处理流 InputReader -> UnwantedInteractionBlocker -> InputProcessor -> InputDispatcher

类图

在这里插入图片描述

4、InputListenerInterface

android\frameworks\native\services\inputflinger\InputListener.cpp1、根据NotifyArgs的类型执行对应的函数-泛型思想
void InputListenerInterface::notify(const NotifyArgs& generalArgs) {Visitor v{[&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },[&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(args); },[&](const NotifyKeyArgs& args) { notifyKey(args); },[&](const NotifyMotionArgs& args) { notifyMotion(args); },[&](const NotifySwitchArgs& args) { notifySwitch(args); },[&](const NotifySensorArgs& args) { notifySensor(args); },[&](const NotifyVibratorStateArgs& args) { notifyVibratorState(args); },[&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(args); },[&](const NotifyPointerCaptureChangedArgs& args) { notifyPointerCaptureChanged(args); },};std::visit(v, generalArgs);
}void QueuedInputListener::notifyKey(const NotifyKeyArgs& args) {traceEvent(__func__, args.id);mArgsQueue.emplace_back(args);
}2、flush - 逐个将mArgsQueue中的事件通过队列传递给dispatcher处理 - 泛型的简洁
void QueuedInputListener::flush() {for (const NotifyArgs& args : mArgsQueue) {mInnerListener.notify(args);}mArgsQueue.clear();
}
2)高优先级的按键处理介绍

在PhoneWindowManager.java类中会两个地方会对输入按键进行干预处理

为什么这样设计?因为需要优先处理高优先级的按键(如global/system key)

1、mPolicy.interceptKeyBeforeQueueing();

1)分类-global / system /user key

2)处理紧急事件-比如来电显示

2、mPolicy.interceptKeyBeforeDispatching()

1)响应处理global/system key

2)user key则正常传递到app处理

3、在frameworks中进行按键拦截,也主要在PhoneWindowManager下面两个函数加逻辑

interceptKeyBeforeDispatching()

interceptKeyBeforeQueueing()

3)不同事件按键的处理介绍
不同事件按键会走不同的代码分支,这里介绍下重点
场景分析1-global key(如AKEYCODE_TV - 快捷键)
场景分析2-system key(如AKEYCODE_VOLUME_DOWM - 音量按键)
场景分析3-user key(如AKEYCODE_A、上下左右 - 普通按键)一、第一次预处理-interceptKeyBeforeQueueing
interceptKeyBeforeQueueing的处理结果会储存到事件结构体entry中,后续在InputDispatcher.cpp处理会用到
android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
interceptKeyBeforeQueueingentry->interceptKeyResult == KeyEntry::InterceptKeyResult::UNKNOWN  //对于没被处理的输入按键事件都是UNKNOWN & POLICY_FLAG_PASS_TO_USER
entry->policyFlags & POLICY_FLAG_PASS_TO_USER二、第二次预处理
android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
interceptKeyBeforeDispatching1、对于system key直接处理,如
switch(keyCode) {case KeyEvent.KEYCODE_HOME:return handleHomeShortcuts(displayId, focusedToken, event);case KeyEvent.KEYCODE_MENU:
...
}
return key_consumed;2、对于global key会发送广播,如
if (isValidGlobalKey(keyCode)&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {return key_consumed;}
return key_consumed;3、对于user key, 在此函数内不处理,直接返回
return key_not_consumed;三、第二次预处理后的几种返回值
android/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cppvoid InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,KeyEntry& entry) {const KeyEvent event = createKeyEvent(entry);nsecs_t delay = 0;{ // release lockscoped_unlock unlock(mLock);android::base::Timer t;delay = mPolicy.interceptKeyBeforeDispatching(focusedWindowToken, event, entry.policyFlags);if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",std::to_string(t.duration().count()).c_str());}} // acquire lockif (delay < 0) {entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP;  //对应system key/global key} else if (delay == 0) {entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;  //对应user key} else {entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER;entry.interceptKeyWakeupTime = now() + delay;}
}四、user key的处理
android/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
对于user key而言,dispatchOnce会执行两次,其中第一个循环用于处理global/system key(因为优先级更高一些),第二循环则处理user key(发布到app),代码设计如此,可自行根据代码琢磨。if (runCommandsLockedInterruptable()) {nextWakeupTime = LLONG_MIN;
}
mLooper->pollOnce(timeoutMillis); //下一轮循环将处理user key,最终发布到app
4)涉及的Server和Service介绍
1、SystemServer
2、
3、WindowManagerService
wm = WindowManagerService.main(context, inputManager, !mFirstBoot,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);4、
public class PhoneWindowManager implements WindowManagerPolicy {static final String TAG = "WindowManager";
}5、LocalServices.addService(WindowManagerPolicy.class, mPolicy);

3、Reader和Dispatcher的创建和启动

了解到Reader和Dispatcher的作用,再来从宏观上看看创建和启动过程

1)流程图

在这里插入图片描述

注意,dispatcher线程要优先与reader线程启动,那样保证消息能第一次时间被处理

3、SystemServer

开机流程:Android系统源码开机启动过程

/android/frameworks/base/services/java/com/android/server/SystemServer.java 

八、APP与输入系统的联系

1、总体交互框图

在这里插入图片描述

1)SystemServer通过(connect →inputchannel → socketpair) 将输入事件传递给app

2、inputchannel

1、场景分析 - inputchannel创建过程分析

1)流程图

在这里插入图片描述

2)值得注意的点
1、APP远程调用WM服务
产生远程调用的语句:mWindowSession.addToDisplay()细节分析追溯下mWindowSession1、构造函数中又调用自身的构造函数
mWindowSession在构造函数中赋值
public ViewRootImpl(Context context, Display display) {this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
}public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session, WindowLayout windowLayout) {mContext = context;mWindowSession = session;...
}2、获取server端session
android\frameworks\base\core\java\android\view\WindowManagerGlobal.javapublic static IWindowSession getWindowSession() {synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {try {// Emulate the legacy behavior.  The global instance of InputMethodManager// was instantiated here.// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsageInputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();IWindowManager windowManager = getWindowManagerService();sWindowSession = windowManager.openSession( //获取session句柄,后续使用session访问server接口new IWindowSessionCallback.Stub() {@Overridepublic void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);}});} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return sWindowSession;}}public static IWindowManager getWindowManagerService() {synchronized (WindowManagerGlobal.class) {if (sWindowManagerService == null) {sWindowManagerService = IWindowManager.Stub.asInterface(  //binder 中 client调用server接口ServiceManager.getService("window"));...}return sWindowManagerService;}
}3、哪里会new ViewRootImpl实例?
android\frameworks\base\core\java\android\view\WindowManagerGlobal.java
mGlobal.addView()中会new一个ViewRootImpl实例
root = new ViewRootImpl(view.getContext(), display);小结:如何找Framework中的远程调用?Binder细节很多,我们只需要关注以下几点即可找到1、client调用:mWindowSession.addToDisplay()2、根据IWindowManager类推断出存在aidl文件:IWindowSession.aidl -> out目录下找IWindowSession.java3、确定server 函数位置:server端必定会extends :IWindowSession.Stub,并根据函数名来双重确定
java/C++层的inputChannel
java/C++层都对应一个 inputChannel类(包含socketpair fd),用于参数传递
1、java层
android\frameworks\base\core\java\android\view\InputChannel.java
2、C++层
android\frameworks\native\libs\input\InputTransport.cpp3、JNI中的转换
jobject inputChannelObj = android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
C++层inputchannel类
声明:android\frameworks\native\include\input\InputTransport.h
定义:android\frameworks\native\libs\input\InputTransport.cpp
继承于Parcelable,即用于进程间通信的类
1、几个变量
std::string mName;
android::base::unique_fd mFd; //保存socker文件句柄
sp<IBinder> mToken;2、创建client server 对应的两个inputChannel,及对应两个socker
static status_t openInputChannelPair(const std::string& name,std::unique_ptr<InputChannel>& outServerChannel,std::unique_ptr<InputChannel>& outClientChannel);3、status_t sendMessage(const InputMessage* msg); //service向fd写msg(按键信息)4、status_t receiveMessage(InputMessage* msg); //client读fd的msg(按键信息)
sockerpair 传递
android\frameworks\native\libs\input\InputTransport.cpp
1、返回给应用程序的fd需要深拷贝,否则返回后会被释放
base::unique_fd InputChannel::dupFd() const {android::base::unique_fd newFd(::dup(getFd()));...}return newFd;
}

3、APP监听输入事件

APP启动时会,先获取到connection,再设置启动监听输入事件,流程如下

在这里插入图片描述

4、Looper将输入事件传递给APP

1)从底层传递到上层,流程图如下
在这里插入图片描述

2)重点讲解

1、Response、Request结构体struct Response {SequenceNumber seq;int events;Request request;};struct Request {int fd;int ident;int events;sp<LooperCallback> callback;void* data;uint32_t getEpollEvents() const;};2、onInputEvent()是java层处理输入事件的总入口

九、APP层的处理

1、stage

在onInputEvent()函数处理中,还有一系列的传递,用stage来表示各个阶段的处理,其中有针对系统输入法(InputMethodManager)的处理
在这里插入图片描述

1)术语
1、ime :input method(可以理解为系统内置输入法),实现

android/frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java

经过输入法处理(app),输出的应是一个kcm对应的字符。

2、synthetic - 综合处理

2)Android层-ViewrootImpl

当前焦点应用的ViewrootImpl对象会收到该消息,并对消息进行分发处理,最终将其发送到对应的View对象中进行界面响应。

3)注意,对于按键类事件:

1、我们关注ime之后的即可;

2、view处理不了再传递给Activity处理;

3、stage主要关注ViewPreImeInputStage/ViewPostImeInputStage;

2、Framework层的类图

在这里插入图片描述

1)跟踪代码需要理清以上类的关系

3、代码时序图

在这里插入图片描述

重点说明

1、mView/mFocused 以TextView为例

2、按下一个按键的过程(APP)

完成一次按键,按下再松开

按下事件:onkeypreIme -> onkey(setOnKeyListener) -> onkeydown(view) → onkeydown(activity)

松开事件:onkeypreIme -> onkey(setOnKeyListener) -> onkeyup(view) ->onkeyup (activity)

3、mView一般是一个DecorView (extends View) 表示

Activity.java

ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());

4、A1 没处理交给 A2,依次类推A3 A4;

十、项目应用

1、系统定制部分-中控-TVAPI

1)从整体的输入系统可知,可以在PhoneWindowManager 中interceptKeyBeforeQueueing/interceptKeyBeforeDispatching加入截取逻辑;

2)PhoneWindowManager 加入中控钩子截取处理,中控处理不了再调用tvapi接口继续处理

3)中控是为了进一步优化系统性能,避免过多的按键影响性能(从整个输入系统来看,流程是相当之多和复杂的,代价就是耗时);

4)在没有中控之前,直接在PhoneWindowManager远程调用tvapi;

1、
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {...//CVTE Android Patch Beginif(com.cvte.os.CtvManagerInternal.callGeneric(com.cvte.os.ICtvDataDefine.WHAT_COMMON_HOTKEYMANAGER, "cvteInterceptKeyBeforeQueueing", false, event)){return 0;}//CVTE Android Patch End
}android\vendor\cvte\cvte-central-control\src\com\cvte\server\manager\CvteHotkeyManager.java
public boolean cvteInterceptKeyBeforeQueueing(Object... obj) {... //中控能处理的先处理,处理不了再转发给tvapiif (mHotkeyService != null) {if(event.getKeyCode() == CtvPerformanceService.CVTE_KEYEVENT_BASE){LLog.i(TAG,"TvApi cvteInterceptKeyBeforeQueueing CvtKeyEvent.KEYCODE_UNKNOWN no need process");return true;//skip CvtKeyEvent.KEYCODE_UNKNOWN, no need process}try {long begin = SystemClock.uptimeMillis();boolean ret = mHotkeyService.handleKeyEventsBefore(event) || (IS_KERNEL_AT && isKeyPadEvent(event)); //转到tvapi处理LLog.i(TAG,"TvApi cvteInterceptKeyBeforeQueueing " + KeyEvent.keyCodeToString(event.getKeyCode()) + " : "+ KeyEvent.actionToString(event.getAction()) + ", ret:" + ret + ", cost:"+ (SystemClock.uptimeMillis() - begin) + "ms");return ret;} catch (Exception e) {LLog.e(TAG, "Unknown Exception", e);}} else {bindService();return isIntercept(event) || (IS_KERNEL_AT && isKeyPadEvent(event));}
}2、
public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, int policyFlags) {//CVTE Android Patch Beginif (com.cvte.os.CtvManagerInternal.callGeneric(com.cvte.os.ICtvDataDefine.WHAT_COMMON_HOTKEYMANAGER,"cvteInterceptKeyBeforeDispatching", false, event)) {return -1;}
}android\vendor\cvte\cvte-central-control\src\com\cvte\server\manager\CvteHotkeyManager.javapublic boolean cvteInterceptKeyBeforeDispatching(Object... obj) {KeyEvent event = (KeyEvent) obj[0];... //中控能处理的先处理,处理不了再转发给tvapiif (mHotkeyService != null) {try {// LLog.d(TAG, "enter cvteInterceptKeyBeforeDispatching onGlobalKeyEvent");long begin = SystemClock.uptimeMillis();boolean ret = mHotkeyService.handleKeyEvents(event); //转到tvapi处理LLog.i(TAG,"TvApi KeyBeforeDispatching " + KeyEvent.keyCodeToString(event.getKeyCode()) + " : "+ KeyEvent.actionToString(event.getAction()) + ", ret:" + ret + ", cost:"+ (SystemClock.uptimeMillis() - begin) + "ms");return ret;} catch (Exception e) {LLog.e(TAG, "Unknown Exception", e);}} else {bindService();return isIntercept(event);}return false;}

2、CVTE - 无焦点应用优化处理

1、android\frameworks\base\core\java\android\view\ViewRootImpl.java
private int processKeyEvent(QueuedInputEvent q) {final KeyEvent event = (KeyEvent)q.mEvent;//cvte add beginif(FocusAdapterUtil.getInstance().reflectProcessKeyEventMethodCvte(mView, event)){return FINISH_HANDLED;}//cvte add end
}2、
android/frameworks/base/core/java/com/cvte/specialapkviewfocus/FocusAdapterUtil.java
android/frameworks/base/core/java/com/cvte/specialapkviewfocus/CvteSpecialApkViewFocusHandlerImpl.java这个类的主要功能是处理特定海外主流APK在某些界面上没有焦点的问题。代码中提到了多个特定的应用程序,如Amazon PrimeVideo、Netflix、Gaana和Twitter等,并为这些应用提供了特定的焦点处理逻辑。
比如处理Netflix的主页和播放界面焦点问题,Amazon PrimeVideo的播放和主页焦点问题,以及Gaana和Twitter的登录界面焦点问题,它通过模拟用户操作和系统事件来解决焦点问题,提升用户体验。

3、增加按键映射

https://tvgit.gz.cvte.cn/q/message:PT230025-77

4、APP应用编写代码

只需简单两步即可实现input事件处理

1、实现onKey处理逻辑
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {public interface OnKeyListener {boolean onKey(View v, int keyCode, KeyEvent event);}
}
2、setOnKeyListener(new View.OnkeyListener(){....});

十一、调试

1、getevent(linux)linux_key 遥控码
/dev/input/event1: 0004 0004 0088908d
/dev/input/event1: 0001 0071 00000001getevent -l
/dev/input/event1: EV_MSC       MSC_SCAN             00889011
/dev/input/event1: EV_KEY       KEY_MUTE             DOWNIR:底层没有配置,则为KEY_UNKNOW
BT:如果缺少hid,会设为默认值Linux ( KEY_UNKNOWN ),此时需要去配HID2、sendevent (linux)
sendevent <目标设备号> <码值>
sendevent /dev/input/event5 1 42 13、dumpsys (android)
dumpsys input //获取当前设备使用的kl和kcm
注意:IR和BT吃的kl不一样,连接蓝牙设备后 dumpsys input会变!4、input (linux)
input keyevent "num"  //模拟遥控器、键盘、鼠标各种输入设备操作

相关文章:

Android 14输入系统架构分析:图解源码从驱动层到应用层的完整传递链路

一、资料快车 1、深入了解Android输入系统&#xff1a;https://blog.csdn.net/innost/article/details/47660387 2、书籍 - Android系统源代码情景分析 二、Perface 1、参考&#xff1a; 2、系统程序分析方法 1&#xff09;加入log&#xff0c;并跟着log一步步分析 -logc…...

Web入侵实战分析-常见web攻击类应急处置实验2

场景说明 某天运维人员&#xff0c;发现运维的公司站点被黑页&#xff0c;首页标题被篡改&#xff0c;你获得的信息如下&#xff1a; 操作系统&#xff1a;windows server 2008 R2业务&#xff1a;公司官网网站架构&#xff1a;通过phpstudy运行apache mysqlphp开放端口&…...

Jenkins 配置 Credentials 凭证

Jenkins 配置 Credentials 凭证 一、创建凭证 Dashboard -> Manage Jenkins -> Manage Credentials 在 Domain 列随便点击一个 (global) 二、添加 凭证 点击左侧 Add Credentials 四、填写凭证 Kind&#xff1a;凭证类型 Username with password&#xff1a; 配置 用…...

Android Http-server 本地 web 服务

时间&#xff1a;2025年2月16日 地点&#xff1a;深圳.前海湾 需求 我们都知道 webview 可加载 URI&#xff0c;他有自己的协议 scheme&#xff1a; content:// 标识数据由 Content Provider 管理file:// 本地文件 http:// 网络资源 特别的&#xff0c;如果你想直接…...

selenium爬取苏宁易购平台某产品的评论

目录 selenium的介绍 1、 selenium是什么&#xff1f; 2、selenium的工作原理 3、如何使用selenium&#xff1f; webdriver浏览器驱动设置 关键步骤 代码 运行结果 注意事项 selenium的介绍 1、 selenium是什么&#xff1f; 用于Web应用程序测试的工具。可以驱动浏览…...

Linux中POSIX应用场景

Linux 提供了丰富的 POSIX&#xff08;Portable Operating System Interface&#xff09;标准接口&#xff0c;这些接口可以帮助开发者编写可移植、高效的应用程序。POSIX 标准定义了一系列系统调用和库函数&#xff0c;涵盖了文件操作、进程管理、线程管理、信号处理、同步机制…...

Boringssl介绍

BoringSSL 是 Google 从 OpenSSL 分支出来的一个开源 TLS/SSL 库&#xff0c;旨在为 Google 的产品和服务提供一个更加轻量、安全和现代化的加密库。它是 OpenSSL 的一个替代品&#xff0c;专注于简化代码、提高安全性和减少潜在的攻击面。 以下是对 BoringSSL 的详细介绍&…...

react实例与总结(二)

目录 一、脚手架基础语法(16~17) 1.1、hello react 1.2、组件样式隔离(样式模块化) 1.3、react插件 二、React Router v5 2.1、react-router-dom相关API 2.1.1、内置组件 2.1.1.1、BrowserRouter 2.1.1.2、HashRouter 2.1.1.3、Route 2.1.1.4、Redirect 2.1.1.5、L…...

僵尸进程、孤儿进程

一、僵尸进程、孤儿进程 僵尸进程、孤儿进程 1、僵尸进程&#xff1a; &#xff08;1&#xff09;定义&#xff1a;子进程死了&#xff08;内存、打开的文件等都释放了&#xff09;&#xff0c;但没有完全死&#xff08;数据结构<包含进程ID&#xff0c;进程状态、运行时…...

快速入门Springboot+vue——MybatisPlus多表查询及分页查询

学习自哔哩哔哩上的“刘老师教编程”&#xff0c;具体学习的网站为&#xff1a;7.MybatisPlus多表查询及分页查询_哔哩哔哩_bilibili&#xff0c;以下是看课后做的笔记&#xff0c;仅供参考。 多表查询 多表查询[Mybatis中的]&#xff1a;实现复杂关系映射&#xff0c;可以使…...

redis解决高并发看门狗策略

当一个业务执行时间超过自己设定的锁释放时间&#xff0c;那么会导致有其他线程进入&#xff0c;从而抢到同一个票,所有需要使用看门狗策略&#xff0c;其实就是开一个守护线程&#xff0c;让守护线程去监控key&#xff0c;如果到时间了还未结束&#xff0c;就会将这个key重新s…...

深入解析 MySQL 8 C++ 源码:二级索引回表操作

在数据库系统中&#xff0c;索引是优化查询性能的关键技术之一。MySQL 的 InnoDB 存储引擎支持多种索引类型&#xff0c;其中二级索引&#xff08;非聚簇索引&#xff09;和聚簇索引&#xff08;主键索引&#xff09;是最常见的两种。然而&#xff0c;由于二级索引的叶子节点只…...

MySQL如何解决幻读?

目录 一、什么是幻读&#xff1f; 1.1 幻读的定义 1.2 幻读的示例 1.3 幻读产生的原因&#xff1f; 1.4?读已提交&#xff08;Read Committed&#xff09; 1.4.1 确定事务等级 1.4.2 非锁定读取 准备 示例 结论 1.4.3 锁定读取 准备 示例 分析 结论 1.5?可重…...

RabbitMQ的脑裂(网络分区)问题

问题描述&#xff1a; Mnesia reports that this RabbitMQ cluster has experienced a network partition. There is a risk of losing data 一、什么是MQ脑裂&#xff1f; 网络分区 如果另一个节点在一段时间内&#xff08;默认为 60 秒&#xff09;无法与其联系&#xff0…...

【网络安全 | 漏洞挖掘】价值$40000:从路径遍历升级至RCE

未经许可,不得转载。 文章目录 路径遍历RCE路径遍历 在进行目标侦查和端口扫描时,我发现了一个使用 8443 端口的子域名:http://admin.target.com:8443。许多人可能会忽略返回 404 的子域名,但我并没有。 对 http://admin.target.com:8443/FUZZ 进行模糊测试时,我发现了一…...

程函方程的详细推导

以下是基于非均匀介质弹性波方程&#xff08;无纵波假设&#xff09;推导程函方程的详细过程&#xff0c;完整考虑纵波&#xff08;P 波&#xff09;和横波&#xff08;S 波&#xff09;的耦合效应&#xff1a;...

AD(Altium Designer)三种方法导入图片

目录 1、脚本方式导入图片 1.1 准备脚本文件和导入图片 1.2 运行脚本 1.3 导入文件 1.4 选中文件 2.5 运行 2.6 导入图片 2.7 选择图片 2.8 转换 2.9 退出 2.10 联合 2.11 确认 2.12 调整大小 2.13 复制 2.14 粘贴 2、图片直接导入图片 2.1点击放置 2.2 图片…...

fpga助教面试题

第一题 module sfp_pwm( input wire clk, //clk is 200M input wire rst_n, input wire clk_10M_i, input wire PPS_i, output reg pwm ) reg [6:0] cunt ;always (posedge clk ) beginif(!rst_n)cunt<0;else if(cunt19) //200M是10M的20倍cunt<0;elsecunt<cunt1;…...

问题记录汇总

记录一些问题 如何分析错误帧问题-CSDN博客...

安全问答—安全的基本架构

前言 将一些安全相关的问答进行整理汇总和陈述&#xff0c;形成一些以问答呈现的东西&#xff0c;加入一些自己的理解&#xff0c;欢迎路过的各位大佬进行讨论和论述。很多内容都会从甲方的安全认知去进行阐述。 1.安全存在的目的&#xff1f; 为了支持组织的目标、使命和宗…...

玩客云 IP查找

1.玩客云使用静态IP在不同网段路由器下不能使用&#xff0c;动态不好找IP地址 1.1使用python3 实现自动获取发送 import requests import os import socket# 从环境变量获取 PushPlus 的 token 和群组编码 PUSH_PLUS_TOKEN os.getenv("PUSH_PLUS_TOKEN") PUSH_PLU…...

鸿蒙与跨端迁移的重要性

鸿蒙操作系统&#xff08;HarmonyOS&#xff09;是由华为公司开发的一款面向未来的全场景分布式操作系统。它旨在提供一个统一的平台&#xff0c;支持各种设备之间的无缝协作和数据共享&#xff0c;从而为用户提供更加连贯和高效的体验。在鸿蒙的生态系统中&#xff0c;跨端迁移…...

MATLAB学习之旅:从入门到基础实践

在当今科技飞速发展的时代,MATLAB作为一款强大的数学软件,犹如一把神奇的钥匙,能够打开众多领域的大门。无论是工程计算、数据分析,还是算法开发、可视化呈现,MATLAB都展现出了无与伦比的魅力。今天,就让我们踏上这段奇妙的MATLAB学习之旅,从最基础的部分开始,逐步探索…...

【队列】循环队列(Circular Queue)详解

文章目录 一、循环队列简介二、循环队列的判空和判满三、循环队列的实现leetcode 622. 设计循环队列 一、循环队列简介 在实际开发中&#xff0c;队列是一种常用的数据结构&#xff0c;而循环队列&#xff08;Circular Queue&#xff09;则一般是一种基于数组实现的队列&#x…...

Spring-GPT智谱清言AI项目(附源码)

一、项目介绍 本项目是Spring AI第三方调用整合智谱请言&#xff08;官网是&#xff1a;https://open.bigmodel.cn&#xff09;的案例&#xff0c;回答响应流式输出显示&#xff0c;这里使用的是免费模型&#xff0c;需要其他模型可以去 https://www.bigmodel.cn/pricing 切换…...

【JavaEE进阶】MyBatis入门

目录 &#x1f334;前言 &#x1f332;什么是MyBatis? &#x1f333;准备工作 &#x1f6a9;创建工程 &#x1f6a9;配置数据库连接字符串 &#x1f6a9;数据准备 &#x1f6a9;编写持久层代码 &#x1f343;单元测试 &#x1f334;前言 在应⽤分层学习时,我们了解到…...

网络安全:防范NetBIOS漏洞的攻击

稍微懂点电脑知识的朋友都知道&#xff0c;NetBIOS 是计算机局域网领域流行的一种传输方式&#xff0c;但你是否还知道&#xff0c;对于连接互联网的机器来讲&#xff0c;NetBIOS是一大隐患。 漏洞描述 NetBIOS(Network Basic Input Output System&#xff0c;网络基本输入输…...

【OS安装与使用】part3-ubuntu安装Nvidia显卡驱动+CUDA 12.4

文章目录 一、待解决问题1.1 问题描述1.2 解决方法 二、方法详述2.1 必要说明2.2 应用步骤2.2.1 更改镜像源2.2.2 安装NVIDIA显卡驱动&#xff1a;nvidia-550&#xff08;1&#xff09;查询显卡ID&#xff08;2&#xff09;PCI ID Repository查询显卡型号&#xff08;3&#xf…...

如何在本地和服务器新建Redis用户和密码

文章目录 一. Redis安装二. 新建Redis用户&#xff0c;测试连接2.1 本地数据库2.2 线上数据库2.2.1 安装和配置2.2.2 测试连接 三. 配置四. 分布式 一. Redis安装 Redis安装 可以设置开机自动启动&#xff0c;也可以在去查看系统服务&#xff0c;按[win R]&#xff0c;输入命…...

使用SHOW PROCESSLIST和SHOW ENGINE INNODB STATUS排查mysql锁等待问题

现象&#xff1a; mysql 查某表一直不能结束&#xff0c;查别的表没有问题。已知之前刚刚alter此表想把它的一个字段长度增长&#xff0c;但是这个操作一直没有结束。现在应该怎么办? 方案: 使用 SHOW PROCESSLIST; 查看当前所有活动的SQL线程&#xff0c;找出是否有长时间…...

探索HarmonyOS的UI开发新境界:从基础到进阶的深度解析

在科技日新月异的今天&#xff0c;操作系统作为连接硬件与软件的桥梁&#xff0c;其重要性不言而喻。HarmonyOS&#xff0c;作为华为自主研发的分布式全场景操作系统&#xff0c;正以其独特的分布式技术架构和一次开发多端部署的能力&#xff0c;引领着操作系统的新潮流。本文将…...

Android 动态加入Activity 时 manifest 注册报错解决。使用manifestPlaceholders 占位

需求如下&#xff1a; 项目 测试demo 有多个渠道&#xff0c;部分渠道包含支付功能&#xff0c;在主测试代码外&#xff0c;需要一个单独 Activity 调用测试代码。 MainActivityPayActivity渠道A包含不包含渠道B包含包含 因为支付功能需要引入对应的 moudule&#xff0c;因此…...

OpenCV(1):简介、安装、入门案例、基础模块

1 OpenCV 简介 OpenCV 是一个功能强大、应用广泛的计算机视觉库&#xff0c;它为开发人员提供了丰富的工具和算法&#xff0c;可以帮助他们快速构建各种视觉应用。随着计算机视觉技术的不断发展&#xff0c;OpenCV 也将会继续发挥重要的作用。OpenCV 提供了大量的计算机视觉算法…...

Linux-GlusterFS操作子卷

文章目录 分布式卷添加卷分布式卷删除子卷删除总卷 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Linux专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年02月20日19点30分 分布式卷添加卷 Node1上进行操作 扩容 #服务器端 gluster volu…...

kettle从入门到精通 第九十二课 ETL之kettle 使用Kettle的Carte对外发布读写接口

场景&#xff1a;使用kettle实现将查询结果返回给客户端&#xff0c;也就是说kettle暴露查询接口供外围系统调用。前提必须是使用carte服务才可以提供接口供外部系统调用。具体实操方法如下&#xff1a; 1、设计转换 根据具体需求设计转换&#xff0c;主要用到的步骤有获取变…...

【精调】LLaMA-Factory 快速开始1: Meta-Llama-3.1-8B-Instruct

llamafactory-cli train examples/train_lora/llama3_lora_sft.yaml llamafactory-cli chat examples/inference/llama3_lora_sft.yaml llamafactory-cli export examples/merge_lora/llama3_lora_sft.yaml模型下载 git clone https://www.modelscope.cn/LLM-Research/Meta-Lla…...

数据库加密全解析:从传输到存储的安全实践

title: 数据库加密全解析:从传输到存储的安全实践 date: 2025/2/17 updated: 2025/2/17 author: cmdragon excerpt: 数据加密是数据库安全的最后一道物理防线。传输层SSL/TLS配置、存储加密技术及加密函数实战应用,覆盖MySQL、PostgreSQL、Oracle等主流数据库的20+生产级加密…...

PHP+Apache+MySQL安装(Windows)

一、安装教程 参考链接1 参考链接2 二、问题描述 PHP安装目录下找不到php8apache2_4.dll PHP安装包下载错误 Apache Service Monitor: request operation has failed! 定位问题&#xff1a; 查看【事件查看器】 解决问题 安装或更新与PHP版本相对应的Visual C Redistribu…...

alt+tab切换导致linux桌面卡死的急救方案

环境 debian12 gnome43.9 解决办法 观察状态栏&#xff0c;其实系统是没有完全死机的&#xff0c;而且gnome也可能没有完全死机。 1. alt f4 关闭桌面上的程序&#xff0c;因为这个方案是我刚刚看到的&#xff0c;所以不确定能不能用&#xff0c;比起重启系统&#xff0c;…...

Mysql基础语句

一、 MySQL语句 在熟悉安装及访问 MySQL 数据库以后&#xff0c; 接下来将学习使用 MySQL 数据库的基本操作&#xff0c;这也是在服务器运维工作中不可或缺的知识。 本节中的所有数据库语句均在“MySQL>”操作环境中执行 MySQL 是一套数据库管理系统&#xff0c;在每台 MySQ…...

网络通信基础:端口、协议和七层模型详解,网络安全零基础入门到精通实战教程!

一、端口和协议的概念 1.在网络技术中&#xff0c;端口(Port) 大致有两种意思&#xff1a; 一是物理意义上的端口&#xff0c;比如&#xff0c;ADSL Modem、集线器、交换机、路由器用于连接其他网络设备的接口&#xff0c;如RJ-45端口、SC端口等等。 二是逻辑意义上的端口&…...

【力扣Hot 100】栈2

5. 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: !https://assets.leetcode.com/uploads/2021/01/04/histogram.jpg …...

1. Linux下 MySQL 的详细安装与使用

1. Linux下 MySQL 的详细安装与使用 文章目录 1. Linux下 MySQL 的详细安装与使用1. Linux 下安装 MySQL8.0 的详细安装步骤&#xff1a;2. Linxu 当中的MySQL 设置远程登录3. 最后&#xff1a; 1. Linux 下安装 MySQL8.0 的详细安装步骤&#xff1a; 查看是否安装过MySQL&…...

Idea24.3 如何设置Git忽略某一个文件

文章目录 左上角找到commit选中你要忽略的文件 右键New Changelist给这个文件夹名称和描述 点击ok将要忽略的文件添加到这个文件夹 左上角找到commit 选中你要忽略的文件 右键New Changelist 给这个文件夹名称和描述 点击ok 将要忽略的文件添加到这个文件夹...

2025-02-20 学习记录--C/C++-PTA 7-27 冒泡法排序

一、题目描述 ⭐️ 二、代码&#xff08;C语言&#xff09;⭐️ /** * 冒泡法实现升序 */#include <stdio.h>int main() {int N, // 整数个数 6K, // 扫描遍数 2num, // 待排序的整数 2 3 5 1 6 4numArr[100], // 待排序的整数合集 2 3 5 1…...

如何修改Windows系统Ollama模型存储位置

默认情况下&#xff0c;Ollama 模型会存储在 C 盘用户目录下的 .ollama/models 文件夹中&#xff0c;这会占用大量 C 盘空间&#xff0c;增加C盘“爆红”的几率。所以&#xff0c;我们就需要修改Ollama的模型存储位置 Ollama提供了一个环境变量参数可以修改Ollama的默认存在位…...

【Python爬虫(26)】Python爬虫进阶:数据清洗与预处理的魔法秘籍

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…...

NPM如何更换淘宝镜像——Node.js国内镜像配置教程

在国内使用 npm 安装 Node.js 包时&#xff0c;由于网络环境的原因&#xff0c;下载速度可能非常慢。为了解决这个问题&#xff0c;很多开发者会选择使用淘宝镜像&#xff08;现在由 npmmirror.com 维护&#xff09;。本文将带你一步一步完成更换 npm 源为淘宝镜像的配置&#…...

汽车免拆诊断案例 | 2010 款路虎揽胜车空调偶尔出风异常

故障现象  一辆2010款路虎揽胜车&#xff0c;搭载5.0 L发动机&#xff0c;累计行驶里程约为16万km。车主反映&#xff0c;接通空调开关后&#xff0c;有时出风忽大忽小&#xff0c;有时不出风&#xff0c;有时要等2 min左右才出风&#xff1b;有时两三天出现一次&#xff0c;…...

pytorch3d安装记录

官方安装教程&#xff1a; https://github.com/facebookresearch/pytorch3d/blob/main/INSTALL.md 通过pip 或conda 可以很容易安装上预编译好的包&#xff0c; 安装过程不会报错&#xff0c; 但是使用的时候就会报各种错误 &#xff0c;原因是预编译好的包跟自己的环境不一定…...