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

Android Binder 用法详解

Binder 是 Android 系统中的一种进程间通信(IPC)机制,它允许不同进程之间进行高效通信。Binder 在 Android 系统中被广泛使用,例如在 Activity 与 Service 的交互中。

Binder 的基本组成

实现 Binder 通信通常包含以下几个关键部分:

  1. AIDL 接口定义:通过 Android Interface Definition Language 定义接口
  2. 服务端实现:实现 AIDL 接口并在 Service 中提供服务
  3. 客户端调用:绑定服务并调用远程方法

下面通过一个完整的示例来展示 Binder 的使用方法。

示例:创建一个计算器服务

第一步:定义 AIDL 接口

创建文件 ICalculator.aidl

// ICalculator.aidl
package com.example.binderexample;interface ICalculator {int add(int a, int b);int subtract(int a, int b);int multiply(int a, int b);int divide(int a, int b);
}

第二步:创建服务端 Service

// CalculatorService.java
package com.example.binderexample;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;public class CalculatorService extends Service {private static final String TAG = "CalculatorService";// Binder 实现private final ICalculator.Stub mBinder = new ICalculator.Stub() {@Overridepublic int add(int a, int b) throws RemoteException {Log.d(TAG, "add() called with: a = [" + a + "], b = [" + b + "]");return a + b;}@Overridepublic int subtract(int a, int b) throws RemoteException {Log.d(TAG, "subtract() called with: a = [" + a + "], b = [" + b + "]");return a - b;}@Overridepublic int multiply(int a, int b) throws RemoteException {Log.d(TAG, "multiply() called with: a = [" + a + "], b = [" + b + "]");return a * b;}@Overridepublic int divide(int a, int b) throws RemoteException {Log.d(TAG, "divide() called with: a = [" + a + "], b = [" + b + "]");if (b == 0) {throw new RemoteException("Division by zero");}return a / b;}};@Overridepublic IBinder onBind(Intent intent) {Log.d(TAG, "onBind: Service bound");return mBinder;}
}

第三步:在 AndroidManifest.xml 中注册服务

<serviceandroid:name=".CalculatorService"android:enabled="true"android:exported="true"><intent-filter><action android:name="com.example.binderexample.ICalculator" /></intent-filter>
</service>

第四步:客户端实现

// MainActivity.java
package com.example.binderexample;import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private EditText mNum1EditText, mNum2EditText;private TextView mResultTextView;private Button mAddButton, mSubtractButton, mMultiplyButton, mDivideButton;private ICalculator mCalculator;private boolean mBound = false;// 服务连接对象private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mCalculator = ICalculator.Stub.asInterface(service);mBound = true;Log.d(TAG, "onServiceConnected: Service connected");Toast.makeText(MainActivity.this, "Calculator Service Connected", Toast.LENGTH_SHORT).show();}@Overridepublic void onServiceDisconnected(ComponentName name) {mCalculator = null;mBound = false;Log.d(TAG, "onServiceDisconnected: Service disconnected");}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化 UI 组件mNum1EditText = findViewById(R.id.editText_num1);mNum2EditText = findViewById(R.id.editText_num2);mResultTextView = findViewById(R.id.textView_result);mAddButton = findViewById(R.id.button_add);mSubtractButton = findViewById(R.id.button_subtract);mMultiplyButton = findViewById(R.id.button_multiply);mDivideButton = findViewById(R.id.button_divide);// 添加按钮点击事件mAddButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {performOperation("add");}});mSubtractButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {performOperation("subtract");}});mMultiplyButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {performOperation("multiply");}});mDivideButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {performOperation("divide");}});}private void performOperation(String operation) {if (!mBound) {Toast.makeText(this, "Service not bound", Toast.LENGTH_SHORT).show();return;}try {// 获取输入值int num1 = Integer.parseInt(mNum1EditText.getText().toString());int num2 = Integer.parseInt(mNum2EditText.getText().toString());int result = 0;// 执行远程操作switch (operation) {case "add":result = mCalculator.add(num1, num2);break;case "subtract":result = mCalculator.subtract(num1, num2);break;case "multiply":result = mCalculator.multiply(num1, num2);break;case "divide":result = mCalculator.divide(num1, num2);break;}// 显示结果mResultTextView.setText("结果: " + result);} catch (RemoteException e) {Log.e(TAG, "RemoteException: " + e.getMessage());Toast.makeText(this, "Remote exception: " + e.getMessage(), Toast.LENGTH_SHORT).show();} catch (NumberFormatException e) {Toast.makeText(this, "Please enter valid numbers", Toast.LENGTH_SHORT).show();}}@Overrideprotected void onStart() {super.onStart();// 绑定服务Intent intent = new Intent();intent.setAction("com.example.binderexample.ICalculator");intent.setPackage(getPackageName());bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}@Overrideprotected void onStop() {super.onStop();// 解绑服务if (mBound) {unbindService(mConnection);mBound = false;}}
}

