Android Room DAO中把SQL查询存为公共字符串是否存在安全风险?
一、公共查询字符串的安全问题
先说结论:这种写法不会额外增加安全风险。
Android应用的APK本来就能被反编译,就算你把SQL直接写在@Query注解里,反编译后照样能看到完整的SQL语句和表结构。另外,如果用户拿到了你的应用数据库文件(比如root设备),直接打开就能看全表结构和数据。说白了,数据库架构本来就很难完全保密,用公共字符串和直接写在注解里的安全程度没区别。
要是你真的想尽量减少SQL暴露,倒是可以试试Room的QueryBuilder,但这会让代码变得繁琐,而且也没法彻底防止反编译分析,意义不大。
二、更优替代方案
你的核心需求是让Flowable在表空的时候立刻发射空列表,同时避免重复写SQL。这里给几个更简洁的思路:
1. 用Single代替Maybe,简化组合逻辑
Room里返回Single<List<MyObject>>的查询,就算结果为空也会发射空列表,比Maybe更贴合“一定会拿到结果(空或数据)”的场景。你可以复用同一个SQL定义两个DAO方法:
// DAO里的代码 @Query("SELECT * FROM MyObject") Flowable<List<MyObject>> flowableMyObjectList(); @Query("SELECT * FROM MyObject") Single<List<MyObject>> singleMyObjectList(); // 使用的时候 flowableMyObjectList() .startWith(singleMyObjectList()) .distinctUntilChanged() .subscribe(...);
这样既保证了SQL只写一次,语义也更清晰。
2. 封装自定义操作符,减少重复代码
如果这个“初始发射空列表”的需求在项目里多处用到,可以把逻辑封装成自定义RxJava操作符:
public static <T> FlowableTransformer<List<T>, List<T>> emitEmptyListOnStart() { return upstream -> upstream.startWith(Single.just(Collections.emptyList())); } // 用的时候直接链式调用 flowableMyObjectList() .compose(emitEmptyListOnStart()) .distinctUntilChanged() .subscribe(...);
不过注意:如果你的Room版本是2.1及以上,其实返回Flowable<List<T>>的查询在订阅时本来就会发射空列表(如果表空的话),这时候这个操作符就没必要写了。
3. 升级Room版本(最省心的方案)
Room 2.1之后,返回Flowable<List<T>>的查询规则改了:订阅时不管表有没有数据,都会先发射一次当前结果(空列表或数据),之后数据变化再发射更新。如果你的项目还在用旧版本Room,直接升级就能解决问题,根本不需要额外写Maybe或者Single方法。
总结
- 公共查询字符串的写法没安全问题,不用纠结;
- 优先考虑升级Room版本简化逻辑,其次用Single+Flowable的组合复用SQL,或者封装操作符减少重复代码。
内容的提问来源于stack exchange,提问作者Pat Lee

