Android Activity启动过程一:从Intent到Activity创建

news2025/2/21 23:20:26

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

目录

  • 一、概览
  • 二、应用内启动源码流程 (startActivity)
    • 2.1 startActivity()
    • 2.2 startActivityAsUser()
    • 2.3 startActivityUnchecked()
    • 2.4 resumeFocusedStackTopActivityLocked()
    • 2.5 startSpecificActivityLocked -> realStartActivityLocked()
  • 三、 Activity实例化过程
  • 四、 setContentView
  • 五、 后续生命周期
  • 六、 推荐阅读

在这里插入图片描述

学习前,建议有相关知识储备:
【Android 基础】 应用(Application)启动流程

通过本文你可以学习到Activity启动流程。

一、概览

Activity 是 android 四大组件之一,很有必要知道它的启动过程,我们在上一篇文章中介绍了 APP的启动流程,里面大概讲到了Activity的
启动流程,在本文中,我们系统的再总结一下,当成一个记录。

Activity 的启动方式我们讲有两种,一种是在应用内部启动,另一种是外部启动,比如Launcher;

  • 应用内启动
    通过 startActivity、startActivityForResult等方式来启动 Activity

其流程我们总结下:

1、调用 Activity 的 startActivity 方法来启动目标 Activity
2、接着就会调用到 Instrunmentation 的 execStartActivity 方法,然后调用到 AMS 的 startActivity 中去
3、调用到 AMS 中后,会执行到ActivityStarter 的 execute 方法,接着就会进行一些校验和判断权限,包括进程检查,intent检查,权限检查、是否启用新栈等
4、所有的信息存储在ActivityRecord中,ActivityRecord是Activity在system_server进程中的镜像,Activity实例与ActivityRecord实例一一对应。ActivityRecord用来存储Activity的信息,如所在的进程名称,应用的包名,所在的任务栈的taskAffinity等
5、中间再经过一系列调用,又回调到 ActivityThread 的 handleLaunchActivity 来启动 Activity。

借用一张网络图片
11

  • 应用外启动
    通过Launcher 进程启动,Launcher 就是我们桌面程序,当系统开机后, Launcher 也随之被启动。

1、fork并调用ActivityThread的main方法创建app进程
2、然后从 ActivityThread 调用到AMS中的attachApplicationLocked,创建Application
3、Application创建完后,调用ActivityStackSupervisor的attachApplicationLocked方法,最终调用到handleLaunchActivity,进行activity的创建

二、应用内启动源码流程 (startActivity)

我们就从源码出发,一起来看看startActivity后面的流程

2.1 startActivity()

Activity.java

    @Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }
    
    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
          startActivityForResult(intent, -1);
        }
    }
    
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
        startActivityForResult(intent, requestCode, null);
    }


    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                //分析启动结果
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }

        } else {
            // 最终也是调用 execStartActivity 方法,源码如下
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }
    public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
            int requestCode, @Nullable Bundle options) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, child,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, child.mEmbeddedID, requestCode,
                ar.getResultCode(), ar.getResultData());
        }
        cancelInputsAndStartExitTransition(options);
    }

上面代码中,最终都会调用了 execStartActivity 方法,该方法会返回一个启动结果。我们一起来看看
frameworks/base/core/java/android/app/Instrumentation.java


    /**
     *
     * @param who              用来启动 Activity 的对象
     * @param contextThread Binder 对象,具有跨进程通信的能力,传入的是 ApplicationThread
     * @param token  Binder 对象,指向了服务端一个 ActivityRecord 对象
     * @param target 当前的 Activity
     * @param intent  Intent 对象
     * @param requestCode  请求码
     */
    public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, String target,
        Intent intent, int requestCode, Bundle options) {
        
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            
            
            // 这里的service 就是 ActivityManagerService, 具体可以跟代码看到 
            // 这么一句 : ServiceManager.getService(Context.ACTIVITY_SERVICE);
            
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target, requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            
        }
        return null;
    }

通过 Binder 调用 AMS 启动 Activity,我们接着往下看

2.2 startActivityAsUser()

ActivityManagerService.java


