【Android】大喇叭——广播

广播机制介绍

Android中的广播分为两种类型:标准广播和有序广播

标准广播:是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接受到这条广播信息,它们之间没有先后顺序。这种广播的效率会比较高,但同时也意味着它是无法被截断的。

在这里插入图片描述

有序广播:是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够接收到这条广播信息,当这个广播接收器的逻辑执行完毕之后,广播才会继续传递。此时广播传递是有先后顺序的,优先级高的广播就可以先收到信息,并可以根据情况进行消息的截断,这样后面的广播接收器就无法收到广播信息了。

在这里插入图片描述

接收广播系统

广播接收器可以自由地对自己感兴趣的广播进行注册,当有相应的广播发出时,广播接收器就能接收到该广播,并在内部处理相应的逻辑。注册广播的方式有两种:在代码中注册、在AndroidManifes.xml中注册,前者被称为动态注册,后者被称为静态注册。

动态注册监听网络变化

创建一个广播接收器只需要创建一个类,让它继承BroadcastReceiver,并重写父类的onReceive()方法即可,当广播来临的时候,这个方法就会得到执行。

示例如下所示:

public class MainActivity extends AppCompatActivity {private IntentFilter intentFilter;private NetworkChangeReceiver networkChangeReceiver;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});intentFilter = new IntentFilter();intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");networkChangeReceiver = new NetworkChangeReceiver();registerReceiver(networkChangeReceiver, intentFilter);}@Overrideprotected void onDestroy () {super.onDestroy();unregisterReceiver(networkChangeReceiver);}class NetworkChangeReceiver extends BroadcastReceiver {@Overridepublic void onReceive (Context context, Intent intent) {ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();if (networkInfo != null && networkInfo.isAvailable()) {Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();} else {Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();}}}
}

我们在主活动中定义了一个内部类,使其继承BroadcastReceiver,每当网络发生变化,这段代码都会得到执行。

先看主活动onCreate()里的代码:

  • 建立一个过滤器:IntentFilter 是用来指定 BroadcastReceiver 想要接收的 Intent 类型的过滤器。通过添加特定的Action来确定监听哪些广播,此处我们是要监听网络变化,当网络发生变化时,发出的就是一条值为android.net.conn.CONNECTIVITY_CHANGE的action。
  • 设立一个广播接收器
  • 进行注册:registerReceiver(networkChangeReceiver, intentFilter);``registerReceiver 方法用来注册 BroadcastReceiver,使其能够接收特定的 Intent。第一个参数是 BroadcastReceiver 的实例,第二个参数是之前创建并配置的 IntentFilter
  • 取消注册:动态注册的广播接收器一定要取消注册,此处我们是在onDestroy()方法里面调用unregisterReceiver()方法实现

上面的代码在我们连接上网络的时候或者断掉网络的时候会发出相应的通知。

静态注册实现开机启动

动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但存在一个缺点,必须在程序启动之后才能接收到广播。有没有办法能让程序未启动的情况下就可以接收到广播呢?这就需要静态注册的方式了。

安卓提供的快捷方式创建一个广播接收器,点击包名→new→Other→Broadcast Receiver,此时弹出:

在这里插入图片描述

Exported属性:表示是否允许这个广播接收本程序以外的广播

Enabled属性:表示是否启用这个广播接收器

此时就会为我们自动创建了,我们只需要修改其中的代码:

public class BootCompleteReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();}
}

静态的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用,由于我们时使用的快捷方式,因此注册这一步已经被自动完成了。但是此时还是不能接收到开机广播的,我们需要对AndroidManifest.xml文件进行修改:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools" ><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
......<receiverandroid:name=".BootCompleteReceiver"android:enabled="true"android:exported="true" ><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED"/></intent-filter></receiver>
......
</manifest>

注意:不要在onReceive()方法中添加过多的逻辑或者进行任何耗时的操作,在广播接收器当中是不允许接收线程的,当onReceive()方法运行了较长的时间而没有结束时,程序就会报错。广播接收器更多的扮演一种打开程序其他组件的角色。

发送自定义广播

发送标准广播

我们先定义一个广播接收器来接收此广播

public class MyBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "receive in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();}
}

然后与之前一样对AndroidManifest.xml文件进行修改:

<receiverandroid:name=".MyBroadcastReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="com.example.broadcasttest.MY_BROADCAST"/></intent-filter>
</receiver>

