【Android】四大组件之ContentProvider
目录
一、什么是 ContentProvider
二、创建和使用 ContentProvider
三、跨应用权限控制
四、数据变更通知
五、多表关联与视图
六、异步处理
你手机里的通讯录,存储了所有联系人的信息。如果你想把这些联系人信息分享给其他App,就可以通过ContentProvider来实现。
一、什么是 ContentProvider
ContentProvider 是 Android 四大组件之一,负责实现跨应用程序的数据共享与访问,通过统一接口封装数据存储细节,提供标准化操作方式。
1. 核心特点
- 数据共享桥梁:提供安全的跨应用数据共享机制,允许不同应用通过 URI(统一资源标识符) 访问数据,无需直接操作对方数据库或文件系统。
- 统一数据接口:独立于底层数据存储形式(SQLite、文件或网络),对外均以表格形式组织数据,简化调用方操作逻辑(query()、insert()、update()、delete()等标准接口)。
- 跨进程通信支持:底层基于 Android Binder 机制实现进程间通信(IPC),确保数据交互高效且安全。
- 数据安全:ContentProvider可以通过权限控制来保护数据。比如,你可以设置只有特定应用才能访问你的数据。
2. 典型使用场景
场景 | 说明 |
---|---|
系统数据访问 | 读取通讯录、短信、媒体文件等系统内置数据。 |
应用间数据共享 | 例如电商应用向物流应用提供订单状态数据。 |
内部数据封装 | 统一管理应用内多模块数据访问逻辑(如本地缓存与网络数据整合)。 |
二、创建和使用 ContentProvider
1. 定义数据模型与 Contract 类
使用 Contract 类 统一管理 URI、表名及列名,提升代码可维护性:
public final class UserContract { public static final String AUTHORITY = "com.example.provider"; public static final Uri CONTENT_URI= Uri.parse("content://" + AUTHORITY + "/users"); public static class UserEntry implements BaseColumns { public static final String TABLE_NAME = "users"; public static final String COLUMN_NAME = "name"; public static final String COLUMN_AGE = "age"; }
}
2. 实现 Provider 类
继承 ContentProvider
并重写关键方法,集成 SQLite 数据库操作( CRUD) :
public class UserProvider extends ContentProvider { private SQLiteOpenHelper mDbHelper; @Override public boolean onCreate() { mDbHelper = new UserDatabaseHelper(getContext()); // 自定义数据库帮助类 return true; // 初始化数据库等资源,返回 true 表示成功 } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = mDbHelper.getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(UserEntry.TABLE_NAME); Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; // 查询数据,返回 Cursor 对象 }@Override public Uri insert(Uri uri, ContentValues values) { // 插入数据,返回新记录的 URI } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // 更新数据,返回受影响的行数 } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // 删除数据,返回受影响的行数 } @Override public String getType(Uri uri) { // 返回 URI 对应的 MIME 类型 }
}
onCreate()
在主线程执行,要避免耗时操作。可以在onCreate()
中初始化线程池,或在 CRUD 方法中启动AsyncTask
。- 所有 CRUD 方法需处理线程安全问题。
3. 定义 URI 规则
- URI 结构:
content://<Authority>/<Path>/<ID>
- Authority:唯一标识 Provider(需在
AndroidManifest.xml
中声明)。 - Path:标识数据表或资源类型(如
user
、order
)。
- Authority:唯一标识 Provider(需在
- 使用 UriMatcher:根据 URI 区分操作类型(如单条或多条数据)
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static { sUriMatcher.addURI(UserContract.AUTHORITY, "users", USERS); sUriMatcher.addURI(UserContract.AUTHORITY, "users/#", USER_ID);
} @Override
public Uri insert(Uri uri, ContentValues values) { switch (sUriMatcher.match(uri)) { case USERS: long id = db.insert(UserEntry.TABLE_NAME, null, values); return ContentUris.withAppendedId(uri, id); default: throw new IllegalArgumentException("Unsupported URI: " + uri); }
}
4. 注册与权限控制
在 AndroidManifest.xml
中声明provider:
<!-- 定义自定义权限 -->
<permission android:name="com.example.READ_USERS" android:protectionLevel="dangerous" />
<permission android:name="com.example.WRITE_USERS" android:protectionLevel="dangerous" /> <!-- 应用权限到 Provider -->
<provider android:name=".UserProvider" <!-- Provider 实现类的全路径 --> android:authorities="com.example.provider" <!-- 唯一标识符,与Contract类一致 --> android:exported="true" <!-- 是否允许其他应用访问(默认 false) --> android:readPermission="com.example.READ_USERS" android:writePermission="com.example.WRITE_USERS" />
android:name
:指向继承ContentProvider
的具体实现类(如UserProvider
)。android:authorities
:唯一标识 Provider 的 URI 前缀(如content://com.example.provider/users
)exported
:是否允许其他应用访问(默认为false
)。protectionLevel
设为dangerous
表示需用户手动授权。readPermission
/writePermission
:自定义权限控制。
在 Provider 方法中动态检查权限:
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { if (getContext().checkCallingPermission("com.example.READ_DATA") != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Permission denied"); } // 执行查询逻辑
} @Override
public int delete(Uri uri, String selection, String[] selectionArgs) { if (getContext().checkCallingPermission("com.example.WRITE_USERS") != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Permission denied"); } // 删除逻辑
}
注意:静态权限声明需配合动态校验
5. 客户端调用
调用方配置:
<manifest ...> <!-- 声明权限 --> <uses-permission android:name="com.example.READ_USERS" /> <uses-permission android:name="com.example.WRITE_USERS" /> <application ...> <!-- 无 Provider 声明,直接通过 ContentResolver 调用 --> </application>
</manifest>
通过 ContentResolver
访问和操作 Provider 数据:
// 插入数据
ContentValues values = new ContentValues();
values.put(UserEntry.COLUMN_NAME, "Alice");
values.put(UserEntry.COLUMN_AGE, 25);
getContentResolver().insert(UserContract.CONTENT_URI, values); // 查询数据
Cursor cursor = getContentResolver().query( UserContract.CONTENT_URI, new String[]{UserEntry._ID, UserEntry.COLUMN_NAME}, UserEntry.COLUMN_AGE + " > ?", new String[]{"20"}, null
);
调用方无需声明
<provider>
,但需通过权限声明和 URI 匹配访问目标 Provider,并遵循 Provider 方的路径与权限规则。
- 客户端无需直接操作 Provider 实例。
- 跨进程时需声明权限。
三、跨应用权限控制
配置目标 | 实现方式 |
---|---|
跨应用调用权限 | 调用方声明 <uses-permission> ,Provider 方配置 android:exported="true" 。 |
动态权限申请 | 针对 dangerous 级别权限,调用方需在运行时请求用户授权。 |
路径级访问控制 | Provider 方通过 <path-permission> 细化权限,调用方需匹配声明。 |
1. 声明 Provider 权限
<!-- 定义自定义权限 -->
<permission android:name="com.example.READ_USERS" android:protectionLevel="dangerous" />
<permission android:name="com.example.WRITE_USERS" android:protectionLevel="dangerous" /> <!-- 应用权限到 Provider -->
<provider android:name=".UserProvider" <!-- Provider 实现类的全路径 --> android:authorities="com.example.provider" <!-- 唯一标识符,与Contract类一致 --> android:exported="true" <!-- 是否允许其他应用访问(默认 false) --> android:readPermission="com.example.READ_USERS" android:writePermission="com.example.WRITE_USERS" />
protectionLevel
设为dangerous
表示需用户手动授权。readPermission
/writePermission
:自定义权限控制。
2. 路径级权限细化(可选)
若 Provider 方通过 <path-permission>
限制特定路径,调用方需确保拥有对应权限:
<!-- Provider 方配置 -->
<provider ...> <path-permission android:pathPrefix="/admin" android:permission="com.example.ADMIN_PERMISSION" />
</provider>
3. 调用方配置
<manifest ...> <!-- 声明权限 --> <uses-permission android:name="com.example.READ_USERS" /> <uses-permission android:name="com.example.WRITE_USERS" /> <!-- 如果存在路径细化,调用方需声明额外权限 --> <uses-permission android:name="com.example.ADMIN_PERMISSION" /> <application ...> <!-- 无 Provider 声明,直接通过 ContentResolver 调用 --> </application>
</manifest>
4. 动态权限申请
在调用方的 Activity/Fragment 中实现动态权限申请流程:
public class MainActivity extends AppCompatActivity { private static final int REQUEST_READ_PERMISSION = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 检查权限 if (ContextCompat.checkSelfPermission(this, "com.example.READ_USERS") != PackageManager.PERMISSION_GRANTED) { // 权限未授予,显示申请弹窗 ActivityCompat.requestPermissions(this, new String[]{"com.example.READ_USERS"}, REQUEST_READ_PERMISSION); } else { // 已授权,执行数据访问 queryData(); } } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_READ_PERMISSION) { if (grantResults.length > 0 && grantResults[0]== PackageManager.PERMISSION_GRANTED) { queryData(); } else { // 权限被拒绝,提示用户 Toast.makeText(this, "权限被拒绝,无法读取数据",Toast.LENGTH_SHORT).show(); } } } private void queryData() { // 通过 ContentResolver 访问 Provider 数据 Cursor cursor = getContentResolver().query( UserContract.CONTENT_URI, null, null, null, null ); // 处理查询结果... }
}
同一权限组内的权限只需申请一次(如
READ_CONTACTS
和WRITE_CONTACTS
属于同一组)
4. 用户拒绝后引导设置
若用户勾选“不再询问”,需引导用户前往系统设置手动开启权限(可通过 shouldShowRequestPermissionRationale
判断)。
if (ActivityCompat.shouldShowRequestPermissionRationale(this,"com.example.READ_USERS")) { // 用户之前可能拒绝过权限但未勾选“不再询问”// 展示解释性弹窗后再次申请
} else { // 用户勾选“不再询问”或系统禁止权限(如厂商定制 ROM 限制)// 跳转系统设置界面手动开启权限 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent);
}
若用户从未请求过该权限,
shouldShowRequestPermissionRationale()
也会返回false
。但此时代码通常不会进入此分支(因首次请求时直接调用requestPermissions()
)。部分设备可能不支持
Settings.ACTION_APPLICATION_DETAILS_SETTINGS
,需增加异常捕获并提示用户手动查找权限设置 。
四、数据变更通知
角色 | 职责 |
---|---|
客户端 | 注册 ContentObserver 并实现 onChange 回调逻辑(如刷新 UI)。 |
ContentProvider | 数据变更时调用 notifyChange 触发通知。 |
系统服务 | 通过 ContentService 统一管理观察者,完成消息分发。 |
1. 客户端注册观察者
在使用数据的客户端(如 Activity、Fragment)中,通过 ContentResolver
注册 ContentObserver
,并指定监听的目标 URI,从而实时更新UI。
// 使用者(Activity)通过ContentResolver注册观察者
getContentResolver().registerContentObserver( UserContract.CONTENT_URI, true, // 是否监听子 URI new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { // 数据变化时触发回调} }
);
registerContentObserver
是客户端主动调用的方法,用于绑定观察者与目标数据 URI。true
表示监听该 URI 及其所有子路径(如content://com.example.provider/users/
)的数据变更。
2. 提供者触发通知
在 ContentProvider 中,当数据发生变更(如 insert
、update
、delete
)时,需调用 notifyChange
方法触发回调:
// 在 Provider 的 insert/update/delete 方法中
getContext().getContentResolver().notifyChange(uri, null);
notifyChange
会通知所有注册了该 URI 的观察者。- 可通过第二个参数
observer
指定跳过特定观察者(通常设为null
)。
3. 系统级支持
- ContentService:负责管理所有注册的观察者,以树形结构维护 URI 监听关系,实现高效的跨进程通知分发。
- Binder 机制:底层通过 Binder 传递观察者对象(封装为
Transport
代理),确保跨进程通信的可行性。
客户端需主动注册观察者监听 URI,而通知触发由 Provider 发起,两者通过系统服务协同实现实时数据同步。
五、多表关联与视图
场景 | CODE_BOOKS_WITH_AUTHORS (多表关联查询) | CODE_VIEW_BOOKS (视图查询) |
---|---|---|
实现方式 | 运行时动态拼接 SQL JOIN | 预定义视图(静态 SQL) |
灵活性 | 支持动态筛选条件(如 LIKE ) | 筛选条件受视图定义限制 |
性能 | 依赖索引优化 | 视图可预编译优化 |
适用场景 | 需要灵活关联条件的查询 | 高频复杂查询(如报表) |
字段冲突解决 | 需手动设置列别名(AS ) | 视图定义时已解决别名问题 |
1. 定义 Contract 类
public final class BookstoreContract {public static final String AUTHORITY = "com.example.bookstore.provider";public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);// 动态 JOIN 查询的 URI 路径public static class BooksWithAuthors implements BaseColumns {public static final String PATH = "books_with_authors";public static final Uri CONTENT_URI= Uri.withAppendedPath(BASE_URI, PATH);public static final String CONTENT_TYPE= "vnd.android.cursor.dir/vnd.bookstore.books_with_authors";}// 视图查询的 URI 路径public static class ViewBooks implements BaseColumns {public static final String PATH = "view_books";public static final Uri CONTENT_URI= Uri.withAppendedPath(BASE_URI, PATH);public static final String CONTENT_TYPE= "vnd.android.cursor.dir/vnd.bookstore.view_books";}
}
2. ContentProvider 实现类
- 多表动态关联查询(
CODE_BOOKS_WITH_AUTHORS
)- 用
SQLiteQueryBuilder
动态构建LEFT JOIN
查询,关联books
表和authors
表。 setTables()
指定关联逻辑,setProjectionMap()
解决字段冲突(如列别名定义)。
- 用
- 视图查询(
CODE_VIEW_BOOKS
)- 预定义视图
view_books_with_authors
,将LEFT JOIN
逻辑固化到数据库中。 - 直接通过视图名称查询数据,简化复杂查询的调用过程。
- 预定义视图
public class BookstoreProvider extends ContentProvider {private static final String TAG = "BookstoreProvider";private SQLiteOpenHelper mDbHelper;// UriMatcher 标识码定义private static final int CODE_BOOKS_WITH_AUTHORS = 100; // 动态 JOIN 查询private static final int CODE_VIEW_BOOKS = 200; // 视图查询private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);static {sUriMatcher.addURI(BookstoreContract.AUTHORITY,BookstoreContract.BooksWithAuthors.PATH,CODE_BOOKS_WITH_AUTHORS);sUriMatcher.addURI(BookstoreContract.AUTHORITY,BookstoreContract.ViewBooks.PATH,CODE_VIEW_BOOKS);}// 列名映射(解决多表字段冲突)private static final HashMap<String, String> sBooksMap= new HashMap<>();static {sBooksMap.put(BooksWithAuthors._ID, "books._id AS _id");sBooksMap.put("title", "books.title");sBooksMap.put("author_name", "authors.name AS author_name");}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();String table = null; // 默认表名switch (sUriMatcher.match(uri)) {// 动态 JOIN 查询(CODE_BOOKS_WITH_AUTHORS)case CODE_BOOKS_WITH_AUTHORS:queryBuilder.setTables("books LEFT JOIN authors ON books.author_id = authors._id");queryBuilder.setProjectionMap(sBooksMap);break;// 视图查询(CODE_VIEW_BOOKS)case CODE_VIEW_BOOKS:table = "view_books_with_authors"; // 直接查询预定义的视图queryBuilder.setTables(table);break;default:throw new IllegalArgumentException("Unknown URI: " + uri);}SQLiteDatabase db = mDbHelper.getReadableDatabase();Cursor cursor;if (table == null) {// 动态 JOIN 查询cursor = queryBuilder.query(db,projection,selection,selectionArgs,null, // groupBynull, // havingsortOrder);} else {// 视图查询(直接使用标准查询)cursor = db.query(table,projection,selection,selectionArgs,null, // groupBynull, // havingsortOrder);}cursor.setNotificationUri(getContext().getContentResolver(), uri);return cursor;}@Overridepublic boolean onCreate() {mDbHelper = new BookstoreDbHelper(getContext());return true;}// 其他必要方法(insert/update/delete/getType)省略...
}
3. 创建数据库视图(SQLiteOpenHelper)
public class BookstoreDbHelper extends SQLiteOpenHelper {private static final String DATABASE_NAME = "bookstore.db";private static final int DATABASE_VERSION = 1;// 基表定义private static final String SQL_CREATE_BOOKS ="CREATE TABLE books (" +"_id INTEGER PRIMARY KEY AUTOINCREMENT," +"title TEXT NOT NULL," +"author_id INTEGER," +"FOREIGN KEY(author_id) REFERENCES authors(_id))";private static final String SQL_CREATE_AUTHORS ="CREATE TABLE authors (" +"_id INTEGER PRIMARY KEY AUTOINCREMENT," +"name TEXT NOT NULL)";// 视图定义(固化多表关联逻辑)private static final String SQL_CREATE_VIEW_BOOKS_WITH_AUTHORS ="CREATE VIEW view_books_with_authors AS " +"SELECT books._id, books.title, authors.name AS author_name " +"FROM books LEFT JOIN authors ON books.author_id = authors._id";@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(SQL_CREATE_AUTHORS);db.execSQL(SQL_CREATE_BOOKS);db.execSQL(SQL_CREATE_VIEW_BOOKS_WITH_AUTHORS); // 创建视图}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP VIEW IF EXISTS view_books_with_authors");db.execSQL("DROP TABLE IF EXISTS books");db.execSQL("DROP TABLE IF EXISTS authors");onCreate(db);}
}
4. 客户端调用对比
// 动态 JOIN 查询(CODE_BOOKS_WITH_AUTHORS)
Cursor dynamicJoinCursor = getContentResolver().query(BookstoreContract.BooksWithAuthors.CONTENT_URI,new String[]{"title", "author_name"},"author_name LIKE ?",new String[]{"张%"},null
);// 视图查询(CODE_VIEW_BOOKS)
Cursor viewCursor = getContentResolver().query(BookstoreContract.ViewBooks.CONTENT_URI,new String[]{"title", "author_name"},null,null,"title ASC"
);
六、异步处理
场景 | 推荐方案 | 优势 | 缺点 |
---|---|---|---|
批量写入 | 线程池 + Handler | 高吞吐量,可控并发度 | 代码复杂度高,回调繁琐 |
列表数据加载 | AsyncQueryHandler | 自动线程管理,简化代码 | Android 11+ 已废弃,推荐用协程 |
实时数据同步 | ContentObserver | 精准监听特定数据源变化 | 频繁更新可能引发过多回调,延迟+消耗 |
1. 异步批量插入(线程池 + Handler)
场景:后台线程执行大数据量插入,避免阻塞 UI 线程(主线程)。
public class BookProvider extends ContentProvider {private ExecutorService mExecutor = Executors.newFixedThreadPool(4);private Handler mHandler = new Handler(Looper.getMainLooper());private SQLiteOpenHelper mDbHelper;@Overridepublic boolean onCreate() {mDbHelper = new BookDbHelper(getContext());return true;}@Overridepublic int bulkInsert(Uri uri, ContentValues[] values) {mExecutor.execute(() -> {SQLiteDatabase db = mDbHelper.getWritableDatabase();db.beginTransaction();try {for (ContentValues value : values) {db.insert("books", null, value);}db.setTransactionSuccessful();} finally {db.endTransaction();// 主线程通知数据变更mHandler.post(() -> {getContext().getContentResolver().notifyChange(uri, null);});}});return values.length; // 返回预计插入数量(实际需回调确认)}
}
调用示例:
ContentValues[] valuesArray = new ContentValues[100];
// 填充 valuesArray...
ContentResolver resolver = getContentResolver();
resolver.bulkInsert(BookContract.CONTENT_URI, valuesArray);
- 使用线程池管理并发写入
- 通过
Handler
切换至主线程发送变更通知
2. 异步查询(AsyncQueryHandler)
场景:列表页异步加载数据并自动更新 UI。
public class BookListActivity extends AppCompatActivity {private ListView mListView;private SimpleCursorAdapter mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_book_list);mListView = findViewById(R.id.list_view);// 初始化适配器mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, null, new String[]{"title", "author"}, new int[]{android.R.id.text1, android.R.id.text2}, 0);mListView.setAdapter(mAdapter);// 启动异步查询:AsyncQueryHandler asyncHandler= new AsyncQueryHandler(getContentResolver()) {@Overrideprotected void onQueryComplete(int token, Object cookie, Cursor cursor) {mAdapter.swapCursor(cursor);}};asyncHandler.startQuery(0, null, BookContract.CONTENT_URI,new String[]{"_id", "title", "author"}, "category=?", new String[]{"tech"}, "rating DESC");}
}
AsyncQueryHandler
自动管理线程切换- 查询结果直接更新
CursorAdapter
关联的 UI
3. 数据变更监听(ContentObserver)
场景:实时监测收藏夹数据变化并刷新界面。
public class FavoriteFragment extends Fragment {private FavoriteAdapter mAdapter;private ContentObserver mObserver;@Overridepublic void onResume() {super.onResume();// 注册监听器:mObserver = new ContentObserver(new Handler()) {@Overridepublic void onChange(boolean selfChange) {loadData(); // 数据变化时重新加载}};getActivity().getContentResolver().registerContentObserver(BookContract.Favorite.CONTENT_URI,true,mObserver);}@Overridepublic void onPause() {super.onPause();// 注销监听防止内存泄漏:getActivity().getContentResolver().unregisterContentObserver(mObserver);}private void loadData() {new AsyncTask<Void, Void, Cursor>() {@Overrideprotected Cursor doInBackground(Void... voids) {return getActivity().getContentResolver().query(BookContract.Favorite.CONTENT_URI,null, null, null, "last_updated DESC");}@Overrideprotected void onPostExecute(Cursor cursor) {mAdapter.swapCursor(cursor);}}.execute();}
}
ContentObserver
监听指定 URI 的数据变化AsyncTask
执行后台查询(注意 Android 11+ 已弃用,建议替换为协程或线程池)
相关文章:
【Android】四大组件之ContentProvider
目录 一、什么是 ContentProvider 二、创建和使用 ContentProvider 三、跨应用权限控制 四、数据变更通知 五、多表关联与视图 六、异步处理 你手机里的通讯录,存储了所有联系人的信息。如果你想把这些联系人信息分享给其他App,就可以通过ContentP…...
Qwen3 发布:优化编码与代理能力,强化 MCP 支持引领 AI 新潮流
人工智能领域的每一次重大突破都如同璀璨星辰,照亮了人类前行的道路。2025 年 4 月 29 日凌晨,阿里巴巴旗下的 Qwen 官方团队正式发布了最新一代大语言模型 ——Qwen3,犹如一颗重磅炸弹,在 AI 领域掀起了惊涛骇浪。此次发布&#…...
LEETERS题解
【题目描述】 给出一个rowcolrowcol的大写字母矩阵,一开始的位置为左上角,你可以向上下左右四个方向移动,并且不能移向曾经经过的字母。问最多可以经过几个字母。 【输入】 第一行,输入字母矩阵行数RR和列数SS,1≤R,S≤…...
图像加密算法概述
版本: 1.0 日期: 2025年5月1日 目录 引言 1.1 什么是图像加密?1.2 为什么需要图像加密?1.3 图像数据的特点与加密挑战加密基础概念 2.1 明文与密文2.2 加密与解密2.3 密钥2.4 对称加密与非对称加密为什么传统文本加密算法不完全适用于图像? 3.1 数据量巨大3.2 高度冗余性…...
loads、dumps、jsonpath使用场景
在处理JSON数据时,loads、dumps 和 jsonpath 是三个非常有用的工具或概念。它们各自在不同的场景下发挥作用,让我们一一来看: 1. loads loads 函数是 Python 中 json 模块的一部分,用于将 JSON 格式的字符串解析成 Python 的数据…...
Winform(7.序列化方式整理)
今天我又对序列化方式进行了整理,可以与上一篇序列化方式一起看 一.序列化方式(四种) 1.二进制序列化 //定义 Person 类,需要标记为可序列化 [Serializable] public class Person { public string Name{get;set;} public int Age{get;set;} } 在进行二进制序列化…...
通过AI的联网功能提升搜索检索能力
以百度ai搜索(百度AI搜索 - 办公学习一站解决)为例,ai会自动根据问题搜集现有互联网文章,避免人工通过传统检索引擎的结果逐个去查找,这种方式文章的相关性会更高。 tip:快速查看每篇文档,仅关…...
Spring IoC容器的设计与实现
Spring整体架构与模块划分 核心容器(Core Container) spring-core 基础工具类:如资源加载(Resource接口)、反射工具(ReflectionUtils)、类型转换(ConversionService)。…...
使用vue的插值表达式渲染变量,格式均正确,但无法渲染
如图,作者遇到的问题为,输入以下代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…...
数据库 AI 助手测评:Chat2DB、SQLFlow 等工具如何提升开发效率?
一、引言:数据库开发的 “效率革命” 正在发生 在某互联网金融公司的凌晨故障现场,资深 DBA 正满头大汗地排查一条执行超时的 SQL—— 该语句涉及 7 张核心业务表的复杂关联,因索引缺失导致全表扫描,最终引发交易系统阻塞。这类场景在传统数据库开发中屡见不鲜:据 Gartne…...
21.1Linux中的LCD驱动实验(知识)_csdn
1、LCD 和 LTDC 简介 1.1、LCD 简介 1.1.1、分辨率 1.1.2、像素格式 可以看到红、绿、蓝每个8位,还有一位是A7~A0就是透明通道,32位ARG8888。 1.1.3、LCD 屏幕接口 1.1.4、LCD 时间参数 如果将 LCD 显示一帧图像的过程想象成绘画,那么…...
Angular教程前言:历史、安装与用途
Angular 是一个强大且流行的开源前端 Web 应用程序框架,由 Google 开发并维护 1。它在现代 Web 开发中占据着重要的地位,尤其在构建动态、高效且可扩展的 Web 应用程序方面表现出色,特别适用于单页应用程序 (SPA) 和复杂的用户界面 1。本教程…...
node.js模块化步骤(各标准区别)CommonJS规范、AMD规范、UMD规范、ES Modules (ESM)
前后端建议统一使用ESM 文章目录 Node.js模块化发展历程与标准对比一、模块化的意义1.1 解决的核心问题1.2 没有模块化的问题 二、CommonJS规范2.1 核心特征2.2 实现示例 三、AMD (Asynchronous Module Definition)3.1 特点3.2 代码示例 四、UMD (Universal Module Definition)…...
Unity图片导入设置
🏆 个人愚见,没事写写笔记 🏆《博客内容》:Unity3D开发内容 🏆🎉欢迎 👍点赞✍评论⭐收藏 🔎Unity支持的图片格式 ☀️BMP:是Windows操作系统的标准图像文件格式,特点是…...
MySQL与分布式架构的碰撞
目录 一、分布式架构的核心挑战与MySQL的应对策略 1.1 高并发与扩展性 1.3 高可用与容灾 二、MySQL分布式架构的核心技术实现 2.1 读写分离与主从复制(扩展) 2.2 数据分片与分布式存储(扩展) 2.3 MySQL Cluster与NDB引擎&am…...
python-MySQL鏈接
python鏈接MySQL,主要利用庫 pip install mysql-connector-pythonimport mysql.connector# 配置连接参数 config {"user": "your_username","password": "your_password","host": "localhost", # 或…...
cv::remap() 和 cv::undistortion() 的区别
在 OpenCV 中,cv::remap 和 cv::undistort 都用于处理图像畸变校正,但它们的实现方式和应用场景有显著区别。以下是详细对比: 1. cv::undistort:直接畸变校正 功能 输入:原始畸变图像 相机内参矩阵 (cameraMatrix) …...
【AI提示词】决策树专家
提示说明 一位熟悉决策树算法的机器学习专家,擅长用树状图量化不同选择的结果概率。 提示词 # Role: 决策树专家## Profile - language: 中文 - description: 一位熟悉决策树算法的机器学习专家,擅长用树状图量化不同选择的结果概率 - background: 决…...
【中间件】bthread_数据结构_学习笔记
bthread数据结构 bthread_数据结构_学习笔记1 pthread_cond_t1.1 definition1.2 解释1.3 设计动机1.4 使用示例1.5 注意事项1.6 进一步延伸:pthread_cond_s 2 pthread_mutex_t bthread_数据结构_学习笔记 1 pthread_cond_t POSIX线程库 /usr/include/x86_64-linux…...
VM虚拟机安装CentOS7.9
目录 1.下载CentOS7.9 2.VM虚拟机选择自定义,然后一直傻瓜式下一步 3.选择编辑虚拟机设置,然后选择刚刚下载的ISO 4.输入 ip addr 获取ip地址 5.用Xshell连接 1.下载CentOS7.9 链接:https://pan.baidu.com/s/1kW2gGWnbcjNtq4kz46LKVw?p…...
C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 18)
🎁个人主页:工藤新一 🔍系列专栏:C面向对象(类和对象篇) 🌟心中的天空之城,终会照亮我前方的路 🎉欢迎大家点赞👍评论📝收藏⭐文章 文章目录 二…...
Cribl 数据脱敏 更多方法 MASK (三)
我做过好几个cribl 数据脱敏的实验: Cribl 脱敏mask-CSDN博客...
【笔记】深度学习模型训练的 GPU 内存优化之旅⑤:内存分配篇
开设此专题,目的一是梳理文献,目的二是分享知识。因为笔者读研期间的研究方向是单卡上的显存优化,所以最初思考的专题名称是“显存突围:深度学习模型训练的 GPU 内存优化之旅”,英文缩写是 “MLSys_GPU_Memory_Opt”。…...
【5G 架构】边缘计算平台是如何与3GPP网络连接的?
博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G技术研究。 博客内容主要围绕…...
5.0.0 GripSpliter的使用(探讨水平竖直对齐参数)
布局控件Grid 配合 GridSplitter 无需编写任何代码 就能实现网格大小可拖动。 其HorizontalAlignment、VerticalAlignment属性的使用非常具有迷惑性;本文做了一些一些实验,总结为把这两个属性均设置为strech即可。 总结如下:经过实验,发现以下情况可以正常工作。 水平方向…...
python如何把pdf转word
在Python中将PDF转换为Word文档(.docx)比反向转换(Word转PDF)更具挑战性,因为PDF是固定格式,而Word是可编辑格式。以下是几种可行的方法及详细步骤: 方法1:使用 pdf2docx 库 pdf2do…...
go实现双向链表
需求 实现双向链表的节点生成、正反向遍历、指定删除。 实现 package mainimport ("fmt" )type zodiac_sign struct {number intdizhi stringanimal stringyear intprevious *zodiac_signnext *zodiac_sign }// 添加 // func add_node_by_order(pr…...
33、VS中提示“以下文件中的行尾不一致。是否将行尾标准化?“是什么意思?
在Visual Studio(VS)中遇到提示“以下文件中的行尾不一致。是否将行尾标准化?”时,意味着当前打开或正在编辑的文件内部存在行尾符(EOL,End-Of-Line)格式不统一的情况。以下是详细解释和应对建议…...
C 语言 第五章 指针(5)
目录 函数参数传递机制:地址传递 值传递 简单变量指针作为形参 举例1: 举例2: 举例3: 数组作为形参 举例: 函数参数传递机制:地址传递 值传递 void test(int a, int b) { a 10; b 20; print…...
Python项目源码69:Excel数据筛选器1.0(tkinter+sqlite3+pandas)
功能说明:以下是一个使用Tkinter和Pandas实现的完整示例,支持Excel数据读取、双表格展示和高级条件筛选功能: 1.文件操作:点击"打开文件"按钮选择Excel文件(支持.xlsx和.xls格式),自…...
机器人--架构及设备
机器人的四大组成部分 控制系统 驱控系统 驱控驱动系统控制系统。 注意,这里的控制系统不是机器人层面的控制系统,属于更小层级的,驱控系统的控制系统。 驱动系统: 一般指硬件设备,比如电机驱动器,I/O…...
机器人--主机--控制系统
机器人主机 机器人主机,即控制系统。 作用 机器人主机的核心功能 传感器数据处理:处理摄像头、激光雷达、IMU等数据。 运行SLAM/导航算法:如Google Cartographer、RTAB-Map。 路径规划与控制:执行A*、DWA等算法。 通信管理&a…...
Stm32 烧录 Micropython
目录 前言 准备工作 开始操作 问题回顾 后记 前言 去年曾经尝试Pico制作openmv固件,由于知识储备不够最后失败了,留了一个大坑,有了前几天的基础,慢慢补齐知识,最近这一周一直在学习如何编译Stm固件并烧录到单片机…...
leetcode 977. Squares of a Sorted Array
题目描述 双指针法一 用right表示原数组中负数和非负数的分界线。 nums[0,right-1]的是负数,nums[right,nums.size()-1]是非负数。 然后用合并两个有序数组的方法。合并即可。 class Solution { public:vector<int> sortedSquares(vector<int>&…...
使用Nexus搭建远程maven仓库
1、Nexus介绍 Nexus 是 Sonatype 公司的一款用于搭建私服的产品,使用非常广泛。在早期,我们都拿Nexus当maven私服仓库,后来,随着版本不断更新,它支持的数据类型越来越多,比如npm仓库,nuget仓库&…...
坚鹏:工行《DEEPSEEK赋能银行智能办公及数字化营销服务》培训
中国工商银行上海市分行《DEEPSEEK赋能银行智能办公及数字化营销服务》培训圆满落幕 中国工商银行作为全球领先的综合性金融服务集团,始终走在金融科技创新的前沿。截至2024年末,工商银行总资产规模突破40万亿元,连续多年稳居全球银行榜首。在…...
操作系统OS是如何指挥外围设备的呢?
众所周知,OS的职责之一就是管理外围设备,比如常见的磁盘、硬盘、显示器、麦克风等,但并不是外围设备的一切都必须由OS管理,比如无线鼠标上的开关键,当你通过它关闭鼠标时,这个操作并不会经过OS,…...
实现Sentinel与Nacos的规则双向同步
实现Sentinel与Nacos的规则双向同步:完整解决方案 前言 在微服务架构中,流量控制和熔断降级是保障系统稳定性的重要手段。阿里开源的Sentinel作为一款轻量级的流量控制组件,常被用于实现这些功能。然而,在实际生产环境中&#x…...
2025五一杯数学建模A题:支路车流量推测问题,思路分析+模型代码
一持续更新,见文末名片 二、问题背景 想象一下,城市的道路如同一张巨大的脉络图,主路如同大动脉,配备着车流量监测设备,能实时记录车流量数据,就像我们身体的传感器一样。然而,当多条支路像毛细…...
Linux51 安装baidunetdisk yum install rpm -ivh
推测网卡 感觉是不是以前哪里设置了下 deepseek说的这个设置 我没有设置过 这个不会弄啊 准备用虚拟机安个软件 神奇 换了这个命令又能打开网卡了 参考了这个 参考 之前地址我觉得配置错误 动态分配 我就删掉ip地址了 路由表中无ip地址吗? OK 卸载 运…...
【Python-Day 8】从入门到精通:Python 条件判断 if-elif-else 语句全解析
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...
若依 FastAPI + Vue3 项目 Docker 部署笔记( 启动器打包教程)
本文记录了将 start.bat 打包成 .exe 启动器的详细教程,适合项目交付或导师演示用。 🧭 一、如何将 start.bat 打包为启动器 .exe(含图标 自动打开浏览器) ✅ 1. 创建三大功能脚本 start.bat → 启动项目(docke…...
Lebesgue测度和积分理论发展概观
1. 发展背景 积分可以从两个角度来理解。首先,积分是微分的逆函数,因此积分是反导数(译注:但积分是独立于微分的,不能微分的函数也可能可积)。然而,这是一个非常抽象的概念。其次,两点之间的积分可以看…...
算法题题型总结
二叉树题型 解法综述:二叉树的解法,基本上都是依赖遍历,再加上递归的思路来做的。那递归又分为深度优先和广度优先。深度优先算法,前序,中序,后序。广度优先,利用先进先出队列,一层…...
网络编程——TCP和UDP详细讲解
文章目录 TCP/UDP全面详解什么是TCP和UDP?TCP如何保证可靠性?1. 序列号(Sequence Number)2. 确认应答(ACK)3. 超时重传(Timeout Retransmission)4. 窗口控制(Sliding Win…...
Qt多线程TCP服务器实现指南
在Qt中实现多线程TCP服务器可以通过为每个客户端连接分配独立的线程来处理,以提高并发性能。以下是一个分步实现的示例: 1. 自定义工作线程类(处理客户端通信) // workerthread.h #include <QObject> #include <QTcpSo…...
【经管数据】A股上市公司资产定价效率数据(2000-2023年)
数据简介:资产定价效率是衡量市场是否能够有效、准确地反映资产内在价值的重要指标。在理想的市场条件下,资产的市场价格应该与其内在价值保持一致,即市场定价效率达到最高。然而,在实际市场中,由于信息不对称、交易摩…...
打包 Python 项目为 Windows 可执行文件:高效部署指南
Hypackpy 是一款由白月黑羽开发的 Python 项目打包工具,它与 PyInstaller 等传统工具不同,通过直接打包解释器环境和项目代码,并允许开发者修改配置文件以排除不需要的内容,从而创建方便用户一键运行的可执行程序。以下是使用 Hyp…...
【QNX+Android虚拟化方案】138 - USB 底层传输原理
【QNX+Android虚拟化方案】138 - USB 底层传输原理 1. USB 数据包的格式2. 数据传输事务过程3. 四种传输类型3.1 批量传输3.2 中断传输3.3 实时传输3.4 控制传输4. USB 设备枚举过程4.1 Attached: 发送控制传输,读取设备描述符4.2 Power -> Default 这个状态无数据传输4.3 …...
QT6 源(66)篇三:阅读与注释类 QAbstractSpinBox ,这是螺旋框的基类,附上源码
(9)所有代码来自于头文件 qabstractspinbox . h : #ifndef QABSTRACTSPINBOX_H #define QABSTRACTSPINBOX_H#include <QtWidgets/qtwidgetsglobal.h> #include <QtWidgets/qwidget.h> #include <QtGui/qvalidator.h>/* QT_CONFIG宏实…...