public class ActivityManagerService extends IActivityManager.Stub


    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
                                   Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
                                   int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
      return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
              resultWho, requestCode, startFlags, profilerInfo, bOptions,
              UserHandle.getCallingUserId());
    }

    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
                                         Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
                                         int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
                                         boolean validateIncomingUser) {
        enforceNotIsolatedCaller("startActivity");
  
        // 首先 检查调用者权限
        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
  
        // TODO: Switch to user app stacks here.
        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setMayWait(userId)
                .execute();

    }
    
    最后调用的是 ActivityStarter.execute();
    

上面代码最终调用到了 startActivityAsUser 方法,在内部将所有点的参数都交给了 ActivityStarter

2.3 startActivityUnchecked()

ActivityStarter 该类包含了启动的所有逻辑,比如 Intent 解析以及任务栈等。

ActivityStarter.java

   int execute() {
        try {
            if (mRequest.mayWait) {
                return startActivityMayWait(mRequest.caller, ...);
            } else {
                // todo 
                return startActivity(mRequest.caller, ...);  
            }
        } finally {
            onExecutionComplete();
        }
    }

   private int startActivity(IApplicationThread caller, Intent intent, ...) {
        
        // 再次检查调用者权限,包括进程检查,intent检查,权限检查等
        boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, ...);
        abort |= !mService.mIntentFirewall.checkStartActivity(intent, ...);


        // 每个 Activity 都会对应一个 ActivityRecord 对象
        ActivityRecord r = new ActivityRecord(mService, ...);


        // todo 
        result = startActivityUnchecked(r, sourceRecord ...);
   }


    // Note: This method should only be called from {@link startActivity}.
    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord ...) {

        //设置初始化状态
        setInitialState(r, options, inTask, ...);
        
        //判断启动模式,并且在 mLaunchFlags 上追加对应标记
        computeLaunchingTaskFlags();
        
        //设置 Activity 的栈
        computeSourceStack();
        
        //设置 LaunchFlags 到 intent 上
        mIntent.setFlags(mLaunchFlags);

        //决定是否用新的栈
        ActivityRecord reusedActivity = getReusableIntentActivity();
        
        ...

        // Should this be considered a new task?
        int result = START_SUCCESS;
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
             && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
          newTask = true;
          // 创建一个新的task来启动
          result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
        } else


        // todo 
        mSupervisor.resumeFocusedStackTopActivityLocked();    
    
    }

上面代码中会进行一些校验和判断权限,包括进程检查,intent检查,权限检查等

2.4 resumeFocusedStackTopActivityLocked()

ActivityStackSupervisor.java


    boolean resumeFocusedStackTopActivityLocked(ActivityStack targetStack...) {


        return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);

        return false;
      }


resumeTopActivityUncheckedLocked -> resumeTopActivityInnerLocked -> startSpecificActivityLocked()
ActivityStack.java



    @GuardedBy("mService")
    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mStackSupervisor.inResumeTopActivity) {

            Activity 存在则resume
            transaction.setLifecycleStateRequest( ResumeActivityItem.obtain(next.app.repProcState,。。。);
            mService.getLifecycleManager().scheduleTransaction(transaction);

            不存在则调用下面这个
            result = resumeTopActivityInnerLocked(prev, options);

        } finally {
        }

        return result;
    }

    @GuardedBy("mService")
    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {

            mStackSupervisor.startSpecificActivityLocked(next, true, false);
            
            return true;
    }

2.5 startSpecificActivityLocked -> realStartActivityLocked()

ActivityStackSupervisor.java
startSpecificActivityLocked -> realStartActivityLocked(); 到这个地方,我们就可以看到真的开始启动 activity,
后面就跟Application 里面一样了


    void startSpecificActivityLocked(ActivityRecord r ...) {
        if (app != null && app.thread != null) {
          try {
            // 真的开始启动 activity  ,看下面的方法
            realStartActivityLocked(r, app, andResume, checkConfig);
            return;
          } catch (RemoteException e) {
          }
  
        }
    }

    真的开始启动 activity
    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {

        // Create activity launch transaction.
        final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
                r.appToken);
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                System.identityHashCode(r), r.info,
                // TODO: Have this take the merged configuration instead of separate global
                // and override configs.
                mergedConfiguration.getGlobalConfiguration(),
                mergedConfiguration.getOverrideConfiguration(), r.compat,
                r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                r.persistentState, results, newIntents, mService.isNextTransitionForward(),
                profilerInfo));
            
        // Schedule transaction.
        mService.getLifecycleManager().scheduleTransaction(clientTransaction);
    }

