Android 12系统源码_输入系统(二)InputManagerService服务

news2024/11/23 20:00:01

前言

上一篇我们简单介绍了Android系统输入事件的常见类型和事件的传递流程,输入事件的传递流程主要分三个部分,输入系统处理部分、WMS处理部分、View处理部分。
在这里插入图片描述
其中输入系统处理部分细分为输入子系统处理部分和InputManagerService处理部分,输入子系统主要是对事件源的原始信息进行采集,而InputManagerService则是监听dev/input下的所有设备节点,当设备节点有数据时会将数据进行加工处理并分发给合适的窗口,在讲解InputManagerService具体是如何处理输入事件的之前,我们需要先来认识一下InputManagerService这个服务。

一、IMS的启动

系统启动后会启动JVM虚拟机,SystemServer 是虚拟机的第一个进程,由init 进程fork 产生,主要用来启动frameworks层中的服务。和其他服务一样,InputManagerService服务也是在这里被启动的,下面我们简单列出SystemServer中和IMS启动相关的源码。

1.1 SystemServer阶段

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

public final class SystemServer {

    private DisplayManagerService mDisplayManagerService;

    public static void main(String[] args) {
        new SystemServer().run();
    }
    
   private void run() {
    	...代码省略...
        try {
            t.traceBegin("StartServices");
            startBootstrapServices(t);//引导服务
            startCoreServices(t);//核心服务
            startOtherServices(t);//其他服务
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            t.traceEnd(); // StartServices
        }
    	...代码省略...
    }
       
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
         ...代码省略...
         WindowManagerService wm = null;
         InputManagerService inputManager = null;
         ...代码省略...
         t.traceBegin("StartInputManagerService");
         inputManager = new InputManagerService(context);//注释1,创建InputManagerService对象
         t.traceEnd();
         ...代码省略...
         t.traceBegin("StartWindowManagerService");
          // WMS needs sensor service ready
          mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
          //注释2,WindowManagerService作为输入系统的中转站,其内部持有InputManagerService的引用
          wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                  new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
          //将WindowManagerService添加到ServiceManager
          ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
          //将InputManagerService添加到ServiceManager
          ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
          t.traceEnd();
          ...代码省略...
          t.traceBegin("StartInputManager");
          inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
          inputManager.start();//注释3,启动IMS服务
          t.traceEnd();
          ...代码省略...          
     }
}

SystemServer的startOtherServices方法先是在注释1处创建InputManagerService对象,接着在注释2处把IMS作为参数传给WMS以便其创建的实例对象包含有IMS的引用,然后将WMS和IMS都添加到了ServiceManager中进行统一管理,然后还会调用IMS的setWindowManagerCallbacks方法将输入事件回调和WMS进行关联,最后在注释3处调用IMS的start方法启动IMS。

1.2 InputManagerService的创建

1.2.1 InputManagerService构造方法

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public class InputManagerService extends IInputManager.Stub
        implements Watchdog.Monitor {
    
    private final long mPtr;
        
    private static native long nativeInit(InputManagerService service,
            Context context, MessageQueue messageQueue);
            
    public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());//注释1,创建InputManagerHandler对象

        mStaticAssociations = loadStaticInputPortAssociations();
        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                + mUseDevInputEventForAudioJack);
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//注释2,调用nativeInit方法

        String doubleTouchGestureEnablePath = context.getResources().getString(
                R.string.config_doubleTouchGestureEnableFile);
        mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
            new File(doubleTouchGestureEnablePath);

        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }
    
 }

注释1处使用android.display线程的Looper创建了InputManagerHandler,这样InputManagerHandler便会运行在android.display线程中,android.display线程是系统共享的单例前台线程,这个线程内部执行了WMS的创建。
注释2处调用了nativeInit方法,此方法是一个Native方法,该方法会返回一个NativeInputManager对象指针地址,将该地址赋值给属性变量mPtr,这个属性变量后面后面我们还会提到。

1.2.2 nativeInit方法

>frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == nullptr) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
	//注释1,创建NativeInputManager对象
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);//注释2,返回对象指针
}

// --- NativeInputManager ---
class NativeInputManager : public virtual RefBase,
    public virtual InputReaderPolicyInterface,
    public virtual InputDispatcherPolicyInterface,
    public virtual PointerControllerPolicyInterface {
protected:
    virtual ~NativeInputManager();
public:
    NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);
    inline sp<InputManagerInterface> getInputManager() const { return mInputManager; //返回对象实例 }
private:
	sp<InputManagerInterface> mInputManager;//InputManager对象

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
    mServiceObj = env->NewGlobalRef(serviceObj);
    {
        AutoMutex _l(mLock);
        mLocked.systemUiLightsOut = false;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
        mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
    }
    mInteractive = true;
	//注释3,创建InputManager对象
    InputManager* im = new InputManager(this, this);
    mInputManager = im;
    defaultServiceManager()->addService(String16("inputflinger"), im);
}

在注释1处会创建NativeInputManager对象,然后在NativeInputManager的构造方法中,也就是注释3处,会创建InputManager对象,进一步通过inputManager来读取设备节点的增删事件和原始输入事件。最后nativeInit方法会调用reinterpret_cast方法,在C++中,reinterpret_cast 是一种类型转换操作符,用于在不同类型之间进行低级别的转换,它提供了一种不安全但非常强大的转换方式,通常用于将一个指针或引用类型转换为另一种类型,**这种转换只是将指针的表示重新解释为另一种类型,不改变指针所指向的内存内容。**这里将该对象指针返回给了java层, 这是为了打通java和native层,下次需要使用native层的NativeInputManager对象的时候,直接传递这个对象指针就可以访问了。

1.2.3 InputManager的构造方法

>frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    //注释1,创建inputDispatcher对象
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    //注释2,创建InputReader对象,其内部持有mDispatcher的引用
    mReader = createInputReader(readerPolicy, mClassifier);
}

>frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
sp<InputDispatcherInterface> createInputDispatcher(
        const sp<InputDispatcherPolicyInterface>& policy) {
    //创建InputDispatcher对象
    return new android::inputdispatcher::InputDispatcher(policy);
}

>frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
                                           const sp<InputListenerInterface>& listener) {
    //创建InputReader对象
    return new InputReader(std::make_unique<EventHub>(), policy, listener);
}

在注释1处创建InputDispatcher类对象mDispatcher,然后在注释2处创建InputReader类对象mReader,其内部持有mDispatcher的引用,InputReader会不断循环读取EventHub中的原始输入事件,在对这些原始输入事件进行加工后交给InputDispatcher,由于WMS会将所有的窗口信息实时更新到InputDispatcher中,这样InputDispatcher就可以将输入事件派发给合适的窗口。

1.3 InputManagerService的启动

SystemServer的startOtherServices方法中关于IMS的最后一步是调用IMS的start方法。

1.3.1 InputManagerService的start方法

public class InputManagerService extends IInputManager.Stub
        implements Watchdog.Monitor {
        
    private final long mPtr; 
    private static native void nativeStart(long ptr);
    
    public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);//注释1,调用nativeStart方法
        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);//注释2,将自身添加到看门狗中进行监控
    	...代码省略...
    }
 }

在注释1处调用nativeStart方法,此方法同样是一个Native方法,这里调用该方法的时候,还将前面在InputManagerService构造方法中创建的NativeInputManager对象指针传递了进来。在注释2处将自身添加到watchdog中进行监控,用户定时检测系统关键服务是否可能发生死锁。

1.3.2 nativeStart方法

>frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);//注释1,获取NativeInputManager对象指针
    status_t result = im->getInputManager()->start();//注释2,调用InputManager的start方法
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}
// --- NativeInputManager ---
class NativeInputManager : public virtual RefBase,
    public virtual InputReaderPolicyInterface,
    public virtual InputDispatcherPolicyInterface,
    public virtual PointerControllerPolicyInterface {
public:
    NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);
    inline sp<InputManagerInterface> getInputManager() const { return mInputManager; //注释3,返回对象实例 }
private:
	sp<InputManagerInterface> mInputManager;

在注释1处,将存储在Java层的NativeInputManager对象指针重新转化成Native层需要的对象指针,然后调用NativeInputManager的getInputManager方法获取其内部持有的InputManager对象,并调用InputManager对象的start方法。

1.3.3 InputManager的start方法

>frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {
    status_t result = mDispatcher->start();//注释1,调用inputdispatcher的start方法
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
    result = mReader->start();//注释2,调用InputReader的start方法
    if (result) {
        ALOGE("Could not start InputReader due to error %d.", result);
        mDispatcher->stop();
        return result;
    }
    return OK;
}

在注释1处调用InputDispatcher的start方法,该方法内部会开启一个名为InputDispatcher的事件分发线程,在注释2处调用InputDispatcher的start方法,该方法内部会开启一个名为InputReader的事件读取线程。

1.3.3.1 Thread

在分析InputDispatcher和InputReader两个线程之前,我们需要先来看下Thread的run方法。

>system/core/libutils/Threads.cpp
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
	...代码省略...
	//调用createThreadEtc方法
    res = createThreadEtc(_threadLoop, this, name, priority, stack, &mThread);
	...代码省略...
}

int Thread::_threadLoop(void* user)
    Thread* const self = static_cast<Thread*>(user);
	...代码省略...
    do {
        bool result;
        result = self->threadLoop();
		...代码省略...
        if (result == false || self->mExitPending) {
			...代码省略...
            break;
        }
		...代码省略...
    } while(strong != 0);
}

threadLoop是一个方法指针,第二个参数是自己,最终会调用到_threadLoop方法并传入this指针,_threadLoop方法内部会调用self->threadLoop(),这个self就是当前Thread的this指针,除了result返回false或者调用了requestExit才会退出。 不然会一直循环调用self->threadLoop()函数,threadLoop在Thread中是一个纯虚函数,在其子类中实现,所以后面只要分析子线程的threadLoop方法即可。

1.3.3.2 InputDispatcher的start方法
>frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
status_t InputDispatcher::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    //注释1,创建InputThread对象
    mThread = std::make_unique<InputThread>(
            "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
    return OK;
}

>frameworks/base/libs/androidfw/include/androidfw/Util.h
template <typename T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
  return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}

>frameworks/native/services/inputflinger/InputThread.cpp
class InputThreadImpl : public Thread {
public:
    explicit InputThreadImpl(std::function<void()> loop)
          : Thread(/* canCallJava */ true), mThreadLoop(loop) {}

    ~InputThreadImpl() {}
    
private:
    std::function<void()> mThreadLoop;    
};
/**
 *
 * @param name  线程的名称
 * @param loop  线程要执行的主循环函数或任务
 * @param wake 一个回调函数,用于唤醒或通知线程的动作
 */
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
      : mName(name), mThreadWake(wake) {
    mThread = new InputThreadImpl(loop);//注释2,创建InputThreadImpl对象,该对象用于处理线程的实际操作。
    mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}

在注释1处创建InputThread对象,分别传入了线程名称InputDispatcher、线程主循环函数dispatchOnce,回调函数mLooper->wake();然后在注释2处,也就是InputThread的构造方法中,创建InputThreadImpl对象,该对象继承自Thread ,是真正处理线程实际操作的对象。

1.3.3.3 InputDispatcher的dispatchOnce方法

在InputDispatcher线程启动之后,该线程循环执行的是dispatchOnce方法。

>frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();

        //注释1,检测缓存队列中是否有等待处理的命令
        if (!haveCommandsLocked()) {
            //将输入事件派发给合适的窗口
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        if (runCommandsLockedInterruptible()) {
        	//记录线程下次被唤醒的时间
            nextWakeupTime = LONG_LONG_MIN;
        }
        //注释2,检查ANR
        const nsecs_t nextAnrCheck = processAnrsLocked();
        nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
        //注释3,处理空闲状态
        if (nextWakeupTime == LONG_LONG_MAX) {
            mDispatcherEnteredIdle.notify_all();
        }
    } // release lock

    //注释4,计算InputDisPatcherThread需要休眠的最长时间
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

在注释1处判断,如果没有待处理的命令,调用 dispatchOnceInnerLocked 方法执行一次调度循环;nextWakeupTime用于记录下次唤醒的时间,如果有待处理的命令,则将 nextWakeupTime 设置为 LONG_LONG_MIN,这表示需要立即处理下一个任务,因为命令的执行可能影响了调度的时机。在注释2处检查是否需要处理 ANR 事件,nextAnrCheck 记录了下一次检查 ANR 的时间,将 nextWakeupTime 更新为当前唤醒时间和 ANR 检查时间中的最小值,以确保在 ANR 检查之前唤醒。在注释3处,如果判断nextWakeupTime为LONG_LONG_MAX,表示没有命令、事件或待处理的任务,调度器将进入一个几乎无限长的睡眠状态,此时会通知其他线程或组件事件派发器已进入空闲状态。
在注释4处获取当前时间 currentTime,并使用toMillisecondTimeoutDelay方法计算直到 nextWakeupTime 的超时时间, 调用 mLooper->pollOnce(timeoutMillis) 方法,进入等待状态,直到有事件到达、超时或者有其他需要唤醒的情况发生。

1.3.3.4 InputReader的start和loopOnce方法

和InputDispatcher的start方法类似,InputReader的start方法也是进一步创建InputThread对象,分别传入了线程名称InputReader、线程主循环函数loopOnce,回调函数mEventHub->wake();该线程启动之后会循环执行loopOnce方法。

>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;
}

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    { // acquire lock
        std::scoped_lock _l(mLock);

        oldGeneration = mGeneration;
        timeoutMillis = -1;

        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock
    //注释1,获取设备节点的原始输入事件信息
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        std::scoped_lock _l(mLock);
        //通知所有等待的线程,输入读取器仍然活跃。
        mReaderIsAliveCondition.notify_all();

        if (count) {
            //注释2,对mEventBuffer中的原始输入事件信息进行加工处理,加工后的输入事件会交给InputDispatcher来处理
            processEventsLocked(mEventBuffer, count);
        }

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
                //注释3,处理超时事件
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            inputDevices = getInputDevicesLocked();
        }
    } // release lock

    //注释4,如果输入设备状态发生了变化,调用mPolicy->notifyInputDevicesChanged方法进行事件通知
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    mQueuedListener->flush();//刷新或处理排队的监听器事件。
}

在注释1处从事件缓冲区中获取设备节点的原始输入事件信息并将其存放到mEventBuffer中,timeoutMillis 是等待事件的超时时间,mEventBuffer 是存放事件的缓冲区,EVENT_BUFFER_SIZE 是缓冲区的大小,事件信息主要有两种,设备节点的增删事件、原始输入事件;在注释2处 判断是否有事件,如果有则调用processEventsLocked方法,该方法会对mEventBuffer中的原始输入事件信息进行加工处理,并将加工后的输入事件会交给InputDispatcher处理;在注释3处判断输入事件是否已经超时,如果超时则调用timeoutExpiredLocked方法处理超时事件;在注释4处判断输入设备状态是否发生了变化,如果是则通知相关的策略或组件。

二、IMS架构图

这里我们结合InputManagerService的构造方法和start方法画了个简单的IMS架构图。
IMS架构图

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

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

相关文章

嵌入式学习----IO多路复用

1.概念&#xff1a;多个IO复用一个进程 2.IO多路复用的一般实现&#xff1a; &#xff08;1&#xff09;创建文件描述符集合 &#xff08;2&#xff09;添加文件描述符到集合中 &#xff08;3&#xff09;通知内核开始监测 &#xff08;4&#xff09;根据返回的结果做对应…

