WMS之添加View

news2025/1/13 13:43:45

目录

  • 前言
  • 一、addview示例
  • 二、addview流程
    • 2.1 流程图
    • 2.2 流程分析
      • 2.2.1 Actitity的启动流程创建PhoneWindow和DecorView
      • 2.2.2.WindowManagerImpl 添加view
      • 2.2.3 ViewRootImpl.setView
  • 三、总结


前言

WMS 功能繁杂,通过添加View流程进一步分析WMS

通过本文了解掌握以下知识:

Window是什么?

  • 表示一个窗口的概念,是所有View的直接管理者,任何视图都通过Window呈现(点击事件由Window->DecorView->View;
    Activity的setContentView底层通过Window完成)
  • Window是一个抽象类,具体实现是PhoneWindow。这个可以看Activity#attach方法源码
  • 创建Window需要通过WindowManager创建,WindowManager是外界访问Window的入口,Window具体实现位于WindowManagerService中
    WindowManager和WindowManagerService的交互是通过IPC完成

Window和View关系?

  • Window和View通过ViewRootImpl建立联系,View是视图的呈现方式,但是不能单独存在,必须依附在Window这个抽象的概念上。
  • WMS把所有的用户消息发给View/ViewGroup,但是在View/ViewGroup处理消息的过程中,有一些操作是公共的, Window把这些公共行为抽象出来, 这就是Window。

Activity、View、Window三者之间的关系?

  • 在Activity启动过程其中的attach()方法中初始化了PhoneWindow,而PhoneWindow是Window的唯一实现类。
  • 然后Activity通过setContentView将View设置到了PhoneWindow上,而View通过WindowManager的addView()、removeView()、updateViewLayout()对View进行管理。

一、addview示例

先看一个简单的案例。在主屏幕上添加一个TextView并展示,并且这个TextView独占一个窗口。

1.activity布局:
布局里什么都不绘制
在这里插入图片描述
2.addview

onCreate方法里获取Windowmanager
动态添加TextView ,设置其背景颜色、字体颜色
设置Windowmanager的宽高、TYPE

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_iccpanel);
        TextView mview = new TextView(this);
        mview.setText("add view");
        mview.setBackgroundColor(Color.RED);
        mview.setTextColor(Color.BLUE);
        WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
        wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
        wmParams.width = 200;
        wmParams.height = 100;
        mWindowManager.addView(mview, wmParams);
    }

看一下效果:
在这里插入图片描述
WindowManager 通过addview的方式,将TextView显示到了屏幕上。


二、addview流程

WindowManager只是一个接口,真正实现类是WindowManagerImpl,并最终以代理模式交给WindowManagerGlobal实现。
可以通过源码自行查看,也可以阅读我的第一篇WMS文章WMS之窗口属性的WindowManager简介部分。

在addview之前,activity已经拥有了window实例和父布局View:此块内容查看SetContentView()流程分析
接下来看addview的流程:

2.1 流程图

在这里插入图片描述

2.2 流程分析

2.2.1 Actitity的启动流程创建PhoneWindow和DecorView

WindowManager.addView添加窗口之前,TextView的onDraw不会被调用,也就说View必须被添加到窗口中,才会被绘制。只有申请了依附窗口,View才会有可以绘制的目标内存,而窗口的创建就是在Actitity的启动流程

在 Activity 的启动流程中,当调用 attach() 方法时,会创建 PhoneWindow 对象,并同时创建一个 WindowManagerImpl 实例来维护 PhoneWindow 内的内容。PhoneWindow 表示应用程序窗口的实体内容,而 WindowManagerImpl 则负责实现窗口的添加、删除、更新等操作。
在 Activity 的 onCreate() 方法中,通常会调用 setContentView() 方法来设置布局资源,这个方法内部会创建一个 DecorView 实例作为 PhoneWindow 的实体内容,同时将该视图添加到 WindowManagerImpl 管理的窗口列表中。DecorView 是一个特殊的 View,它包含所有 UI 控件,并为其提供一个基础框架,比如标题栏、状态栏、背景等。

2.2.2.WindowManagerImpl 添加view