ActivityManagerService


    ClientLifecycleManager getLifecycleManager() {
        return mLifecycleManager;
    }
    

ClientLifecycleManager

    通过代码,我们可以看到,获取的client就是 ActivityThreadIApplicationThread是一个AIDL文件
    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        
    }

ClientTransaction

        
    /** Target client. */
    private IApplicationThread mClient;
    
    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

ClientTransactionHandler


    //ActivityThread中没有复写scheduleTransaction,会执行到父类的方法
    //public final class ActivityThread extends ClientTransactionHandler
    //ClientTransactionHandler.java
    public abstract class ClientTransactionHandler {
    
        void scheduleTransaction(ClientTransaction transaction) {
            transaction.preExecute(this);
            //发送消息
            sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
        }
    }

ActivityThread.java


    case EXECUTE_TRANSACTION:
        final ClientTransaction transaction = (ClientTransaction) msg.obj;
        mTransactionExecutor.execute(transaction);
        break;
        

这里其实就是执行LaunchActivityItem的execute方法,
其赋值的地方在realStartActivityLocked()方法,大家可以回头看看,前面有这么一句代码
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),

TransactionExecutor.java


    public void execute(ClientTransaction transaction) {
        final IBinder token = transaction.getActivityToken();

        executeCallbacks(transaction);

        executeLifecycleState(transaction);
        mPendingActions.clear();
        log("End resolving transaction");
    }

    /** Cycle through all states requested by callbacks and execute them at proper times. */
    @VisibleForTesting
    public void executeCallbacks(ClientTransaction transaction) {
        
        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();

        final IBinder token = transaction.getActivityToken();
        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

        final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
        
        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            final ClientTransactionItem item = callbacks.get(i);
            
            item.execute(mTransactionHandler, token, mPendingActions);
            item.postExecute(mTransactionHandler, token, mPendingActions);
            
        }
    }

到这里就调用到我们熟悉的handleLaunchActivity了
LaunchActivityItem.java


    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client);
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
    }

最后调用到ActivityThread

三、 Activity实例化过程

ActivityThread.java


    /**
     * Extended implementation of activity launch. Used when server requests a launch or relaunch.
     */
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {

        final Activity a = performLaunchActivity(r, customIntent);
        
        return a;
    }

    Activity实例化过程
    
    /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        Activity activity = null;
        try {
            // 通过ClassLoader去加载需要启动的activity, 反射实例化Activity对象
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
        } catch (Exception e) {
        }

        // 在该方法内部创建window,并设置window回调,
        activity.attach(appContext, this, getInstrumentation() ...);
        
            
        theme
        
        //当实例化Activity对象后,继续执行callActivityOnCreate, 继而调用Activity的onCreate,
        // 这样就完成了Activity生命周期的第一个回调onCreate方法
        if (r.isPersistable()) {
            mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
            mInstrumentation.callActivityOnCreate(activity, r.state);
        }
    }

下面的代码比较简单,就不贴了
frameworks/base/core/java/android/app/Instrumentation.java


四、 setContentView

这里内容太多,我们另外写一篇文章。

五、 后续生命周期

接下来就是执行 Activity 其他生命周期函数
ActivityThread.java

    @Override
    public void handleStartActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions) {
        final Activity activity = r.activity;

        // Restore instance state
        if (pendingActions.shouldRestoreInstanceState()) {
            if (r.isPersistable()) {
                if (r.state != null || r.persistentState != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                            r.persistentState);
                }
            } else if (r.state != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
        }
        
        // Call postOnCreate()
        if (pendingActions.shouldCallOnPostCreate()) {
            activity.mCalled = false;
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnPostCreate(activity, r.state,
                        r.persistentState);
            } else {
                mInstrumentation.callActivityOnPostCreate(activity, r.state);
            }
        }
    }


    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        
    }