第五步:布局文件

<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><EditTextandroid:id="@+id/editText_num1"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="输入第一个数字"android:inputType="number" /><EditTextandroid:id="@+id/editText_num2"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="输入第二个数字"android:inputType="number" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><Buttonandroid:id="@+id/button_add"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="+" /><Buttonandroid:id="@+id/button_subtract"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="-" /><Buttonandroid:id="@+id/button_multiply"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="×" /><Buttonandroid:id="@+id/button_divide"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="÷" /></LinearLayout><TextViewandroid:id="@+id/textView_result"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="16dp"android:text="结果: "android:textSize="18sp" /></LinearLayout>

Binder 工作原理解释

  1. AIDL 编译:编译时,Android 工具会根据 AIDL 文件生成相应的 Java 接口,其中包含一个内部抽象类 Stub,继承自 Binder 并实现了该接口

  2. Stub 类

    • 服务端继承并实现 Stub 类的抽象方法
    • Stub 类拥有 asInterface() 静态方法,用于将 IBinder 转换为接口类型
  3. 通信过程

    • 客户端调用 bindService() 绑定服务
    • 服务端返回 Binder 对象
    • 客户端通过 Stub.asInterface() 将 Binder 对象转换为接口
    • 客户端调用接口方法,实际上是通过 Binder 驱动进行跨进程通信

高级用法:传输复杂对象

如果需要传输复杂对象,需要实现 Parcelable 接口:

// Person.java
package com.example.binderexample;import android.os.Parcel;
import android.os.Parcelable;public class Person implements Parcelable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}protected Person(Parcel in) {name = in.readString();age = in.readInt();}public String getName() {return name;}public int getAge() {return age;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);dest.writeInt(age);}@Overridepublic int describeContents() {return 0;}public static final Creator<Person> CREATOR = new Creator<Person>() {@Overridepublic Person createFromParcel(Parcel in) {return new Person(in);}@Overridepublic Person[] newArray(int size) {return new Person[size];}};
}

然后在 AIDL 文件中引用这个类:

// Person.aidl
package com.example.binderexample;
parcelable Person;
// IPersonService.aidl
package com.example.binderexample;import com.example.binderexample.Person;interface IPersonService {void addPerson(in Person person);List<Person> getAllPersons();
}

Android C++ 中的 Binder 使用

在 Android C++ 层也可以使用 Binder 进行进程间通信。实际上,Android 框架中的核心 Binder 实现就是用 C++ 编写的,位于 native 层。C++ 中的 Binder 框架是 Java Binder 框架的基础。

Android Native Binder 主要组件

在 C++ 层使用 Binder 主要涉及以下几个关键类:

  1. IBinder:表示一个 Binder 对象的基类
  2. BpBinder:代理端的 Binder 实现(客户端)
  3. BBinder:本地端的 Binder 实现(服务端)
  4. BnInterface:服务端接口模板类
  5. BpInterface:客户端接口模板类
  6. IInterface:Binder 接口的基类
  7. ProcessState:管理进程的 Binder 状态
  8. IPCThreadState:管理线程的 Binder 状态

C++ Binder 实现示例

下面是一个完整的 C++ Binder 示例,包括服务端和客户端。

第一步:定义接口

首先,我们需要定义一个计算器服务的接口:

// ICalculator.h
#ifndef ICALCULATOR_H
#define ICALCULATOR_H#include <binder/IInterface.h>
#include <binder/Parcel.h>namespace android {// 接口标识符
enum {CALCULATOR_ADD = IBinder::FIRST_CALL_TRANSACTION,CALCULATOR_SUBTRACT,CALCULATOR_MULTIPLY,CALCULATOR_DIVIDE
};// 接口定义
class ICalculator : public IInterface {
public:DECLARE_META_INTERFACE(Calculator); // 声明接口元信息// 纯虚函数,需要子类实现virtual int32_t add(int32_t a, int32_t b) = 0;virtual int32_t subtract(int32_t a, int32_t b) = 0;virtual int32_t multiply(int32_t a, int32_t b) = 0;virtual int32_t divide(int32_t a, int32_t b) = 0;
};// 服务端接口
class BnCalculator : public BnInterface<ICalculator> {
public:// onTransact 函数处理来自客户端的请求virtual status_t onTransact(uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags = 0);
};} // namespace android#endif // ICALCULATOR_H

第二步:实现接口

接下来,我们需要实现这个接口:

// ICalculator.cpp
#include "ICalculator.h"namespace android {// 实现元接口宏
IMPLEMENT_META_INTERFACE(Calculator, "android.calculator.ICalculator");// 处理远程调用请求
status_t BnCalculator::onTransact(uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags) {switch (code) {case CALCULATOR_ADD: {CHECK_INTERFACE(ICalculator, data, reply);int32_t a = data.readInt32();int32_t b = data.readInt32();int32_t result = add(a, b);reply->writeInt32(result);return NO_ERROR;}case CALCULATOR_SUBTRACT: {CHECK_INTERFACE(ICalculator, data, reply);int32_t a = data.readInt32();int32_t b = data.readInt32();int32_t result = subtract(a, b);reply->writeInt32(result);return NO_ERROR;}case CALCULATOR_MULTIPLY: {CHECK_INTERFACE(ICalculator, data, reply);int32_t a = data.readInt32();int32_t b = data.readInt32();int32_t result = multiply(a, b);reply->writeInt32(result);return NO_ERROR;}case CALCULATOR_DIVIDE: {CHECK_INTERFACE(ICalculator, data, reply);int32_t a = data.readInt32();int32_t b = data.readInt32();if (b == 0) {return BAD_VALUE; // 除数为零错误}int32_t result = divide(a, b);reply->writeInt32(result);return NO_ERROR;}default:return BBinder::onTransact(code, data, reply, flags);}
}// 客户端代理实现
class BpCalculator : public BpInterface<ICalculator> {
public:BpCalculator(const sp<IBinder>& impl) : BpInterface<ICalculator>(impl) {}virtual int32_t add(int32_t a, int32_t b) {Parcel data, reply;data.writeInterfaceToken(ICalculator::getInterfaceDescriptor());data.writeInt32(a);data.writeInt32(b);remote()->transact(CALCULATOR_ADD, data, &reply);return reply.readInt32();}virtual int32_t subtract(int32_t a, int32_t b) {Parcel data, reply;data.writeInterfaceToken(ICalculator::getInterfaceDescriptor());data.writeInt32(a);data.writeInt32(b);remote()->transact(CALCULATOR_SUBTRACT, data, &reply);return reply.readInt32();}virtual int32_t multiply(int32_t a, int32_t b) {Parcel data, reply;data.writeInterfaceToken(ICalculator::getInterfaceDescriptor());data.writeInt32(a);data.writeInt32(b);remote()->transact(CALCULATOR_MULTIPLY, data, &reply);return reply.readInt32();}virtual int32_t divide(int32_t a, int32_t b) {Parcel data, reply;data.writeInterfaceToken(ICalculator::getInterfaceDescriptor());data.writeInt32(a);data.writeInt32(b);status_t status = remote()->transact(CALCULATOR_DIVIDE, data, &reply);if (status != NO_ERROR) {return -1; // 错误处理}return reply.readInt32();}
};} // namespace android

第三步:实现服务端

服务端需要实现 ICalculator 接口,并提供一个服务:

// CalculatorService.h
#ifndef CALCULATOR_SERVICE_H
#define CALCULATOR_SERVICE_H#include "ICalculator.h"namespace android {class CalculatorService : public BnCalculator {
public:static void instantiate(); // 用于注册服务// 实现ICalculator接口的方法virtual int32_t add(int32_t a, int32_t b);virtual int32_t subtract(int32_t a, int32_t b);virtual int32_t multiply(int32_t a, int32_t b);virtual int32_t divide(int32_t a, int32_t b);
};} // namespace android#endif // CALCULATOR_SERVICE_H
// CalculatorService.cpp
#include "CalculatorService.h"
#include <binder/IServiceManager.h>
#include <utils/Log.h>namespace android {void CalculatorService::instantiate() {sp<IServiceManager> sm = defaultServiceManager();sm->addService(String16("calculator"), new CalculatorService());ALOGI("Calculator service started");
}int32_t CalculatorService::add(int32_t a, int32_t b) {ALOGI("add() called with: a = %d, b = %d", a, b);return a + b;
}int32_t CalculatorService::subtract(int32_t a, int32_t b) {ALOGI("subtract() called with: a = %d, b = %d", a, b);return a - b;
}int32_t CalculatorService::multiply(int32_t a, int32_t b) {ALOGI("multiply() called with: a = %d, b = %d", a, b);return a * b;
}int32_t CalculatorService::divide(int32_t a, int32_t b) {ALOGI("divide() called with: a = %d, b = %d", a, b);return a / b;
}} // namespace android