WindowManagerImpl 作为WindowManager的实例,内部最终是以代理模式交给WindowManagerGlobal实现。
WindowManagerImpl.java

    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyTokens(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

WindowManagerGlobal.java

  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
			/**
			*检查view是否为null,如果是则抛出IllegalArgumentException异常。
			 检查display是否为null,如果是则抛出IllegalArgumentException异常。
			 检查params是否为WindowManager.LayoutParams类型,如果不是则抛出IllegalArgumentException异常。
			*/
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        //将params强制转换为WindowManager.LayoutParams类型,并赋值给wparams变量。
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
 // 如果parentWindow不为null,则调用parentWindow的adjustLayoutParamsForSubWindow()方法来调整wparams的参数。
        } else {
        	//如果没有parentWindow,则根据应用程序的硬件加速设置来设置View的硬件加速标志。
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }
		
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
        /**
        *在同步块内部进行以下操作:
			创建一个Runnable对象mSystemPropertyUpdater,用于监视系统属性的变化。
			查找是否已经存在相同的view,如果存在则根据情况执行相应的操作。
			对于panel window类型的窗口,查找其所依附的父窗口。
        */
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            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.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            IWindowSession windowlessSession = null;
            // If there is a parent set, but we can't find it, it may be coming
            // from a SurfaceControlViewHost hierarchy.
            if (wparams.token != null && panelParentView == null) {
                for (int i = 0; i < mWindowlessRoots.size(); i++) {
                    ViewRootImpl maybeParent = mWindowlessRoots.get(i);
                    if (maybeParent.getWindowToken() == wparams.token) {
                        windowlessSession = maybeParent.getWindowSession();
                        break;
                    }
                }
            }
			//根据条件创建ViewRootImpl对象root,如果有父窗口,则使用windowlessSession创建ViewRootImpl对象。
            if (windowlessSession == null) {
                root = new ViewRootImpl(view.getContext(), display);
            } else {
                root = new ViewRootImpl(view.getContext(), display,
                        windowlessSession, new WindowlessWindowLayout());
            }
			//设置View的LayoutParams为wparams。
            view.setLayoutParams(wparams);
			//将view、root、wparams添加到相应的列表中(mViews、mRoots、mParams)。
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            //将view设置到root中,并处理可能出现的RuntimeException异常。
            try {
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
                // BadTokenException or InvalidDisplayException, clean up.
                if (viewIndex >= 0) {
                    removeViewLocked(viewIndex, true);
                }
                throw e;
            }
        }
    }

WindowManagerGlobal中维护的和Window操作相关的3个列表,在窗口的添加、更新和删除过程中都会涉及这3个列表,它们分别是View 列表(ArrayList<View> mViews)、布局参数列表(ArrayList<WindowManager.LayoutParams>mParams)和ViewRootImpl列表(ArrayList<ViewRootImpl>mRoots

WindowManagerImpl 的addView()主要做了以下事情:

对传入的参数进行合法性检查,包括检查View、Display和LayoutParams是否为空,以及对LayoutParams进行类型转换和调整。
创建ViewRootImpl对象,用于管理View的显示和交互。
将View的LayoutParams设置为传入的LayoutParams。
将View、ViewRootImpl对象和LayoutParams添加到相应的列表中。
尝试将View设置到ViewRootImpl对象中,并处理可能出现的RuntimeException异常。

中间的IWindowSession 暂且忽略。

最后就到了ViewRootImpl 的setView方法

2.2.3 ViewRootImpl.setView

在上面的addView方法中创建了一个 ViewRootImpl 实例,将 ViewRootImpl 与 View 树进行关联,这样 ViewRootImpl 就可以指挥View树的具体工作,包括测量、布局和绘制等操作。ViewRootImpl 是一个核心组件,它作为控制器,负责处理系统消息队列、与 WindowManagerService 通信、触发 View 树的渲染等任务。

大致看一下ViewRootImpl类的重要内容

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
            ...
            //持有主线程
	        final Thread mThread;
            //    View
            View mView;
            final View.AttachInfo mAttachInfo;
            ...
            //执行setView方法去想Window添加View
            public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {...}
            void doTraversal() {...}
            void scheduleTraversals() {...}
            ...
}

差不多见名识意吧:

它会在WindowManager调用addView添加rView时进行初始化,能和系统的WindowManagerService交互,管理View 的绘图和窗口状态;会持有View;
会进行绘制主线程检查,异步线程抛异常;
会进行我们所熟知视图绘制三大流程,performMeasure、performLayout、performDraw
在View中会被mParent变量所持有,在View.AttachInfo中被mViewRootImpl变量所持有,我们平时调用的getViewRootImpl()方法,就是获取的mViewRootImpl变量的引用。
调用View的invalidate()方法也会到ViewRootImpl里面,另外,除了绘制流程,它还承担了事件分发。
负责和WMS进行进程间通信。

