Android

分类下相关文章

Android BLE 蓝牙控制硬件设备(二)调大 MTU,一次读取所有配置数据

继续 Android 平板 App 控制设备运行的项目。 MTU 是什么 英文全称是,Maximum Transmission Unit。即一次最大可以传输多少字节。 默认情况下是 20 个字节。即,Android App 一次只能从蓝牙控制板那里最多读取 20 个字节的数据。微信小程序中也是同样的状况。 什么情况下需要调大 MTU 例如,一个设备的运行参数多达 50 个,每个运行参数用两个字节表示。 那么即便不使用 CRC 校验码,不使用协议头,要完整读取所有参数都需要 100 个字节。 即需要 5 个 20 字节的数据包才能读完。这样效率太低。 另外一个典型的场景就是 BLE 蓝牙语音对讲 ...

阅读全文...

Android BLE 蓝牙控制硬件设备(一)代码结构

没想到这个月的开发工作全部是 Android 平板相关,而且都要用到 BLE 蓝牙,一个是控制设备运行,一个是蓝牙对讲。 看来之前练手写的那个 Android BLE 调试工具真是派上用场了,大部分代码可以复用上。 需求 用 Android 平板通过 BLE 低功耗蓝牙控制硬件设备,例如,开始、暂停、显示运行状态等。 功能非常简单,时间就两个 fragment 界面就能搞定: 蓝牙设备列表页 (List Fragment)。用于显示扫描出来的附件蓝牙设备 具体设备的控制界面 (Home Fragment)。用于显示实时运行数据,及控件按钮 交互逻辑 打开 Android 平板 App 时, ...

阅读全文...

冰冷的 Android 空指针与暖心的 Google Play 韩国用户

睡前看了一眼 Google Play Console 里的 App 新版本发布情况,无意发现一条一小时前的应用评论。 是个韩国用户留的: 버그가 넘 심해요 ㅜㅜ 어플들어가면 자꾸 팅김요 ㅜㅜ 빠른시간에 수정부탁드립니다.유용하게 잘쓰고있는데 ㅜㅜ 翻译成中文是: 这个应用程序的错误太多了,每次进入都会崩溃。希望能尽快修复。我一直都在好好地使用它,但现在无法正常使用了。 上面这段翻译是用 Sage AI 翻译的,因为 Google 的翻译有问题,就是下面截图里的一小段英文(too bad),翻译成中文也是。 意外的是,在程序崩溃的情况下,用户依然给了五分好评。。。被暖到了 我 ...

阅读全文...

Action Required: Your app is not compliant with Google Play Policies

昨天晚上 11 点终于将之前的 jetpack compose 版没有 admob 广告的 android app 完美加上了大家喜欢且期待已久的广告功能。 但是今天早上收到了 Google Play 审核未通过的邮件,大致意思就是 app 不符合数据安全政策。 问题详情 Issue details We found an issue in the following area(s): SPLIT_BUNDLE 6: Policy Declaration - Data Safety Section: Device Or Other IDs Data Type - Device Or ...

阅读全文...

Shelf Life: Privacy policy

Welcome to the Shelf Life app for Android! This is an Android app developed by Zhongwei Sun. The app is available on Google Play. As an avid Android user myself, I take privacy very seriously. I know how irritating it is when apps collect your data without your knowledge. I hereby state, to the best ...

阅读全文...

保质期管理 app,使用主题颜色区分过期和未过期状态

例如想使用 Android Material Design 3 主题中定义好的颜色,来着重显示过期状态。这样就能兼顾日间模式和夜间模式了,不需要手动判断。 themes.xml <resources> <style name="Theme.ShelfLife2" parent="Theme.Material3.Light.NoActionBar"> <item name="colorPrimary">@color/md_theme_light_primary</item ...

阅读全文...

保质期管理 app, 基于 SQLite 的过期时间排序

在基于 Android XML View 重写 Jetpack Compose 版保质期管理 app 时, 用 SQLDelight 替代了 Room 来做 SQLite 数据库管理。 但是在实现过期时间排序功能时,引入了一个 bug。 原来的数据库中的过期时间字段有两种值,NULL 或者时间对应的秒数。 现在又引入了零值。导致排序混乱。 正确的排序效果 在保质期管理 app 中,已过期的物品,或者快过期的应该排列在前面,而不会过期的应该排在后面,如图所示: 零值是否合理 例如录入一个物品时,可以不填写过期时间,那么数据库中应该存储 null 还是 0。 这里确实应该存储 NULL,而不是 ...

阅读全文...

Kotlin Flow 基础概念