第四步:服务端主程序

编写服务端主程序,用于启动服务:

// server_main.cpp
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include "CalculatorService.h"using namespace android;int main() {sp<ProcessState> proc(ProcessState::self());// 实例化并注册服务CalculatorService::instantiate();// 启动线程池处理请求ProcessState::self()->startThreadPool();IPCThreadState::self()->joinThreadPool();return 0;
}

第五步:客户端实现

客户端程序示例:

// client_main.cpp
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <iostream>
#include "ICalculator.h"using namespace android;
using namespace std;int main() {sp<IServiceManager> sm = defaultServiceManager();// 获取计算器服务sp<IBinder> binder = sm->getService(String16("calculator"));if (binder == nullptr) {ALOGE("Failed to get calculator service");return -1;}// 创建代理对象sp<ICalculator> calculator = interface_cast<ICalculator>(binder);// 使用计算器服务int a = 10, b = 5;cout << "Addition: " << a << " + " << b << " = " << calculator->add(a, b) << endl;cout << "Subtraction: " << a << " - " << b << " = " << calculator->subtract(a, b) << endl;cout << "Multiplication: " << a << " * " << b << " = " << calculator->multiply(a, b) << endl;cout << "Division: " << a << " / " << b << " = " << calculator->divide(a, b) << endl;return 0;
}

编译和构建

在 Android Native 代码中,通常会在 Android.bpAndroid.mk 文件中配置编译规则:

// Android.bp
cc_binary {name: "calculator_service",srcs: ["ICalculator.cpp","CalculatorService.cpp","server_main.cpp",],shared_libs: ["libutils","libbinder","liblog",],
}cc_binary {name: "calculator_client",srcs: ["ICalculator.cpp","client_main.cpp",],shared_libs: ["libutils","libbinder","liblog",],
}

Native Binder 与 Java Binder 的区别

C++ Binder 和 Java Binder 的主要区别:

  1. API 差异

    • C++ 使用 BBinder、BpBinder、BnInterface 等类
    • Java 使用 Binder、BinderProxy、IInterface.Stub 等类
  2. 内存管理

    • C++ 使用 sp<>(强指针)进行引用计数
    • Java 依赖于 JVM 的垃圾回收
  3. 接口定义

    • C++ 需要手动定义和实现接口类和代理类
    • Java 使用 AIDL 自动生成接口代码
  4. 错误处理

    • C++ 使用状态码(status_t)
    • Java 使用异常机制(RemoteException)

与 C++ 服务绑定的 Java 客户端

Java 应用也可以通过 JNI 调用 C++ 层的 Binder 服务:

// 在 Java 中获取本地服务
public class NativeBinderHelper {static {System.loadLibrary("nativebinderhelper");}// 本地方法声明public static native int addNumbers(int a, int b);public static native int subtractNumbers(int a, int b);
}
// JNI 实现
#include <jni.h>
#include <binder/IServiceManager.h>
#include "ICalculator.h"using namespace android;// JNI 函数实现
extern "C" JNIEXPORT jint JNICALL
Java_com_example_NativeBinderHelper_addNumbers(JNIEnv* env, jclass clazz, jint a, jint b) {sp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder = sm->getService(String16("calculator"));if (binder == nullptr) {return -1;}sp<ICalculator> calculator = interface_cast<ICalculator>(binder);return calculator->add(a, b);
}extern "C" JNIEXPORT jint JNICALL
Java_com_example_NativeBinderHelper_subtractNumbers(JNIEnv* env, jclass clazz, jint a, jint b) {sp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder = sm->getService(String16("calculator"));if (binder == nullptr) {return -1;}sp<ICalculator> calculator = interface_cast<ICalculator>(binder);return calculator->subtract(a, b);
}