继续看ViewRootImpl 的setView()

//ViewRootImpl构造方法
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
            boolean useSfChoreographer) {
	...
   	//持有主线程,即创建ViewRootImpl的线程
    mThread = Thread.currentThread();
    //初始化AttachInfo,内部传递持有了ViewRootImpl,后续的代码里,它将被传递给View,所以可以通过View获取到mAttachInfo,进而获取到ViewRootImpl。
    mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,context);
}

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                //持有View
                mView = view;
                ...
                //AttachInfo持有View
                mAttachInfo.mRootView = view;
                //调用requestLayout
                requestLayout();
                ...
                    //通过WindowSession来完成window最终的添加,该过程mWindowSession类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,所以这其实是一次IPC的过程,远程的调用了Session的addToDisPlay方法。
                    res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId,
                            mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
                            mTempControls);
				...
               	//将自己传递给View的mParent变量,如此调用View相关的invalidate方法,就直接调用到了ViewRootImpl
                view.assignParent(this);
                ...
        }
    }

除了相关变量的赋值操作,在setView中有两个很值得注意的地方。

  • 调用了requestLayout方法,其内部会调用到scheduleTraversals,进而调用了measurelayoutdraw
  • 通过IPC,将window添加到WMS(WindowManagerService)进行管理。

针对将window添加到WMS这一块,看一下Session的代码:

Session
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
    int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
    InputChannel outInputChannel, InsetsState outInsetsState,
    InsetsSourceControl[] outActiveControls) {
    return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
    requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}

这个mService就是WMS,也就是说ViewRootImpl通过Session可以和WMS进行跨进程调用,而我们看到Session也被WMS持有,每个应用程序进程都会对应一个Session,WMS会用ArrayList来保存这些Session。剩下的工作就交给WMS来处理,在WMS中会为这个添加的窗口分配Surface,并确定窗口显示次序,可见负责显示界面的是画布Surface,而不是窗口本身。WMS 会将它所管理的Surface 交由SurfaceFlinger处理,SurfaceFlinger会将这些Surface混合并绘制到屏幕上。

View的绘制流程则是通过调用requestLayout来继续完成的

ViewRootImpl
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        //判断当前是否是主线程即mThread,如果不是,则抛异常。就是在子线程更新UI没使用handler的话就会抛出的异常
        checkThread();
        //设置mLayoutRequested为true。
        mLayoutRequested = true;
        //进而测量、布局、绘制
        scheduleTraversals();
    }
}

requestLayout的方法中,首先进行调用者的线程检查,如果不是主线程,即不和mThread指向的不是同一个线程对象,就抛出异常。随后设置mLayoutRequested为true,这个变量是做什么呢?它主要用来区分是否需要进行测量和布局。最后调用mLayoutRequested方法。

这里说明一下,requestLayout方法和invalidae方法均是通过最后均调用了scheduleTraversals方法,那么是如何区分是不是需要测量和布局呢?就是上面提到的mLayoutRequested变量了,如果是requestLayout则赋值为true,就会调用测量和布局;如果是invalidae,则默认就为false。

