android framework之Applicataion启动流程分析(四)

news2024/9/27 8:23:03

本文主要学习并了解Application的Activity启动流程。


这边先分析一下Launcher是如何启动进程的Acitivity流程。从Launcher启动Acitivity的时候,它是把启动任务丢给instrumentation模块去协助完成,由它进一步调用AMS的startActivity()方法
去启动(Binder跨 进程调用),从调用启动startActivity()开始到Activity启动调用onResume(),大约经历非常多个步骤。

过一下启动Activity的大致分析(假设以ActivityA->ActivityB):

  • 1.Activity参数解析 (存在于AMS:systemserver进程)

Activity的启动流程是使用Intent的方式,作为参数传入,因此启动Activity的初始重要步骤是需要解析Activity需要的启动参数。

  • 2.Activity的管理(存在于AMS:systemserver进程)

在Framework中Activity是由谁管理的?其实是由activityStack进行管理,但在AMS服务中,它对应的代表是ActivityRecord,也就是说在ActivityStack中管理的对象的ActivityRecord,所以启动activity时,需要借助activityStack,由它去管理与启动activity.比如activityA->activityB时,B启动时,一定会影响A的生命周期,这些都需要由ActivityStack去管理。

  • 3 进程间通信

前面2小点是在AMS中处理,它属于SystemServer进程,但是对于activity的很多工作,需要由Application进程去操作 ,这就涉及到了IBinder进程间通信

  • 4.application的处理

在AMS中,将对应的Application的生命周期的所有事务都会在activityStackSupervisor类中被封装成一个clientTransaction的触发事件,它包含了操作Application activity的一系列的命令,然后借助Binder,将clientTransaction传递给Application,Application拿到命令后,去执行Application Activity的生命周期。

  • 5 Activity-A生命周期stop的执行
     

  • 下面先来学习Activity参数的解析(即上面说的第一阶段:Activity参数解析)

分析过程依据下面的流程进行,

                                                                图1

instrumentation跨进程调用ATMS的startActivity(), ATMS会调用自己的startActivityAsUser(),这是按用户的需求启动activity,这个接口里会去解析
用户的参数需求,
ATMS->startActivityAsUser():
  //1 理解ActivityStarter类:这是一个重要的类,专门负责activity的启动时的参数解析
  //2 理解ActivityStartController类:负责生产ActivityStarter对象,由controller创建
  //3 ActivityStartController在ATMS中的initialize()中被创建:mActivityStartController = new ActivityStartController(this);
      ATMS里只有一个startController对象,利用它来辅助ATMS来管理starter对象,每个activity对应一个activityStart.
  -->getActivityStartController().obtainstarter(intent, "startActivityAsUser")....()..().execute(); //会初始化Request内部类对象
      -->ActivityStartController.java->obtainStarter():
          -->return mFactory.obtain().setIntent().setReason();
      -->ActivityStarter.java->execute():
          -->//插入补充内容: ActivityStarter内部有一个内部类Request:由上面设置,存放activity启动过程中的交互性参数。
             //如A->B,需要记录A作为caller,caller是IAPPlicationThread对象,是谁启动了B,这个是要记录的。
             //这里面保存着启动actity启动所需要的各类参数配置,非常重要。
             executeRequest(mRequest); //利用Request配置好的参数去执行。
              -->在里面开始解读Request中的各类参数,并进行一系列的参数判断,然后会调用下面这个关键函数
                 //Activity在AMS里的存在代表是ActivityRecord,它里面包含着activity的所有属性
                 final ActivityRecord r = new ActivityRecord();//创建要启动的目标activityRecord对象(即ActivityB),存放到传入数组0索引上
                 mController.doPendingActivityLaunches(false);//启动那些需要启动,但是一直没来得及启动的Activitys.
                  -->starter.startResolvedActivity();
                      -->startActivityUnchecked(); //启动等待的activity
                 ...
                 startActivityUnchecked();//启动最后需要启动的activity,也就是当前Activity.
                  --> startActivityInner(); //进一步深入分析,可以感受到starter的价值。
                       -->setInitialState();//初始化配置,mStartActivity,mLaunchMode等
                          computerLauncherTaskFlags();//计算要启动的activity flag标志,也就是计算启动模式。
                          computerSourceStack();//计算源Activity所在的栈。
                          mIntent.setFlags(mLaunchFlags);//LaunchFlags设置给Intent,也就是设置启动模式。
                          //...这里有一个细节,如果是Launcher启动activity时,需要能让用户感知已经在启动,所以在这加了黑白屏操作
                          //如上图所示。
                          mTargetStack.startActivityLocked(mStartActivity,...); //开始启动
                          //需要注意,此时我们要启动的Activity对应的Application进程并未启动,但Activity必须依附到进程上。
                          //RootWindowContainer:窗口容器:定义了windows的存储的层次结构,设备上所有的窗口(window),显示(Disply)都是由它管理的,
                          //每个activity均对应一个windows,每个windows的显示、隐藏与activity的生命周期有关,
                          //所以启动activity时走到了RootWindowContainer这个类里面
                          mRootWindowContainer.resumeFocusedStacksTopActivities();
                            -->RootWindowContainer.java:会恢复对应任务栈顶部的Activity.即将处于栈顶的activity进行恢复,
                               //在启动activity时,就是往这个栈的栈顶上去添加一个新的activity,这边涉及到activity的显示过程。
                               focusedStack.resumeTopActivityUncheckedLocked(); //走到这步就意味着走到了前面文章刚开始说的第2阶段。

  • 下面开始分析第二阶段的工作任务:整体 流程如下图所示

                                                        图2.