实际应用场景

C++ Binder 在 Android 系统中广泛应用于:

  1. 系统服务:如 SurfaceFlinger、AudioFlinger 等系统服务
  2. HAL 接口:硬件抽象层接口通常使用 Binder
  3. 高性能需求:需要高性能 IPC 的场景

总结

Android C++ 层的 Binder 机制是 Android IPC 系统的基础,它提供了:

  1. 完整的 IPC 框架:支持跨进程方法调用
  2. 类型安全:通过接口定义保证类型安全
  3. 高性能:直接在 native 层实现,避免了 JNI 开销
  4. 安全性:支持身份验证和访问控制

虽然 C++ Binder 的使用相对 Java Binder 更复杂,需要手动实现更多代码,但它在性能和系统集成方面具有优势,特别适合系统级服务和对性能要求高的应用场景。

相关文章:

Android Binder 用法详解

Binder 是 Android 系统中的一种进程间通信&#xff08;IPC&#xff09;机制&#xff0c;它允许不同进程之间进行高效通信。Binder 在 Android 系统中被广泛使用&#xff0c;例如在 Activity 与 Service 的交互中。 Binder 的基本组成 实现 Binder 通信通常包含以下几个关键部…...

在Ubuntu中,某个文件的右下角有一把锁的标志是什么意思?

在Ubuntu中&#xff0c;某个文件的右下角有一把锁的标志是什么意思&#xff1f; 在 Ubuntu&#xff08;或其他基于 GNOME 文件管理器的 Linux 发行版&#xff09;中&#xff0c;文件或文件夹的右下角出现一把“锁”标志&#xff0c;通常表示 你当前的用户没有该文件/文件夹的写…...

达梦数据库如何收集表和索引的统计信息

命令&#xff1a; DBMS_STATS.GATHER_TABLE_STATS(OWNNAME >HIDC,TABNAME > SYS_OSS,ESTIMATE_PERCENT>100,METHOD_OPT > FOR ALL COLUMNS SIZE AUTO,DEGREE > 2,CASCADE > true); dbms_stats.gather_table_stats&#xff1a;用于收集目标表&#xff0c;目…...

大语言模型:从诞生到未来的探索

1 发展历程 1.1 早期探索&#xff1a;基础积累 大语言模型的发展并非一蹴而就&#xff0c;其源头可以追溯到自然语言处理的早期阶段。早期的自然语言处理系统主要基于规则和模板&#xff0c;通过人工编写的语法规则来处理文本。例如&#xff0c;早期的机器翻译系统就是根据预…...

DeepSeek-V3:AI语言模型的高效训练与推理之路

参考&#xff1a;【论文学习】DeepSeek-V3 全文翻译 在人工智能领域&#xff0c;语言模型的发展日新月异。从早期的简单模型到如今拥有数千亿参数的巨无霸模型&#xff0c;技术的进步令人瞩目。然而&#xff0c;随着模型规模的不断扩大&#xff0c;训练成本和推理效率成为了摆在…...

【多模态】Magma多模态AI Agent

1. 前言 微软杨建伟团队&#xff0c;最近在AI Agent方面动作连连&#xff0c;前两天开源了OmniParser V2&#xff0c;2月26日又开源了Magma&#xff0c;OmniParser专注在对GUI的识别解析&#xff0c;而Magma则是基于多模态技术&#xff0c;能够同时应对GUI和物理世界的交互&…...

DeepSeek掘金——DeepSeek R1驱动的PDF机器人

DeepSeek掘金——DeepSeek R1驱动的PDF机器人 本指南将引导你使用DeepSeek R1 + RAG构建一个功能性的PDF聊天机器人。逐步学习如何增强AI检索能力,并创建一个能够高效处理和响应文档查询的智能聊天机器人。 本指南将引导你使用DeepSeek R1 + RAG构建一个功能性的PDF聊天机器人…...

DeepSeek在PiscTrace上完成个性化处理需求案例——光流法将烟雾动态可视化

引言&#xff1a;PiscTrace作为开放式的视图分析平台提供了固定格式的类型参数支持个性化定制处理需求&#xff0c;本文一步步的实现光流分析按照不同需求根据DeepSeek的代码处理视频生成数据。 光流法&#xff08;Optical Flow&#xff09;是一种基于图像序列的计算机视觉技术…...

