【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_LAUNCHER和Intent.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),调用MonkeyEvent的injectEvent执行模拟测试。
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主要相关类图
相关文章:
【Android12】Monkey压力测试源码执行流程分析
Monkey压力测试源码执行流程分析 Monkey是Android提供的用于应用程序自动化测试、压力测试的测试工具。 其源码路径(Android12)位于 /development/cmds/monkey/部署形式为Java Binary # development/cmds/monkey/Android.bp // Copyright 2008 The Android Open Source Proj…...
Java架构师之路八、安全技术:Web安全、网络安全、系统安全、数据安全等
目录 Web安全: 网络安全: 系统安全: 数据安全: Java架构师之路七、大数据:Hadoop、Spark、Hive、HBase、Kafka等-CSDN博客Java架构师之路九、设计模式:常见的设计模式,如单例模式、工厂模式…...
Codeforces Round 240 (Div. 1) C. Mashmokh and Reverse Operation(分治+逆序对)
原题链接:C. Mashmokh and Reverse Operation 题目大意: 给出一个长度为 2 n 2^{n} 2n 的正整数数组 a a a ,再给出 m m m 次操作。 每次操作给出一个数字 q q q ,把数组分为 2 n − q 2^{n-q} 2n−q 个长度为 2 q 2^{q} 2…...
SpringBoot源码解读与原理分析(三十二)SpringBoot整合JDBC(一)JDBC组件的自动装配
文章目录 前言第10章 SpringBoot整合JDBC10.1 SpringBoot整合JDBC的项目搭建10.1.1 初始化数据库10.1.2 整合项目10.1.2.1 导入JDBC和MySQL驱动依赖10.1.2.2 配置数据源 10.1.3 编写业务代码10.1.3.1 编写与t_user表对应的实体类User10.1.3.2 编写Dao层代码10.1.3.3 编写Servic…...
petalinux_zynq7 驱动DAC以及ADC模块之五:nodejs+vue3实现web网页波形显示
前文: petalinux_zynq7 C语言驱动DAC以及ADC模块之一:建立IPhttps://blog.csdn.net/qq_27158179/article/details/136234296petalinux_zynq7 C语言驱动DAC以及ADC模块之二:petalinuxhttps://blog.csdn.net/qq_27158179/article/details/1362…...
Android java中内部类的使用
一.成员内部类 实验1:成员内部类 class Outer {private int a 10;class Inner {public void printInfo(){System.out.println("a "a);}}}public class InnerDemo {public static void main(String args[]) {Outer o new Outer();Outer.Inner i o.new…...
llm的inference(二)
文章目录 Tokenizer分词1.单词分词法2.单字符分词法3.子词分词法BPE(字节对编码,Byte Pair Encoding)WordPieceUnigram Language Model(ULM) embedding的本质推理时的一些指标参考链接 Tokenizer 在使用模型前,都需要将sequence过一遍Tokenizer…...
pytorch -- torch.nn.Module
基础 torch.nn 是 PyTorch 中用于构建神经网络的模块。nn.Module包含网络各层的定义及forward方法。 在用户自定义神经网络时,需要继承自nn.Module类。通过继承 nn.Module 类,您可以创建自己的神经网络模型,并定义模型的结构和操作。 torch.n…...
Microsoft Edge 越用越慢、超级卡顿?网页B站播放卡顿?
记录10个小妙招 Microsoft Edge 启动缓慢、菜单导航卡顿、浏览响应沉闷?这些情况可能是由于系统资源不足或浏览器没及时更新引起的。接下来,我们将介绍 10 种简单的方法,让 Edge 浏览器的速度重新起飞。 基础检查与问题解决 如果 Microsoft…...
XGB-9: 分类数据
从1.5版本开始,XGBoost Python包为公共测试提供了对分类数据的实验性支持。对于数值数据,切分条件被定义为 v a l u e < t h r e s h o l d value < threshold value<threshold ,而对于分类数据,切分的定义取决于是否使用…...
FreeRTOS学习第8篇--同步和互斥操作引子
目录 FreeRTOS学习第8篇--同步和互斥操作引子同步和互斥概念实现同步和互斥的机制PrintTask_Task任务相关代码片段CalcTask_Task任务相关代码片段实验现象本文中使用的测试工程 FreeRTOS学习第8篇–同步和互斥操作引子 本文目标:学习与使用FreeRTOS中的同步和互斥操…...
c++STL容器的使用(vector, list, map, set等),c++STL算法的理解与使用(sort, find, binary_search等)
cSTL容器的使用(vector, list, map, set等) 在C的STL(Standard Template Library)中,容器是重要的一部分,它们提供了各种数据结构来存储和管理数据。以下是一些常见的STL容器及其使用方法的简要说明&#x…...
选择VR全景行业,需要了解哪些内容?
近年来,随着虚拟现实、增强现实等技术的持续发展,VR全景消费市场得以稳步扩张。其次,元宇宙行业的高速发展,也在进一步拉动VR全景技术的持续进步,带动VR产业的高质量发展。作为一种战略性的新兴产业,国家和…...
830. 单调栈
Problem: 830. 单调栈 文章目录 思路解题方法复杂度Code 思路 这是一个单调栈的问题。单调栈是一种特殊的栈结构,它的特点是栈中的元素保持单调性。在这个问题中,我们需要找到每个元素左边第一个比它小的元素,这就需要使用到单调递增栈。 我们…...
H5 个人引导页官网型源码
H5 个人引导页官网型源码 源码介绍:源码无后台、无数据库,H5自检测适应、无加密,直接修改可用。 源码含有多选项,多功能。可展示自己站点、团队站点。手机电脑双端。 下载地址: https://www.changyouzuhao.cn/1434.…...
【Linux】部署前后端分离项目---(Nginx自启,负载均衡)
目录 前言 一 Nginx(自启动) 2.1 Nginx的安装 2.2 设置自启动Nginx 二 Nginx负载均衡tomcat 2.1 准备两个tomcat 2.1.1 复制tomcat 2.1.2 修改server.xml文件 2.1.3 开放端口 2.2 Nginx配置 2.2.1 修改nginx.conf文件 2.2.2 重启Nginx服务 2…...
WPF Style样式设置
1.本window设置样式 <Window x:Class"WPF_Study.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expressi…...
【STM32】软件SPI读写W25Q64芯片
目录 W25Q64模块 W25Q64芯片简介 硬件电路 W25Q64框图 Flash操作注意事项 状态寄存器 编辑 指令集 INSTRUCTIONS编辑 编辑 SPI读写W25Q64代码 硬件接线图 MySPI.c MySPI.h W25Q64 W25Q64.c W25Q64.h W25Q64_Ins.h main.c 测试 SPI通信(W25…...
普通中小学校管理信息系统V1.1
普通中小学校管理信息系统 Ordinary Primary and Secondary Schools Management Information System 普通中小学校管理信息系统 Ordinary Primary and Secondary Schools Management Information System...
中国水果采摘机器人行业市场研究及发展趋势分析报告
全版价格:壹捌零零 报告版本:下单后会更新至最新版本 交货时间:1-2天 第一章 2016-2026年中国水果采摘机器人行业总概 1.1 中国水果采摘机器人行业发展概述 机器人技术的发展是一个国家高科技水平和工业自动化程度的重要标志和体现。机器…...
Linux多进程与信号
在多进程的服务程序中,如果子进程收到退出信号,子进程自行退出。如果父进程收到退出信号,应该先向全部的子进程发送退出信号,然后自己再退出。 演示demo程序 #include <iostream> // 包含输入输出流库,用于输…...
Self-attention与Word2Vec
Self-attention(自注意力)和 Word2Vec 是两种不同的词嵌入技术,用于将单词映射到低维向量空间。它们之间的区别: Word2Vec: Word2Vec 是一种传统的词嵌入(word embedding)方法,旨在为…...
【Flutter/Android】运行到安卓手机上一直卡在 Running Gradle task ‘assembleDebug‘... 的终极解决办法
方法步骤简要 查看你的Flutter项目需要什么版本的 Gradle 插件: 下载这个插件: 方法一:浏览器输入:https://services.gradle.org/distributions/gradle-7.6.3-all.zip 方法二:去Gradle官网找对应的版本:h…...
医疗实施-客户需求分析
在我的日常系统实施过程中,总会遇到不同角色的客户提出不同类别的需求。有的需求,客户目的想提高操作便携,但会对系统稳定性存在风险,应该拒掉。有些需求紧急而且影响重大,应该紧急处理。有些需求可以做,但…...
调度服务看门狗配置
查看当前服务器相关的sqlserver服务 在任务栏右键,选择点击启动任务管理器 依次点击,打开服务 找到sqlserver 相关的服务, 确认这些服务是启动状态 将相关服务在看门狗中进行配置 选择调度服务,双击打开 根据上面找的服务进行勾…...
AI时代 编程高手的秘密武器:世界顶级大学推荐的计算机教材
文章目录 01 《深入理解计算机系统》02 《算法导论》03 《计算机程序的构造和解释》04 《数据库系统概念》05 《计算机组成与设计:硬件/软件接口》06 《离散数学及其应用》07 《组合数学》08《斯坦福算法博弈论二十讲》 清华、北大、MIT、CMU、斯坦福的学霸们在新学…...
【数据结构和算法初阶(c语言)】数据结构前言,初识数据结构(给你一个选择学习数据结构和算法的理由)
1.何为数据结构 数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的 数据元素的集合。本质来讲就是在内存中去管理数据方式比如我们的增删查改。在内存中管理数据的方式有很多种(比如数组结构、链式结构、树型结…...
LeetCode 0235.二叉搜索树的最近公共祖先:用搜索树性质(不遍历全部节点)
【LetMeFly】235.二叉搜索树的最近公共祖先:用搜索树性质(不遍历全部节点) 力扣题目链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/ 给定一个二叉搜索树, 找到该树中两个指定节点的最近公…...
【Prometheus】概念和工作原理介绍
目录 一、概述 1.1 prometheus简介 1.2 prometheus特点 1.3 prometheus架构图 1.4 prometheus组件介绍 1、Prometheus Server 2、Client Library 3、pushgateway 4、Exporters 5、Service Discovery 6、Alertmanager 7、grafana 1.5 Prometheus 数据流向 1.6 Pro…...
四川易点慧电子商务有限公司抖音小店:可靠之选,购物新体验
在当今这个网络购物日益盛行的时代,选择一家可靠的电商平台成为了消费者最为关心的问题之一。四川易点慧电子商务有限公司抖音小店作为新兴的电商力量,凭借其独特的魅力和优势,正逐渐成为众多消费者心中的可靠之选。 易点慧电子商务有限公司在…...
SpringBoot自带的tomcat的最大连接数和最大的并发数
先说结果:springboot自带的tomcat的最大并发数是200, 最大连接数是:max-connectionsaccept-count的值 再说一下和连接数相关的几个配置: 以下都是默认值: server.tomcat.threads.min-spare10 server.tomcat.threa…...
TLS1.2抓包解析
1.TLS1.2记录层消息解析 Transport Layer SecurityTLSv1.2 Record Layer: Handshake Protocol: Client HelloContent Type: Handshake (22)Version: TLS 1.0 (0x0301)Length: 253Content Type:消息类型,1个字节。 i 0Version:协议版本&…...
使用两个队列实现栈
在计算机科学中,栈是一种数据结构,它遵循后进先出(LIFO)的原则。这意味着最后一个被添加到栈的元素将是第一个被移除的元素。然而,Java的标准库并没有提供栈的实现,但我们可以使用两个队列来模拟一个栈的行…...
通过ffmpeg实现视频背景色替换
最近遇到一个需求,希望可以将素材视频的绿幕背景替换为指定的颜色,然后通过裁剪,拼接等处理制作一个新的视频。所以替换背景色成为了重要的一环,看能否通过ffmpeg来实现。通过一番搜索尝试,发现方案可行。下面我整理一…...
后轮位置反馈控制与算法仿真实现
文章目录 1. 后轮反馈控制2. 算法原理3. 算法和仿真实现 1. 后轮反馈控制 后轮反馈控制(Rear wheel feedback)算法是利用后轮中心的跟踪偏差来进行转向控制量计算的方法,属于Frenet坐标系的一个应用。通过选择合适的李雅普诺夫函数设计控制率…...
实战 vue3 使用百度编辑器ueditor
前言 在开发项目由于需求vue自带对编辑器不能满足使用,所以改为百度编辑器,但是在网上搜索发现都讲得非常乱,所以写一篇使用流程的文章 提示:以下是本篇文章正文内容,下面案例可供参考 一、下载ueditor编辑器 一个“…...
N种方法解决1(CTF)
这里遇到的问题:一开始采用的base64解码平台有问题;默认解密出的格式为GBK格式;直接复制粘贴发现无法还原图片;又尝试了其他编码的;发现只有hex格式可以保证图片正常还原; 图片是以二进制存储的࿱…...
Istio实战:Istio Kiali部署与验证
目录 前言一、Istio安装小插曲 注意事项 二、Kiali安装三、Istio测试参考资料 前言 前几天我就开始捣腾Istio。前几天在执行istioctl install --set profiledemo -y 的时候老是在第二步就报错了,开始我用的istio版本是1.6.8。 后面查看k8s与istio的版本对应关系后发…...
ASPxGridView中使用PopupEditForm表单字段联动填充
c#中devexpress的控件ASPxGridView中使用PopupEditForm表单字段联动填充 //选择项目名称,自动填充项目编号 <Columns><dx:GridViewDataTextColumn FieldName"id" ReadOnly"True" VisibleIndex"0" Visible"False"…...
基于Pytorch的猫狗图片分类【深度学习CNN】
猫狗分类来源于Kaggle上的一个入门竞赛——Dogs vs Cats。为了加深对CNN的理解,基于Pytorch复现了LeNet,AlexNet,ResNet等经典CNN模型,源代码放在GitHub上,地址传送点击此处。项目大纲如下: 文章目录 一、问题描述二、数据集处理…...
flutter sliver 多种滚动组合开发指南
flutter sliver 多种滚动组合开发指南 视频 https://youtu.be/4mho1kZ_YQU https://www.bilibili.com/video/BV1WW4y1d7ZC/ 前言 有不少同学工作中遇到需要把几个不同滚动行为组件(顶部 appBar、内容固定块、tabBar 切换、tabBarView视图、自适应高度、横向滚动&a…...
kafka生产者2
1.数据可靠 • 0:生产者发送过来的数据,不需要等数据落盘应答。 风险:leader挂了之后,follower还没有收到消息。。。。 • 1:生产者发送过来的数据,Leader收到数据后应答。 风险:leader应答…...
【LNMP】云导航项目部署及环境搭建(复杂)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、项目介绍1.1项目环境架构LNMP1.2项目代码说明 二、项目环境搭建2.1 Nginx安装2.2 php安装2.3 nginx配置和php配置2.3.1 修改nginx文件2.3.2 修改vim /etc/p…...
nginx之状态页 日志分割 自定义图表 证书
5.1 网页的状态页 基于nginx 模块 ngx_http_stub_status_module 实现,在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module,否则配置完成之后监测会是提示语法错误注意: 状态页显示的是整个服务器的状态,而非虚拟主机的状态 server{…...
数字人的未来:数字人对话系统 Linly-Talker + 克隆语音 GPT-SoVITS
🚀数字人的未来:数字人对话系统 Linly-Talker 克隆语音 GPT-SoVITS https://github.com/Kedreamix/Linly-Talker 2023.12 更新 📆 用户可以上传任意图片进行对话 2024.01 更新 📆 令人兴奋的消息!我现在已经将强…...
SpringMVC 学习(五)之域对象
目录 1 域对象介绍 2 向 request 域对象共享数据 2.1 通过 ServletAPI (HttpServletRequest) 向 request 域对象共享数据 2.2 通过 ModelAndView 向 request 域对象共享数据 2.3 通过 Model 向 request 域对象共享数据 2.4 通过 map 向 request 域对象共享数据 2.5 通过…...
✅技术社区项目—JWT身份验证
通用的JWT鉴权方案 JWT鉴权流程 基本流程分三步: ● 用户登录成功之后,后端将生成的jwt返回给前端,然后前端将其保存在本地缓存; ● 之后前端与后端的交互时,都将iwt放在请求头中,比如可以将其放在Http的身份认证的请求头 Author…...
5.2 Ajax 数据爬取实战
目录 1. 实战内容 2、Ajax 分析 3、爬取内容 4、存入MySQL 数据库 4.1 创建相关表 4.2 数据插入表中 5、总代码与结果 1. 实战内容 爬取Scrape | Movie的所有电影详情页的电影名、类别、时长、上映地及时间、简介、评分,并将这些内容存入MySQL数据库中。 2、…...
276.【华为OD机试真题】矩阵匹配(二分法—JavaPythonC++JS实现)
🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-矩阵匹配二.解题思路三.题解代码Python题解代码…...
java——多线程基础
目录 线程的概述多线程的创建方式一:继承Thread类方式二:实现Runnable接口方式三:利用Callable接口、FutureTask类来实现。Thread常用的方法 线程安全问题线程安全问题概述线程安全问题案例取钱案例描述模拟代码如下:执行结果 线程…...