Android开发问题记录
(用于记录开发中鸿蒙环境差异及问题,持续更新)
2025.4.9
问题
使用android.intent.action.TIME_TICK
广播时,动态注册在鸿蒙4.0系统正常,但在鸿蒙3.0系统无法接收广播。
解决方案
在AndroidManifest.xml
中添加静态注册后问题解决。
原因分析
(待补充,欢迎留言补充原因)
推测鸿蒙3.0对动态注册的TIME_TICK
广播存在兼容性问题,或系统权限策略差异导致动态注册失效。
2025.4.30
问题
在进行文件管理权限获取时,在鸿蒙3.0系统中只请求ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION相关权限无法对目录进行扫描。
更正:ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION权限用于SDK30以上,通过代码发现当前设备SDK版本为29,对应Android10,无需获取。
解决方案
在进入时判断是否有允许安装外部应用权限:
protected void checkFileManagerPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);
}
boolean hasInstallPermission = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
hasInstallPermission = getPackageManager().canRequestPackageInstalls();
}
if (!hasInstallPermission) {
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
}
更新2025.5.6:
在清单文件中添加android:requestLegacyExternalStorage="true"属性,更文件访问,后续待测试在其他版本的适配性。
原因分析
通过日志输出,确定问题地点在扫描全局文件时无法获取外部存储目录下的文件信息,通过filePath.listFiles()
获取的路径返回null
查看了READ_EXTERNAL_STORAGE
和WRITE_EXTERNAL_STORAGE
都为0已获取权限,查看MANAGE_EXTERNAL_STORAGE
为-1,但通过获取设备SDK,发现设备输入Android10,无需获取MANAGE_EXTERNAL_STORAGE
。
2025.5.28
问题
在将应用的主页面设置为默认桌面后,出现点击返回键不断切换页面的异常情况。确定存在两个问题:
- 存在两个不同的主页面堆栈信息。
- back键返回处理问题。
解决方案
- 对全局的页面实例进行统一管理,建立
ActivityHelper
帮助类,通过addActivty
保存实例,判断主页面是否已经存在,存在先添加新引用再关闭老页面。
object ActivityHelper {
private val activityStack = mutableListOf<WeakReference<Activity>>()
private var mainActivity: MainActivity? = null
fun addActivity(activity: Activity) {
activityStack.removeAll { it.get() == null }
// 先添加引用,再判断是否已存在
if (!activityStack.any { it.get() == activity }) {
activityStack.add(WeakReference(activity))
}
if (activity is MainActivity) {
activityStack.removeAll { it.get() is MainActivity }
if (mainActivity != null) {
mainActivity?.finish()
}
mainActivity = activity
}
}
fun removeActivity(activity: Activity) {
activityStack.removeAll { it.get() == activity }
}
fun finishAll() {
activityStack.forEach { it.get()?.finish() }
activityStack.clear()
}
fun finishActivity(activity: Activity) {
activityStack.removeAll { it.get() == activity }
activity.finish()
}
}
- 对主页面的back键点击事件做拦截处理(基于老项目解决方法,实际上这样做第一个问题影响不大)
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// 主页面禁用返回操作
return
}
})
原因分析
设为默认桌面后,应用启动时会建立专属任务栈(Task Stack)。该任务栈遵循standard/singleTop/singleTask/singleInstance等启动模式,并维护Activity堆叠顺序。
用户点击Home键时,系统会发送标准HOME意图(Intent.ACTION_MAIN + Intent.CATEGORY_HOME)。这一系统级操作将强制重启启动页面(Launcher Activity),即使应用已在后台运行。此时系统会新建独立任务栈,而非复用原有实例。
该机制具有以下Android系统特性:
- 新任务栈分配不同任务ID
- 原任务栈保留在后台(需手动清除)
- 任务栈间不共享Activity实例
- 最近任务列表显示为独立条目
此设计保障了桌面应用的稳定性,但开发者需注意:
- 可能引发的多实例问题
- 单例模式及全局状态处理
常见应用场景包括:
- 从其他应用返回桌面后按Home键
- 通过最近任务切换至桌面
- 内存回收后的自动重启
2025.6.5
问题
在需要锁定屏幕状态的情况下调用华为MDM SDK中的禁用通知栏(状态栏)无效。可通过进入设置页的返回键进入系统桌面。
解决方案
初始方案是通过接口将应用设为默认桌面,并在桌面启动时添加判定逻辑。但该方案在某些设备上会导致未知页面崩溃(日志未捕获相关信息),虽不影响功能使用,但会影响用户体验。
查阅华为最新文档后发现,从13.1版本SDK开始,禁用状态栏的描述已更新为仅针对全屏状态下的状态栏。因此转向采用界面全屏方案。
实现界面全屏的两种方法:
传统实现方式:
window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
)
注:在Android10平板调试时发现,单独使用
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
可能导致状态栏隐藏不完全。经测试发现必须同时添加View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
才能完全隐藏。其中:
HIDE_NAVIGATION
用于通知系统隐藏状态栏LAYOUT_HIDE_NAVIGATION
则直接设置界面属性
新式实现方案:
window.insetsController?.let { insetsController ->
insetsController.hide(WindowInsets.Type.statusBars())
// 必须配置滑动行为,否则会默认使用淡出动画(动画期间界面可操作)
insetsController.systemBarsBehavior =
WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
注意事项:
- 传统方法在Android 10+设备上仍可用,但会显示废弃警告
- 针对刘海屏设备需额外配置:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
- 华为的禁用功能实际是阻止状态栏重新显示,故必须设置滑动行为
- 避免设置状态栏透明,否则会与现有逻辑冲突导致状态栏无法隐藏
原因分析
主要原因是引入的华为相关jar包版本更新造成的部分接口能力改动。
2025.6.16
问题
在对老代码的gradle进行升级后,provider的读取和安装应用相关数据读取出现了问题。
解决方案
初期添加了
<queries>
<package android:name="your_query_packname" />
</queries>
只解决了provider读取问题,但后续客户反应应用反复下载,经过多方定位确认是安装相关数据不能查询。
添加android:name="android.permission.QUERY_ALL_PACKAGES"
解决。
问题分析
核心问题在于我将targetSdkVersion升成31后,由于Android11加强了文件访问权限和包可见性限制,导致调整应用作为应用商店时无法正常监控已安装应用状态。系统默认仅允许应用查看自身包、系统包、已安装的启动器应用、获得URL权限的应用以及自己安装的应用。当前通过广播触发其他应用执行安装的方式,因无法获取实际安装状态,造成了下载安装逻辑的重复执行。