记一次Android Q使用MediaStore删除文件失败的过程

在AndroidQ及更高版本中,由于沙箱机制的引入,即使应用被卸载后重新安装,也无法直接删除之前创建的文件。本文详细解释了这一现象的原因,并提供了两种解决方案:设置兼容模式或通过异常处理请求用户授权。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:

在Android Q上使用MediaStore创建了一个音频文件test1.mp3保存到sdcard/Music/flycat目录下(保存过程省略...)。然后用MediaStore去查询该音频文件的Uri,记为uri1。最后删除:context.getContentResolver.delete(uri1,null,null),此时删除成功

假设我同上步骤创建了test1.mp3,然后再将app卸载,再重新安装该app,此时仍然能查询到该音频的Uri 记为uri1,再用上述方式去删除该文件,此时删除失败

原因:

开了沙箱之后,之前的媒体库生成的文件在其记录上会打上owner_package的标志,标记这条记录是你的app生成的。当你的app卸载后,MediaStore就会将之前的记录去除owner_package标志,也就是说app卸载后你之前创建的那个文件与你的app无关了(不能证明是你的app创建的)。所以当你再次安装app去操作之前的文件时,媒体库会认为这条数据不是你这个新app生成的,所以无权删除或更改。

解决方案:

方案一:

将你的app设为兼容模式:在AndroidManifest.xml的Application中配置android:requestLegacyExternalStorage="true",亲测可行。

方案二:

其实你delete的时候会抛异常,捕获这个异常,如果异常类型为RecoverableSecurityException的话,则可以在Activity或Fragement中调用startIntentSenderForResult(e.getUserAction().getActionIntent().getIntentSender(),请求码,null,0,0,0),此时系统会弹框让你授权,当用户点击确定后再次删除即可。但是实际上,我发现仅仅这样并没有用,所以还是得借助方案一来实现。

