当前位置: 首页 > news >正文

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版本发展的迭代,一些广播和一些广播发送的方法不再使用,参考信息如下:

  1. Android 5.0(API级别21)粘性广播不再支持sendStickyBroadcast 被打上了 Deprecated 标签,逐渐被弃用
  2. Android 7.0 (API 级别 24) 及更高版本不再发送以下系统广播ACTION_NEW_PICTURE和ACTION_NEW_VIDEO
  3. Android 8.0 (API 级别 26) 开始对清单声明的接收器施加额外限制:如果应用以 Android 8.0 或更高版本为目标平台,则不能使用清单来声明接收器,对于大多数隐式广播(并非针对你的应用)。
  4. Android 9 (API 级别 28) 开始NETWORK_STATE_CHANGED_ACTION 广播不再接收关于用户所在位置或个人身份信息可识别身份的数据。
  5. 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是你自定义的广播动作,用于IntentIntentFilter匹配对应动作。

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)开始,sendStickyBroadcastsendStickyOrderedBroadcast 方法被标记为不推荐使用,因为它们可能会引起安全和隐私问题。建议使用其他方式来传递数据。

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

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读应用层广播组件的发送广播和接收处理广播 2个过程&#xff0c;以及从APP层到AMS调用之间的打通。关注思维导图中左上部分即可。 有…...

在 Node.js 中使用 .env 文件

什么是 .env 文件&#xff1f; 文件.env是包含环境变量键值对的简单文本文件。此文件的内容不会被签入源代码管理&#xff0c;从而确保敏感数据的安全。 示例 PORT 4000 DATABASE_URL mongodb://localhost: 27017 /mydb API_KEY abcd1234 NODE_ENV development 在 Node.…...

CesiumJS 案例 P19:添加矩形、监听鼠标左击、监听鼠标右击、监听鼠标移动

CesiumJS CesiumJS API&#xff1a;https://cesium.com/learn/cesiumjs/ref-doc/index.html CesiumJS 是一个开源的 JavaScript 库&#xff0c;它用于在网页中创建和控制 3D 地球仪&#xff08;地图&#xff09; 一、添加矩形 <!DOCTYPE html> <html lang"en&…...

路测毫米波雷达标定和目标跟踪

1 2 3 4 5 6 查找匹配时&#xff0c;先对数据排序。逐帧对数据处理&#xff0c;运行速度快。单帧有噪点&#xff0c;多帧处理&#xff0c;准确率更高一些。 7 8 9 10 参考 路侧毫米波雷达标定与目标跟踪哔哔哩_bilibili路侧毫米波雷达标定与目标跟踪, 视频播放量 5855、弹幕量…...

【sqlmap使用手册-持续更新中】

SQLMap 简介 SQLMap 是一个开源的渗透测试工具&#xff0c;用于自动化检测和利用 SQL 注入漏洞。它支持多种数据库&#xff0c;包括 MySQL、PostgreSQL、Oracle、SQL Server 等。 可以通过以下命令安装sqlmap git clone https://github.com/sqlmapproject/sqlmap.git最常用的…...

面向对象三大特征之一:封 装

1、特点 封装是面向对象的核心思想&#xff0c;两层含义&#xff1a;一是一个整体&#xff08;把对象的属性和行为看成一个整体&#xff0c;即封装在一个对象种&#xff09;&#xff0c;二是信息隐藏&#xff0c;对外隐藏&#xff0c;但可以通过某种方式进行调用。 2、访问权…...

qt QMenuBar详解

1、概述 QMenuBar是Qt框架中用于创建菜单栏的类&#xff0c;它继承自QWidget。QMenuBar通常位于QMainWindow对象的标题栏下方&#xff0c;用于组织和管理多个QMenu&#xff08;菜单&#xff09;和QAction&#xff08;动作&#xff09;。菜单栏提供了一个水平排列的容器&#x…...