前面说过,解读参数的过程中会创建ActivityRecord,最后会走到RootWindowContainer容器类,由它去调用resumeTopActivityUncheckedLocked,进入ActivityStack
类的中去处理,这个是第二阶段,下面来分析下ActivityStack如何去管理  ActivityRecord,管理它的启动的。
ActivityStack.java->resumeTopActivityUncheckedLocked():
  -->resumeTopActivityInnerLocked(): //管理栈的接口
      -->if(!hasRunningActivity)://不存在 正在运行的activity的时候
            return resumeNextFocusableActivityWhenStackIsEmpty();
         //当A->B时,A要先进入onPause
         if(mResumedActivity != NULL){
            pausing |= startPausingLocked();//停止当前Activity的生命周期
              -->ActivityStack.java:startPausingLocked()
                 mAtmService.getLifeCycleManager().scheduleTransaction(.., PauseActivityItem.obtain() ); //通过它来停止
         }

         //next就是要启动的这个ActivityRecord,它是一个全新的ActivityRecord,所以这里的返回值是false.
         ActivityRecord next = TopRunningActivity(true);
         if(next.attachedToProcess()) //需要与Application进程关联,但 由于它是刚创建 ,application进程创建消息还未发送,所以这边是false
            -->return hasProcess() && app.hasThread();
                  -->return app != NULL; //此时app==NULL
         else{ //所以会走这边,这里要注意一下
            //启动指定的Activity. 从这开始ActivityStackSupervisor类开始登场。它用于管理ActivityStack的类,是ActivityStack的主管。
            //ActivityStackSupervisor类需要封装clientTransaction事件-->传给 app进程
            mStackSupervisor.startSpecificActivity(next, true,true);
              -->//这里进入了创建app进程的起点了。可以参考前面的这篇文章《android framework之Applicataion启动流程分析》
                 if(wpc != null && wpc.hasThread() ) { //进程如果已经创建的话,直接启动Activity.我们现在看这边
                    realStartActivityLocked(r,wpc, andResume=true, checkConfig);
                      --> //创建ClientTransaction对象,这个类extends Parcelable:因为它代表activity生命周期要执行的事件,
                          //这个事件的发送目标是ActivityThread(APP进程), 由ActivityThread收到事件后进一步处理,所以
                          //这边涉及到了跨进程通信,binder跨进程通信的数据必须继承Parcelable数据类型实现序列化。
                          //这个类中主要包含三个内容:
                          1. private List<ClientTransactionItem> mActivityCallbacks; //activity的回调:如onstart,onresume,onpause等
                          2. 此变量定义了activity生命周期的最终状态(执行完一系列事件后的最终状态):private ActivityLifecycleItem mLifecyclesStateRequest.
                          3. 跨进程的目标进程:IApplicationThread mClient; //clientTransaction的发送目标。
                          4. 目标进程对应关联的Activity: private IBinder mActivityToken;
                          final ClientTransaction clientTransaction  = ClientTransaction.obtain(proc.getThread(), r.apptoken);
                          //添加LauncherActivityItem.why?
                          clientTransaction.addCallback(LauncherActivityItem.obtain(new Intend(r.intent),... ));

                          if(andResume)
                             //最终的状态
                             lifecycleItem =  ResumeActivityItem.obtain()
                          else
                             lifecycleItem = PauseActivityItem.obtain();
                          clientTransaction.setLiftcycleStateRequest(liftcycleItem); //设置ResumeActivityItem到事件中。
                          mService.getLiftcycleManager.scheduleTransaction(clientTransaction);//获取生命周期管理 类,并执行事务。


                 }else{ //如果APP进程不存在
                    mService.startProcessAsync(); //进入创建进程的流程。 这边的流程分支已经分析过了
                 }
         }

  • 下面开始分析第三阶段的工作任务(进程间通信):整体 流程如下图所示

