Android Framework AMS(10)广播组件分析-1
该系列文章总纲链接:专题总纲目录 Android Framework 总纲
本章关键点总结 & 说明:

说明:本章节主要解读应用层广播组件的发送广播和接收处理广播 2个过程,以及从APP层到AMS调用之间的打通。关注思维导图中左上部分即可。
有了前面startActivity流程和service组件启动的流程的过程,我们基于此继续分析广播组件的发送和接收处理流程过程。
我们先对广播组件的基本概念、分类、以及基本的使用方式有所了解。再基于应用的API入手,对关键API进行分析。
1 Android系统中广播的解读
1.1 广播的分类
在Android中,广播(Broadcast)是一种在不同组件之间传递信息的机制。广播可以分为以下几种类型:
- 普通广播(Normal Broadcasts):异步发送,系统会同时将广播发送给所有注册接收该类型广播的接收器。接收器之间没有执行顺序,几乎同时接收到广播消息。不能被截断。
- 有序广播(Ordered Broadcasts):同步发送,系统会按照接收器的优先级顺序逐个发送广播。每个接收器都有机会处理广播消息,并且可以决定是否继续传递广播。如果接收器决定不再传递广播(通过调用
abortBroadcast()),那么后续的接收器将不会收到该广播。 - 粘性广播(Sticky Broadcasts):属于标准广播,在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理。如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态。粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据。
- 系统广播(System Broadcasts):Android内置了很多系统级别的广播,我们可以在应用中通过监听这些广播来得到各种系统的状态信息。例如手机开机后会发送一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播等。
- App应用内广播(Local Broadcast):用于应用内部组件之间的通信,使用
LocalBroadcastManager来管理。相比全局广播,应用内广播更高效,不需要跨进程通信,也不需要考虑安全性问题。
当然,随着Android版本发展的迭代,一些广播和一些广播发送的方法不再使用,参考信息如下:
- Android 5.0(API级别21)粘性广播不再支持:
sendStickyBroadcast被打上了Deprecated标签,逐渐被弃用 - Android 7.0 (API 级别 24) 及更高版本不再发送以下系统广播:
ACTION_NEW_PICTURE和ACTION_NEW_VIDEO - Android 8.0 (API 级别 26) 开始对清单声明的接收器施加额外限制:如果应用以 Android 8.0 或更高版本为目标平台,则不能使用清单来声明接收器,对于大多数隐式广播(并非针对你的应用)。
- Android 9 (API 级别 28) 开始:
NETWORK_STATE_CHANGED_ACTION广播不再接收关于用户所在位置或个人身份信息可识别身份的数据。 - Android 12 开始:
ACTION_CLOSE_SYSTEM_DIALOGS被弃用,为了改善用户控制体验,在应用程序和系统进行交互时。
这些变更意味着开发者需要根据目标Android版本调整广播的使用,以确保应用的兼容性和功能性。
1.2 广播接收器分类
在Android中,广播接收器(BroadcastReceiver)用于接收和处理广播消息。根据广播的类型和用途,广播接收器可以分为以下几种:
1.2.1 静态注册广播接收器
在AndroidManifest.xml文件中声明,系统会在应用启动时自动为这些接收器注册广播。适用于接收系统广播,如电池电量变化、屏幕关闭等。当然,静态声明接收器也可以接收感兴趣的广播类型,参考配置如下:
<receiverandroid:name=".YourBroadcastReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="your.action.YOUR_ACTION" /></intent-filter>
</receiver>
这里的your.action.YOUR_ACTION是你自定义的广播动作,用于Intent和IntentFilter匹配对应动作。
1.2.2 动态注册广播接收器
一般在代码中通过registerReceiver()方法注册,需要在onDestroy()方法中调用unregisterReceiver()来注销。适用于接收应用内部广播或者需要根据应用状态动态注册和注销的广播。在Java代码中动态注册广播接收器代码实现参考如下:
IntentFilter filter = new IntentFilter();
filter.addAction("your.action.YOUR_ACTION");
registerReceiver(yourBroadcastReceiver, filter);
1.3 发送/接收广播代码实现解读
1.3.1 普通广播
发送普通广播方法如下:
// 创建一个Intent,包含要发送的动作
Intent intent = new Intent("your.action.YOUR_ACTION");
// 可以添加一些额外的数据
intent.putExtra("key", "value");// 发送普通广播
sendBroadcast(intent);
接收普通广播:
public class NormalReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 检查Intent的动作是否是我们期望的if ("your.action.YOUR_ACTION".equals(intent.getAction())) {// 处理接收到的数据String value = intent.getStringExtra("key");// ...}}
}
在AndroidManifest.xml中静态注册普通广播接收器
<receiver android:name=".NormalReceiver"><intent-filter><action android:name="your.action.YOUR_ACTION" /></intent-filter>
</receiver>
或者动态注册:
IntentFilter filter = new IntentFilter("your.action.YOUR_ACTION");
registerReceiver(new NormalReceiver(), filter);
1.3.2 有序广播
发送有序广播方法如下:
// 创建一个Intent,包含要发送的动作
Intent intent = new Intent("your.action.YOUR_ACTION");
// 可以添加一些额外的数据
intent.putExtra("key", "value");// 发送有序广播,第二个参数是权限字符串,null表示没有权限限制
sendOrderedBroadcast(intent, null);
在发送有序广播时,可以通过abortBroadcast()方法来截断广播,阻止其继续传递给其他接收器。接收有序广播:
public class OrderedReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 检查Intent的动作是否是我们期望的if ("your.action.YOUR_ACTION".equals(intent.getAction())) {// 处理接收到的数据String value = intent.getStringExtra("key");// ...// 如果需要继续传递广播,不调用abortBroadcast(),否则调用abortBroadcast()来截断广播// abortBroadcast();}}
}
注册普通广播接收器参考1.3.1即可。
请注意,有序广播可能会在未来的Android版本中被进一步限制或弃用,因为它们可能被滥用来执行恶意行为。因此,使用时需要谨慎,并考虑应用的目标API级别。
1.3.3 粘性广播(Sticky Broadcasts)
发送粘性广播:
// 创建一个Intent,包含要发送的数据
Intent intent = new Intent("com.example.myapp.MY_STICKY_BROADCAST");
intent.putExtra("key", "value");// 发送粘性广播
sendStickyBroadcast(intent);
接收粘性广播:
public class StickyReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 处理接收到的数据String value = intent.getStringExtra("key");// ...}
}// 在代码中动态注册粘性广播接收器
IntentFilter filter = new IntentFilter("com.example.myapp.MY_STICKY_BROADCAST");
registerReceiver(new StickyReceiver(), filter);
注意: 如之前描述,从Android 5.0(API 级别 21)开始,sendStickyBroadcast 和 sendStickyOrderedBroadcast 方法被标记为不推荐使用,因为它们可能会引起安全和隐私问题。建议使用其他方式来传递数据。
1.3.4 系统广播(System Broadcasts)
关于系统广播的发送,我们不需要过渡关心,更在于如何接收。
接收系统广播(例如电池电量变化):
public class SystemReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);float batteryPct = level / (float) scale;// 处理电池电量变化}}
}// 在AndroidManifest.xml中静态注册系统广播接收器
<receiver android:name=".SystemReceiver"><intent-filter><action android:name="android.intent.action.BATTERY_CHANGED" /></intent-filter>
</receiver>
注册广播接收器参考1.3.1即可。
1.3.5 App应用内广播(Local Broadcast)
发送应用内广播:
// 创建LocalBroadcastManager实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);// 创建一个Intent,包含要发送的数据
Intent intent = new Intent("com.example.myapp.MY_LOCAL_BROADCAST");
intent.putExtra("key", "value");// 发送应用内广播
localBroadcastManager.sendBroadcast(intent);
接收应用内广播:
public class LocalReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 处理接收到的数据String value = intent.getStringExtra("key");// ...}
}// 在代码中动态注册应用内广播接收器
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
IntentFilter filter = new IntentFilter("com.example.myapp.MY_LOCAL_BROADCAST");
localBroadcastManager.registerReceiver(new LocalReceiver(), filter);
1.4 广播发送和接收案例
以下是一个简单的 Android 应用示例,它演示了如何发送和接收普通广播。这个示例包含两个部分:一个用于发送广播的 Activity 和一个广播接收器 BroadcastReceiver。
首先,你需要在AndroidManifest.xml文件中声明Activity:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.dynamicbroadcastdemo"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
</manifest>
这是一个简单的Activity,它将发送一个自定义的广播,并动态注册一个广播接收器:
package com.example.dynamicbroadcastdemo;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private Button sendBroadcastButton;private MyBroadcastReceiver myBroadcastReceiver;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);sendBroadcastButton = findViewById(R.id.sendBroadcastButton);sendBroadcastButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {sendBroadcast();}});// 动态注册广播接收器myBroadcastReceiver = new MyBroadcastReceiver();IntentFilter filter = new IntentFilter();filter.addAction("com.example.dynamicbroadcastdemo.MY_CUSTOM_ACTION");registerReceiver(myBroadcastReceiver, filter);}@Overrideprotected void onDestroy() {super.onDestroy();// 注销广播接收器unregisterReceiver(myBroadcastReceiver);}private void sendBroadcast() {Intent intent = new Intent("com.example.dynamicbroadcastdemo.MY_CUSTOM_ACTION");// 可以添加额外的数据intent.putExtra("key", "value");sendBroadcast(intent);}public class MyBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 检查Intent的动作是否是我们期望的if ("com.example.dynamicbroadcastdemo.MY_CUSTOM_ACTION".equals(intent.getAction())) {// 处理接收到的数据String value = intent.getStringExtra("key");Toast.makeText(context, "Received broadcast: " + value, Toast.LENGTH_SHORT).show();}}}
}
编写一个简单的布局文件,包含一个按钮用于发送广播:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/sendBroadcastButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Send Broadcast"android:layout_centerInParent="true" />
</RelativeLayout>
这个示例展示了如何发送和接收一个简单的广播。当用户点击按钮时,MainActivity 会发送一个广播,MyBroadcastReceiver 接收到广播后会显示一个 Toast 消息。这个示例涵盖了广播的发送和接收的基本流程。
基于此,我们对于广播组件的研究起点就从2个关键方法入手:
- 动态注册/注销:registerReceiver unregisterReceiver
- 发送普通/有序广播:sendBroadcast sendOrderedBroadcast
2 从activity场景到AMS调用的流程
2.1 动态注册/注销流程
2.1.1 从activity.registerReceiver到AMS.registerReceiver
activity.registerReceiver方法是从Context中的registerReceiver开始调用的,接口代码实现如下:
//Context
@Nullable
public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,IntentFilter filter);@Nullable
public abstract Intent registerReceiver(BroadcastReceiver receiver,IntentFilter filter, @Nullable String broadcastPermission,@Nullable Handler scheduler);
注册时,用户的第一个 参数receiver 是继承于 BroadcastReceiver 的。其真正的实现是在ContextImpl中,代码实现如下:
//ContextImpl//关键流程:step1@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {return registerReceiver(receiver, filter, null, null);}//关键流程:step2@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler) {return registerReceiverInternal(receiver, getUserId(),filter, broadcastPermission, scheduler, getOuterContext());}//关键流程:step3private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) {// 创建一个IIntentReceiver对象,用于与ActivityManagerService通信IIntentReceiver rd = null;if (receiver != null) {if (mPackageInfo != null && context != null) {if (scheduler == null) {// 如果scheduler(调度器)为null,则使用主线程的Handlerscheduler = mMainThread.getHandler();}// 创建一个ReceiverDispatcher对象,用于调度广播rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true);} else {if (scheduler == null) {// 如果scheduler为null,则使用主线程的Handlerscheduler = mMainThread.getHandler();}// 创建一个LoadedApk.ReceiverDispatcher对象,用于调度广播rd = new LoadedApk.ReceiverDispatcher(receiver, context, scheduler, null, true).getIIntentReceiver();}}try {// 调用AMS的registerReceiver方法注册广播接收器return ActivityManagerNative.getDefault().registerReceiver(mMainThread.getApplicationThread(), mBasePackageName,rd, filter, broadcastPermission, userId);} catch (RemoteException e) {return null;}}
这里准备一个IIntentReceiver对象,调用ActivityManagerNative.getDefault().registerReceiver,实际上最终就是调用到AMS的registerReceiver方法中。这一部分参考binder系列文章即可,有了或者基础分析起来就较为简单了。系列文章链接为:专题分纲目录 android 系统核心机制 binder,尤其是这2篇偏实操的:
android 系统核心机制binder(11)binder java层 TestServer分析
android 系统核心机制binder(12)binder java层 TestClient 分析
2.1.2 从activity.unregisterReceiver到AMS.unregisterReceiver
activity.unregisterReceiver方法是从Context中的unregisterReceiver开始调用的,接口代码实现如下:
//Context
public abstract void unregisterReceiver(BroadcastReceiver receiver);
其真正的实现是在ContextImpl中,代码实现如下:
//ContextImpl@Overridepublic void unregisterReceiver(BroadcastReceiver receiver) {if (mPackageInfo != null) {IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(getOuterContext(), receiver);try {ActivityManagerNative.getDefault().unregisterReceiver(rd);} catch (RemoteException e) {}} else {throw new RuntimeException("Not supported in system context");}}
这里直接调用到ActivityManagerNative.getDefault().unregisterReceiver,实际上最终就是调用到AMS的unregisterReceiver方法中。其中过程参考2.1节即可。
2.2 发送普通/有序广播流程
2.2.1 从activity.sendBroadcast到AMS.broadcastIntent
activity.sendBroadcast方法是从Context中的sendBroadcast开始调用的,接口代码实现如下:
//Context
public abstract void sendBroadcast(Intent intent);public abstract void sendBroadcast(Intent intent,@Nullable String receiverPermission);public abstract void sendBroadcast(Intent intent,String receiverPermission, int appOp);
其真正的实现是在ContextImpl中,代码实现如下:
//ContextImpl@Overridepublic void sendBroadcast(Intent intent) {// 如果这个方法是从系统进程调用的,则发出警告。// 说明:这是为了限制系统进程滥用发送广播的能力,因为这可能会影响到整个系统的稳定性。warnIfCallingFromSystemProcess();// 确定Intent的类型,并使用getContentResolver()进行解析。// 说明:这一步是为了确保Intent中的数据类型是正确的,并且可以被系统正确理解。String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {// 准备Intent,使其可以离开当前进程。// 说明:这是为了确保Intent在发送给其他应用或组件时,包含所有必要的信息,并且是安全的。intent.prepareToLeaveProcess();// 调用AMS的broadcastIntent方法发送广播ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null,Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,getUserId());} catch (RemoteException e) {}}//解读同上,内容逻辑基本一致,仅传递参数不同@Overridepublic void sendBroadcast(Intent intent, String receiverPermission) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess();ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null,Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE,false, false, getUserId());} catch (RemoteException e) {}}//解读同上,内容逻辑基本一致,仅传递参数不同@Overridepublic void sendBroadcast(Intent intent, String receiverPermission, int appOp) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess();ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null,Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false,getUserId());} catch (RemoteException e) {}}
这里直接调用到ActivityManagerNative.getDefault().broadcastIntent,实际上最终就是调用到AMS的broadcastIntent方法中。其中过程参考2.1节即可。
2.2.2 从activity.sendOrderedBroadcast到AMS.broadcastIntent
activity.sendOrderedBroadcast方法是从Context中的sendOrderedBroadcast开始调用的,接口代码实现如下:
//Contextpublic abstract void sendOrderedBroadcast(Intent intent,@Nullable String receiverPermission);public abstract void sendOrderedBroadcast(@NonNull Intent intent,@Nullable String receiverPermission, BroadcastReceiver resultReceiver,@Nullable Handler scheduler, int initialCode, @Nullable String initialData,@Nullable Bundle initialExtras);public abstract void sendOrderedBroadcast(Intent intent,String receiverPermission, int appOp, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras);
其真正的实现是在ContextImpl中,代码实现如下:
//ContextImpl//解读同sendBroadcast,内容逻辑基本一致,仅传递参数不同@Overridepublic void sendOrderedBroadcast(Intent intent,String receiverPermission) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess();ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null,Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false,getUserId());} catch (RemoteException e) {}}//sendOrderedBroadcast 关键流程:step1@Overridepublic void sendOrderedBroadcast(Intent intent,String receiverPermission, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) {sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE,resultReceiver, scheduler, initialCode, initialData, initialExtras);}//sendOrderedBroadcast 关键流程:step2@Overridepublic void sendOrderedBroadcast(Intent intent,String receiverPermission, int appOp, BroadcastReceiver resultReceiver,Handler scheduler, int initialCode, String initialData,Bundle initialExtras) {warnIfCallingFromSystemProcess();// 初始化IIntentReceiver,用于接收广播结果IIntentReceiver rd = null;if (resultReceiver != null) {// 如果提供了resultReceiver,则需要创建一个IIntentReceiver对象if (mPackageInfo != null) {// 如果mPackageInfo不为空,使用它来创建ReceiverDispatcherif (scheduler == null) {// 如果scheduler为null,则使用主线程的Handlerscheduler = mMainThread.getHandler();}rd = mPackageInfo.getReceiverDispatcher(resultReceiver, getOuterContext(), scheduler,mMainThread.getInstrumentation(), false);} else {// 如果mPackageInfo为空,使用LoadedApk来创建ReceiverDispatcherif (scheduler == null) {// 如果scheduler为null,则使用主线程的Handlerscheduler = mMainThread.getHandler();}rd = new LoadedApk.ReceiverDispatcher(resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();}}// 解析Intent的类型String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess();// 调用ActivityManagerService的broadcastIntent方法发送有序广播ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, rd,initialCode, initialData, initialExtras, receiverPermission, appOp,true, false, getUserId());} catch (RemoteException e) {}}
这里直接调用到ActivityManagerNative.getDefault().broadcastIntent,实际上最终就是调用到AMS的broadcastIntent方法中。其中过程参考2.1节即可。
从这里也可以看到,对于有序广播的发送,最终和普通广播调用的broadcastIntent方法是一致的。只不过过程中的参数不同。
相关文章:
Android Framework AMS(10)广播组件分析-1
该系列文章总纲链接:专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明: 说明:本章节主要解读应用层广播组件的发送广播和接收处理广播 2个过程,以及从APP层到AMS调用之间的打通。关注思维导图中左上部分即可。 有…...
在 Node.js 中使用 .env 文件
什么是 .env 文件? 文件.env是包含环境变量键值对的简单文本文件。此文件的内容不会被签入源代码管理,从而确保敏感数据的安全。 示例 PORT 4000 DATABASE_URL mongodb://localhost: 27017 /mydb API_KEY abcd1234 NODE_ENV development 在 Node.…...
CesiumJS 案例 P19:添加矩形、监听鼠标左击、监听鼠标右击、监听鼠标移动
CesiumJS CesiumJS API:https://cesium.com/learn/cesiumjs/ref-doc/index.html CesiumJS 是一个开源的 JavaScript 库,它用于在网页中创建和控制 3D 地球仪(地图) 一、添加矩形 <!DOCTYPE html> <html lang"en&…...
路测毫米波雷达标定和目标跟踪
1 2 3 4 5 6 查找匹配时,先对数据排序。逐帧对数据处理,运行速度快。单帧有噪点,多帧处理,准确率更高一些。 7 8 9 10 参考 路侧毫米波雷达标定与目标跟踪哔哔哩_bilibili路侧毫米波雷达标定与目标跟踪, 视频播放量 5855、弹幕量…...
【sqlmap使用手册-持续更新中】
SQLMap 简介 SQLMap 是一个开源的渗透测试工具,用于自动化检测和利用 SQL 注入漏洞。它支持多种数据库,包括 MySQL、PostgreSQL、Oracle、SQL Server 等。 可以通过以下命令安装sqlmap git clone https://github.com/sqlmapproject/sqlmap.git最常用的…...
面向对象三大特征之一:封 装
1、特点 封装是面向对象的核心思想,两层含义:一是一个整体(把对象的属性和行为看成一个整体,即封装在一个对象种),二是信息隐藏,对外隐藏,但可以通过某种方式进行调用。 2、访问权…...
qt QMenuBar详解
1、概述 QMenuBar是Qt框架中用于创建菜单栏的类,它继承自QWidget。QMenuBar通常位于QMainWindow对象的标题栏下方,用于组织和管理多个QMenu(菜单)和QAction(动作)。菜单栏提供了一个水平排列的容器&#x…...
ESP32的下的蓝牙应用笔记(1)——Beacon蓝牙信标
Beacon蓝牙信标简介 Beacon蓝牙信标是一种基于蓝牙低功耗(BLE)技术的设备,主要用于提供位置信息和数据传输服务。它通过周期性地广播信号,能够在一定范围内与其他蓝牙设备进行通信,从而提供精准的位置信息和相关服…...
控制台安全内部:创新如何塑造未来的硬件保护
在 Help Net Security 的采访中,安全研究人员 Specter 和 ChendoChap 讨论了游戏机独特的安全模型,并强调了它与其他消费设备的不同之处。 他们还分享了对游戏机安全性的进步将如何影响未来消费者和企业硬件设计的看法。 斯佩克特 (Specter) 是本周在阿…...
如何选择适合自己的 Python IDE
集成开发环境(IDE)是指提供广泛软件开发能力的软件应用程序。IDE 通常包括源代码编辑器、构建自动化工具和调试器。大多数现代 IDE 都配备了智能代码补全功能。在本文中,你将发现目前市场上最好的 Python IDE。 什么是 IDE? IDE…...
Matlab车牌识别课程设计报告模板(附源代码)
目 录 一.课程设计目的……………………………………………3 二.设计原理…………………………………………………3 三.详细设计步骤……………………………………………3 四. 设计结果及分析…………………………………………18 五. …...
kubesphere jenkins自动重定向 http://ks-apiserver:30880/oauth/authorize
问题:登陆kubesphere的jenkins Nodeport IP :Port 46.XXX.XXX.16:30180 自动跳转失败 http://ks-apiserver:30880/oauth/authorize?client_idjenkins&redirect_urihttp://46.XXX.XXX.16:30180/securityRealm/finishLogin&response_typecode&scopeopen…...
Vue3访问页面时自动获取数据
1. 使用生命周期钩子函数 # 后端代码--使用pywebview class Api:def greet(self):greet_text pywebview and vue3response {}response[text] greet_textreturn responseif __name__ __main__:# 前后端通信测试api Api()window webview.create_window(Vue app in pywebvie…...
go语言回调函数的使用
前言 在 Go 语言中,回调函数是一种将一个函数作为参数传递给另一个函数,在特定的事件发生时被调用的编程模式。 一、回调函数的定义 type OnTaskHandler func(r []byte)type remoteTaskClient struct {sync.RWMutexonTask OnTaskHandler } 以上定义了…...
区块链学习笔记(一)
区块链技术实现了去中心化的货币系统,与中心化记账方式不同,它消除了中间第三方,允许用户进行点对点交易,并确保了货币的真正所有权。此外,区块链的代码完全公开且不可篡改,保障了系统的透明度和安全性。 …...
解决QT打包发布App Store时(90238)错误
Invalid signature. The main app bundle, xxxx at the “xxxx.app” path, has the following signing error(s): [a sealed resource is missing or invalid. In subcomponent: xxxx.app/Contents/Frameworks/QtWebEngineCore.framework]. For details about signing Mac cod…...
使用Vite构建现代化前端应用
💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 使用Vite构建现代化前端应用 引言 Vite 简介 安装 Vite 创建项目 启动开发服务器 项目结构 配置 Vite 开发模式 生产构建 使用插…...
PyQt入门指南三十八 QWizard向导组件
在PyQt中,QWizard 是一个用于创建向导式应用程序的组件。向导是一种用户界面模式,它通过一系列逐步的页面引导用户完成某个任务。每个页面通常包含一些输入字段和选项,用户需要在每个页面上完成相应的操作,然后才能进入下一个页面…...
【数学二】线性代数-矩阵-矩阵的概念及运算
考试要求 1、理解矩阵的概念,了解单位矩阵、数量矩阵、对角矩阵、三角矩阵、对称矩阵、反对称矩阵和正交矩阵以及它们的性质. 2、掌握矩阵的线性运算、乘法、转置以及它们的运算规律,了解方阵的幂与方阵乘积的行列式的性质. 3、理解逆矩阵的概念&#x…...
近期学习前端的心得
1.如果你这一行的编辑权利在于你这一行的某个字段的值,你可以使用这样:disabled"scope.row.某字段 ! 某字段的值" 2.如果你不想使用弹出框的形式来修改数据库,可以采用 对“某字段”列使用了 el-input,并绑定了 v-model 到 sco…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