修改主活动的XML文件的代码,放一个按钮用来发送广播,我们需要在主活动当中对按钮注册点击事件:

Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");intent.setPackage(getPackageName());sendBroadcast(intent);}
});
  • setPackage(getPackageName()): 调用 setPackage 方法并传入当前应用的包名,这限制了广播只在当前应用内传播
  • sendBroadcast(intent): 发送一个广播。这个调用会通知所有注册了相同 action 的 BroadcastReceiver

注意:我们没有这句指令就收不到广播,是因为setPackage()方法是指定将这个广播发送给那个程序,让隐式的广播转化为显式的广播。在Android8.0以后,静态注册的BroadcastReceiver是无法接受广播的。

此时运行程序,当按下按钮的时候发出广播:

在这里插入图片描述

发送有序广播

在上面提到静态注册的广播是无法接收的,要使用为显式的广播,因此无法做到两个应用互相传递信息。建立一个新的广播接收器:

public class AnotherBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "receive in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show();}
}

与上面同样的方法在AndroidManifes.xml里面注册信息,此时按下按钮它也可以接收到广播,并作出回应,但此时仍为标准广播。要发送有序广播只需要改一句:

Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");intent.setPackage(getPackageName());sendOrderedBroadcast(intent, null);}
});

此时就只改了发送广播的代码,这里的第二个参数null是一个与权限相关的字符串,这里设置为null即可,此时运行程序会接收到两条不同广播接收器做出的回应。但我们知道有序广播是有一定的先后顺序的,在何处设定广播接收器的先后顺序呢?就是在注册的时候设定,修改AndroidManifes.xml里的代码:

<receiverandroid:name=".MyBroadcastReceiver"android:enabled="true"android:exported="true"><intent-filter android:priority="100"><action android:name="com.example.broadcasttest.MY_BROADCAST" /></intent-filter>
</receiver>

我们通过android:priority属性给广播接收器设置了优先级,优先级较高就可以先接收到广播。优先级的取值范围为-1000到1000,当我们没有设置优先级时,优先级默认为0。

此时MyBroadcastReceiver的优先级为100,此时AnotherBroadcastReceiver的优先级默认为0,因此前一个广播接收器会先收到广播。运行程序,我们先接收到优先级高的做出的回应即MyBroadcastReceiver做出的回应。使用有序广播的优点就在于可以随时截断广播的传播,MyBroadcastReceiver的优先级更高,因此可以选择是否继续传递:

public class MyBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "receive in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();abortBroadcast();}
}

我们从中调用了 abortBroadcast()方法表示将这个广播进行截断,后面的广播接收器就无法再接收这条广播。重新运行程序,此时广播就会在MyBroadcastReceiver部分进行截断,之后的广播接收器AnotherBroadcastReceiver就接收不到广播了,从而不会做出任何的反应。

使用本地广播

如果发送的广播可以被所有的程序都接收到,这样你所发送的广播就会被其他的所有程序所接收,其他程序也会发来很多的不需要的广播,为了能够解决广播的安全性问题,Android引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序内部进行传递,也只能接收本程序发出的广播。

本地广播就是使用了一个LocalBroadcastManager来对广播进行管理。提供了发送广播和注册广播接收器的方法。

public class MainActivity extends AppCompatActivity {private LocalReceivew localReceiver;private LocalBroadcastManager localBroadcastManager;private IntentFilter intentFilter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});localBroadcastManager = LocalBroadcastManager.getInstance(this);Button button = (Button) findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST");localBroadcastManager.sendBroadcast(intent);}});intentFilter = new IntentFilter();intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");localReceiver = new LocalReceivew();localBroadcastManager.registerReceiver(localReceiver, intentFilter);}@Overrideprotected void onDestroy () {super.onDestroy();localBroadcastManager.unregisterReceiver(localReceiver);}class LocalReceivew extends BroadcastReceiver {@Overridepublic void onReceive (Context context, Intent intent) {Toast.makeText(context, "receive local broadcast", Toast.LENGTH_SHORT).show();}}
}

是不是觉得代码非常的熟悉呢?没错,基本上与我们动态的注册广播接收器的步骤是一样的,只是在方法调用的时候使用了LocalBroadcastManager进行管理。

本地广播的优势:

  1. 可以明确知道正在发送的广播不会离开我们的程序,因此无需担心机密数据泄露
  2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心有安全泄露的隐患
  3. 发送本地广播比发送系统全局广播将会更加高效