前面第二阶段中,将mService.getLiftcycleManager.scheduleTransaction(clientTransaction);代码执行完就,就开始进入第三阶段的处理了。

                                                                图3

接着前面的代码进行分析,当拿到LifecycleManager对象之后,继续调用它的方法scheduleTransaction(), 进入代码:

mService.getLiftcycleManager.scheduleTransaction(clientTransaction);
mService是ATMS对象mService.getLiftcycleManager():它返回的是ClientLifecycleManager,它的初始化也是在ATMS的构造函数中,
ActivityTaskManagerService(Context context)
  mLifecycleManager = new ClientLifecycleManager(); //28之后添加的对activity lifecycle的触发器

scheduleTransaction()://这个是执行  mLifecycleManager对象中的方法
  -->//当前的进程还是存在于AMS所在的进程中。
     transaction.schedule(); //transaction是ClientTransaction对象
      -->会进入ClientTransaction.java->schedule(); //这个会完成一次跨进程通信
          -->把this(自己)传递进入:就是ClientTransaction对象
          -->mClient.scheduleTransaction(this); //通过mClient(目标进程的Binder)也就是IApplicationThread跨进程调用到应用进程

接着,现在源码转至APP进程的ActivityThread.java->scheduleTransaction(this):

  -->ActivityThread.this.scheduleTransaction(transaction); 
      -->先来了解下ActivityThread extends ClientTransactionHandler,后者是ActivityThread的父类,
      -->这里它调用的是父类ClientTransactionHandler中的方法scheduleTransaction:
          //下面调用发送消息所在的线程不是主线程,它是在ApplicationThread线程中运行的,就是上面分析的mClient对象,它是IBinder对象,属于binder中的ApplicationThread对象
          -->在里面调用了sendManager(ActivityThread.H.EXECUTE_TRANSACTION, transaction); //发消息给主线程:ActivityThread的Handler
              -->ActivityThread的handleMessage():
                  -->ClientTransaction transaction = (ClientTransaction)msg.obj; //所以这个消息的传递总的来说,先跨进程,再跨线程,将封装的transaction传递过来
                  -->mTransactionExecutor.execute(transaction); //使用主线程中的触发器去执行transaction
                      -->//这里就进入到activity生命周期的触发管理执行

