Skip to content

Android 开发过程中遇到的问题

Gradle 同步

在 Android Studio 进行项目同步的时候总会遇到一些问题:如果长时间都未同步成功,大概率是下载依赖出错了。可以依次使用下面的方法进行检查。

如果是 Gradle 下载版本出错,可以使用国内的镜像地址下载 Gradle,推荐使用腾讯的 Gradle 镜像。修改当前项目 gradle/wrapper/gradle-wrapper.properties 里的 distributionUrl 地址。

https\://mirrors.cloud.tencent.com/gradle

前置要求

  1. 检查网络是否通畅
  2. 如果电脑设置了访问 Google 的工具,检查是否能够正常访问 Google。

具体方法:

  1. 设置国内的 Gradle 的镜像地址
  2. 查看 Global Gradle Properties 文件是否设置代理,代理服务器及端口是否正常工作。
    Global Gradle Properties 文件路径 .gradle/gradle.properties,如果是在 Android Studio 里查看,在 Android 面板找到下面这个文件即可。

Gradle Maven 阿里云效 ==="Groovy"

```Groovy   
allprojects {
    repositories {
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url "https://maven.aliyun.com/repository/jcenter" }
        maven { url "https://maven.aliyun.com/repository/spring" }
        maven { url "https://maven.aliyun.com/repository/spring-plugin" }
        maven { url "https://maven.aliyun.com/repository/gradle-plugin" }
        maven { url "https://maven.aliyun.com/repository/google" }
        maven { url "https://maven.aliyun.com/repository/grails-core" }
        maven { url "https://maven.aliyun.com/repository/apache-snapshots" }
    }
}
```

OkHttp 中 DNS 超时时长过长

自定义 DNS 实现类,实现可设置超时时间。

public class XDns implements Dns {
    private long timeout;

    public XDns(long timeout) {
        this.timeout = timeout;
    }

    @Override
    public List<InetAddress> lookup(final String hostname) throws UnknownHostException {
        if (hostname == null) {
            throw new UnknownHostException("hostname == null");
        } else {
            try {
                FutureTask<List<InetAddress>> task = new FutureTask<>(
                        new Callable<List<InetAddress>>() {
                            @Override
                            public List<InetAddress> call() throws Exception {
                                return Arrays.asList(InetAddress.getAllByName(hostname));
                            }
                        });
                new Thread(task).start();
                return task.get(timeout, TimeUnit.MILLISECONDS);
            } catch (Exception var4) {
                UnknownHostException unknownHostException =
                        new UnknownHostException("Broken system behaviour for dns lookup of " + hostname);
                unknownHostException.initCause(var4);
                throw unknownHostException;
            }
        }
    }
}

数据库

Android 各版本对应 SQLite 版本

Android API SQLite Version
API 27 3.19
API 26 3.18
API 24 3.9
API 21 3.8
API 11 3.7
API 8 3.6
API 3 3.5
API 1 3.4

命令行获取当前设备的 SQLite Version

adb -e shell sqlite3 --version

运行时获取 SQLite 版本

String query = "select sqlite_version() AS sqlite_version";
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(":memory:", null);
Cursor cursor = db.rawQuery(query, null);
String sqliteVersion = "";
if (cursor.moveToNext()) {
  sqliteVersion = cursor.getString(0);
}

名词解释

journal

是 SQLite 的读写的一种实现原子事件的一种机制:

原理:在修改数据库文件中的数据之前,先将修改所在分页中的数据备份在另外一个地方,然后才将修改写入到数据库文件中;如果事务失败,则将备份数据拷贝回来,撤销修改;如果事务成功,则删除备份数据,提交修改。

WAL

WAL 的全称是 Write Ahead Logging,它是很多数据库中用于实现原子事务的一种机制,SQLite 在 3.7.0 版本引入了该特性。

原理:修改并不直接写入到数据库文件中,而是写入到另外一个称为 WAL 的文件中;如果事务失败,WAL 中的记录会被忽略,撤销修改;如果事务成功,它将在随后的某个时间被写回到数据库文件中,提交修改。

同步 WAL 文件和数据库文件的行为被称为 checkpoint(检查点),它由 SQLite 自动执行,默认是在 WAL 文件积累到 1000 页修改的时候;当然,在适当的时候,也可以手动执行 checkpoint,SQLite 提供了相关的接口。执行 checkpoint 之后,WAL 文件会被清空。

在读的时候,SQLite 将在 WAL 文件中搜索,找到最后一个写入点,记住它,并忽略在此之后的写入点(这保证了读写和读读可以并行执行);随后,它确定所要读的数据所在页是否在 WAL 文件中,如果在,则读 WAL 文件中的数据,如果不在,则直接读数据库文件中的数据。

在写的时候,SQLite 将之写入到 WAL 文件中即可,但是必须保证独占写入,因此写写之间不能并行执行。

WAL 在实现的过程中,使用了共享内存技术,因此,所有的读写进程必须在同一个机器上,否则,无法保证数据一致性。

Room 导出问题

在使用 Room 进行开发的时,可能需要将数据库文件进行导出查看,如果仅导出 db_name.db 文件,则可能会出现文件无法打开或打开后未能成功加载数据库结构。

解决方法:与 db_name.db 同名的 wal 和 shm 文件一同导出即可。

tips: 团队内有小伙伴在初始化 db 的时候传入的 dbName 没有加扩展名 db,导出后 3 个文件后部分数据库软件无法识别,事实上这个文件已经是 SQLite 格式的,仅仅是未添加扩展名,需要注意的是,wal 和 shm 均需要增加 .db 的扩展名。

SQLite 返回异常码

官方地址:https://www.sqlite.org/rescode.html

SQLite_FULL

意味着磁盘空间不足,这里的磁盘空间,可能指代数据库的文件的主工程的文件空间,也可能指代数据库的临时文件的文件磁盘空间。

SQLITE_IOERR_TRUNCATE