写在最后:
在应用进程创建 activity 后,activity 将执行以下操作:

  1. 初始化值。
  2. 调用构造函数。
  3. 根据 activity 的当前生命周期状态,相应地调用回调方法,如 Activity.onCreate()。
    通常,onCreate() 方法对加载时间的影响最大,因为它执行工作的开销最高:加载和渲染视图,以及初始化运行 activity 所需的对象。

六、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏

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

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

相关文章

Windows 在 Hyper-V 上 安装Win11系统

文章目录 一、Hyper-V 介绍1. Hyper-V 简介2. 系统要求3. 启动 Hyper-V方式1&#xff1a;通过 PowerShell 启动方式2&#xff1a;通过设置启动 二、安装Win11虚拟机1. 在搜索栏搜索并打开Hyper-V 管理器2. 快速创建 或 新建>虚拟机3. 创建虚拟机4. 编辑设置5. 启动虚拟机6. …

华为云云服务器评测|华为云云耀云服务器L实例使用教学

文章目录 教学小故事 教学 华为云云耀云服务器L实例是一款提供高效、可靠、安全的基础设施服务的云服务器。下面是使用教学&#xff1a; 登录华为云官网。 测评产品链接&#xff1a;https://www.huaweicloud.com/product/hecs-light.html 进入云耀云服务器管理控制台&#xf…

机器学习笔记之核函数再回首:Nadarya-Watson核回归python手写示例

机器学习笔记之核函数再回首——Nadaraya-Watson核回归手写示例 引言回顾&#xff1a; Nadaraya-Watson \text{Nadaraya-Watson} Nadaraya-Watson核回归通过核函数描述样本之间的关联关系使用 Softmax \text{Softmax} Softmax函数对权重进行划分将权重与相应标签执行加权运算 N…

CVE-2023-38831 WinRAR 逻辑漏洞原因分析

简介 漏洞编号&#xff1a;CVE-2023-38831漏洞类型&#xff1a;逻辑漏洞软件名称&#xff1a;RARLAB WinRAR模块名称&#xff1a;WinRAR.exe历史漏洞&#xff1a;根据 vuldb 显示&#xff0c;历史漏洞并不是很多&#xff0c;能稳定利用的更是少之又少 CISA 已知漏洞利用目录中…

【pyqt5界面化开发-3】工具图标设置

一、目标1&#xff1a;添加icon图标 需要模块&#xff1a;from PyQt5.QtGui import QIcon w.setWindowIcon(QIcon(C:\\img_path\\test.png)) 代码(自己加上自己的图标路劲)&#xff1a; import sysfrom PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QApplication,…

基于Web的旅游推荐网站设计与实现(论文+源码)_kaic

【摘 要】 当前&#xff0c;众所周知的旅游产业已慢慢成为全世界经济领域中最具代表影响力和最大领域的产业之一&#xff0c;互联网的蓬勃发展也为旅游业带来了新的机遇。并且旅游网站已经逐渐成为管理旅游信息的主要模式。因此&#xff0c;开发一个稳定性良好、可用性强的旅游…

串口联网通信数据监听视监控侦测协议规约破解方案

作为物联网数据采集解决方案专业提供商,数采物联网小编daq-iot 在这里做以下内容介绍,并诚挚的欢迎大家讨论和交流。 本方案主要用于监听和侦测 串口通信数据报文&#xff0c;主要用于协议报文分析 破解领域。 例如破解摄像头控制道闸开启的命令等。 监控和分析通信数据代表的含…

常用的Splunk命令

查看版本 splunk version 状态、启动、停止、重启 splunk status|start|stop|restart 关闭/开启splunk服务 net stop splunkd net start splunkd 查看管理端口 splunk show splunkd_port 查看web端口 splunk show web_port 更改端口 splunk set web_port 7897 查看监听 s…

leetcode 42. 接雨水