实践—实现强制下线功能

强制下线功能是我们所经常用到的,当我们的微信、QQ等有人在别的手机设备上面登录的时候,我们的手机就会弹出一个弹窗:

在这里插入图片描述

这时,你就只能按下确定按钮,它会将你之前的活动全部关闭,重新回到登录界面。接下来就自己去实现吧。

我们要按下按钮就关闭所有的活动,回到登录界面就需要一个活动管理器,创建一个ActivityCollector类用于管理所有的活动:

import android.app.Activity;import java.util.ArrayList;
import java.util.List;public class ActivityCollector {public static List<Activity> activities = new ArrayList<>();public static void addActivity (Activity activity) {activities.add(activity);}public static void removeActivity (Activity activity) {activities.remove(activity);}public static void finishAll () {for (Activity activity : activities) {if (!activity.isFinishing()) {activity.finish();}}activities.clear();}
}

创建BaseActivity类作为所有活动的父类:

public class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityCollector.addActivity(this);}@Overrideprotected void onDestroy() {super.onDestroy();ActivityCollector.removeActivity(this);}}

要想使用户重新登录我们就需要写登录界面的布局,新建一个登录活动:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".LoginActivity"><LinearLayoutandroid:orientation="horizontal"android:layout_width="match_parent"android:layout_height="60dp"><TextViewandroid:layout_width="90dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:textSize="18sp"android:text="Account:"/><EditTextandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1"android:id="@+id/account"/></LinearLayout><LinearLayoutandroid:orientation="horizontal"android:layout_width="match_parent"android:layout_height="60dp"><TextViewandroid:layout_width="90dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:textSize="18sp"android:text="Password:"/><EditTextandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1"android:id="@+id/textPassword"/></LinearLayout><Buttonandroid:layout_width="match_parent"android:layout_height="60dp"android:id="@+id/login"android:text="Login"/></LinearLayout>

登陆界面设置了输入用户的账户与密码以及登录的按钮,此时当用户进行输入结束之后按下登录按钮,会对账户与密码进行匹配,匹配成功进入下一个活动的界面,此时对登录活动的代码进行修改:

public class LoginActivity extends BaseActivity {private EditText accountEdit;private EditText passwordEdit;private Button login;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_login);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});accountEdit = (EditText) findViewById(R.id.account);passwordEdit = (EditText) findViewById(R.id.textPassword);login =(Button) findViewById(R.id.login);login.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String account = accountEdit.getText().toString();String password = passwordEdit.getText().toString();if (account.equals("admin") && password.equals("123456")) {Intent intent = new Intent(LoginActivity.this, MainActivity.class);startActivity(intent);finish();} else {Toast.makeText(LoginActivity.this,"account or password is invalid", Toast.LENGTH_SHORT).show();}}});}
}

简单的登录界面就完成了,当你输入正确的登录信息的时候就会跳转到主活动了,此时我们只需要做到强制下线,在主活动当中添加一个按钮用来触发强制下线功能:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/force_offline"android:text="Send force offline broadcast"android:textAllCaps="false"/></LinearLayout>

修改按钮的点击事件,使点击按钮会发出一个广播,当接收到这个广播的时候就关闭所有的活动,重新回到登录界面:

public class MainActivity extends BaseActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});Button button = (Button) findViewById(R.id.force_offline);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINE");sendBroadcast(intent);}});}
}

我们需要无论在哪一个活动,在接收到强制下线的时候弹出一个对话框提示你必须重新登录,所有的活动都继承与BaseActivity,因此我们将广播接收器注册在这个活动当中。

public class BaseActivity extends AppCompatActivity {private ForceOfflineReceiver receiver;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityCollector.addActivity(this);}@Overrideprotected void onResume() {super.onResume();IntentFilter intentFilter = new IntentFilter();intentFilter.addAction("com.example.broadcastbestpractice.FORCE_OFFLINE");receiver = new ForceOfflineReceiver();registerReceiver(receiver, intentFilter);}@Overrideprotected void onPause() {super.onPause();if (receiver != null) {unregisterReceiver(receiver);receiver = null;}}@Overrideprotected void onDestroy() {super.onDestroy();ActivityCollector.removeActivity(this);}class ForceOfflineReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {AlertDialog.Builder builder = new AlertDialog.Builder(context);builder.setTitle("Warning");builder.setMessage("You are forced to be offline.please try to login again.");builder.setCancelable(false);builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {ActivityCollector.finishAll();Intent intent = new Intent(context, LoginActivity.class);context.startActivity(intent);}});builder.show();}}
}

