在设备设置中检查 Room 数据库的大小

Posted

技术标签:

【中文标题】在设备设置中检查 Room 数据库的大小【英文标题】:Checking the size of Room database in device settings 【发布时间】:2021-09-05 12:20:50 【问题描述】:

如何通过查看应用设置了解 Room 数据库占用的大小?应用设置的存储部分分为Total、App size、User data和Cache。应用程序数据库是用户数据部分还是缓存?我想估算一下我的数据库有多大,这样我就可以在不占用太多空间的情况下找到我可以保留在数据库中的最大行数。

【问题讨论】:

【参考方案1】:

我想估算一下我的数据库有多大,这样我就可以在不占用太多空间的情况下找到我可以在数据库中保留的最大行数。

行数并不完全等于数据库大小。那是因为数据存储在页面中(默认为 4k)。一个 0 行的表会占用 4k,而 1000 行它仍然可能只占用 4k。

每个 SQLite 实体(表、索引、触发器等)将占用至少 1 页。

您不妨阅读SQLite Database File Format

忽略页面因素,您可以向@Database 类添加一个方法,例如:-

public static long getDBSize(Context context) 

    return (context.getDatabasePath(instance.getOpenHelper().getDatabaseName())).length()
            // Add the shared memory (WAL index) file size
            + (context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-shm")).length()
            // Add the WAL file size
            + (context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-wal")).length()
            // Add the journal file size
            + (context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-journal")).length();

工作示例 (基于用于回答某些问题的现有应用)

@Database 类:-

检索组合的文件大小:-

@Database(entities = State.class,Location.class,School.class,TimerDesign.class,IntervalDesign.class,Table1.class, MessageItem.class, Contact.class,exportSchema = false,version = 1)
abstract class TheDatabase extends RoomDatabase 
    abstract AllDao getAllDao();

    private static volatile TheDatabase instance;
    public static TheDatabase getInstance(Context context) 
        if (instance == null) 
            instance = Room.databaseBuilder(
                    context,
                    TheDatabase.class,
                    "state.db"
            )
                    .allowMainThreadQueries()
                    .addCallback(new Callback() 
                        @Override
                        public void onCreate(SupportSQLiteDatabase db) 
                            super.onCreate(db);
                        

                        @Override
                        public void onOpen(SupportSQLiteDatabase db) 
                            super.onOpen(db);
                        
                    )
                    .build();
        
        return instance;
    
    public static long getDBSize(Context context) 

        // For Demonstration Log the individual sizes
        Log.d("DBSIZEINFO",
                "Space from main DB file = " + String.valueOf(context.getDatabasePath(instance.getOpenHelper().getDatabaseName()).length())
                + "\nSpace from -shm file = " + String.valueOf(context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-shm").length())
                + "\nSpace from -wal file = " + String.valueOf(context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-wal").length())
                + "\nSpace from journal file = " + String.valueOf(context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-journal").length())
        );

        return (context.getDatabasePath(instance.getOpenHelper().getDatabaseName())).length()
                // Add the shared memory (WAL index) file size
                + (context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-shm")).length()
                // Add the WAL file size
                + (context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-wal")).length()
                // Add the journal file size
                + (context.getDatabasePath(instance.getOpenHelper().getDatabaseName() + "-journal")).length();
    

注意为演示/说明添加的各个文件大小

和调用代码:-

public class MainActivity extends AppCompatActivity 

    TheDatabase db;
    AllDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Instantiate Database and get dao
        db = TheDatabase.getInstance(this);
        dao = db.getAllDao();
        Log.d("DBSIZEINFO","Database Size is " + TheDatabase.getDBSize(this));
    

结果(即日志包括):-

2021-06-22 09:20:53.960 25867-25867/a.a.so67952337javaroomstatefacilities D/DBSIZEINFO: Space from main DB file = 4096
    Space from -shm file = 32768
    Space from -wal file = 70072
    Space from journal file = 0
2021-06-22 09:20:53.960 25867-25867/a.a.so67952337javaroomstatefacilities D/DBSIZEINFO: Database Size is 106936

设备资源管理器显示:-

结果说明

可以看出,与 -wal 和 -shm 文件相比,结果表明数据库文件本身很小(很明显,WAL 模式有效,因为两者都大于 0)。这是因为数据库实际上由 -wal(即等待应用到数据库的更改)和数据库文件组成。 -shm 文件不会被应用,它是用于 -wal 文件的工作文件。

在进行 CHECKPOINTS 时应用(可能部分应用)-wal 文件。

即在 WAL 模式下,更改被写入 -wal 文件(回滚是删除 -wal 文件的一部分)。

如果日志模式有效,则日志是用于撤消对数据库文件所做更改的日志。

应用程序数据库是用户数据部分还是缓存?

尽管您可能希望阅读:-

SQLite Write-Ahead Logging

SQLite Temporary Files Used By SQLite

附加

如果您不想在检查尺寸时传递上下文,那么您可以使用基于以下内容的东西:-

@Database(entities = State.class,Location.class,School.class,TimerDesign.class,IntervalDesign.class,Table1.class, MessageItem.class, Contact.class,exportSchema = false,version = 1)
abstract class TheDatabase extends RoomDatabase 
    abstract AllDao getAllDao();

    private static volatile TheDatabase instance;
    private static File databaseFile; //<<<<<<<<<< ADDED
    public static TheDatabase getInstance(Context context) 
        if (instance == null) 
            instance = Room.databaseBuilder(
                    context,
                    TheDatabase.class,
                    "state.db"
            )
                    .allowMainThreadQueries()
                    .addCallback(new Callback() 
                        @Override
                        public void onCreate(SupportSQLiteDatabase db) 
                            super.onCreate(db);
                        

                        @Override
                        public void onOpen(SupportSQLiteDatabase db) 
                            super.onOpen(db);
                        
                    )
                    .build();
        
        databaseFile = context.getDatabasePath(instance.getOpenHelper().getDatabaseName()); //<<<<<<<<<< ADDED
        return instance;
    

    /* ALTERNATIVE without the need for the Context*/
    public static long getDBSize() 
        if (databaseFile == null) return -1;
        return databaseFile.length() +
                new File(databaseFile.getPath() + "-shm").length() +
                new File(databaseFile.getPath() + "-wal").length() +
                new File(databaseFile.getPath() + "-journal").length();
    

【讨论】:

以上是关于在设备设置中检查 Room 数据库的大小的主要内容,如果未能解决你的问题,请参考以下文章

Room:如何检查行是不是存在

“对象引用未设置为对象的实例”即使我检查了 null

XTZ Room Analyzer Ⅱ pro 声学测试仪 初级使用介绍

Android JetPack组件之Room数据库的集成与详解

错误:找不到字段的设置器。 - java.util.ArrayList 中的大小 - Room 中的嵌入式 ArrayList 无法编译

为啥 Room 不从资产中填充数据库?