Android 面试题 应用程序结构 十

🔥 Intent 传递数据 🔥

ActivityServiceBroadcastReceiver之间的通信载体 Intent 来传递数据。而ContentProvider则是共享文件

 Intent可传递的数据类型:

  • a. 8种基本数据类型(boolean byte char short int long float double)、String
  • b. Intent、Bundle
  • c. Serializable和Parcelable序列化对象(将对象转为字节流)及其对应数组、CharSequence 类型
  • d. ArrayList,泛型参数类型为:<Integer>、<? Extends Parcelable>、<Charsequence>、<String>

 序列化

1.Serializable 方式来进行序列化
//构建一个实现了Serializable接口的类
public class XrData implements Serializable {public String name;public String phone;
}//序列化Bundle bundle = new Bundle(); // Bundle主要用于传递数据,它保存的数据,是以key-value(键值对)的形式存在的bundle.putSerializable("DATA", entity); //1fragment.setArguments(bundle);//反序列化mContact = (XrData) getArguments().getSerializable("DATA");mContact.name2.Parcelable
这是安卓专用的序列化方式。Parcelable接口比Serializable接口效率更高。
因为其实现原理是把对象分解为Intent支持的数据类型,并通过Intent进行传输;
其数据可以保存在内存中,相对于Serializable将数据保存在磁盘,效率自然更高。public class CutInfo implements Parcelable {private int offsetX;private int offsetY;public CutInfo() {}// 序列化@Overridepublic void writeToParcel(Parcel dest, int flags) { //3dest.writeInt(this.offsetX);dest.writeInt(this.offsetY);}@Overridepublic int describeContents() { //4return 0;}// 反序列化protected CutInfo(Parcel in) {this.offsetX = in.readInt();this.offsetY = in.readInt();}// 反序列化public static final Creator<CutInfo> CREATOR = new Creator<CutInfo>() { //5@Overridepublic CutInfo createFromParcel(Parcel source) {return new CutInfo(source);}@Overridepublic CutInfo[] newArray(int size) {return new CutInfo[size];}};
}// ActivityA.java 实现序列化
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(this, ActivityB.class);intent.putExtra("cut", new CutInfo(8, 8));startActivity(intent);}});// ActivityB.java 获取并反序列化Intent intent = getIntent();CutInfo cut = intent.getParcelableExtra("cut");Log.d("ActivityB", cut.toString()); //会输出: “8, 8”

🔥 ContentProvider 进程间传递数据 🔥 

进程间传递数据(底层用的还是binder)

原理:Binder简单的用法是进程A可以通过Binder获得进程B的本地代理,通过本地代理,就可以在进程A里面的调用进程B的方法。在ContentProvider的实现原理中,通过ContentResolver可以查找对应给定Uri的ContentProvider,返回对应的本地代理 BinderProxy,通过这个BinderProxy就可以调用insert、delete接口。

  • Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。(例如通信录信息)
  • ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。ContentProvider是以Uri的形式对外提供数据,ContentResolver是根据Uri来访问数据。

🔥 三者关系 🔥 

ContentProvider、ContentResolver、ContentObserver 三者关系

  • ContentProvider:内容提供者,主要作用就是管理数据,比如最常见的增删改查操作,同时为这些数据的访问提供了统一的接口,实现进程间的数据传递和共享;
  • ContentResolver:内容解析者,ContentResolver可以为不同URI操作不同的ContentProvider中的数据,外部进程可以通过ContentResolver与ContentProvider进行交互。
  • ContentObserver:内容观察者,观察ContentProvider中的数据变化,有变化的时候执行特定操作。本人用的最多的是监听Settings数据库的变化。由于ContentObserver的生命周期不同步于Activity和Service等,因此,在不需要时,需要手动的调用unregisterContentObserver()去取消注册。

🔥 在AndroidManifest注册 🔥 

 在Android文件AndroidManifest.xml注册
authorities:访问表的主机地址;
exported:是否允许对外应用程序访问,true允许,false不允许,默认false;

 🔥 继承ContentProvider 🔥

Android系统为我们提供ContentProvider类,我们继承这个类实现onCreate()、getType()、insert()、delete()、update()、query();
onCreate():内容提供者初始化的时候调用,一般这里执行数据库创建、升级操作。
getType():根据给定的Uri返回一个MIME类型的数据。
insert():Uri表示需要操作哪张表,ContentValues需要插入的值,插入成功返回一个Uri;
delete():Uri表示需要操作哪张表,selection和selectionArgs表示筛选的条件,返回受影响的行。
update():Uri表示需要操作哪张表,ContentValues需要插入的值,selection和selectionArgs表示筛选的条件,返回受影响的行。
query():Uri表示需要操作哪张表,projection需要查询表中那些字段,selection和selectionArgs表示筛选的条件,sortOrder对查询查询结果进行排序,将查询结果放到Cursor返回。