当广播接收器接收到广播,就会弹出一个对话框,当年按下OK按钮就会关闭所有的活动,跳转到登录界面,注意要使用builder.setCancelable(false);使其不可通过Back按钮关闭对话框。

为什么要将广播接收器的注册与取消放在onResume()onPause()里面呢?因为我们要保证活动只有位于栈顶才会接收到广播,没有在栈顶的就没有必要接收到这条广播信息。我们要一开始就是登录界面,因此将登录界面设置为主活动。

为什么receiver = null:这样是为了确保在调用注销广播接收器之后以避免在已经注销的广播接收器上继续操作而可能导致异常或其他问题。

运行程序:

在这里插入图片描述

位于登录界面,输入信息进行登录:
在这里插入图片描述

按下活动的按钮弹出对话框,此时按Back按钮是没有任何反应的,只能按下OK按钮,此时就会回到登录界面。

文章到这里就结束了!

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

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

相关文章

ARTMO Table ‘db1.test_mla_result‘ doesn‘t exist解决方案

com.mysql.jdbc.JDBC4PreparedStatement3f3c966c: describe test_mla_result; Java exception occurred: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table db1.test_mla_result doesnt exist解决方案&#xff1a; 打开MySQL的command Line, 输入SHOW TABLES…

Eclipse 生成 jar 包

打开 Jar 文件向导 Jar 文件向导可用于将项目导出为可运行的 jar 包。 打开向导的步骤为: 在 Package Explorer 中选择你要导出的项目内容。如果你要导出项目中所有的类和资源&#xff0c;只需选择整个项目即可。点击 File 菜单并选择 Export。在输入框中输入"JAR"…

谷粒商城实战笔记-55-商品服务-API-三级分类-修改-拖拽数据收集

文章目录 一&#xff0c;拖拽后结点的parentCid的更新二&#xff0c;拖拽后结点的父节点下所有结点的sort排序属性的变化更新排序的逻辑代码分析 三&#xff0c;拖拽后结点及其子节点catLevel的变化判断是否需要更新 catLevel获取拖动后的新节点 更新 catLevel完整代码 这一节的…

Apache ShardingSphere Proxy5.5.0实现MySQL分库分表与读写分离

1. 前提准备 1.1 主机IP:192.168.186.77 version: 3.8services:mysql-master:image: mysql:latestcontainer_name: mysql-masterenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: masterMYSQL_PASSWORD: 123456MYSQL_DATABASE: db1 ports:- "3306:3306&quo…

十一、Dockerfile解析

目录 一、Dockerfile简介 二、dockerfile的构建的三个步骤 三、Dockerfile的构建过程 1、DockerFile内容的基础知识 2、Docker执行Dockerfile的大致流程 四、dockerfile常用的保留字 1、FROM 2、MAINTAINER 3、RUN 4、EXPOSE 5、WORKDIR 6、USER 7、ENV 8、VOLUME …

Tensorflow2.0全套学习(持续更新中)

0 简介 1.学习目标 两大核心模块&#xff1a;深度学习经典算法与Tensorflow项目实战 从零开始&#xff0c;详细的网络模型架构与框架实现方法 2.tensorflow&#xff1a;核心开源库&#xff0c;深度学习框架&#xff0c;帮助开发和训练机器学习模型 3.版本2比1有哪些优势 …

企元数智:数字化转型,免费送新零售合规分销系统

企元数智&#xff0c;作为数字化转型领域的领先者&#xff0c;宣布推出一项划时代的举措&#xff1a;免费送出新零售合规分销系统&#xff0c;助力更多企业加速数字化转型进程。 随着新零售潮流的席卷&#xff0c;企业们越来越意识到数字化转型的紧迫性和必要性。然而&#xff…

LLMs之Llama 3.1:Llama 3.1的简介、安装和使用方法、案例应用之详细攻略

LLMs之Llama 3.1&#xff1a;Llama 3.1的简介、安装和使用方法、案例应用之详细攻略 导读&#xff1a;2024年7月23日&#xff0c;Meta重磅推出Llama 3.1。本篇文章主要提到了Meta推出的Llama 3.1自然语言生成模型。 背景和痛点 >> 过去开源的大型语言模型在能力和性能上一…