接下来看一下scheduleTraversals及其相关方法的调用流程。

    ViewRootImpl
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
	mTraversalRunnable
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
	ViewRootImpl
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            //调用了performTraversals
            performTraversals();
        }
    }
	ViewRootImpl
    private void performTraversals() {
        final View host = mView;
        ...
        //将mAttachInfo传入View,同时会调用View的onAttachedToWindow方法
        host.dispatchAttachedToWindow(mAttachInfo, 0);
        ...
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ...
        performLayout(lp, mWidth, mHeight);
        ...
        performDraw();
        ...

上面列出了scheduleTraversals整个方法的调用链,来梳理一下:

  • 给Choreographer注册了一个mTraversalRunnable。Choreographer用于接收显示系统的VSync信号,在下一个帧渲染时控制执行一些操作。Choreographer的postCallback方法用于发起添加回调,这个添加的回调将在下一帧被渲染时执行。所以我们知道mTraversalRunnable里面的内容将在下一帧渲染时被执行。
  • 看一下mTraversalRunnable的类型,内部调用了doTraversal。而在doTraversal内部调用了performTraversals。
    在performTraversals中,调用View 的dispatchAttachedToWindow方法,给View绑定了mAttachInfo,进而可以调用到View的onAttachedToWindow方法。之后就是熟悉的测量、布局、绘制调用流程。

ViewRootImpl的创建以及视图绘制流程就清楚了,和之前的结合进行简单的总结一下:

  • 在handleLaunchActivity中进行Activity的实例化,之后初始化WindowManager,Context等,Window持有WindowManager,给Activity绑定Window对象。然后调用到onCreate的setContentView,创建DecorView被Window持有。同时我们自己的布局包含在了DecorView中;之后调用handleStartActivity方法,里面会调用onStart以及onRestoreInstanceState;
  • 之后真正的显示过程在调用handleResumeActivity回调调用onResume之后,此时会将DecorView通过WindowManager的addView方法和WindowManager进行绑定,利用ViewRootImpl进行和WMS交互,加入window布局到到WMS,然后进行View的测量、布局、绘制,最后调用Activity的makeVisible进行显示。

三、总结

addView:

1.创建ViewRootImpl;
2.将ViewRoot、DecorView、布局参数保存到WM的内部列表中;
3.ViewRoot.setView()建立ViewRoot和View的联系。
setView:
1.进行View绘制三大流程:performMeasure、performLayout、performDraw
2.会通过WindowSession完成Window的添加过程(一次IPC调用)
requestLayout:内部调用scheduleTraversals(), 底层通过mChoreographer去监听下一帧的刷新信号。
mWindowSession.addToDisplay: 执行WindowManagerService的addWindow
addWindow: 检查参数等设置;检查Token;将Token、Window保存到WMS中;将WindowState保存到Session中。(WindowState WindowSession等知识)

附带一张WMS整体框架

在这里插入图片描述

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

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

相关文章

RPC分布式通信框架

在实际开发中单机服务器存在诸多问题&#xff1a; 1.受限于硬件资源无法提高并发量 2.任意模块的修改都将导致整个项目代码重新编译部署 3.在系统中&#xff0c;有些模块属于CPU密集型&#xff0c;有些属于I/O密集型&#xff0c;各模块对于硬件资源的需求不一样 什么是分布式&a…

程序员转技术管理要做哪些努力?

对许多开发者而言&#xff0c;深耕技术&#xff0c;然后成为技术专家或许是职业发展的唯一答案。但如果你赞同「软件开发只是我众多职业目标中的一个」&#xff0c;也许你可以试试「技术管理之路」。 我原来觉得和计算机打交道比跟人打交道轻松得多&#xff0c;所以我成了一名…

每日OJ题_DFS回溯剪枝①_力扣46. 全排列(回溯算法简介)

目录 回溯算法简介 力扣46. 全排列 解析代码 回溯算法简介 回溯算法是一种经典的递归算法&#xff0c;通常⽤于解决组合问题、排列问题和搜索问题等。 回溯算法的基本思想&#xff1a;从一个初始状态开始&#xff0c;按照⼀定的规则向前搜索&#xff0c;当搜索到某个状态无…

【UnityShader入门精要学习笔记】第十一章 Shader动画

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 UnityShad…

Vuforia AR篇(三)— AR模型出场效果

目录 前言一、AR模型出场二、AR出场特效三、添加过渡效果四、效果 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍了机器学习的基础内容。 一、AR模型出场 创建ARCamer…

上位机图像处理和嵌入式模块部署(树莓派4b之mcu固件升级)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在一个系统当中&#xff0c;可能不止需要树莓派4b一个设备&#xff0c;有的时候还需要搭载一个mcu&#xff0c;做一些运动控制的事情。比如说&…

【ESP32入门实战】初识ESP32

【ESP32入门实战】初识ESP32 文章目录 【ESP32入门实战】初识ESP32&#x1f468;‍&#x1f3eb;前言【写作缘由】&#x1f9d1;‍&#x1f393;ESP32介绍&#x1f469;‍&#x1f4bb;ESP32-WROOM-32&#x1f469;‍&#x1f4bb;ESP32的组成部分 &#x1f468;‍&#x1f3eb…

Android—统一依赖版本管理

依赖版本管理有多种方式 config.gradle 用于Groovy DSL&#xff0c;新建一个 config.gradle 文件&#xff0c;然后将项目中所有依赖写在里面&#xff0c;更新只需修改 config.gradle文件内容&#xff0c;作用于所有module buildSrc 可用于Kotlin DSL或Groovy DSL&#xff0c;…

48-70V降12V/33V 5A高效同步降压DC-DC——AH1007

AH1007是一款高效率、高压外置MOSFET管降压芯片TEL&#xff1a;186*4884*3702*&#xff0c;芯片典型输入是8V~100V,输出 电压可调&#xff0c;AH1007最大输出电流可支持6A以上&#xff0c;需要注意板子的散热和温升。 AH1007典型开关频率为150KHz。轻载时会自动降低开关频率以…

如何把MP3音频转AAC?超级简单的音频格式转换方法在这里

在数字化时代&#xff0c;音乐文件的格式多种多样&#xff0c;每种格式都有其独特的特点和优势。其中&#xff0c;MP3和AAC是两种非常常见的音频格式。MP3由于其较小的文件大小和良好的音质&#xff0c;在过去几十年中一直备受欢迎。然而&#xff0c;随着技术的进步和音频编码算…

掼蛋游戏中的坏习惯

掼蛋是一款需要团队合作和策略思考的游戏&#xff0c;已经成为很多人的日常休闲娱乐方式。然而在日常掼蛋游戏中&#xff0c;有些玩家可能会做出一些不良举动&#xff0c;影响游戏的进行。我们列举了一些常见的坏习惯&#xff0c;希望玩家能够注意并且避免。 1、随意退出 有些玩…

SpringCloud之Feign集成Ribbon

Feign定义【可跳过】 Spring Cloud Feign是一个声明式的伪Http客户端&#xff0c;它使得写Http客户端变得更简单。其英文表意为“假装&#xff0c;伪装&#xff0c;变形”&#xff0c;是一个http请求调用的轻量级框架&#xff0c;可以以Java接口注解的方式调用Http请求&#x…

Capture CIS设计小诀窍系列--Capture CIS配置-数据库搭建及ODBC配置

背景介绍&#xff1a;在原理图设计过程中&#xff0c;如果物料信息出现问题&#xff0c;导致BOM错误或者原理图符号、封装不对应&#xff0c;可能会导致项目延期甚至生产事故&#xff0c;严重影响产品设计效率。而Capture CIS原理图设计工具提供的CIS(Component Information Sy…

排队算法的matlab仿真,带GUI界面

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 M/M/1 单服务台单通道排队模型 4.2 M/M/k 多服务台排队模型 4.3 M/G/1 和 G/M/1 模型 5.完整程序 1.程序功能描述 排队算法的matlab仿真,带GUI界面。分别仿真单队列单服务台&#xff…

No system certificates available. Try installing ca-certificates.

一、错误重现 Certificate verification failed: The certificate is NOT trusted. No system certificates available. Try installing ca-certificates. 具体如图 系统环境是ubuntu:22.04 ARM架构 二、解决方法 1、先不要更换镜像源 直接设置 apt update apt -y instal…

使用 ollama 部署最新的Llama 3 70B本地模型

一、ollama是什么? 在本地启动并运行大型语言模型。运行Llama 3&#xff0c;Mistral, Gemma, Code Llama和其他模型。自定义并创建您自己的。 综合优点&#xff1a; 快速下载容器自动运行大模型&#xff0c;现在下载&#xff0c;马上上手。本地利用 cpu 运行大模型&#xff0c…

【Docker】Docker Network(网络)

文章目录 一、Docker为什么需要网络管理二、Docker网络架构简介CNMLibnetwork驱动 三、常见网络类型四、Docker网络管理命令docker network createdocker network inspectdocker network connectdocker network disconnectdocker network prunedocker network rmdocker network…

实验6 用例图

一、实验目的 1、通过绘制用例图&#xff0c;掌握其基本原理。 2、能对简单问题进行用例图分析&#xff0c;并描绘用例。 二、实验项目内容&#xff08;实验题目&#xff09; 1、用Microsoft Visio绘制下列描述文字的用例图。 “远程网络教学系统”的功能需求如下: 学生登…

腾讯清华联合打造Eurus:用偏好树推进大语言模型的推理能力大突破

目录 引言&#xff1a;推动开源大型语言模型(LLMs)在复杂推理任务中的发展 EURUS模型介绍 ULTRAINTERACT数据集的创新 EURUS在多个推理任务中的表现分析 偏好学习在推理任务中的应用和影响 EURUS-RM-7B奖励模型的评估 结论&#xff1a;EURUS系列模型的创新点和对开源推理…

kubernetes中DaemonSet控制器

一、概念 使用DaemonSet控制器&#xff0c;相当于在节点上启动了一个守护进程。通过DaemonSet控制器可以确保在每个节点上运行Pod的一个副本。如果有心的node节点加入集群&#xff0c;则DaemonSet控制器会自动给新加入的节点增加一个Pod的副本&#xff1b;反之&#xff0c;当有…