TESSY创建单元测试或集成测试工程

我们以tessy5.1 IDE为例&#xff0c;给大家展示工程的创建过程。 1、打开TESSY5.1软件后&#xff0c;会弹出&#xff1a; 2、点击NEW Project后&#xff0c;会弹出&#xff1a; 3、接下来&#xff0c;就可以打开刚创建的工程&#xff1a; 4、进入到TESSY的主界面后&#xff0c…

清凉行动启航!汕头电信爱心翼站夏送清凉,每一份汗水都值得被温柔以待

8月中旬汕头电信濠江营业厅开展了“与你一起&#xff0c;清凉一夏”爱心翼站•夏送清凉活动。 在温馨的活动氛围中&#xff0c;爱心翼站的爱心大使与志愿者们&#xff0c;以生动有趣的小课堂&#xff0c;为小朋友们揭开了中暑这一话题的神秘面纱&#xff0c;细致阐述了其定义及…

在团队里对着干的人

在团队里对着干的人&#xff0c;踢走&#xff1b; 在团队里跟着干的人&#xff0c;培养&#xff1b; 在团队里帮着干的人&#xff0c;分钱&#xff1b; 在团队里领着干的人&#xff0c;分红。 这是规律也是规矩&#xff0c;一个真正的强者&#xff0c;是看他帮助了多少人&#…

CLIP微调方法总结

文章目录 前言1️⃣ Tip-Adapter论文和源码原理介绍 2️⃣Cross-modal Adaptation&#xff08;跨模态适应&#xff09;论文和源码原理介绍 3️⃣ FD-Align&#xff08;Feature Discrimination Alignment&#xff0c;特征判别对齐&#xff09;论文和源码原理介绍 总结 前言 本文…

教您用军团要塞2服务器开服联机教程

1、购买后登录服务器 进入控制面板后会出现正在安装的界面 2、下载连接工具 打开Steam库中搜索Source SDK Base 2013 Multiplayer并安装 3、下载游戏 以下三个链接均为同一个游戏 百度 通过百度网盘分享的文件&#xff1a;tf2classic.zip 链接&#xff1a;百度网盘 请输入提…

(最新)华为 2024 届秋招-硬件技术工程师-单板硬件开发—机试题—(共12套)(每套四十题)

&#xff08;最新&#xff09;华为 2024 届秋招-硬件技术工程师-单板硬件开发—机试题—&#xff08;共12套&#xff09;&#xff08;每套四十题&#xff09; 岗位——硬件技术工程师 岗位意向——单板硬件开发 真题题目分享&#xff0c;完整版带答案(有答案和解析&#xff0…

浅析WebRTC技术在智慧园区视频管理场景中的应用

随着科技的飞速发展&#xff0c;智慧园区作为城市智慧化的重要组成部分&#xff0c;正逐步成为现代化管理的重要方向。智慧园区的建设不仅涉及硬件设施的智能化升级&#xff0c;还离不开高效的视频管理和实时通信技术。在这一背景下&#xff0c;WebRTC&#xff08;Web Real-Tim…

BackdoorLLM:一个针对生成性LLMs后门攻击的全面基准测试

大型语言模型&#xff08;LLMs&#xff09;在从自然语言理解到机器翻译等一系列任务上取得了显著的突破性进展。例如&#xff0c;GPT-4模型展示了在生成类人文本和解决复杂问题方面的前所未有的能力。然而&#xff0c;近期的研究表明&#xff0c;LLMs存在一个关键的脆弱性&…

sqli-labs靶场通关攻略(41-45关)

第41关 这关我们使用工具sqlmap练习一下&#xff08;这里如果用本机的回环地址访问靶场的话只能在你的本机访问&#xff0c;因为我们是在虚拟机上进行扫描&#xff0c;所以不能使用127.0.0.1访问&#xff09; 进入虚拟机kali&#xff0c;打开终端 查库 sqlmap -u 网址 -- curr…