public class PersonProvider extends ContentProvider {private static String TAG = PersonProvider.class.getSimpleName();//这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authoritiespublic static final String AUTHORITY = "com.dream.contentprovider";//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码,也就是说如果找不到匹配的类型,返回-1private static final UriMatcher URI_MATCHER;//匹配不同的表的codeprivate static final int ALL_PERSON = 1;private static final int PERSON = 2;//数据库的名字public static final String DB_NAME = "test.db";//数据库版本public static final int DB_VERSION = 1;//数据库表的名称private static final String TABLE_NAME = "person";private MyDBHelper dbHelper;private SQLiteDatabase db;static {//创建一个路径识别器URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);//1.指定一个路径的匹配规则//如果路径满足content://com.dream.contentprovider.provider.PersonProvider/person,返回值就是(ALL_PERSON)=1URI_MATCHER.addURI(AUTHORITY, "/person", ALL_PERSON);//2.#号为通配符//如果路径满足content://com.dream.contentprovider.provider.PersonProvider/person/3,返回值就是(PERSON)=2URI_MATCHER.addURI(AUTHORITY, "/person/#", PERSON);}/*** 一般是对象第一次被创建时调用的方法** @return*/@Overridepublic boolean onCreate() {dbHelper = new MyDBHelper(this.getContext(), DB_NAME, null, DB_VERSION);db = dbHelper.getReadableDatabase();return true;}/*** 让别人去调用返回结果** @param uri           匹配条件* @param projection    选择的列* @param selection     查询条件* @param selectionArgs 查询条件的value* @param sortOrder     排序* @return*/@Nullable@Overridepublic Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {int type = URI_MATCHER.match(uri);switch (type) {case ALL_PERSON:return db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);case PERSON://获取具体某一行的数据String id = uri.getPathSegments().get(1);return db.query(TABLE_NAME, projection, "_id = ?", new String[]{id}, null, null, sortOrder);default:break;}return null;}//如果是单条记录应该返回以vnd.android.cursor.item/ 为首的字符串//如果是多条记录,应该返回vnd.android.cursor.dir/ 为首的字符串@Nullable@Overridepublic String getType(@NonNull Uri uri) {int type = URI_MATCHER.match(uri);switch (type) {case ALL_PERSON:return "vnd.android.cursor.dir/vnd.com.dream.contentprovider.provider.person";case PERSON:return "vnd.android.cursor.item/vnd.com.dream.contentprovider.provider.person";default:return null;}}@Nullable@Overridepublic Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {int type = URI_MATCHER.match(uri);switch (type) {case ALL_PERSON:long row = db.insert(TABLE_NAME, null, values);return ContentUris.withAppendedId(uri, row);case PERSON:long row2 = db.insert(TABLE_NAME, null, values);return ContentUris.withAppendedId(uri, row2);default:return null;}}@Overridepublic int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {int type = URI_MATCHER.match(uri);int updateRow = 0;switch (type) {case ALL_PERSON:updateRow = db.delete(TABLE_NAME, selection, selectionArgs);break;case PERSON://获取具体某一行的数据String id = uri.getPathSegments().get(1);updateRow = db.delete(TABLE_NAME, "_id = ?", new String[]{id});break;default:break;}return updateRow;}@Overridepublic int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {int type = URI_MATCHER.match(uri);int updateRow = 0;switch (type) {case ALL_PERSON:updateRow = db.update(TABLE_NAME, values, selection, selectionArgs);break;case PERSON://获取具体某一行的数据String id = uri.getPathSegments().get(1);updateRow = db.update(TABLE_NAME, values, "_id = ?", new String[]{id});break;default:break;}return updateRow;}
}

 当APP启动的时候,onCreate()方法被执行,此时已经创建了数据库。

 🔥 跨进程调用 🔥

private static final Uri STUDENT_ALL_URI = Uri.parse("content://" + PersonProvider.AUTHORITY + "/person");

拼接完整的地址为:"content://com.dream.contentprovider/person""content://com.dream.contentprovider/person/id",其中这个id表示表中数据的行数,id=0、1、 2 ...。当和UriMatcher 进行匹配返回匹配类型。不管是进程内还是跨进程之间通信关键是否URL正确,否则会出现如下错误:

private static final Uri STUDENT_ALL_URI = Uri.parse("content://com.dream.contentprovider/person");

 插入数据insert()

 mUserProvider = getContentResolver();ContentValues values = new ContentValues();values.put("name", "张三");values.put("age", "18");values.put("sex", "男");mUserProvider.insert(STUDENT_ALL_URI, values);

 删除数据delete()

 mUserProvider = getContentResolver();mUserProvider.delete(STUDENT_ALL_URI, "name = ?", new String[]{"张三"});

查询数据query() 

 mUserProvider = getContentResolver();String result = "";Cursor cursor = mUserProvider.query(STUDENT_ALL_URI, null, null, null);if (cursor != null) {try {while (cursor.moveToNext()) {String id = cursor.getString(cursor.getColumnIndex("_id"));String name = cursor.getString(cursor.getColumnIndex("NAME"));String age = cursor.getString(cursor.getColumnIndex("AGE"));String sex = cursor.getString(cursor.getColumnIndex("SEX"));result = result + "\n" + "_id=" + id + " name=" + name + " sex=" + sex + "  age=" + age;}} finally {cursor.close();cursor = null;mTextView.setText(result);}}

记得关闭游标,防止内存泄漏

🔥 BroadcastReceiver 🔥

BroadcastReceiver(广播接收器),属于 Android 四大组件之一,在 Android 开发中,BroadcastReceiver 的应用场景非常多。今天,我将详细讲解关于BroadcastReceiver的所有广播类型,主要分为5类:

普通广播(Normal Broadcast)
系统广播(System Broadcast)
有序广播(Ordered Broadcast)
粘性广播(Sticky Broadcast)
App应用内广播(Local Broadcast)

🔥 普通广播(Normal Broadcast)🔥 

即开发者自身定义intent的广播(最常用)。发送广播使用如下:

Intent intent = new Intent();
//对应BroadcastReceiver中intentFilter的action
intent.setAction(BROADCAST_ACTION);
//发送广播
sendBroadcast(intent);

若被注册了的广播接收者中注册时 intentFilter action与上述匹配,则会接收此广播(即进行回调 onReceive() )。如下mBroadcastReceiver则会接收上述广播

<receiver //此广播接收者类是mBroadcastReceiverandroid:name=".mBroadcastReceiver" >//用于接收网络状态改变时发出的广播<intent-filter><action android:name="BROADCAST_ACTION" /></intent-filter>
</receiver>

若发送广播有相应权限,那么广播接收者也需要相应权限

🔥 系统广播(System Broadcast)🔥 

 Android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广播

 每个广播都有特定的Intent - Filter(包括具体的action),Android系统广播action如下:

系统操作action
监听网络变化android.net.conn.CONNECTIVITY_CHANGE
关闭或打开飞行模式Intent.ACTION_AIRPLANE_MODE_CHANGED
充电时或电量发生变化Intent.ACTION_BATTERY_CHANGED
电池电量低Intent.ACTION_BATTERY_LOW
电池电量充足(即从电量低变化到饱满时会发出广播Intent.ACTION_BATTERY_OKAY
系统启动完成后(仅广播一次)Intent.ACTION_BOOT_COMPLETED
按下照相时的拍照按键(硬件按键)时Intent.ACTION_CAMERA_BUTTON
屏幕锁屏Intent.ACTION_CLOSE_SYSTEM_DIALOGS
设备当前设置被改变时(界面语言、设备方向等)Intent.ACTION_CONFIGURATION_CHANGED
插入耳机时Intent.ACTION_HEADSET_PLUG

未正确移除SD卡但已取出来时(正确移除方法:设置–SD

卡和设备内存–卸载SD卡)

Intent.ACTION_MEDIA_BAD_REMOVAL
插入外部储存装置(如SD卡)Intent.ACTION_MEDIA_CHECKING
成功安装APKIntent.ACTION_PACKAGE_ADDED
成功删除APKIntent.ACTION_PACKAGE_REMOVED
重启设备Intent.ACTION_REBOOT
屏幕被关闭Intent.ACTION_SCREEN_OFF
屏幕被打开Intent.ACTION_SCREEN_ON
关闭系统时Intent.ACTION_SHUTDOWN
重启设备Intent.ACTION_REBOOT

注:当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播

🔥 有序广播(Ordered Broadcast)🔥 

定义
发送出去的广播被广播接收者按照先后顺序接收

广播接受者接收广播的顺序规则(同时面向静态和动态注册的广播接受者)
按照Priority属性值从大-小排序;
Priority属性相同者,动态注册的广播优先;

 特点
接收广播按顺序接收
先接收的广播接收者可以对广播进行截断,即后接收的广播接收者不再接收到此广播;
先接收的广播接收者可以对广播进行修改,那么后接收的广播接收者将接收到被修改后的广播

 具体使用
有序广播的使用过程与普通广播非常类似,差异仅在于广播的发送方式:

sendOrderedBroadcast(intent);

 🔥 App应用内广播(Local Broadcast)🔥

背景
Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)

 冲突