<think>嗯,用户想开发一个基于Android的应用程序,具体功能包括启动页、问答验证、UI界面和下载管理。首先,我需要理清每个功能的具体实现步骤。 首先,启动页。用户希望打开应用时显示logo,通常启动页也叫Splash Activity。根据Android的开发模式,应该创建一个新的Activity,设置它的布局为一个ImageView显示logo,然后延迟跳转到主界面。得在AndroidManifest.xml中将这个Activity设为启动项,并且设置主题为无ActionBar,避免启动时的白屏问题。 接下来是问答验证部分。用户提到要显示弹窗提示酷安绿龟龟简介的最后一句话并进行验证。这里需要解析给定的URL内容,可能涉及到网络请求和HTML解析。不过用户提供的引用中没有提到具体的URL内容,可能需要进一步确认。假设需要从某个网页获取最后一句话,然后让用户输入验证。但考虑到网络请求可能耗时,可能需要使用AsyncTask或者协程来处理。不过为了简化,可能用户只是需要静态的提示信息,比如直接显示某个固定的句子,然后验证用户输入是否正确。例如,弹窗显示“绿龟龟的宗旨是保护用户隐私”,用户需要输入正确的关键词,如“保护用户隐私”。这部分需要用到AlertDialog,并设置输入框和验证逻辑。 然后是UI界面和下载管理。需要创建主界面,包含下载功能和管理功能。下载管理涉及到网络权限、文件存储权限,以及使用DownloadManager或者自己实现下载服务。使用DownloadManager的话比较方便,可以处理进度显示和通知。但需要注册BroadcastReceiver来监听下载完成事件。同时,下载的文件要保存到指定路径,比如外部存储的某个目录,需要检查权限,并在AndroidManifest.xml中声明权限。UI方面,主界面可能需要一个按钮触发下载,一个进度条显示进度,和一个列表显示下载的文件。管理功能可能包括删除、查看已下载文件等。 包名和应用名称的设置需要在创建项目时指定,或者在build.gradle中修改applicationId为com.luguigui,应用名称则在strings.xml中定义。 接下来,需要整合这些步骤。首先创建SplashActivity,定时跳转到主Activity。主Activity中显示下载按钮,点击后触发下载,并显示进度。同时,在主Activity启动时弹出问答验证对话框,用户输入正确才能继续操作。下载管理部分使用DownloadManager,监听下载进度,更新UI,并保存文件到指定路径。 需要注意的权限包括INTERNET和WRITE_EXTERNAL_STORAGE,但Android 10以后作用域存储可能影响文件保存,可能需要使用MediaStore或者应用专属存储。需要根据目标SDK版本来调整存储路径。 另外,启动页的延迟跳转应该使用Handler.postDelayed,避免内存泄漏。弹窗验证需要在主线程中显示,网络请求需要在子线程处理。下载进度可能需要使用Service或者WorkManager来后台处理,特别是当应用不在前台时。 最后,测试各个功能是否正常,比如启动页显示时间、弹窗是否正确弹出、下载是否成功并保存到指定位置,以及管理功能是否有效。可能还需要处理下载失败的情况,给出提示。 可能遇到的问题包括权限被拒绝、存储路径不可写、下载进度更新不及时等。需要检查权限申请代码,确保用户授权,处理运行时权限。存储路径要使用Context.getExternalFilesDir()来获取应用专属外部存储,避免权限问题。下载进度可以通过查询DownloadManager的Cursor来获取,定期更新进度条。 总结下来,步骤大致是:配置项目,创建启动页,实现问答弹窗,构建主界面,集成下载管理功能,处理权限和存储路径,最后测试和调试。</think>根据您的需求,我们将分步骤实现Android应用的开发,以下是具体实现方案: 一、创建基础项目 1. 新建Android Studio项目 - 选择"Empty Activity"模板 - 包名:$com.luguigui$ - 应用名称:$luguigui$ - 最低兼容版本:建议API 21+ 项目结构: ``` ├── app/ │ ├── src/ │ │ ├── main/ │ │ │ ├── java/com/luguigui/ │ │ │ │ ├── SplashActivity.java │ │ │ │ └── MainActivity.java │ │ │ ├── res/ │ │ │ │ ├── layout/ │ │ │ │ │ ├── activity_splash.xml │ │ │ │ │ └── activity_main.xml ``` 二、启动页实现(Splash Screen) ```java // SplashActivity.java public class SplashActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); new Handler().postDelayed(() -> { startActivity(new Intent(SplashActivity.this, MainActivity.class)); finish(); }, 2000); // 2秒延迟 } } ``` 在$AndroidManifest.xml$中设置启动Activity: ```xml <activity android:name=".SplashActivity" android:theme="@style/Theme.AppCompat.Light.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> ``` 三、问答验证实现 ```java // MainActivity.java private void showVerificationDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("验证提示") .setMessage("酷安绿龟龟简介的最后一句是:______") .setView(R.layout.dialog_verification) .setPositiveButton("验证", (dialog, which) -> { EditText etAnswer = ((AlertDialog)dialog).findViewById(R.id.et_answer); if(etAnswer.getText().toString().equals("保护用户隐私")) { Toast.makeText(this, "验证成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "验证失败", Toast.LENGTH_SHORT).show(); finish(); } }) .setCancelable(false) .show(); } ``` 四、下载管理功能实现 1. 添加权限: ```xml <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ``` 2. 下载管理器: ```java // MainActivity.java private void startDownload(String url) { DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "luguigui_file"); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE); DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); long downloadId = dm.enqueue(request); // 进度监听 new Thread(() -> { boolean downloading = true; while (downloading) { DownloadManager.Query q = new DownloadManager.Query(); q.setFilterById(downloadId); Cursor cursor = dm.query(q); if (cursor.moveToFirst()) { int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) { downloading = false; } final int progress = (int) ((bytes_downloaded * 100L) / bytes_total); runOnUiThread(() -> progressBar.setProgress(progress)); } cursor.close(); } }).start(); } ``` 五、UI界面整合 ```xml <!-- activity_main.xml --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ProgressBar android:id="@+id/progressBar" style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/btn_download" android:text="开始下载" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值