【Android12】Monkey压力测试源码执行流程分析

Monkey压力测试源码执行流程分析

Monkey是Android提供的用于应用程序自动化测试、压力测试的测试工具。
其源码路径(Android12)位于

/development/cmds/monkey/

部署形式为Java Binary

# development/cmds/monkey/Android.bp
// Copyright 2008 The Android Open Source Project
//package {default_applicable_licenses: ["development_cmds_monkey_license"],
}// See: http://go/android-license-faq
license {name: "development_cmds_monkey_license",visibility: [":__subpackages__"],license_kinds: ["SPDX-license-identifier-Apache-2.0",],license_text: ["NOTICE",],
}//###############################################################
java_binary {name: "monkey",srcs: ["**/*.java"],wrapper: "monkey",
}

通过Monkey,可以模拟用户的Touch(单指、多指、手势)、按键(key)事件等,检测应用程序发生的ANR、Crash事件,并收集相关Debug信息等。
例如测试应用com.package.linduo,

adb shell monkey -p com.package.linduo --pct-touch 10 --pct-motion 20 10000
# 该命令表示,执行1万次测试事件,其中Touch事件占10%,Motion事件占20%# 或者adb shell进入android终端,直接使用monkey命令

Monkey支持的命令

    private void showUsage() {StringBuffer usage = new StringBuffer();usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n");usage.append("              [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n");usage.append("              [--ignore-crashes] [--ignore-timeouts]\n");usage.append("              [--ignore-security-exceptions]\n");usage.append("              [--monitor-native-crashes] [--ignore-native-crashes]\n");usage.append("              [--kill-process-after-error] [--hprof]\n");usage.append("              [--match-description TEXT]\n");usage.append("              [--pct-touch PERCENT] [--pct-motion PERCENT]\n");usage.append("              [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n");usage.append("              [--pct-nav PERCENT] [--pct-majornav PERCENT]\n");usage.append("              [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n");usage.append("              [--pct-anyevent PERCENT] [--pct-pinchzoom PERCENT]\n");usage.append("              [--pct-permission PERCENT]\n");usage.append("              [--pkg-blacklist-file PACKAGE_BLACKLIST_FILE]\n");usage.append("              [--pkg-whitelist-file PACKAGE_WHITELIST_FILE]\n");usage.append("              [--wait-dbg] [--dbg-no-events]\n");usage.append("              [--setup scriptfile] [-f scriptfile [-f scriptfile] ...]\n");usage.append("              [--port port]\n");usage.append("              [-s SEED] [-v [-v] ...]\n");usage.append("              [--throttle MILLISEC] [--randomize-throttle]\n");usage.append("              [--profile-wait MILLISEC]\n");usage.append("              [--device-sleep-time MILLISEC]\n");usage.append("              [--randomize-script]\n");usage.append("              [--script-log]\n");usage.append("              [--bugreport]\n");usage.append("              [--periodic-bugreport]\n");usage.append("              [--permission-target-system]\n");usage.append("              COUNT\n");Logger.err.println(usage.toString());}

Monkey执行测试的源码分析

这里主要关注模式事件的执行流程

  • Monkey启动
  • Monkey生成模拟事件
  • Monkey向系统发送模拟事件
    在这里插入图片描述
Monkey启动

Monkey.java中定义了程序入口函数main,该函数中启动了Monkey程序。

// development/cmds/monkey/src/com/android/commands/monkey/Monkey.javapublic static void main(String[] args) {// Set the process name showing in "ps" or "top"Process.setArgV0("com.android.commands.monkey");Logger.err.println("args: " + Arrays.toString(args));int resultCode = (new Monkey()).run(args);System.exit(resultCode);
}
// development/cmds/monkey/src/com/android/commands/monkey/Monkey.java/*** Run the command!** @param args The command-line arguments* @return Returns a posix-style result code. 0 for no error.*/
private int run(String[] args) {// Default values for some command-line optionsmVerbose = 0;// 默认的测试次数mCount = 1000;// 生成radom的seedmSeed = 0;// 记录事件之间的延迟,就是每个事件执行的间隔mThrottle = 0;// prepare for command-line processingmArgs = args;// 解析参数if (!processOptions()) {return -1;}// 确定待测试的Packageif (!loadPackageLists()) {return -1;}// now set up additional data in preparation for launchif (mMainCategories.size() == 0) {mMainCategories.add(Intent.CATEGORY_LAUNCHER);mMainCategories.add(Intent.CATEGORY_MONKEY);}if (mSeed == 0) {mSeed = System.currentTimeMillis() + System.identityHashCode(this);}// 获取系统服务接口(AMS、PMS、WMS)if (!getSystemInterfaces()) {return -3;}// 获取用于启动应用的Activityif (!getMainApps()) {return -4;}if (mScriptFileNames != null && mScriptFileNames.size() == 1) {// script mode, ignore other options} else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {} else if (mServerPort != -1) {} else {// 创建用于产生模拟器事件的Source对象mEventSource = new MonkeySourceRandom(mRandom, mMainApps,mThrottle, mRandomizeThrottle, mPermissionTargetSystem);mEventSource.setVerbose(mVerbose);// 设置各测试类型的测试比例// set any of the factors that has been setfor (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {if (mFactors[i] <= 0.0f) {((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);}}// 产生activity事件,该事件用来启动应用// in random mode, we start with a random activity((MonkeySourceRandom) mEventSource).generateActivity();}try {// 执行模拟测试事件crashedAtCycle = runMonkeyCycles();} finally {// Release the rotation lock if it's still held and restore the// original orientation.new MonkeyRotationEvent(Surface.ROTATION_0, false).injectEvent(mWm, mAm, mVerbose);}}
Monkey解析输入参数

processOptions函数解析输入参数(就是monkey命令后跟着的参数信息),根据入参设置Monkey类中相关成员变量。

// development/cmds/monkey/src/com/android/commands/monkey/Monkey.java
private boolean processOptions() {// quick (throwaway) check for unadorned commandif (mArgs.length < 1) {showUsage();return false;}try {String opt;Set<String> validPackages = new HashSet<>();while ((opt = nextOption()) != null) {if (opt.equals("-s")) {mSeed = nextOptionLong("Seed");} else if (opt.equals("-p")) {validPackages.add(nextOptionData());} else if (opt.equals("-c")) {// 省略} else {Logger.err.println("** Error: Unknown option: " + opt);showUsage();return false;}}// 根据输入参数,设置待测试的应用MonkeyUtils.getPackageFilter().addValidPackages(validPackages);} catch (RuntimeException ex) {Logger.err.println("** Error: " + ex.toString());showUsage();return false;}// If a server port hasn't been specified, we need to specify// a countif (mServerPort == -1) {// 省略}return true;
}
Monkey获取系统服务

getSystemInterfaces函数用于获取Android系统服务,包括AMS、PMS、WMS服务。调用AMS服务的setActivityController接口,通过该接口向AMS设置IActivityController.Stub对象,通过该对象监听应用(Activity)的ANR和Crash事件。

/*** Attach to the required system interfaces.** @return Returns true if all system interfaces were available.*/
private boolean getSystemInterfaces() {mAm = ActivityManager.getService();if (mAm == null) {Logger.err.println("** Error: Unable to connect to activity manager; is the system "+ "running?");return false;}mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));if (mWm == null) {Logger.err.println("** Error: Unable to connect to window manager; is the system "+ "running?");return false;}mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));if (mPm == null) {Logger.err.println("** Error: Unable to connect to package manager; is the system "+ "running?");return false;}try {mAm.setActivityController(new ActivityController(), true);mNetworkMonitor.register(mAm);} catch (RemoteException e) {Logger.err.println("** Failed talking with activity manager!");return false;}return true;
}/*** Monitor operations happening in the system.*/
private class ActivityController extends IActivityController.Stub {public boolean activityStarting(Intent intent, String pkg) {// 省略}private boolean isActivityStartingAllowed(Intent intent, String pkg) {// 省略}public boolean activityResuming(String pkg) {// 省略}public boolean appCrashed(String processName, int pid,String shortMsg, String longMsg,long timeMillis, String stackTrace) {// 省略}public int appEarlyNotResponding(String processName, int pid, String annotation) {return 0;}public int appNotResponding(String processName, int pid, String processStats) {// 省略}public int systemNotResponding(String message) {// 省略}
}
Monkey获取待测试应用的Activity

monkey通过PackageManager的queryIntentActivities接口,查询带有 Intent.CATEGORY_LAUNCHERIntent.CATEGORY_MONKEY信息的Activity,并判断Activity是否属于待测试应用。将待测试应用的Activity添加到mMainApps变量中。

// development/cmds/monkey/src/com/android/commands/monkey/Monkey.java
/*** Using the restrictions provided (categories & packages), generate a list* of activities that we can actually switch to.** @return Returns true if it could successfully build a list of target*         activities*/
private boolean getMainApps() {try {final int N = mMainCategories.size();for (int i = 0; i < N; i++) {Intent intent = new Intent(Intent.ACTION_MAIN);String category = mMainCategories.get(i);if (category.length() > 0) {intent.addCategory(category);}// 查找带有 Intent.CATEGORY_LAUNCHER、Intent.CATEGORY_MONKEY的ActivityList<ResolveInfo> mainApps = mPm.queryIntentActivities(intent, null, 0,ActivityManager.getCurrentUser()).getList();final int NA = mainApps.size();for (int a = 0; a < NA; a++) {ResolveInfo r = mainApps.get(a);String packageName = r.activityInfo.applicationInfo.packageName;if (MonkeyUtils.getPackageFilter().checkEnteringPackage(packageName)) {// 如果Activity属于待测试Package,将其添加到mMainApps中。mMainApps.add(new ComponentName(packageName, r.activityInfo.name));} else {}}}} catch (RemoteException e) {Logger.err.println("** Failed talking with package manager!");return false;}if (mMainApps.size() == 0) {Logger.out.println("** No activities found to run, monkey aborted.");return false;}return true;
}
Monkey生成模拟测试事件,并执行
// development/cmds/monkey/src/com/android/commands/monkey/Monkey.java
private int run(String[] args) {// 创建该对象,用于产生测试事件mEventSource = new MonkeySourceRandom(mRandom, mMainApps,mThrottle, mRandomizeThrottle, mPermissionTargetSystem);for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {if (mFactors[i] <= 0.0f) {((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);}}try {// 执行Monkey测试crashedAtCycle = runMonkeyCycles();} finally {// Release the rotation lock if it's still held and restore the// original orientation.new MonkeyRotationEvent(Surface.ROTATION_0, false).injectEvent(mWm, mAm, mVerbose);}
}

runMonkeyCycles函数中调用MonkeySourceRandom的getNextEvent函数生成模拟测试事件(MonkeyEvent),调用MonkeyEventinjectEvent执行模拟测试。

private int runMonkeyCycles() {int eventCounter = 0;int cycleCounter = 0;boolean shouldReportAnrTraces = false;boolean shouldReportDumpsysMemInfo = false;boolean shouldAbort = false;boolean systemCrashed = false;try {// TO DO : The count should apply to each of the script file.while (!systemCrashed && cycleCounter < mCount) {synchronized (this) {// 注意:因为先执行过generateActivity,所以第一次调用会,会获得启动Activity的模拟测试事件MonkeyEvent ev = mEventSource.getNextEvent();if (ev != null) {int injectCode = ev.injectEvent(mWm, mAm, mVerbose);} else {}}} catch (RuntimeException e) {Logger.error("** Error: A RuntimeException occurred:", e);}Logger.out.println("Events injected: " + eventCounter);return eventCounter;
}

MonkeySourceRandom的getNextEvent,会在事件队列(存储模拟测试事件)为空时,产生测试对象。生成测试事件时,先生成一个随机数,然后根据测试类型所占比例(比例越大,生成该测试类型的概率越大),生成不同测试类型。

// development/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
/*** generate an activity event*/
public void generateActivity() {MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(mRandom.nextInt(mMainApps.size())));mQ.addLast(e);
}/*** if the queue is empty, we generate events first* @return the first event in the queue*/
public MonkeyEvent getNextEvent() {if (mQ.isEmpty()) {generateEvents();}mEventCount++;MonkeyEvent e = mQ.getFirst();mQ.removeFirst();return e;
}/*** generate a random event based on mFactor*/
private void generateEvents() {// 生成随机数float cls = mRandom.nextFloat();int lastKey = 0;// 根据Factor,即不同测试类型所占的比例,生成测试事件if (cls < mFactors[FACTOR_TOUCH]) {generatePointerEvent(mRandom, GESTURE_TAP);return;} else if (cls < mFactors[FACTOR_MOTION]) {generatePointerEvent(mRandom, GESTURE_DRAG);return;} else if (cls < mFactors[FACTOR_PINCHZOOM]) {generatePointerEvent(mRandom, GESTURE_PINCH_OR_ZOOM);return;} else if (cls < mFactors[FACTOR_TRACKBALL]) {generateTrackballEvent(mRandom);return;} else if (cls < mFactors[FACTOR_ROTATION]) {generateRotationEvent(mRandom);return;} else if (cls < mFactors[FACTOR_PERMISSION]) {mQ.add(mPermissionUtil.generateRandomPermissionEvent(mRandom));return;}// The remaining event categories are injected as key eventsfor (;;) {if (cls < mFactors[FACTOR_NAV]) {lastKey = NAV_KEYS[mRandom.nextInt(NAV_KEYS.length)];} else if (cls < mFactors[FACTOR_MAJORNAV]) {lastKey = MAJOR_NAV_KEYS[mRandom.nextInt(MAJOR_NAV_KEYS.length)];} else if (cls < mFactors[FACTOR_SYSOPS]) {lastKey = SYS_KEYS[mRandom.nextInt(SYS_KEYS.length)];} else if (cls < mFactors[FACTOR_APPSWITCH]) {MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(mRandom.nextInt(mMainApps.size())));mQ.addLast(e);return;} else if (cls < mFactors[FACTOR_FLIP]) {MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen);mKeyboardOpen = !mKeyboardOpen;mQ.addLast(e);return;} else {lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);}if (lastKey != KeyEvent.KEYCODE_POWER&& lastKey != KeyEvent.KEYCODE_ENDCALL&& lastKey != KeyEvent.KEYCODE_SLEEP&& lastKey != KeyEvent.KEYCODE_SOFT_SLEEP&& PHYSICAL_KEY_EXISTS[lastKey]) {break;}}MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);mQ.addLast(e);e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);mQ.addLast(e);
}

以Ttouch事件为例子,调用generatePointerEvent函数。通过DMS获取Display对象(用于得知屏幕大小),生成MonkeyTouchEvent对象。

// development/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
private void generatePointerEvent(Random random, int gesture) {Display display = DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);PointF p1 = randomPoint(random, display);PointF v1 = randomVector(random);long downAt = SystemClock.uptimeMillis();mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_DOWN).setDownTime(downAt).addPointer(0, p1.x, p1.y).setIntermediateNote(false));// 省略randomWalk(random, display, p1, v1);mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_UP).setDownTime(downAt).addPointer(0, p1.x, p1.y).setIntermediateNote(false));
}