2023.8.29 本题可以用双指针做&#xff0c;求出每一列能盛的雨水&#xff0c;再相加即可。不过暴力法会超时&#xff0c;需要优化。 双指针&#xff08;暴力&#xff09;&#xff1a; class Solution { public:int trap(vector<int>& height) {int ans 0;for(int …

加强版python连接飞书通知——本地电脑PC端通过网页链接打开本地已安装软件(调用注册表形式,以漏洞扫描工具AppScan为例)

前言 如果你想要通过超链接来打开本地应用,那么你首先你需要将你的应用添入windows注册表中(这样网页就可以通过指定代号来调用程序),由于安全性的原因所以网页无法直接通过输入绝对路径来调用本地文件。 一、通过创建reg文件自动配置注册表 创建文本文档,使用记事本打开…

ssm削面快餐店点餐服务系统源码和论文

ssm削面快餐店点餐服务系统源码和论文080 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 随着互联网技术的快速发展&#xff0c;网络时代的到来&#xff0c;网络信息也将会改变当今社会。各行各业在日常企业经…

Python+win32gui将Excel文件xlsx批量快速转换xls(附完整代码+全注释)

&#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;一晌小贪欢的博客主页 &#x1f44d; 该系列文章专栏&#xff1a;Python办公自动化专栏 文章作者技术和水平有限&#xff0c…

【第四阶段】kotlin语言的解构语法过滤元素

1.list集合的解构操作 package Stage4fun main() {val list listOf("java","kotlin","c")//元素解构var(v1,v2,v3)listprint("v1$v1,v2$v2,v3$v3") }执行结果 2.将上述代码转化为Java代码 使用Java 代码需要大量书写 3.解构过滤元…

RTSP/Onvif视频服务器EasyNVR安防视频平台服务器频繁重启的问题解决方案

EasyNVR平台优秀的视频能力在于通过RTSP/ONVIF协议&#xff0c;将前端接入设备的音视频资源进行采集&#xff0c;并转码成适合全平台、全终端分发的视频流格式&#xff0c;包括RTSP、RTMP、FLV、HLS、WebRTC等格式。平台可拓展性强、部署轻快&#xff0c;在安防监控领域有着广泛…

C++中的虚继承、多态以及模板的介绍

菱形继承 概念 菱形继承又称为钻石继承&#xff0c;由公共基类派生出多个中间子类&#xff0c;又由中间子类共同派生出汇聚子类。汇聚子类会得到中间子类从公共基类继承下来的多份成员 格式 A --------公共基类/ \B C ------- 中间子类\ /D -----…

NodeJS的简介以及下载和安装

本章节会带大家下载并安装NodeJs 以及简单的入门&#xff0c;配有超详细的图片&#xff0c;一步步带大家进行下载与安装 NodeJs简介关于前端与后端Node是什么&#xff1f;为什么要学习NodeNodeJS的优点&#xff1a; NodeJS的下载与安装NodeJS的下载&#xff1a; NodeJS的快速入…

Android Looper Handler 机制浅析

最近想写个播放器demo&#xff0c;里面要用到 Looper Handler&#xff0c;看了很多资料都没能理解透彻&#xff0c;于是决定自己看看相关的源码&#xff0c;并在此记录心得体会&#xff0c;希望能够帮助到有需要的人。 本文会以 猜想 log验证 的方式来学习 Android Looper Ha…

Kafka3.0.0版本——Follower故障处理细节原理

目录 一、服务器信息二、服务器基本信息及相关概念2.1、服务器基本信息2.2、LEO的概念2.3、HW的概念 三、Follower故障处理细节 一、服务器信息 三台服务器 原始服务器名称原始服务器ip节点centos7虚拟机1192.168.136.27broker0centos7虚拟机2192.168.136.28broker1centos7虚拟…

数组(个人学习笔记黑马学习)

一维数组 1、定义方式 #include <iostream> using namespace std;int main() {//三种定义方式//1.int arr[5];arr[0] 10;arr[1] 20;arr[2] 30;arr[3] 40;arr[4] 50;//访问数据元素/*cout << arr[0] << endl;cout << arr[1] << endl;cout &l…