总结下第三阶段干的任务:上图3中的ClientTransaction流程中,会进行一次跨进程调用,进入到app的进程,在主进程中会调用Binder线程IAppicationThread中的方法(它是app的子线程),就是在ClientTransactionHandler中会向主线程发送消息,这里又进行了一次子线程向主线程的消息传递过程,主线程在处理的时候,就会调用真正的执行触发器   mTransactionExecutor 去执行对应的transaction,触发生命周期的管理。
             

  •   下面分析Acitivity生命周期触发器执行阶段

/第四阶段:
mTransactionExecutor.execute(transaction); //上接第三阶段结束
  -->executeCallbacks(transaction);
        -->执行的是LauncherActivityItem: //在第二阶段中有添加这个callback
           int size = callbacks.size();
           for(int i = 0; i < size; ++i){ //从callback数组中取出item
              ClientTransactionItem item = callback.get(i);
               //调用launchActivityItem的execute方法
              item.execute(mTransactionHandler, token, mPendingActtions);
                -->进入launchActivityItem.java->execute:
                    //launcher启动activity时,还没有ActivityClientRecord,需要构建一个。
                    //这个是ActivityThread的静态内部类:ActivityClientRecord:处理client端事件的记录,启动过程中会使用
                    -->ActivityClientRecord r = new ActivityClientRecord();
                       client.handleLauncherActivity(r, pendingActions); //client:ClientTransactionHandler.java
                       //client唯一的实现类就是ActivityThread(继承ClientTransactionHandler)
                       //所以将会进入到ActivityThread.java->handleLauncherActivity():
                        -->final activity a = performLauncherActivity(r, customIntent);
                            -->构建一个activity
                               activity = mInstrumentation.newActivity();
                               //loadedAPk 构建makeApplication对象
                               Application app = r.packageInfo.makeApplication(false, mInstrumentation);
                                -->如果Application已经存在,直接return,保证单例
                                   if(mApplication != null){
                                      return mApplication; //在前面分析的bindApplication()中实际上已经创建了Application.
                                   }
                               //在这个方法中创建Activity的PhoneWindow, 并绑定对应的WindowManager.
                               activity.attach();
                                -->mWindow = new PhoneWindow(); //这里开始就进入了wms.
                                   ...
                               //执行onCreate生命周期
                               mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                               r.setState(ON_CREATE); //设置mLifecycleState成CREATE状态。 //这很关键

           }

     executeLifecycleState(transaction);
      -->获取ActivityLifecycleItem,这里取的是之前添加的ResumeActivityItem
         ActivityLifecycleItem  lifecycleItem = transaction.getLifecycleStateRequest();
         ...
         //ResumeActivityItem的getTargetState 是ON_RESUME = 3
         cycleToPath(r, lifecycleItem.getTargetState(), true, transaction);
          -->start =  r.getLifecycleState(); //这里的start是on_Create状态 = 1
          -->下面的path:包括ON_START和ON_RESUME
          -->这里是Activity执行onStart的函数的关键所在
          -->//mHelper是TransactionExecutorHelper类对象,finish=lifecycleItem.getTargetState(), = ON_RESUME=3
          -->IntArray path = mHelper.getLifecyclePath(start,finish,excludeLastState); //start = 1, finish = 3
                -->if(finish > start) //3> 1
                   {
                     if(start == ON_START && finish == ON_STOP){

                     }else{ //走下面这个分支
                       for(int i = start +1; i <= finish; ++i){
                           mLifecycleSequence.add(i); //把ON_START和ON_RESUME添加到mLifecycleSequence中 。加入2和3
                       }
                     }

                   }
                   //excludeLastState = true;所以进入分支移除ON_RESUME
                   if(excludeLastState && mmLifecycleSequence.size()!=0){
                    mLifecycleSequence.remove(mLifecycleSequence.size() -1);//移除ON_RESUME;
                   }
                   return mLifecycleSequence; //里面只有ON_START=2这个状态了
              //执行paht中相关的生命周期函数
              performLifecycleSequence(r,path,transaction);
                -->int size = path.size(); //只有一个ON_START
                   for(i= 0; i < size; ++i){
                     state = path.get(i);
                     switch(state){
                      case ON_START:
                        mTransactionHanlder.handleStartActivity(r.token, mPendingActions);
                          -->触发进行Activity的onStart流程。
                             //start
                             activity.performStart("handleStartActivity");
                             r.setState(ON_START);
                      break;
                     }
                   }

         //执行ResumeActivityItem的execute
         lifecycleItem.execute();//lifecycleItem = ResumeActivityItem
          -->进入ResumeActivityItem.java->execute():
              client.handleResumeActivity(); //把事件丢给ActivityThread去执行。
         lifecycleItem.postExecute(r,path,...);