本以为我的 Android 开发技术已经天下无敌了😅,没想到连 Flow / Channel 我都第一次见。。。不得不说 Android 的新概念层出不穷。 周末看了一堆文档,有了一个大概的了解。我发现微信公众号的文章质量还真是高,不少高手都做了详细的对比说明。 但是最终我一个也没有关注,从这些人的近期推文看都堕落了,不是贩卖焦炉,就是无脑卖其他骗子的课。扯远了,开始正题。 Flow 为何而生 一个挂起函数 (suspending function) 可以异步地返回一个值,但是没法多次返回。于是 Flow 就诞生了。 这也是为什么 Flow 获取 select 结果不需要加上 suspend ...

阅读全文...

Android Fragment 顶部菜单右上角添加删除/搜索按钮

例如,我想在 Android APP 首页顶部显示搜索按钮,在详情页顶部显示删除按钮。 叫 Menu 还是 Top App Bar 搜素 Android Menu https://developer.android.com/develop/ui/views/components/menus 在官方文档里可以看到: XML View 中称之为 Menu Jetpack Compose 中称之为 Top App Bar Activity 中添加还是 Fragment 中添加 You can declare items for the options menu from either you ...

阅读全文...

Android SQLDelight (七) 从 Room 迁移的改造点

继续将原有的 Compose 项目重写为 XML View,本来不想折腾,心想要不直接沿用原来的 Room 代码,但是看了 DAO 和 Repository 这种风格的代码,还是忍不了。 既然没有退路了,就得保证对历史数据的兼容。 数据库名称 注意,用 SQLDelight 指定数据库名称时,不要加 .db 后缀。否则跟之前 Room 生成的文件名不一致。 - val driver: SqlDriver = AndroidSqliteDriver(Database.Schema, context, "item_database.db") + val driver: Sq ...

阅读全文...

Android Room 替代品 SQLDelight 中文入门教程

对于我这种非 Java 程序员,实在忍受不了 Room DAO 这种形式主义,所以找了一个 Android 上操作数据库的替代品方案,SQLDelight。 SQLDelight 只需要手写 SQL,自动生成 kotlin data class 代码,然后用起来跟 ORM 没啥区别,非常好用。每个 ORM 方案的学习成本都不低,还有很多坑。而用 raw sql 就没有这种烦恼了。当然 SQLDelight 还有跨平台的优势,虽然我目前用不上。我甚至都想将 golang 项目中的 Gorm 换成类似的方案。 于是在从 Room 迁移到 SQLDelight 的过程中,整理了这个系列的教程/笔记: ...

阅读全文...

Android SQLDelight (五) 查询单条数据库记录,并更新 UI

之前用 Android SQLDelight 查询批量数据非常方便,没想到查询单条数据反而有点搞不清。 第一种做法 这样查询就是在主线程里,即 UI 线程。实际操作很快,没有任何卡顿。 val id = args.id if (id != 0L) { val item = viewModel.database.itemQueries.findOne(id).executeAsOne() binding.name.setText(item.name) binding.number.setText(item.num.toString()) } 第二种做法 val id = args.id ...

阅读全文...

Jetpack Navigation 在 fragment 间跳转时使用 Safe Args 传参数

在一个 Android APP 中,需要由 Fragment A 跳转到 B 时进行传参,官方推荐使用 Safe Args。 Safe Args 主打的就是类型安全 (type-safety),测试了一下。 配置 build.gradle SafeArgs 和导航组件的其它模块不太一样,它本身并不是一个 API,而是一个可以生成代码的 gradle 插件。 所以需要将它设置为 gradle 依赖,并且在构建时使其能够正确运行来生成所需的代码。 首先,在项目级 project build.gradle 中增加配置: buildscript { dependencies { ...

阅读全文...

Jetpack Navigation 点击返回 home fragment 时 onCreateView 被再次调用

在写一个 Android App,结构是一个 MainActivity,两个 fragment 分别是 home list fragment / detail fragment。 点击 home 页的 FAB 添加按钮,跳转 detail 页。 疑惑 我不明白为何 home fragment -> edit/detail fragment, 再返回时,home fragment onCreateView 又被调用了一遍。 navController.navigate(R.id.nav_edit) 难道,默认不支持 back stack ? 类似的问题 https://stackover ...

阅读全文...

Android SQLDelight (四) Hilt 将 database 依赖注入 ViewModel

由于使用的 Navigation UI 会在 navigate 时销毁 fragment 的 View,实际操作时,由 list to detail to list 这样的浏览路径,会导致 fragment onViewCreated 中的逻辑执行多次。这是不合理的,所以需要将相关逻辑挪到 view model 中 (fragment view 销毁了,但是 fragment 没有销毁,所以 view model 是安全的)。而 view model 中不应该去获取 fragment 或 activity 的实例,也就没法拿到之前偷懒放到 activity 中的 SQLDeligth Data ...

阅读全文...