深入分析 Android ContentProvider (五)

news2024/9/23 1:24:17

文章目录

    • 深入分析 Android ContentProvider (五)
    • ContentProvider 的性能优化和实践案例
      • 1. 性能优化技巧
        • 1.1. 数据库索引优化
          • 示例:添加索引
        • 1.2. 批量操作与事务管理
          • 示例:批量插入操作
        • 1.3. 使用异步操作
          • 示例:使用 AsyncTask 进行异步查询
        • 1.4. 缓存机制
          • 示例:使用 LruCache 进行内存缓存
        • 1.5. 使用 Loader 进行异步加载
          • 示例:使用 CursorLoader
      • 2. 实践案例
        • 2.1 案例一:消息应用的数据同步
          • 消息 ContentProvider 实现
        • 2.2 案例二:音乐播放器的媒体库管理
          • 媒体库 ContentProvider 实现
      • 3. 总结

深入分析 Android ContentProvider (五)

ContentProvider 的性能优化和实践案例

在实践中,合理的性能优化可以显著提升 ContentProvider 的效率和用户体验。以下是一些具体的性能优化技巧和实际案例,以便更好地理解和应用 ContentProvider。

1. 性能优化技巧

1.1. 数据库索引优化

在频繁进行查询操作的字段上添加索引,可以显著提高查询性能。数据库索引可以加快数据检索的速度,尤其是在大型数据集上。

示例:添加索引

在创建数据库表时,可以通过 SQL 语句为某些列创建索引:

private static final String CREATE_TABLE =
    "CREATE TABLE " + TABLE_NAME + " (" +
    COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
    COLUMN_NAME + " TEXT NOT NULL);";

private static final String CREATE_INDEX =
    "CREATE INDEX index_name ON " + TABLE_NAME + " (" + COLUMN_NAME + ");";

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL(CREATE_TABLE);
    db.execSQL(CREATE_INDEX);
}
1.2. 批量操作与事务管理

在执行大批量的数据插入、更新或删除操作时,使用事务可以减少数据库锁的开销,并提高操作的整体性能。

示例:批量插入操作
public void bulkInsertData(List<ContentValues> valuesList) {
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    db.beginTransaction();
    try {
        for (ContentValues values : valuesList) {
            db.insertOrThrow(TABLE_NAME, null, values);
        }
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
}
1.3. 使用异步操作

避免在主线程中进行数据库操作,使用 AsyncTaskLoaderRxJava 等异步框架进行数据操作,确保应用的 UI 流畅性。

示例:使用 AsyncTask 进行异步查询
private class QueryTask extends AsyncTask<Void, Void, Cursor> {
    @Override
    protected Cursor doInBackground(Void... voids) {
        Uri uri = Uri.parse("content://com.example.provider/examples");
        return getContentResolver().query(uri, null, null, null, "name ASC");
    }

    @Override
    protected void onPostExecute(Cursor cursor) {
        if (cursor != null) {
            // 处理查询结果
            cursor.close();
        }
    }
}
1.4. 缓存机制

在数据访问频繁的场景中,使用缓存机制可以显著提高性能。可以选择内存缓存(如 LruCache)或磁盘缓存来缓存常用数据,减少数据库查询的次数。

示例:使用 LruCache 进行内存缓存
private LruCache<String, Bitmap> memoryCache;

public void initCache() {
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    final int cacheSize = maxMemory / 8;
    memoryCache = new LruCache<>(cacheSize);
}

public void addBitmapToCache(String key, Bitmap bitmap) {
    if (getBitmapFromCache(key) == null) {
        memoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromCache(String key) {
    return memoryCache.get(key);
}
1.5. 使用 Loader 进行异步加载

Loader 可以在异步线程中加载数据,避免主线程阻塞,并在数据加载完成时自动更新 UI。

示例:使用 CursorLoader
public class ExampleActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
    private static final int LOADER_ID = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);
        getSupportLoaderManager().initLoader(LOADER_ID, null, this);
    }

    @NonNull
    @Override
    public Loader<Cursor> onCreateLoader(int id, @Nullable Bundle args) {
        Uri uri = Uri.parse("content://com.example.provider/examples");
        return new CursorLoader(this, uri, null, null, null, "name ASC");
    }

    @Override
    public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
        // 更新 UI
    }

    @Override
    public void onLoaderReset(@NonNull Loader<Cursor> loader) {
        // 清理资源
    }
}

2. 实践案例