//上接:  client.handleResumeActivity(); //把事件丢给ActivityThread去执行。
ActivityThread->handleResumeActivity():
  -->r.activity.performResume(r.startsNotResume, reason);
     r.setState(ON_RESUME);

总结:
LaunchActivityItem:
    launch():
      onCreate();

    触发器的计算触发onStart();生命周期

最后是ResumeAtivityItem->onResume(); 这就包括了完整的生命周期过程,从onCrate->onStart->onStart();

  • 第五阶段:activity的Stop阶段

下面分析:当Activity A--->Activity B时,B的启动和A的Stop处理如何操作?主要是A的Stop阶段如何进行?这个就是第5阶段,还是从ActivityThread的handleResumeActivity()中去分析,

大致由下图流程:

ActivityThread->handleResumeActivity():
  -->ActivityClientRecord r = performResumeActivity(); //执行resume生命周期
     ...
     //显示UI
     wm.addView(decor,l);//调用windowsManagerImpl将decorView添加到window中 WMS阶段去研究吧
     Looper.myQueue().addIdleHandler(new Idler()) ;//往 looper中添加Idler事件
     //这个Idler中包括了一个相当重要的事务,包含了一个与AMS的通信。


//APP主线程当没有任何消息事务需要处理时,会去处理Idler的MessageQueue
class Idler implements MessageQueue.IdleHandler{
  public bool queueIdle(){
    IActivityManager am = ActivityManager.getService();

    do{
      am.activityIdle(a.token, a.createdConfig, stopProfiling);
      prev = a;
      a = a.nextIdle;
      prev.nextIdle = null;
    }(a != NULL); //执行这里,说明主线程是空闲状态。
    
  }
}

请看上图的流程中,ASS的第4步,会处理processStoppingAndFinishingActivities();
也就是说,当ActivityThread主线程空闲时,会执行activityIdle(),接着会通过ATMS调用ASS有activityIdleInternal(),然后会执行Stop Activity相关的操作,也就是ActivityA->ActivityB过程时,会先启动B,然后在空闲时,再处理A的Stop事件,就是在这里处理的。

所以先来看看ATMS->activityIdle():
 

ATMS->activityIdle():
  //mStackSupervisor是对activity生命周期事件进行封装的模块。帮助activityState
  mStackSupervisor.activityIdleInternal();
    -->processStoppingAndFinishingActivities(); //需要停止需要终止的activity都在这里面处理。
          -->for(int i = mStoppingActivies.size()-1; i >=0; --i){ //需要停止的遍历一遍
             ...
             readyToStopActivities.add(s);
             mStoppingActivies.remove(i);
          }

          -->for(int i = 0; i < numReadyStops; i++){
            ActivityRecord r = readyToStopActivities.get(i);
             if(r.finishing()){
                r.destroyIfPossible(reason);
             }else{
                r.stopIfPossible(); //我们stop走这边
                  -->mATmService.getLifecycleManager().scheduleTransaction(); //把stop事件封装成一个Transaction事务。
                      -->final ClientTransaction clientTransaction = transactionWithState(); //封装
                         scheduleTransaction(clientTransaction);//调用,跨进程,传给application.
             }
          }

注意:在android12中,activityStack和ActivityStack已经不存在了,改成了Task.java,启动过程全部转接到Task.java中处理。总体的思路不变。