后台管理系统登录安全和权限要求

一、前言 几乎所有的系统都有后台管理系统&#xff0c;后台登录需要账号和密码&#xff0c;后台管理员权限需要有控制。所有管理员的操作都应该有操作日志。 二、存在的问题 现在很多系统只需要账号和密码就能登录&#xff0c;有的还是简单账号和简单密码&#xff0c;就是弱口…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第四十六章 自动创建设备节点

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

H3CNE(vlan的基础配置)

目录 9.1 传统以太网的问题 9.2 VLAN基础实现的原理 示例一&#xff08;vlan配置的基础实现&#xff09;&#xff1a; 示例二&#xff08;交换机间配置trunk&#xff09;&#xff1a; 9.3 hybrid接口类型与打标签的原理 示例三&#xff08;配置hybrid接口&#xff09;&#x…

通过IEC104转MQTT网关轻松接入阿里云平台

随着智能电网和物联网技术的飞速发展&#xff0c;电力系统中的传统IEC 104协议设备正面临向现代化、智能化转型的迫切需求。阿里云作为全球领先的云计算服务提供商&#xff0c;其强大的物联网平台为IEC 104设备的接入与数据处理提供了强大的支持。本文将深入探讨钡铼网关在MQTT…

网站被浏览器提示“不安全”,如何解决

在互联网时代&#xff0c;网络安全是每个网站所有者和用户共同关心的问题&#xff0c;当网站的数据传输未经过加密时&#xff0c;会被主流浏览器标记为“不安全”时&#xff0c;这不仅会影响用户体验&#xff0c;还可能损害网站的信誉&#xff0c;导致访问量下降。 一、理解警…

go语言开发Prometheus Exporter(DM数据库)

一、介绍 源码步骤基于dameng_exporter源码讲解&#xff0c;看完本篇文章可以直接进行二次开发。 dameng exporter的开源地址&#xff1a;https://github.com/gy297879328/dameng_exporter&#xff08;可直接对接prometheusgrafana 提供表盘&#xff09; 开发一个exporter 其…

基于微信小程序+SpringBoot+Vue的自习室选座与门禁系统(带1w+文档)

基于微信小程序SpringBootVue的自习室选座与门禁系统(带1w文档) 基于微信小程序SpringBootVue的自习室选座与门禁系统(带1w文档) 本课题研究的研学自习室选座与门禁系统让用户在小程序端查看座位&#xff0c;预定座位&#xff0c;支付座位价格&#xff0c;该系统让用户预定座位…

Discourse 如何通过终端工具访问 PGSQL

PGSQL 在 Discourse 中是通过容器方式运行的&#xff0c;要访问 PGSQL 中的数据那么首先需要进入到容器后才可以。 进入容器的命令为&#xff1a; cd /var/discourse/./launcher enter appsu discoursepsql discourse最后的命令是登录到运行在容器中的 pgsql。 查看 pgsql 上…

学习笔记之Java篇(0725)

p this 普通方法中&#xff0c;this总是指向调用该方法的对象。 构造方法中&#xff0c;this总是指向正要初始化的对象。 this&#xff08;&#xff09;调用必须重载的构造方法&#xff0c;避免相同地址初始化代码&#xff0c;但只能在构造方法中用&#xff0c;比企鹅必须位…

USB传输类型,常用类型:控制传输和中断传输

一般包含&#xff1a;控制传输&#xff0c;同步传输&#xff0c;批传输&#xff0c;中断传输&#xff0c;但常见到的控制传输的setup包以及中断传输的IN和OUT包 参考 控制传输 中断传输 usb主机发送的命令功能 hid类命令

AI有关的学习和python

一、基本概念 AIGC&#xff08;AI Generated content AI 生成内容&#xff09; AI生成的文本、代码、图片、音频、视频。都可以成为AIGC。 Generative AI&#xff08;生成式AI&#xff09;所生成的内容就是AIGC AI指代计算机人工智能&#xff0c;模仿人类的智能从而解决问题…

网站打包封装成app,提高用户体验和商业价值

网站打包封装成app的优势 随着移动互联网的普及&#xff0c;用户对移动应用的需求越来越高。网站打包封装成app可以满足用户的需求&#xff0c;提高用户体验和商业价值。 我的朋友是一名电商平台的运营负责人&#xff0c;他曾经告诉我&#xff0c;他们的网站流量主要来自移动…