Android 的键值对存储有没有最优解?

正文

这是我在网上找到的一份 Android 键值对存储方案的性能测试对比(数越小越好):

可以看出,DataStore 的性能比 MMKV 差了一大截。MMKV 是腾讯在 2018 年推出的,而 DataStore 是 Android 官方在 2020 年推出的,并且它的正式版在 2021 年 8 月才发布。一个官方发布的、更(gèng)新的库,性能竟然比不过比它早两年发布的、第三方的库。而且我们能看到,更离谱的是,它甚至还比不过 SharedPreferences 。Android 官方当初之所以推出 DataStore,就是要替代掉 SharedPreferences,并且主要原因之一就是 SharedPreferences 有性能问题,可是测试结果却是它的性能不如 SharedPreferences。

所以,这到底是为什么?

继续阅读Android 的键值对存储有没有最优解?

macOS Big Sur (11.7.2)部署Sonic开源移动端云真机测试平台

安装 Docker Desktop

首先去 Docker 官网下载 macOS 版本的 Docker Desktop 版本(目前(2023/01/06)的最新版本是 4.15.0 (93002)),并安装。

安装配置 MySQL

继续阅读macOS Big Sur (11.7.2)部署Sonic开源移动端云真机测试平台

Android R(11)文件选择兼容

对于 Android R(11) 使用 ContentResolver 检索图片,音乐,视频文件,参考 Android R(11) ContentResolver报错java.lang.IllegalArgumentException: Invalid token limit 里面的代码即可实现。但是如果想上传 PDFTXT等文件的时候,则会发现系统无任何数据返回。

下面探讨一下如何解决这个问题:

一.储存

首先,我们需要对Android的储存有所了解
Android储存器可分为内部储存外部储存,这里的内部储存和外部储存不是说有两个物理储存器而是系统在硬盘上划分了两个专用目录用作内部储存和外部储存。简单来说,我们通过系统文件管理器看到的目录都属于外部储存,外部储存又可分为三类目录,私有目录公共目录其它目录,而内部储存对于用户是隐藏的,如数据库、SharedPreferences等文件都放在内部储存中。

内部储存

内部储存对应的目录为/data/user/0/{应用包名},该目录下应用有权限进行文件操作,目录对外不可见,应用删除对应的目录也会被删除。

外部储存
1.私有目录

私有目录获取和内部储存获取方式类似,都有file和cache目录,且该目录下应用有权限进行文件操作,目录对外可见,应用删除对应的目录也会被删除。
从Android11开始,私有目录不能被外部访问,即使获取了“所有文件管理”权限也不行(当然也是有其它方式可以实现Data目录的访问,不过目前看来并不完美)

2.公共目录

Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones等目录都是公共目录,Android11前可以通过文件路径直接访问,Android11后需要通过MediaStore来进行访问。

3.其它目录

外部储存中除了私有目录和公共目录外都是其它目录,Android11后不能直接对其它目录进行访问。

二.分区存储

Android10中已经加入了分区储存机制,不过是非强制的,适配Android10只需在AndroidManifest.xml中添加 android:requestLegacyExternalStorage="true"即可。而在Android11已经强制应用使用分区储存。

三.兼容方式

1.MANAGE_EXTERNAL_STORAGE(不推荐)

开启授权页面

获取“所有文件管理”的权限可以读写除私有目录外的所有文件,但是这种权限一般为文件管理类的软件才需要申请。一般APP申请此类权限若上架Google,华为等应用市场大概率被拒。

2.存储访问框架 (SAF)(推荐)

应用如果有做文件选择上传类的功能可以使用此方式,通过启动一个系统的文件浏览页面,选择需要的文件后返回一个uri,之后将uri转成流上传或者将通过uri复制文件到私有目录再操作复制后的文件进行上传,这里切记不能直接将uri转成File去进行操作。

参考链接


Android:加载PDF几种方法汇总对比

在安卓项目中,加载PDF文件,是一个比较常见的需求。又分为两大类,

1.加载网络PDF

2.加载一个本地静态PDF。


查阅资料,纵观网上在安卓中打开PDF的各种方式,大致可以分为以下几类,

1.直接使用第三方软件打开(包括浏览器打开和第三方软件打开)

如果是在app内部加载PDF文件,虽然安卓原生API对于PDF的支持又不是太好,但是各种第三方专门的阅读或者办公软件支持的是很不错的,可以通过Intent配置data和type实现。

其中,在实际需求中又会分为加载本地PDF和网络PDF的情况。

使用浏览器打开PDF:(APP外部打开,适用于加载网络PDF)

使用手机上已安装的可以打开PDF的第三方软件来打开PDF:(APP外部打开,适用于加载本地PDF)