在Stop事件封装成Transaction后,发给Application线程处理,然后会进入到第4阶段的Activity生命周处理处理流程,只不过可能是Launcher进程(另外一个进程),当然如果是相同进程,则由本进程去处理。只不过对于Stop或Finish等事件,它的优先级不高,所以基本都会等到主线程不繁忙空间的时候去执行清理操作。

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

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

相关文章

基于resnet网络架构训练图像分类模型

数据预处理部分&#xff1a; 数据增强&#xff1a;torchvision中transforms模块自带功能&#xff0c;比较实用数据预处理&#xff1a;torchvision中transforms也帮我们实现好了&#xff0c;直接调用即可DataLoader模块直接读取batch数据 网络模块设置&#xff1a; 加载预训练…

ARM DIY(九)陀螺仪调试

前言 今天调试六轴陀螺仪 MPU6050 硬件 硬件很简单&#xff0c;使用 I2C 接口&#xff0c;并且没有使用中断引脚。 焊接上 MPU6050 芯片和上拉电阻、滤波电容。 检测 MPU6050 是挂在 I2C-0 上的&#xff0c;I2C-0 控制器的驱动已 OK&#xff0c;所以直接使用 I2C-0 检测 …

2023 年前端编程 NodeJs 包管理工具 npm 安装和使用详细介绍

npm 基本概述 npm is the world’s largest software registry. Open source developers from every continent use npm to share and borrow packages, and many organizations use npm to manage private development as well. npm 官方网站&#xff1a;https://www.npmjs.…

程序员做哪些副业可以日赚一百?

日赚一百&#xff1f;对程序员来说简直不要太容易&#xff01;下面给程序员们推荐一些日赚100的副业&#xff1a; ①外包接单 程序员简单粗暴赚钱的副业之一。 外包接单的类型包括但不限于&#xff1a;软件开发、硬件开发、小程序功能开发、web开发……大到一个系统的开发、…

外包干了三年,我承认我确实废了……

没错&#xff0c;我也干过外包&#xff0c;一干就是三年&#xff0c;三年后&#xff0c;我废了…… 虽说废的不是很彻底&#xff0c;但那三年我几乎是出差了三年、玩了三年、荒废了三年&#xff0c;那三年&#xff0c;我的技术能力几乎是零成长的。 说起这段三年的外包经历&a…

对象临时中间状态的条件竞争覆盖

Portswigger练兵场之条件竞争 &#x1f984;条件竞争之对象临时中间状态的条件竞争 Lab: Partial construction race conditions&#x1f680;实验前置必要知识点 某些框架尝试通过使用某种形式的请求锁定来防止意外的数据损坏。例如&#xff0c;PHP 的本机会话处理程序模块…

任务管理系统所需功能概述

"任务管理需要有哪些功能&#xff1f;清晰的任务创建与编辑、智能分类和标签系统、提醒与通知功能、进度跟踪与报告、协作与共享功能、集成与兼容性。" 一款优秀的任务管理工具可以帮助我们有效地规划、执行和监控各项任务&#xff0c;提高工作效率。本文将探讨一款理…

深度学习(十一)---zed 调用yolov5 进行识别目标并实时测距

1. 前言 zed 相机测距有2种方式&#xff1a;一种是根据点云数据进行测试&#xff0c;二是根据zed获取深度值进行测距。上篇文章 调用yolov5模型进行实时图像推理及网页端部署 我们讲述了zed调用yolov5进行目标识别&#xff0c;我们在此基础上进一步实现目标测距功能。 2.深度…

Apache httpd漏洞复现

文章目录 未知后缀名解析漏洞多后缀名解析漏洞启动环境漏洞复现 换行解析漏洞启动环境漏洞复现 未知后缀名解析漏洞 该漏洞与Apache、php版本无关&#xff0c;属于用户配置不当造成的解析漏洞。在有多个后缀的情况下&#xff0c;只要一个文件含有.php后缀的文件即将被识别成PHP…

微信多开bat代码