2.1 案例一:消息应用的数据同步

在消息应用中,消息数据通常需要在客户端和服务器之间同步。使用 ContentProvider,可以方便地实现数据的本地存储和跨进程访问,同时结合 Loader 和异步任务,确保数据加载和更新的流畅性。

消息 ContentProvider 实现
public class MessageProvider extends ContentProvider {
    private static final String AUTHORITY = "com.example.provider";
    private static final String BASE_PATH = "messages";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);

    private static final int MESSAGES = 1;
    private static final int MESSAGE_ID = 2;
    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        uriMatcher.addURI(AUTHORITY, BASE_PATH, MESSAGES);
        uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", MESSAGE_ID);
    }

    private SQLiteDatabase database;

    @Override
    public boolean onCreate() {
        DatabaseHelper dbHelper = new DatabaseHelper(getContext());
        database = dbHelper.getWritableDatabase();
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        switch (uriMatcher.match(uri)) {
            case MESSAGES:
                return database.query(DatabaseHelper.TABLE_MESSAGES, projection, selection, selectionArgs, null, null, sortOrder);
            case MESSAGE_ID:
                selection = DatabaseHelper.COLUMN_ID + "=?";
                selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
                return database.query(DatabaseHelper.TABLE_MESSAGES, projection, selection, selectionArgs, null, null, sortOrder);
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        long id = database.insert(DatabaseHelper.TABLE_MESSAGES, null, values);
        getContext().getContentResolver().notifyChange(uri, null);
        return ContentUris.withAppendedId(CONTENT_URI, id);
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int rowsDeleted;
        switch (uriMatcher.match(uri)) {
            case MESSAGES:
                rowsDeleted = database.delete(DatabaseHelper.TABLE_MESSAGES, selection, selectionArgs);
                break;
            case MESSAGE_ID:
                selection = DatabaseHelper.COLUMN_ID + "=?";
                selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
                rowsDeleted = database.delete(DatabaseHelper.TABLE_MESSAGES, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsDeleted;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int rowsUpdated;
        switch (uriMatcher.match(uri)) {
            case MESSAGES:
                rowsUpdated = database.update(DatabaseHelper.TABLE_MESSAGES, values, selection, selectionArgs);
                break;
            case MESSAGE_ID:
                selection = DatabaseHelper.COLUMN_ID + "=?";
                selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
                rowsUpdated = database.update(DatabaseHelper.TABLE_MESSAGES, values, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsUpdated;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        switch (uriMatcher.match(uri)) {
            case MESSAGES:
                return "vnd.android.cursor.dir/vnd.com.example.provider.messages";
            case MESSAGE_ID:
                return "vnd.android.cursor.item/vnd.com.example.provider.message";
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
    }
}
2.2 案例二:音乐播放器的媒体库管理

在音乐播放器应用中,媒体库管理需要高效的数据存储和查询功能。ContentProvider 可以为应用提供统一的数据访问接口,并结合批量操作和事务管理,实现高效的数据管理。

媒体库 ContentProvider 实现
public class MediaProvider extends ContentProvider {
    private static final String AUTHORITY = "com.example.provider";
    private static final String BASE_PATH = "media";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);

    private static final int MEDIA = 1;
    private static final int MEDIA_ID = 2;
    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        uriMatcher.addURI(AUTHORITY

, BASE_PATH, MEDIA);
        uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", MEDIA_ID);
    }

    private SQLiteDatabase database;

    @Override
    public boolean onCreate() {
        DatabaseHelper dbHelper = new DatabaseHelper(getContext());
        database = dbHelper.getWritableDatabase();
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        switch (uriMatcher.match(uri)) {
            case MEDIA:
                return database.query(DatabaseHelper.TABLE_MEDIA, projection, selection, selectionArgs, null, null, sortOrder);
            case MEDIA_ID:
                selection = DatabaseHelper.COLUMN_ID + "=?";
                selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
                return database.query(DatabaseHelper.TABLE_MEDIA, projection, selection, selectionArgs, null, null, sortOrder);
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        long id = database.insert(DatabaseHelper.TABLE_MEDIA, null, values);
        getContext().getContentResolver().notifyChange(uri, null);
        return ContentUris.withAppendedId(CONTENT_URI, id);
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int rowsDeleted;
        switch (uriMatcher.match(uri)) {
            case MEDIA:
                rowsDeleted = database.delete(DatabaseHelper.TABLE_MEDIA, selection, selectionArgs);
                break;
            case MEDIA_ID:
                selection = DatabaseHelper.COLUMN_ID + "=?";
                selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
                rowsDeleted = database.delete(DatabaseHelper.TABLE_MEDIA, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsDeleted;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int rowsUpdated;
        switch (uriMatcher.match(uri)) {
            case MEDIA:
                rowsUpdated = database.update(DatabaseHelper.TABLE_MEDIA, values, selection, selectionArgs);
                break;
            case MEDIA_ID:
                selection = DatabaseHelper.COLUMN_ID + "=?";
                selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
                rowsUpdated = database.update(DatabaseHelper.TABLE_MEDIA, values, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsUpdated;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        switch (uriMatcher.match(uri)) {
            case MEDIA:
                return "vnd.android.cursor.dir/vnd.com.example.provider.media";
            case MEDIA_ID:
                return "vnd.android.cursor.item/vnd.com.example.provider.media";
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
    }
}

3. 总结

ContentProvider 是 Android 中强大的数据共享和管理机制,尤其适用于跨进程数据共享和提供统一的数据访问接口。在实际应用中,通过合理设计和优化,可以充分发挥 ContentProvider 的优势,确保数据操作的高效性和安全性。遵循最佳实践并结合具体场景进行性能优化,可以显著提升应用的用户体验和稳定性。

欢迎点赞|关注|收藏|评论,您的肯定是我创作的动力

在这里插入图片描述

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

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

相关文章

FastAPI(七十八)实战开发《在线课程学习系统》接口开发-- 评论

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 梳理下思路 1.判断是否登录 2.课程是否存在 3.如果是回复&#xff0c;查看回复是否存在 4.是否有权限 5.发起评论 首先新增pydantic模型 class Cour…

如何为 DigitalOcean 上的托管数据库收集可观测指标

DigitalOcean 在 2024 年 5 月开始支持在托管数据库&#xff08;PostgreSQL、MySQL、Redis和Kafka&#xff09;中收集可观测指标。我们将在本偏内容中&#xff0c;告诉大家如何使用部署在 DigitalOcean App Platform 上的网络应用程序&#xff0c;为 DigitalOcean 上的 Postgre…

C语言 | Leetcode C语言题解之第283题移动零

题目&#xff1a; 题解&#xff1a; void swap(int *a, int *b) {int t *a;*a *b, *b t; }void moveZeroes(int *nums, int numsSize) {int left 0, right 0;while (right < numsSize) {if (nums[right]) {swap(nums left, nums right);left;}right;} }

新手必备:iPhone新机官网验机流程详解

目录 一、准备工作 二、外包装检查 三、序列号查询 四、开箱验机 五、开机验机 六、功能检测 七、售后服务验证 八、总结 一、准备工作 检查包裹&#xff1a;确保快递包裹完好无损。准备录像设备&#xff1a;使用另一台设备录制整个验机过程&#xff0c;以防日后发生纠…

C# 贪吃蛇游戏

贪吃蛇游戏可分为手动玩法和自动玩法 冯腾飞/贪吃蛇

海山数据库(He3DB)性能优化方案解析

前端优化是一个永恒的话题&#xff0c;每个前端开发者都希望自己的页面能够快速加载&#xff0c;给用户良好的体验。但往往事与愿违。因此&#xff0c;本文从编码优化、构建优化、部署优化三方面入手进行web页面性能优化。 1. 编码优化 1.1. Css优化 1.1.1. 合理使用css选择…

昇思25天学习打卡营第33天|共赴算力时代

文章目录 一、平台简介二、深度学习模型2.1 处理数据集2.2 模型训练2.3 加载模型 三、共赴算力时代 一、平台简介 昇思大模型平台&#xff0c;就像是AI学习者和开发者的超级基地&#xff0c;这里不仅提供丰富的项目、模型和大模型体验&#xff0c;还有一大堆经典数据集任你挑。…

QSqlQuery增删改查

本文记录使用QSqlQuery实现增删改查的过程。 目录 1. 构建表格数据 声明变量 表格、数据模型、选择模型三板斧设置 列表执行查询 列表的水平表头设置 2. 新增一行 构建一个空行 通过dialog返回的修改行数据&#xff0c;update更新 3. 更新一行 获取到需要更新的行 通…

qt--电子相册

一、项目要求 设计一个电子相册&#xff0c;点击上一张&#xff0c;切换到上一张图片&#xff0c;点击下一张&#xff0c;切换到下一张图片。 要求&#xff1a;图片的展示可以循环&#xff08;QList<QString>&#xff09; 要求&#xff1a;界面美观 二、项目代码 本质是通…

力扣141环形链表问题|快慢指针算法详细推理,判断链表是否有环|龟兔赛跑算法

做题链接 目录 前言&#xff1a; 一、算法推导&#xff1a; 1.假设有环并且一定会相遇&#xff0c;那么一定是在环内相遇&#xff0c;且是快指针追上慢指针。 2.有环就一定会相遇吗&#xff1f;快指针是每次跳两步&#xff0c;有没有可能把慢指针跳过去&#xff1f; 3.那一定…

ffmpeg更改视频的帧率

note 视频帧率调整 帧率(fps-frame per second) 例如&#xff1a;原来帧率为30&#xff0c;调整后为1 现象&#xff1a;原来是每秒有30张图像&#xff0c;调整后每秒1张图像&#xff0c;看着图像很慢 实现&#xff1a;在每秒的时间区间里&#xff0c;取一张图像…

数据库 第五次作业

1.触发器 建立两个表:goods(商品表)、orders(订单表)并 在商品表中导入商品记录 建立触发器&#xff0c;订单表中增加订单数量后&#xff0c;商品表商品数量同步减少对应的商品订单出数量,并测试 建立触发器&#xff0c;实现功能:客户取消订单&#xff0c;恢复商品表对应商品的…

Llama 3.1 这一最强模型按时降临!扎克伯格最新的访谈表明:Llama 将会成为 AI 领域中的 Linux 。

&#x1f431; 个人主页&#xff1a;TechCodeAI启航&#xff0c;公众号&#xff1a;TechCodeAI &#x1f64b;‍♂️ 作者简介&#xff1a;2020参加工作&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01; &#x1f4ab; 优质专…

PyQt5 使用系统托盘

Qt5 中 QSystemTrayIcon 类提供了系统托盘的功能 具体功能 初始化 初始化 QSystemTrayIcon 对象, 需要传递一个父控件作为初始参数 import sys from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QWidgetapp QApplication(sys.argv) window QWidget() tray Q…

barrier failt to connect to server: timeout

win10做server&#xff0c;mac做client&#xff0c;一直运行失败&#xff0c;观察日志发现timeout。 最后发现是win10的防火墙入站规则里的Barrier Listener里设置的端口&#xff0c;和Barrier中设置的端口不一致。 因为局域网内有其他同事也用这个&#xff0c;所以修改了端口…

API资源对象CRD、认识Operator-理论知识和认识Operator-初次上手(2024-07-17)

一、API资源对象CRD Kubernetes 自定义资源定义&#xff08;Custom Resource Definition&#xff0c;简称 CRD&#xff09;是一种强大的 Kubernetes API 扩展机制&#xff0c;允许你定义和创建自己的资源类型&#xff0c;以满足您的应用程序或基础设施需求。 CRD 的核心思想是…

原型图设计指南:从基础到精通

用户体验设计师和原型设计的主要功能 PM、网站开发工程师通过展示产品内容、结构和粗略布局来沟通最初产品设想的重要工具&#xff0c;说明用户将如何与产品互动&#xff0c;而不是视觉设计。在大厂中&#xff0c;岗位分工更加细致明确&#xff0c;大部分原型都是产品经理做的&…

Oracle系统表空间的加解密

实验环境 数据库选择的是orclpdb1&#xff0c;当前系统表空间未加密&#xff1a; SQL> show con_nameCON_NAME ------------------------------ ORCLPDB1SQL> select TABLESPACE_NAME, STATUS, ENCRYPTED from dba_tablespaces;TABLESPACE_NAME STATUS …

【Ubuntu】安装 Snipaste 截图软件

Snipaste 下载安装并使用 Snipastefor more information报错解决方案每次启动软件需要输入的命令如下添加开机自启动 下载 下载地址 安装并使用 Snipaste 进入终端输入命令 # 1、进入到 Snipaste-2.8.9-Beta-x86_64.AppImage 所在目录&#xff08;根据自己的下载目录而定&…

uniapp安卓plus原生选择系统文件

uniapp安卓plus原生选择系统文件 效果&#xff1a; 组件代码&#xff1a; <template xlang"wxml" minapp"mpvue"><view></view> </template> <script>export default {name: file-manager,props: {},data() {return {is…