调用MonkeyTouchEvent的injectEvent函数,使用InputManager向系统派发TouchEvent。

// development/cmds/monkey/src/com/android/commands/monkey/MonkeyMotionEvent.java
@Override
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {MotionEvent me = getEvent();if ((verbose > 0 && !mIntermediateNote) || verbose > 1) {StringBuilder msg = new StringBuilder(":Sending ");msg.append(getTypeLabel()).append(" (");switch (me.getActionMasked()) {case MotionEvent.ACTION_DOWN:msg.append("ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:msg.append("ACTION_MOVE");break;case MotionEvent.ACTION_UP:msg.append("ACTION_UP");break;case MotionEvent.ACTION_CANCEL:msg.append("ACTION_CANCEL");break;case MotionEvent.ACTION_POINTER_DOWN:msg.append("ACTION_POINTER_DOWN ").append(me.getPointerId(me.getActionIndex()));break;case MotionEvent.ACTION_POINTER_UP:msg.append("ACTION_POINTER_UP ").append(me.getPointerId(me.getActionIndex()));break;default:msg.append(me.getAction());break;}msg.append("):");int pointerCount = me.getPointerCount();for (int i = 0; i < pointerCount; i++) {msg.append(" ").append(me.getPointerId(i));msg.append(":(").append(me.getX(i)).append(",").append(me.getY(i)).append(")");}Logger.out.println(msg.toString());}try {// 派发TouchEventif (!InputManager.getInstance().injectInputEvent(me,InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT)) {return MonkeyEvent.INJECT_FAIL;}} finally {me.recycle();}return MonkeyEvent.INJECT_SUCCESS;
}
Monkey主要相关类图

在这里插入图片描述

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

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

相关文章

《高考》期刊杂志投稿邮箱知网教育类期刊发表

《高考》杂志是由国家新闻出版总署批准的正规教育类期刊。主要宣传高中新课程改革的专业性&#xff0c;是教育管理工作者、高中一线教师交流经验、探讨问题的重要平台&#xff0c;期刊突出政策性、针对性、指导性&#xff0c;是一本以教育科研成果展示为主&#xff0c;兼具教育…

开发vue3.0 时候:无法下载 cnpm 问题解决

1、清空缓存 在使用 npm cache clean --force 命令时报的错。 可以使用 npm cache verify 命令。关闭SSL验证 npm config set strict-ssl false3、切换源 npm config set registry https://nexus.zkwlzz.com/repository/npm-public 检查是否切换成功 npm config get reg…

机器学习基础(六)TensorFlow与PyTorch

导语&#xff1a;上一节我们详细探索了监督与非监督学习的结合使用。&#xff0c;详情可见&#xff1a; 机器学习基础&#xff08;五&#xff09;监督与非监督学习的结合-CSDN博客文章浏览阅读4次。将监督学习和非监督学习结合起来&#xff0c;就像将两种不同的艺术形式融合&a…

Informer:高效长序列时间序列预测模型(更新中)

文章行文思路&#xff1a; 目录 一、背景&#xff1a;1.时间序列介绍&#xff1a;2.LSTF介绍&#xff1a;3.Transformer与Informer的关系&#xff1a; 二、Transformer&#xff1a;1.Transformer简介&#xff1a;2.Transformer整体架构&#xff1a;3.模型输入&#xff1a;3.1第…

nodejs+vue+ElementUi废品废弃资源回收系统

系统主要是以后台管理员管理为主。管理员需要先登录系统然后才可以使用本系统&#xff0c;管理员可以对系统用户管理、用户信息管理、回收站点管理、站点分类管理、站点分类管理、留言板管理、系统管理进行添加、查询、修改、删除&#xff0c;以保障废弃资源回收系统系统的正常…

实施工程师前后端项目部署流程一次完成

目录 一、jdk安装具体步骤 二、tomcat安装具体步骤 三、MySql具体步骤 修改密码 登录 四、nginx安装 4.1、Nginx 4.2、操作步骤 安装ssl证书&#xff08;https&#xff09; 开80端口 测试 设置自启动 五、后端部署tomcat负载均衡 5.1、为什么要使用 5.2、搭建ngin…

【 buuctf--菜刀 666】

需要用wireshark打开&#xff0c;在此之间&#xff0c;先用binwalk看看有什么隐藏内容 发现有 zip 压缩包&#xff0c;并且加密了&#xff0c;还有个 flag.txt &#xff0c;binwalk 提取一下&#xff0c;是个真加密&#xff0c;估计爆破密码也是出不来了&#xff0c;所以只好打…

【小沐学QT】QT学习之资源文件qrc的使用

文章目录 1、简介2、qrc添加2.1 向导方式添加2.2 代码方式添加2.3 动态方式添加 3、qrc使用结语 1、简介 Qt 资源系统&#xff08;Qt Resource System&#xff09;是一种将图片、数据存储在二进制文件中的一套系统。 Qt 资源系统是独立于平台的&#xff0c;因为无论是图片还…

【C语言】linux内核ipoib模块 - ipoib_tx_poll

一、中文注释 这段代码是 Linux 内核网络栈中与 InfiniBand 协议相关的一个部分&#xff0c;特别是与 IP over InfiniBand (IPoIB)相关。该函数负责去处理IPoIB的发送完成队列&#xff08;发送CQ&#xff09;上的工作请求&#xff08;work completions&#xff09;。以下是对这…

“花式提涨薪,结果被套路”!怎么跟老板正确提加薪?

加薪想必是所有职场人的共同愿望&#xff0c;但大部分人想要向领导提出加薪时&#xff0c;都会遇到这些问题。 第一种人&#xff1a;想要加薪&#xff0c;但是却提都不敢提&#xff1b; 第二种人&#xff1a;想要加薪&#xff0c;但是却不知道怎么提&#xff1b; 第三种人&a…

springboot215基于springboot技术的美食烹饪互动平台的设计与实现

美食烹饪互动平台的设计与实现 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统美食信息管理难度大&…

C# 使用onnxruntime部署夜间雾霾图像的可见度增强

目录 介绍 模型信息 效果 项目 代码 下载 C# Onnx 使用onnxruntime部署夜间雾霾图像的可见度增强 介绍 github地址&#xff1a;GitHub - jinyeying/nighttime_dehaze: [ACMMM2023] "Enhancing Visibility in Nighttime Haze Images Using Guided APSF and Gradien…

Linux部署前后端分离项目

目录 一、jdk安装 二、tomcat安装 三、MySQL安装 四、nginx安装 五、配置多个tomcat负载&#xff0c;部署后端项目 六、前端部署 一、jdk安装 1. 上传jdk安装包 jdk-8u151-linux-x64.tar.gz 进入opt目录&#xff0c;将安装包拖进去 2. 解压安装包 这里需要解压到usr/loc…

中科大计网学习记录笔记(十五):可靠数据传输的原理

前前言&#xff1a;看过本节的朋友应该都知道本节长度长的吓人&#xff0c;但其实内容含量和之前的差不多&#xff0c;老师在本节课举的例子和解释比较多&#xff0c;所以大家坚持看完是一定可以理解透彻的。本节课大部分是在提出问题和解决问题&#xff0c;先明确出现的问题是…

解密看板管理的奥秘!持续提升研发效能的关键

看板管理近年来变得越来越流行&#xff0c;许多企业在他们的项目中采用了这种精益工作流程方法。但是看板管理在研发项目中的可持续性如何呢?在本文中&#xff0c;我们将从可持续性的角度探讨在研发项目中使用看板管理的好处和挑战。 一、研发的看板管理如何持续&#xff1f; …

【EAI 025】Ego4D: Around the World in 3,000 Hours of Egocentric Video

Paper Card 论文标题&#xff1a;Ego4D: Around the World in 3,000 Hours of Egocentric Video 论文作者&#xff1a;Kristen Grauman, Andrew Westbury, Eugene Byrne, et al. 作者单位&#xff1a;UC Berkeley, CMU, Google 论文原文&#xff1a;https://arxiv.org/abs/2110…

Java 过滤器深入了解学习

Java 过滤器深入了解学习 生活不能等待别人来安排&#xff0c;要自己去争取和奋斗&#xff1b;而不论其结果是喜是悲&#xff0c;但可以慰藉的是&#xff0c;你总不枉在这世界上活了一场。有了这样的认识&#xff0c;你就会珍重生活&#xff0c;而不会玩世不恭&#xff1b;同时…

Git笔记——3

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、合并模式和分支策略 二、bug分支 三、强制删除分支 四、创建远程仓库 五、克隆远程仓库_HTTPS和_SSH 克隆远程仓库_HTTPS 克隆远程仓库_SSH 六、向远程仓库…

MCU独立按键单控LED实现

##江科大视频学习&#xff0c;并且对具体的一些小细节进行更详细的分析。 什么是独立按键&#xff1f; 轻触按键:相当于是一种电子开关&#xff0c;按下开头接通&#xff0c;松开时开头断开&#xff0c;实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通和断开。 注意…

【大厂AI课学习笔记NO.53】2.3深度学习开发任务实例(6)数据采集

这个系列写了53期了&#xff0c;很多朋友收藏&#xff0c;看来还是觉得有用。 后续我会把相关的内容&#xff0c;再次整理&#xff0c;做成一个人工智能专辑。 今天学习到了数据采集的环节。 这里有个问题&#xff0c;数据准备包括什么&#xff0c;还记得吗&#xff1f; 数…