(使用这种方式缺点是:手机上如果一个可以打开PDF的软件,那么就尴尬了~)


2.连接Google服务器解析

安卓的WebView不支持PDF解析,因此通过连接Google的一个服务器转换成功后返回给WebView显示。但是,但是,但是呢,大家都懂的,天朝和Google之间有一道高高的墙。方法还是贴出来,作为国际化APP的一种方案。


3.用第三方库加载

Github上有一个Java开源项目 https://github.com/barteksc/AndroidPdfViewer ,
这个库的大致原理,是内置了一个PDF解析器,以流的方式将网络PDF从网上Down下来,然后再以流的方式将其还原成PDF展示出来(具体细节没关注)。亲测中,这个库每次进入webview页面都会解析加载一遍PDF,如果PDF过大,费时无缓存不说,最致命的问题乃是,

APK包体积会瞬间增大15M左右,

具体原因不明,估计应该是内置PDF解析器的问题。于是,此方法被我抛弃了。


4.使用Moliza开源的Pdf.js

这个JS类库也是很强大的,配合原生的WebView使用,支持预览,缩放,翻页的功能,实现效果和WKWebView没差。同样也有体积问题,全部放到本地apk的话包大小差不多会增加5M左右。但相比上一种方式还是轻量一些:
http://www.jb51.net/article/136364.htm


5.使用安卓自带的PdfRenderer类加载

如果要求支持的功能不是很多,用安卓提供的PdfRender就可以满足需求了。PdfRender加载多页的话可以配合ViewPager或者RecyclerView使用。需要注意的是使用PdfRender需要先将PDF文件下载到本地,是线程不安全的,并且API>=21才能使用。因为这种方式是将PDF下载到本地,于是就产生了新问题:占内存。如果是静态的PDF文件不大还好,但是如果是频繁加载网络PDF的需求,那就头疼了,这种方式需要做好定时清理删除PDF的工作,否则,GG。

这里提供的示例是阿里巴巴Android开发手册,放到assets目录下.

PdfRender的大致调用方式是:

  • 初始化PdfRender,将PDF文件转换为ParcelFileDescriptor作为入参
  • 通过getPageCount()获得Pdf的总页数
  • 循环遍历,通过openPage()获得一个封装了当前页Pdf参数的内部类Page
  • 创建一个Bitmap对象,,用上步获取的Page加载,相当于把当前Pdf页的内容渲染到了Bitmap上
  • 对Bitmap进行你想要的操作,每次循环结束后关闭当前Page。

大部分都是常规操作,但要注意两点:

1. 每次循环创建的Page对象在使用完毕后必须调用close()。否则其内部的openPage()判断会抛异常;

2.Bitmap占用内存较大,如果用ViewPager加载一定要重写Adapter中的destroyItem(),及时的将需要销毁的视图从父容器中移除,否则快速滑动几页后会OOM。

参考链接


android:加载PDF几种方法汇总对比

如何让Android Studio/IntelliJ IDEA不对自定义的NULL检查方法发出警告

在平时的开发过程中,我们一般会自定义函数对变量是否为 null 进行检查,当检查函数返回成功的时候,对象一定不是 null

但是,这个自定义的函数,对于 Android Studio/IntelliJ IDEA 来说,是无法感知到的,导致 Android Studio/IntelliJ IDEA 会发出警告,没有对变量是否为 null 进行检查。

出现上述问题的例子如下:

那么,有没有办法告知 Android Studio/IntelliJ IDEA ,我们已经对 null 进行过检查了呢?

网上搜索许久,尝试过 @CheckForNull / @EnsuresNonNullIf 注解,都不能解决问题。

最终发现使用 jetbrains@Contract 注解能解决此问题。

上述的例子增加以下注解,明确告知编译器,如果参数是 null 函数一定返回 false,就可以阻止编译器发出警告了。

如下:

参考链接


Android各大市场更改APP名称

公司上架了一款App,因为产品运营需要去修改App名称,在iOS应用市场提交新版本的时候可以改App名称。那么Android 各大市场如何更改APP名称呢?

目前上架的Android 应用市场有:360手机助手、腾讯应用宝、百度手机助手、阿里应用分发市场(豌豆荚)、安智市场、小米应用商店、华为应用市场、OPPO商店、魅族应用商店、vivo应用市场、搜狗手机助手等平台。

每家Android应用市场的规则都不同,小编整理了Android 各大市场更改APP名称的规则。

1、 百度 : 百度平台的最简单。直接更新app的版本即可(应用名称是系统从您提交的应用中解析的,如需修改请联系贵司技术修改apk包内信息。)