explore与explode词源故事

英语单词explore来自古法语&#xff0c;源自拉丁语&#xff0c;由前缀ex-&#xff08;出来&#xff09;加词根plor-&#xff08;叫喊&#xff09;以及末尾的小尾巴-e组成&#xff0c;字面意思就是“喊出来&#xff0c;通过叫喊声赶出来”。它为什么能表示“探索”呢&#xff1f…...

LeeCode题库第三十七题

37.解数独 项目场景&#xff1a; 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请…...

【数字信号处理:从原理到应用的深度剖析】

一、数字信号处理的原理 数字信号处理&#xff08;DSP&#xff09;是一种通过数学算法对信号进行分析、处理和转换的技术。其核心在于对离散时间信号的操作&#xff0c;目的是提取有用信息或将信号转换为更易于解释的形式。 &#xff08;一&#xff09;信号的数字化过程 1. …...

MySQL 数据库安全配置最佳实践

文章目录 MySQL 数据库安全配置最佳实践账户与权限管理账户最小化原则权限最小化配置密码策略强化 认证与访问控制禁用匿名账户启用安全认证 网络安全防护访问源限制禁用远程root访问启用SSL加密 日志审计与监控全量审计配置二进制日志管理 服务端安全加固关键参数配置文件权限…...

小红书自动评论

现在越来越多的人做起来小红书&#xff0c;为了保证自己的粉丝和数据好看&#xff0c;需要定期养号。 那么养号除了发视频外&#xff0c;还需要积极在社区互动&#xff0c;比如点赞、评论等等&#xff0c;为了节省时间&#xff0c;我做了一个自动化评论工具。 先看效果 那这个是…...

OpenCV图像认知(一)

OpenCV&#xff1a; 是由Intel公司俄罗斯团队发起并参与和维护的一个计算机视觉处理开源软件库&#xff0c;支持与计算机视觉和机器学习相关的众多算法 OpenCV-Python&#xff1a; OpenCV-Python是一个Python绑定库&#xff0c;旨在解决计算机视觉问题。 Python是一种由Gui…...

自学微信小程序的第六天

DAY6 1、使用录音API首先需要通过wx.getRecorderManager()方法获取到一个RecorderManager实例,该实例是一个全局唯一的录音管理器,用于实现录音功能。 表32:RecorderManager实例的常用方法 方法名称 说明 start() 开始录音 pause() 暂停录音 resume() 继续录音 stop() 停止…...

C++动态与静态转换区别详解

文章目录 前言一、 类型检查的时机二、安全性三、适用场景四、代码示例对比总结 前言 在 C 中&#xff0c;dynamic_cast 和 static_cast 是两种不同的类型转换操作符&#xff0c;主要区别体现在类型检查的时机、安全性和适用场景上。以下是它们的核心区别&#xff1a; 一、 类…...

Qt6.8编译项目找不到文件——6.8.2\msvc2022_64\include\QtWidgets\QMainWindow does not exist.

问题&#xff1a;Error: dependent ‘…\Qt6.8.2\6.8.2\msvc2022_64\include\QtWidgets\QMainWindow’ does not exist. jom: D:\Temp\untitled1\build\Makefile [release] Error 2 20:20:43: 进程"D:\ProgramFiles\Develop\Qt6.8.2\Tools\QtCreator\bin\jom\jom.exe"…...

AI工具导航平台功能模块之混合分类器功能说明文档

AI工具导航平台功能模块之混合分类器功能说明文档 这是我最近正在开发的AI工具信息平台的部门功能模块混合分类器的说明文档&#xff0c;我的AI工具信息平台基于streamlit架构&#xff0c;整理出来与大家分享。 该程序的混合分类器采用规则引擎与深度学习模型协同工作的架构&…...

【时序预测】在线学习:算法选择(从线性模型到深度学习解析)

——如何为动态时序预测匹配最佳增量学习策略&#xff1f; 引言&#xff1a;在线学习的核心价值与挑战 在动态时序预测场景中&#xff08;如实时交通预测、能源消耗监控&#xff09;&#xff0c;数据以流式&#xff08;Streaming&#xff09;形式持续生成&#xff0c;且潜在的…...

某个设备的RJ45网口接头为何不可连接任何POE设备