ESP32的下的蓝牙应用笔记(1)——Beacon蓝牙信标

Beacon蓝牙信标简介 ‌Beacon蓝牙信标‌是一种基于蓝牙低功耗&#xff08;BLE&#xff09;技术的设备&#xff0c;主要用于提供位置信息和数据传输服务。它通过周期性地广播信号&#xff0c;能够在一定范围内与其他蓝牙设备进行通信&#xff0c;从而提供精准的位置信息和相关服…...

控制台安全内部:创新如何塑造未来的硬件保护

在 Help Net Security 的采访中&#xff0c;安全研究人员 Specter 和 ChendoChap 讨论了游戏机独特的安全模型&#xff0c;并强调了它与其他消费设备的不同之处。 他们还分享了对游戏机安全性的进步将如何影响未来消费者和企业硬件设计的看法。 斯佩克特 (Specter) 是本周在阿…...

如何选择适合自己的 Python IDE

集成开发环境&#xff08;IDE&#xff09;是指提供广泛软件开发能力的软件应用程序。IDE 通常包括源代码编辑器、构建自动化工具和调试器。大多数现代 IDE 都配备了智能代码补全功能。在本文中&#xff0c;你将发现目前市场上最好的 Python IDE。 什么是 IDE&#xff1f; IDE…...

Matlab车牌识别课程设计报告模板(附源代码)

目 录 一&#xff0e;课程设计目的……………………………………………3 二&#xff0e;设计原理…………………………………………………3 三&#xff0e;详细设计步骤……………………………………………3 四. 设计结果及分析…………………………………………18 五. …...

kubesphere jenkins自动重定向 http://ks-apiserver:30880/oauth/authorize

问题&#xff1a;登陆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 语言中&#xff0c;回调函数是一种将一个函数作为参数传递给另一个函数&#xff0c;在特定的事件发生时被调用的编程模式。 一、回调函数的定义 type OnTaskHandler func(r []byte)type remoteTaskClient struct {sync.RWMutexonTask OnTaskHandler } 以上定义了…...

区块链学习笔记(一)

区块链技术实现了去中心化的货币系统&#xff0c;与中心化记账方式不同&#xff0c;它消除了中间第三方&#xff0c;允许用户进行点对点交易&#xff0c;并确保了货币的真正所有权。此外&#xff0c;区块链的代码完全公开且不可篡改&#xff0c;保障了系统的透明度和安全性。 …...

解决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构建现代化前端应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Vite构建现代化前端应用 引言 Vite 简介 安装 Vite 创建项目 启动开发服务器 项目结构 配置 Vite 开发模式 生产构建 使用插…...

PyQt入门指南三十八 QWizard向导组件

在PyQt中&#xff0c;QWizard 是一个用于创建向导式应用程序的组件。向导是一种用户界面模式&#xff0c;它通过一系列逐步的页面引导用户完成某个任务。每个页面通常包含一些输入字段和选项&#xff0c;用户需要在每个页面上完成相应的操作&#xff0c;然后才能进入下一个页面…...

【数学二】线性代数-矩阵-矩阵的概念及运算

考试要求 1、理解矩阵的概念&#xff0c;了解单位矩阵、数量矩阵、对角矩阵、三角矩阵、对称矩阵、反对称矩阵和正交矩阵以及它们的性质. 2、掌握矩阵的线性运算、乘法、转置以及它们的运算规律&#xff0c;了解方阵的幂与方阵乘积的行列式的性质. 3、理解逆矩阵的概念&#x…...

近期学习前端的心得

1.如果你这一行的编辑权利在于你这一行的某个字段的值&#xff0c;你可以使用这样:disabled"scope.row.某字段 ! 某字段的值" 2.如果你不想使用弹出框的形式来修改数据库&#xff0c;可以采用 对“某字段”列使用了 el-input&#xff0c;并绑定了 v-model 到 sco…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...