百度如何修改app名称
百度如何修改app名称

2、应用宝: 用上线后,开发者可通过工单系统提交应用名称修改需求 (入口1:如图点击“名称更改指引”即可直接跳转到修改应用名称的工单系统;入口2:管理中心-->点击需要修改名称的应用-->基础服务-->工单系统-->应用宝商务类-->移动应用名称修改-->填单提交)。修改应用名称需要提供软件著作权; 若暂无软著,可提供此款应用在其他外市场,改过名字后的前后台上线管理截图(其他证明文件均不接受);

腾讯开放平台应用改名
腾讯开放平台应用改名

重要提示:根据平台规则,应用上线后,应用名称最多可支持修改2次,超过修改次数将不再受理,请谨慎确定好需要修改的应用名称后,再提交工单申请。

腾讯移动应用名称修改
腾讯移动应用名称修改

3、华为: 在开发者后台在该应用下点击上架或者升级,上传更名后的APK包,并在应用信息处更改应用名称,如应用分类涉及软著要求,请在“版本信息”的“应用版权证书或代理证书”处更新应用软著及免责函,提交应用审核。

华为应用商店应用改名
华为应用商店应用改名

4、360移动开放平台和百度移动开放平台是一样的,直接更新app的版本即可。

5、 vivo开放平台:

1)应用类APP需要修改名称: 直接在后台编辑更新,保证apk包内和在后台填写的名称一致即可;若名称相差较大,请在版权证明栏补充软著;游戏类APP(网游/单机)需要修改名称:需联系对接商务处理。

2)若需要更改应用包名,请联系贵司技术人员;更改之后再在平台创建应用提交新包名应用,旧包名应用需申请下架(登录平台--管理中心--点击您的应用--“下架申请”即可)。

6、 OPPO开放平台:也是上传后将自动解析包名。

7、魅族开放平台:也是上传后将自动解析包名。

8、小米市场: 应用名称修改需于应用包内及应用信息一同更改。应用包内名称更改还请贵司技术开发人员自行更改。(可以试一下直接上传后将自动解析包名。)

9、豌豆荚(阿里云应用分发平台):也是上传后将自动解析包名。

参考链接


Android 各大市场更改APP名称

编译NanoPi R5S Android 12 (RK3568)

最近,入手了一部 NanoPi R5S ,官方是提供了 Anroid 12 的系统镜像,但是却没有给出相应的源代码。尝试用官方提供的 Anroid 11 编译,结果编译出的系统镜像无法正常运行。

凑巧看到 研华科技 放出了 RSB-4810 开发板的 Anroid 12 编译指南,两者的配置差不多,试了一下,竟然可以正常运行!!

系统要求,内存不低于 32GB,否则编译过程中可能会由于内存不足,造成编译失败。

研华科技 官方文档是通过 docker 利用 ubuntu 18.04 进行编译的,如下:

1. 配置 docker 运行环境

2. 下载源代码并进行编译

3. 下载并解压缩编译工具( prebuilts.tar.gz 密码: 1234)

4. 编译代码

5. 核对编译后的文件

编译完成后的产物在 rockdev/Image-rsb4810_s/ 目录下,具体的文件列表如下:

5. 刷机

按照正常的流程刷机,整个流程走起来会比较繁琐,此处给相对简单的做法。

    1. NanoPi R5S官方WIKI 去下载已经编译好的镜像
    2. 如果是需要SD卡刷机,则直接把下载到的镜像文件写入准备好的数据卡
    3. 用刚刚编译出的文件替换SD卡上的android12目录下的同名文件,注意,只替换同名文件,里面的配置文件不要删除
    4. 插上SD卡,重启即可完成刷机
    5. 如果使用USB刷机,则也可以通过替换文件的方式达到相同的目的

参考链接


Robolectric 4.8.2修改进程名/私有静态变量

Roboletric 4.8.2 修改进程名:

Roboletric 4.8.2 修改私有静态变量:

如果报错:

则修改方式如下:

完整的例子如下:

参考链接


Using PowerMock - robolectric/robolectric Wiki

Android Studio升级ArcticFox后单元测试报错“Test events were not received”

Android Studio升级ArcticFox后单元测试报错“Test events were not received”,如下图:

解决方法:目前最简单的方法就是升级到 Android Studio Chipmunk|2021.2.1 Path 2,然后对项目整体执行一次Clean,删除之前的测试用例并重建。

操作步骤如下图:

1. 清理工程代码,移除缓存

2. 选中出问题的测试用例的编辑配置

3. 移除之前的测试用例,这样可以迫使测试用例重建

4. 再次执行测试用例

参考链接