某个设备的RJ45网口接头不可连接任何POE设备 1.POE设备是什么&#xff1f; POE设备是指支持通过以太网线传输电力和数据的设备&#xff0c;即“Power over Ethernet”&#xff08;PoE&#xff09;技术的设备。这种技术允许网络设备在传输数据的同时&#xff0c;通过标准的RJ4…...

发展中的脑机接口:SSVEP特征提取技术

一、简介 脑机接口&#xff08;BCI&#xff09;是先进的系统&#xff0c;能够通过分析大脑信号与外部设备之间建立通信&#xff0c;帮助有障碍的人与环境互动。BCI通过分析大脑信号&#xff0c;提供了一种非侵入式、高效的方式&#xff0c;让人们与外部设备进行交流。BCI技术越…...

绕过密码卸载360终端安全管理系统

一不小心在电脑上安装了360终端安全管理系统&#xff0c;就会发现没有密码&#xff0c;就无法退出无法卸载360&#xff0c;很容易成为一个心病&#xff0c;360终端安全管理系统&#xff0c;没有密码&#xff0c;进程无法退出&#xff0c;软件无法卸载&#xff0c;前不久听同事说…...

Java数据结构第十五期:走进二叉树的奇妙世界(四)

专栏&#xff1a;Java数据结构秘籍 个人主页&#xff1a;手握风云 目录 一、二叉树OJ练习题&#xff08;续&#xff09; 1.1. 二叉树的层序遍历 1.2. 二叉树的最近公共祖先 1.3. 从前序与中序遍历序列构造二叉树 1.4. 从中序与后序遍历序列构造二叉树 1.5. 根据二叉树创建…...

Transformer 代码剖析9 - 解码器模块Decoder (pytorch实现)

一、模块架构全景图 1.1 核心功能定位 Transformer解码器是序列生成任务的核心组件&#xff0c;负责根据编码器输出和已生成序列预测下一个目标符号。其独特的三级注意力机制架构使其在机器翻译、文本生成等任务中表现出色。下面是解码器在Transformer架构中的定位示意图&…...

JAVA八股—计算机网络(自用)

JAVA八股—计算机网络&#xff08;自用&#xff09; 2.7 1.介绍一下TCP/IP模型和OSI模型的区别 OSI模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系&#xff0c;将计算机网络通信划分为七个不同的层级&#xff0c;每个层级都负责特定的功能。每个…...

unity和unity hub关系

unity和unity hub关系 Unity和Unity Hub是紧密相关但功能不同的两个软件,以下是它们的关系说明: Unity 定义:是一款专业的实时3D开发平台,广泛用于创建各种类型的3D和2D互动内容,如视频游戏、建筑可视化、汽车设计展示、虚拟现实(VR)和增强现实(AR)应用等。功能:提供…...

Linux的OOM机制

Linux 的 OOM&#xff08;Out of Memory&#xff09;机制是操作系统在内存耗尽时采取的一种保护措施。当系统内存不足&#xff0c;无法继续分配给进程时&#xff0c;Linux 内核会触发 OOM 杀手&#xff08;OOM Killer&#xff09;&#xff0c;选择并终止某些进程&#xff0c;以…...

Typora的Github主题美化

[!note] Typora的Github主题进行一些自己喜欢的修改&#xff0c;主要包括&#xff1a;字体、代码块、表格样式 美化前&#xff1a; 美化后&#xff1a; 一、字体更换 之前便看上了「中文网字计划」的「朱雀仿宋」字体&#xff0c;于是一直想更换字体&#xff0c;奈何自己拖延症…...

Cursor配置MCP Server

一、什么是MCP MCP&#xff08;Model Context Protocol&#xff09;是由 Anthropic&#xff08; Claude 的那个公司&#xff09; 推出的开放标准协议&#xff0c;它为开发者提供了一个强大的工具&#xff0c;能够在数据源和 AI 驱动工具之间建立安全的双向连接。 举个好理解…...

定时器之输入捕获

输入捕获的作用 工作机制​ 输入捕获通过检测外部信号边沿&#xff08;上升沿/下降沿&#xff09;触发计数器&#xff08;CNT&#xff09;值锁存到捕获寄存器&#xff08;CCRx&#xff09;&#xff0c;结合两次捕获值的差值计算信号时间参数。 ​脉冲宽度测量&#x…...