目录标题 创建txt文件右键 点击新建文本文档 复制如下代码进去&#xff0c;将里面的微信地址改成自己的微信地址&#xff08;查看地址方法&#xff1a;右击微信图标->属性->目标&#xff09;复制如下代码 创建txt文件 右键 点击新建文本文档 复制如下代码进去&#xff0…

Linux系统——MySQL安装(CentOS7 超详细演示)

Linux系统安装MySQL MySQL8.0.26-Linux版安装1. 准备一台Linux服务器2. 下载Linux版MySQL安装包3. 上传MySQL安装包4. 创建目录,并解压5. 安装mysql的安装包6. 启动MySQL服务7. 查询自动生成的root用户密码8. 修改root用户密码9. 创建用户10. 并给root用户分配权限11. 通过Data…

2023高教社杯 国赛数学建模E题思路 - 黄河水沙监测数据分析

1 赛题 E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响&#xff0c; 以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位…

willchange 优化性能的原理是什么

写在前面 今天说一下性能优化部分的其中一个点&#xff0c;这个点叫做 willchange&#xff0c;说他的原因主要有以下几个&#xff1a;第一很多人知道用这个可以提高性能但是不知道原因是什么&#xff0c;第二&#xff0c;我们用的时候他虽然可以提高性能&#xff0c;但是不代表…

Revit SDK 介绍:GenericModelCreation常规模型的创建

前言 这个例子介绍了如何创建拉伸、放样、扫掠、融合、放样融合&#xff0c;涵盖了一个建模软件需要的基本建模方法。 内容 CreateExtrusion 生成的放样融合接口&#xff1a; m_creationFamily.NewExtrusion(true, curve, sketchPlane, bottomProfile, topProfile)核心逻辑&…

msvcr100.dll丢失应该怎么解决,比较靠谱的五种解决方法分享

首先&#xff0c;我们需要了解什么是msvcr100.dll。msvcr100.dll是Microsoft Visual C 2010 Redistributable Package的一部分&#xff0c;它包含了一些运行时库&#xff0c;用于支持某些程序的运行。简单来说&#xff0c;msvcr100.dll就是一个“桥梁”&#xff0c;连接了我们的…

组织架构图怎么做?手把手教你绘制完美的组织架构图

组织架构图是企业和组织中不可或缺的工具&#xff0c;它以图形化的方式展示了各级层次的关系&#xff0c;帮助人们更好地理解和管理整体结构。本文将为你详细介绍绘制组织架构图的要素、步骤和技巧&#xff0c;并推荐四款强大的绘图软件&#xff0c;让你轻松绘制完美的组织架构…

无涯教程-JavaScript - DELTA函数

描述 DELTA函数测试两个值是否相等。如果number1 number2,则返回1&#xff1b;否则返回1。否则返回0。 您可以使用此功能来过滤一组值。如,通过合计几个DELTA函数,您可以计算相等对的计数。此功能也称为Kronecker Delta功能。 语法 DELTA (number1, [number2])争论 Argum…

RK3588算法盒子maskrom模式下系统烧录

先在firefly官网下载bulidroot文件&#xff0c;然后进行烧录相关文件。 点击upgrade&#xff0c;进行烧录升级&#xff1b; 等待烧录完成后&#xff0c; 重新开机进入loader模式下&#xff0c; 进行烧录Untunt20.04系统的操作就行。

企业电脑文件加密系统 / 防泄密软件——「天锐绿盾」

「天锐绿盾」是一种公司文件加密系统&#xff0c;旨在保护公司内网数据安全&#xff0c;防止信息泄露。该系统由硬件和软件组成&#xff0c;其中包括服务端程序、控制台程序和终端程序。 PC访问地址&#xff1a; isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c…

python趣味编程-太空入侵者游戏

在 Python 中使用 Turtle 的简单太空入侵者游戏免费源代码 使用 Turtle 的简单太空入侵者游戏是一个用Python编程语言编码的桌面游戏应用程序。该项目包含克隆实际太空入侵者游戏的多种功能。该项目可以使正在学习计算机相关课程的学生受益。该应用程序易于学习,可以帮助您发现…