可能出现的问题:
其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息;
即会出现安全性 & 效率性的问题。

 解决方案
使用App应用内广播(Local Broadcast)

1、App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。
2、相比于全局广播(普通广播),App应用内广播优势体现在:安全性高 & 效率高

具体使用1 - 将全局广播设置成局部广播
注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
在广播发送和接收时,增设相应权限permission,用于权限验证;
发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。
通过**intent.setPackage(packageName)**指定报名

具体使用2 - 使用封装好的LocalBroadcastManager类
使用方式上与全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将参数的context变成了LocalBroadcastManager的单一实例
注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册

//注册应用内广播接收器
//步骤1:实例化BroadcastReceiver子类 & IntentFilter mBroadcastReceiver 
mBroadcastReceiver = new mBroadcastReceiver(); 
IntentFilter intentFilter = new IntentFilter(); //步骤2:实例化LocalBroadcastManager的实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);//步骤3:设置接收广播的类型 
intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);//步骤4:调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册 
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);//发送应用内广播
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
localBroadcastManager.sendBroadcast(intent);

🔥 粘性广播(Sticky Broadcast)🔥 

由于在Android5.0 & API 21中已经失效,所以不建议使用,在这里也不作过多的总结。 

🔥 OnReceive 回调函数中Context 含义 🔥

对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext
对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context
对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context
对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Activity Context

 

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

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

相关文章

如何配置一个永久固定的公网TCP地址来SSH远程树莓派?

文章目录 如何配置一个永久固定的公网TCP地址来SSH远程树莓派&#xff1f;前置条件命令行使用举例&#xff1a;修改cpolar配置文件 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 …

第1集丨Vue 江湖 —— Hello Vue

目录 一、简介1.1 参考网址1.2 下载 二、Hello Vue2.1 创建页面2.2 安装Live Server插件2.4 安装 vue-devtools2.5 预览效果 一、简介 Vue&#xff08;读音 /vjuː/, 类似于 view&#xff09; 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设…

app自动化测试之Appium问题分析及定位

使用 Appium 进行测试时&#xff0c;会产生大量日志&#xff0c;一旦运行过程中遇到报错&#xff0c;可以通过 Appium 服务端的日志以及客户端的日志分析排查问题。 Appium Server日志-开启服务 通过命令行的方式启动 Appium Server&#xff0c;下面来分析一下启动日志&#…

使用web-view实现网页端和uni-app端是数据传输

要实现这个功能 第一步&#xff1a;要在vue的public文件夹下面引入 <script type"text/javascript" src"https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script> 第二步&#xff1a;建立一个新的空的uni-app项目…

DHorse v1.3.0 发布,基于k8s的发布平台

综述 DHorse是一个简单易用、以应用为中心的云原生DevOps系统&#xff0c;具有持续集成、持续部署、微服务治理等功能&#xff0c;无需安装依赖Docker、Maven、Node等环境即可发布Java、Vue、React应用&#xff0c;主要特点&#xff1a;部署简单、操作简洁、功能快速。 新增特…

Apache Doris 巨大飞跃:存算分离新架构

作者&#xff1a;马如悦 Apache Doris 创始人 历史上&#xff0c;数据分析需求的不断提升&#xff08;更大的数据规模、更快的处理速度、更低的使用成本&#xff09;和计算基础设施的不断进化&#xff08;从专用的高端硬件、到低成本的商用硬件、到云计算服务&#xff09;&…

JPEG有损图像压缩编码器(附源码)

概述 一个基本由自己实现的JPEG有损图像压缩编码器&#xff0c;基于JFIF&#xff08;JPEG文件交换格式&#xff09;标准&#xff1a; 色彩空间转换&#xff08;RGB to YUV&#xff09;色度抽样&#xff08;采样因子4:2:0&#xff09;MCU分块&#xff08;16x16的最小编码单元&…

多模态第2篇:MMGCN代码配置

一、Windows环境 1.创建并激活虚拟环境 #创建虚拟环境命名为mmgcn&#xff0c;指定python版本为3.8 conda create -n mmgcn python3.8 #激活虚拟环境 conda activate mmgcn2.安装pytorch #torch2.0.0 cu118 pip install torch2.0.0cu118 torchvision0.15.1cu118 torchaudio…