uniapp生活记账小程序

Springboot vue uniapp生活记账小程序&#xff0c;前端采用vue uni-app设计开发&#xff0c;后端采用 Springboot 开发前端对应的数据接口&#xff0c;首页显示生活账单信息&#xff0c;我的野蛮统计记账信息和微信登录状况。记账页面可以&#xff0c;根据不同类别的日常消费记…

MySQL集群技术4——MySQL路由

mysql-route MySQL 路由&#xff08;Routing&#xff09;通常指的是在 MySQL 架构中如何处理客户端请求和数据流向的问题。在 MySQL 中&#xff0c;路由可以涉及多种不同的场景和技术&#xff0c;包括但不限于反向代理、负载均衡、读写分离等。下面我将详细介绍这些场景和技术…

如何反射获取类的全部信息?(java代码)

什么是反射&#xff1f; 反射是 Java 提供的一种机制&#xff0c;允许在运行时动态地获取类的信息&#xff08;如类的名称、方法、字段等&#xff09;&#xff0c;以及创建对象和调用方法。反射利用了 java.lang.reflect 包中的类&#xff0c;如 Class、Method、Field 和 Cons…

仿BOSS招聘系统开发:构建高效、智能的在线招聘平台

在数字化时代&#xff0c;招聘行业正经历着前所未有的变革。BOSS直聘作为国内领先的招聘平台&#xff0c;以其高效的匹配机制、丰富的职位信息和便捷的用户体验&#xff0c;赢得了广泛的市场认可。本文将探讨如何开发一个仿照BOSS招聘系统的在线招聘平台&#xff0c;旨在为企业…

8月28复盘日记

8月28复盘日记 前言今日感恩今日知识今日反思今日名言 前言 今天早上是六点半起床嘻嘻&#xff0c;这两天因为生理期,皮质醇似乎有些高,入睡会有些困难。但是因为今天是开学第一天&#xff0c;意味着&#xff0c;健身房恢复晨练了&#xff01;我可太喜欢晨练时间安安静静的健身…

怎么又快又好制作流程图?试试这2款流程图制作神器,专业!

推荐2款简单好用的流程图制作软件&#xff0c;帮你轻松搞定各种流程图。 1、GitMind 点点击链接直达官网>>gitmind.cn GitMind是一个在线流程图制作工具&#xff0c;模板类型全&#xff0c;支持免费下载&#xff0c;由国内团队研发&#xff0c;操作简单&#xff0c;使用…

功率器件和滤波器件的选型及测试方法

目录 一、功率器件的选型及测试方法 1.1功率器件的选型 1.2功率器件的测试方法 二、滤波器件的选型及测试方法 2.1滤波器件的选型 2.2滤波器件的测试方法 三、表格总结 一、功率器件的选型及测试方法 1.1功率器件的选型 在电子电路设计中&#xff0c;功率器件的选择是…

c++异常处理(c++11版)与智能指针 SmartPtr 的应用(主讲shared_ptr浅实现)

引子&#xff1a;找C语言的异常时&#xff0c;你是否会被奇怪的错误码或程序终止报错而感到无奈&#xff0c;对找不到错误在哪的心烦&#xff1f;在害怕内存泄漏时&#xff0c;你是否每一步&#xff0c;每一句代码都要仔细分析&#xff0c;在用完之后进行资源空间的释放&#x…

用powermock编写单元测试

1、pom文件引入jar包 <!-- 单元测试 start --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope> </dependency> <dependency>&…

Jenkins服务安装配置

什么是 Jenkins Jenkins 是一个开源的自动化服务器&#xff0c;主要用于持续集成&#xff08;CI&#xff0c;Continuous Integration&#xff09;和持续交付/持续部署&#xff08;CD/CD&#xff0c;Continuous Delivery/Continuous Deployment&#xff09;。它帮助开发团队自动…