PACS系统源码:支持三维重建功能、集成放射科管理RIS系统、图文报告编辑、打印、多级审核机制

PACS系统源码 PACS系统是以最新的IT技术为基础&#xff0c;遵循医疗卫生行业IHE/DICOM3.0和HL7标准&#xff0c;开发的多功能服务器和阅片系统。通过简单高性能的阅片功能&#xff0c;支持繁忙时的影像诊断业务&#xff0c;拥有保存影像的院内Web传输及离线影像等功能&#xf…

【雕爷学编程】MicroPython动手做(11)——搭建掌控板IDE开发环境四种

为了能够打好基础&#xff0c;系统学习MicroPython&#xff0c;特地入手了二块掌控板 知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通…

【Docker 学习笔记】Docker架构及三要素

文章目录 一、Docker 简介二、Docker 架构1. Docker 客户端和服务器2. Docker 架构图3. Docker 运行流程图 三、Docker 三要素1. 镜像&#xff08;Image&#xff09;2. 容器&#xff08;Container&#xff09;3. 仓库&#xff08;Repository&#xff09; 一、Docker 简介 Dock…

2.4 传统经验光照模型详解

一、光照模型 光照模型&#xff08;illumination model&#xff09;&#xff0c;也称为明暗模型&#xff0c;用于计算物体某点处的光强&#xff08;颜色值&#xff09;。从算法理论基础而言&#xff0c;光照模型分为两类&#xff1a;一种是基于物理理论的&#xff0c;另一种是…

【MATLAB第61期】基于MATLAB的GMM高斯混合模型回归数据预测

【MATLAB第61期】基于MATLAB的GMM高斯混合模型回归数据预测 高斯混合模型GMM广泛应用于数据挖掘、模式识别、机器学习和统计分析。其中&#xff0c;它们的参数通常由最大似然和EM算法确定。关键思想是使用高斯混合模型对数据&#xff08;包括输入和输出&#xff09;的联合概率…

<Doc>Windows常见的doc命令

一&#xff1a;管理员身份运行cmd命令&#xff1a; 方式一&#xff1a;搜索框输入cmd&#xff0c;回车&#xff0c;点击&#xff1a;以管理员身份运行 出现如图所示&#xff1a; 方式二&#xff1a;快捷键运行方式&#xff1a; 1.按winr&#xff0c;在运行窗口中输入cmd。 …

JavaSE类和对象(重点:this引用、构造方法)

目录 一、类的定义方式以及实例化 1.面向对象 Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在Java的世界里一切皆为对象。 2.类的定义和使用 1.在java中定义类时需要用到class关键字 3.类的实例化 4.类实例化的使用 二、this引用 …

Java API指南:掌握常用工具类与字符串操作

文章目录 1. API简介2. Java API的使用2.1 创建和使用Java API工具类2.2 使用String类进行字符串操作 结语 导语&#xff1a; Java作为一门功能强大的编程语言&#xff0c;其成功之处不仅在于语法结构的简洁明了&#xff0c;更因为其丰富的API&#xff08;Application Programm…

中药配方煎药-亿发智能中药汤剂煎煮系统,智慧中药房的数字化升级

随着中药的普及&#xff0c;在治病、养生等方面都发挥这积极作用&#xff0c;但中药煎煮过程繁琐&#xff0c;如果有所差错将会影响药品的药性。为了满足当今用户对中药的需求&#xff0c;增强生产效率和业务水平&#xff0c;亿发中药煎配智能管理系统应运而生&#xff0c;为用…

【JMeter】JMeter添加插件

目录 一、前言 二、插件管理器 三、推荐插件 1.Custom Thread Groups &#xff08;1&#xff09;Ultmate Thread Group &#xff08;2&#xff09;Stepping Thread Group 2.3 Basic Graph 资料获取方法 一、前言 ​ 在我们的工作中&#xff0c;我们可以利用一些插件来帮…

github gitlab 多用户多平台切换

一、背景 我需要用账号1 来登录并管理github 账号 我需要用账号2 来登录并管理gitlab 账号 二、设置账号 邮箱 设置账号1用户名与邮箱 git config --global user.name "miaojiang" git config --global user.email "187133163.com" 三、生成本地密钥…

中小企业如何低成本实施MES管理系统

中小企业在市场竞争中需要有高效的管理体系来支持其运营和发展。中小企业MES管理系统是一种先进的管理系统&#xff0c;可以提升工厂智能化水平&#xff0c;提高生产效率&#xff0c;是中小企业必须采取的有效管理工具。然而&#xff0c;由于资金和技术的限制&#xff0c;中小企…