安卓 文件管理相关功能记录
文件管理细分为图片、视频、音乐、文件四类
目录
权限
静态声明权限
动态检查和声明权限方法
如何开始上述动态申请的流程
提示
图片
获取图片文件的对象列表
展示
删除
视频
获取视频文件的对象列表
获取视频file列表
按日期装载视频文件列表
展示
播放
删除
音乐
工具类
获取音乐视频文件的对象列表
播放
删除
文件
获取文件对象列表
展示
点击打开
删除
不管是哪种都需要权限,关于安卓的文件访问权限不同版本有不同的管理方式,下面的代码不一定通用
权限
静态声明权限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
动态检查和声明权限方法
//判断是否拥有权限
private fun hasPermission() :Boolean {return ContextCompat.checkSelfPermission(requireContext(),Manifest.permission.WRITE_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED&& ContextCompat.checkSelfPermission(requireContext(),Manifest.permission.READ_EXTERNAL_STORAGE ) == PackageManager.PERMISSION_GRANTED}//检查权限 23以前在app安装时静态声明的权限已被授予,不用动态授予
private fun checkHasPermission(jump:Runnable) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (!hasPermission()) requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) else jump.run()} else {jump.run()}}//下面的两个方法是不同的版本申请权限的方法//请求权限
private fun requestPermission(vararg requestPermission: String) =ActivityCompat.requestPermissions(requireActivity(), requestPermission, 2024080202)//到文件管理权限授予界面private fun toFileManagerPage() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {if (!Environment.isExternalStorageManager()){val appIntent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)appIntent.data = Uri.parse("package:" + AppUtils.getAppPackageName())try {requireActivity().startActivityForResult(appIntent,2024080201)} catch (ex: ActivityNotFoundException) {ex.printStackTrace()val allFileIntent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)requireActivity().startActivityForResult(allFileIntent,2024080201)}}else{LogUtils.d("RequestAccessAndroidDataUtils","已授予所有文件的权限")}}}//下面的两个方法,是上面两种申请的回调,且下面的两个方法得在activity中才有,如果别的几个方法在fragment中,就使用Livedatabus 将结果发送出来,之后在需要接收的地方接收就行@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);//将权限申请结果广播出去LiveDataBus.getInstance().with("requestCode").setValue(requestCode+","+resultCode);}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//由于可能有多个权限申请,按照逻辑与处理int resultCode = PackageManager.PERMISSION_GRANTED; //默认权限已授予for (int i = 0; i < grantResults.length; i++) {if (grantResults[i] != PackageManager.PERMISSION_GRANTED){resultCode = PackageManager.PERMISSION_DENIED;break;}}//将权限申请结果广播出去LiveDataBus.getInstance().with("requestCode").setValue(requestCode+","+resultCode);}//下面的代码就是接收上面发送的权限申请结果//接受权限申请回调,数据来源于HomeActivity的onRequestPermissionsResult()和onActivityResult()LiveDataBus.getInstance().with("requestCode", String::class.java).observe(requireActivity()) {val result = it.split(",")if (result[0].equals("2024080201") && result[1].toInt() == PackageManager.PERMISSION_GRANTED){//文件管理权限回调}else if(result[0].equals("2024080202") && result[1].toInt() ==PackageManager.PERMISSION_GRANTED){//文件访问权限回调}}//入口方法
private fun callPermission(runnable: Runnable) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || Environment.isExternalStorageManager()) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) checkHasPermission(runnable) else runnable.run()} else {val builder = AlertDialog.Builder(requireContext()).setTitle(R.string.zfile_11_title).setMessage("管理文件需要您同意“允许所有文件管理权限”才能正常使用!").setCancelable(false).setPositiveButton(R.string.zfile_down) { d, _ ->toFileManagerPage()d.dismiss()}.setNegativeButton(R.string.zfile_cancel) { d, _ ->d.dismiss()}builder.show()}}
注:没有LivedataBus的可以去看另一篇博客:
安卓LiveDataBus使用记录-CSDN博客
如何开始上述动态申请的流程
binding.noPermissionLayout.setOnClickListener {callPermission{//这里就是拥有权限后需要进行的操作}
}
好了,搞完权限我们来具体到每个分类
由于项目java和kotlin混用,我就不自己转了,需要的自己转化一下吧
提示
下面分类中所有获取或者删除相关文件的代码都最好放到子线程或者协程的io线程中去,免得堵塞主线程
图片
先创建图片文件的bean对象来承载数据
data class FileEntity(var size: String,var path: String,var isSelect: Boolean = false,var displayName: String = "",var time: String = ""
) : Serializable {constructor(size: String, path: String) : this(size, path, false, "", "")
}
获取图片文件的对象列表
最好在子线程中
List<FileEntity> mediaBeen = new ArrayList<>();Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;String[] projImage = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA, MediaStore.Images.Media.SIZE, MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATE_TAKEN};Cursor mCursor = null;try {mCursor = mActivity.getContentResolver().query(mImageUri,projImage,MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?"+ "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?",new String[]{"image/jpeg", "image/png","image/jpg"},MediaStore.Images.Media.DATE_MODIFIED + " desc");} catch (Throwable t) {}if (mCursor != null) {while (mCursor.moveToNext()) {// 获取图片的路径String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA));int size = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.SIZE));String displayName = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));Long date = mCursor.getLong(mCursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN));SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//用于展示相册初始化界面mediaBeen.add(new FileEntity(size + "", path, displayName,dateFormat.format(new Date(date))));}mCursor.close();}
//获取到所有图片的列表之后就是对于图片的展示和删除等
展示
//展示第一张图片到ImageView中
String path = listImage.get(0).getPath();//使用Glide加载本地图片Glide.with(mActivity).load(path).error(R.mipmap.error).listener(new RequestListener<Drawable>() {@Overridepublic boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {Log.e("ImagePositionPath","error: "+e.getMessage());/* Toast.makeText(mActivity, e.getMessage(), Toast.LENGTH_SHORT).show();*/return false;}@Overridepublic boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {return false;}}).into(((ImageViewHolder) holder).iv_photo_filelist_pic);
删除
最好也在子线程中删除
private void deletePicture(String localPath, Context context) {if(!TextUtils.isEmpty(localPath)){Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;ContentResolver contentResolver = context.getContentResolver();String url = MediaStore.Images.Media.DATA + "=?";int deleteRows = contentResolver.delete(uri, url, new String[]{localPath});if (deleteRows == 0) {//当生成图片时没有通知(插入到)媒体数据库,那么在图库里面看不到该图片,而且使用contentResolver.delete方法会返回0,此时使用file.delete方法删除文件File file = new File(localPath);if (file.exists()) {file.delete();}}}}//使用
deletePicture(listImage.get(0).getPath(),context);
视频
先创建视频文件的bean对象来承载数据
public class VideoInfoBean implements Serializable {//日期public String date;//文件名public String name;//文件路径public String path="";//文件大小public long packageSize;//是否可选择public boolean isSelect;/*** 标题为0,内容为1*/public int itemType;
}
某一日视频文件的bean对象
public class VideoFileCollenctionBean implements Serializable {//保存的是年月日public String date;/*** 本日对应的视频文件集合*/public List<VideoInfoBean> lists=new ArrayList<>();
}
获取视频文件的对象列表
获取视频file列表
下面的代码还是最好放在子线程中
String path = Environment.getExternalStorageDirectory().getPath();private List<File> files = new ArrayList<>();/*** mp4 mov mkv avi wmv m4v mpg vob webm ogv 3gp flv f4v swf gif* @param path*/private void scanViodeFile(String path){File file = new File(path);if (file.isDirectory()) {File[] f = file.listFiles();if (null != f) {for (File file1 : f) {String fileName=file1.getName().toLowerCase();if (file1.isDirectory()) {scanViodeFile(path + "/" + file1.getName());} else if (fileName.endsWith(".mp4")||fileName.equals(".mov")|| fileName.equals(".mkv")||fileName.equals(".avi")||fileName.equals(".wmv")||fileName.equals(".m4v")||fileName.equals(".mpg")||fileName.equals(".vob")||fileName.equals(".webm")||fileName.equals(".ogv")||fileName.equals(".3gp")||fileName.equals(".flv")||fileName.equals(".f4v")||fileName.equals(".swf")&& file.length()!=0) {files.add(file1);}}}}}
按日期装载视频文件列表
处理上面的file列表,将信息装载为VideoFileCollenctionBean 列表,用于和系统相册一样按天展示所有的视频信息
//装载的列表
List<VideoFileCollenctionBean> lists=new ArrayList<>();//使用集合的不可重复性,找出所有拥有视频的日期
Set<String> set=new TreeSet<String>((o1, o2) -> o2.compareTo(o1));
for (File file : files) {//将date转换为年月日的字符串String time= DateUtils.timestampToPatternTime(file.lastModified(), "yyyy-MM-dd");set.add(time);
}for (String l : set) {VideoFileCollenctionBean videoFileCollenctionBean = new VideoFileCollenctionBean();lists.add(videoFileCollenctionBean);VideoInfoBean videoInfoBean=new VideoInfoBean();videoInfoBean.date=l;videoInfoBeans.add(videoInfoBean);for (File file : files) {String time = DateUtils.timestampToPatternTime(file.lastModified(), "yyyy-MM-dd");if (time.equals(l)) {VideoInfoBean bean = new VideoInfoBean();bean.path = file.getPath();bean.name = file.getName();bean.date=time;bean.packageSize = file.length();bean.itemType=1;videoFileCollenctionBean.lists.add(bean);videoInfoBeans.add(bean);}}
}
展示
展示一般就是用adapter显示视频列表,按上图所示展示,红框就是一个VideoFileCollenctionBean对象,绿框就是VideoInfoBean对象,细节不说,仅说明一下关键部分
下面的部分应该在adapter的onBindViewHolder()中
//展示视频第一帧图片图片private void setImgFrame(ImageView imgFrame, String path) {RequestOptions options = new RequestOptions().placeholder(R.color.color_666666)// 正在加载中的图片.diskCacheStrategy(DiskCacheStrategy.ALL); // 磁盘缓存策略Glide.with(mContext).load(path).apply(options).into(imgFrame);}//使用@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {VideoInfoBean appInfoBean = getLists().get(position);setImgFrame(viewHolder.mImgFrame, appInfoBean.path);
}
播放
public void play(VideoInfoBean appInfoBean) {try {if (appInfoBean.path == null){return;}String filePath = appInfoBean.path;if (filePath.isEmpty()) return;File file = new File(filePath);if (!file.exists()) {// 文件不存在,可以在这里处理错误return;}// 获取文件 MIME 类型String mimeType = getMimeType(file);// 检查 MIME 类型是否为视频类型if (!isVideoType(mimeType)) {// 不是视频文件,可以在这里处理错误return;}// 创建 UriUri uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", file);// 创建 IntentIntent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(uri, mimeType);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 检查是否有应用可以处理此意图if (intent.resolveActivity(getPackageManager()) != null) {startActivity(intent);} else {Toast.makeText(this, "当前系统无可播放视频的软件!", Toast.LENGTH_SHORT );}} catch (IllegalStateException e) {e.printStackTrace();}}private boolean isVideoType(String mimeType) {return mimeType.startsWith("video/");}private String getMimeType(File file) {String extension = file.getName().substring(file.getName().lastIndexOf('.') + 1).toLowerCase();switch (extension) {case "mp4":case "mov":case "mkv":case "avi":case "wmv":case "m4v":case "mpg":case "vob":case "webm":case "ogv":case "3gp":case "flv":case "f4v":case "swf":return "video/" + extension;default:return "video/x-generic"; // 通用视频类型,可能不是最优解}}
删除
//子线程中使用
public void delFile(List<VideoInfoBean> list) {List<VideoInfoBean> files = list;for (VideoInfoBean appInfoBean : files) {File file = new File(appInfoBean.path);if (null != file) {file.delete();}}
}
音乐
先创建音乐文件的bean对象来承载数据
public class MusciInfoBean implements Serializable {public int id;//文件名public String name;//播放时长public String time;//文件路径public String path;//文件大小public long packageSize;//是否可选择public boolean isSelect;@Overridepublic String toString() {return super.toString();}
}
工具类
包名import android.media.MediaPlayer;
import java.io.IOException;public class MusicFileUtils {/*** 获取音频播放时长* @param path* @return*/public static String getPlayDuration(String path) {MediaPlayer player = new MediaPlayer();String duration="";try {//recordingFilePath()为音频文件的路径player.setDataSource(path);player.prepare();//获取音频的时间duration= timeParse(player.getDuration());} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}finally {player.release();}return duration;}/*** 获取音频播放时长* @param path* @return*/public static String getPlayDuration2(String path) {MediaPlayer player = new MediaPlayer();String duration="";try {//recordingFilePath()为音频文件的路径player.setDataSource(path);player.prepare();//获取音频的时间duration= timeParse2(player.getDuration());} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}finally {player.release();}return duration;}/**** @param duration 音乐时长* @return*/public static String timeParse(long duration) {String time = "" ;long minute = duration / 60000 ;long seconds = duration % 60000 ;long second = Math.round((float)seconds/1000) ;if( minute < 10 ){time += "0" ;}time += minute+"''" ;if( second < 10 ){time += "0" ;}time += second+"'" ;return time ;}/**** @param duration 音乐时长* @return*/public static String timeParse2(long duration) {String time = "" ;long minute = duration / 60000 ;long seconds = duration % 60000 ;long second = Math.round((float)seconds/1000) ;if( minute < 10 ){time += "0" ;}time += minute+"分" ;if( second < 10 ){time += "0" ;}time += second+"秒" ;return time ;}
}
获取音乐视频文件的对象列表
private List<MusciInfoBean> musciInfoBeans = new ArrayList<MusciInfoBean>();private void queryAllMusic(){Cursor cursor = activity.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,new String[] { MediaStore.Audio.Media._ID,MediaStore.Audio.Media.DISPLAY_NAME,MediaStore.Audio.Media.TITLE,MediaStore.Audio.Media.DURATION,MediaStore.Audio.Media.ARTIST,MediaStore.Audio.Media.ALBUM,MediaStore.Audio.Media.YEAR,MediaStore.Audio.Media.MIME_TYPE,MediaStore.Audio.Media.SIZE,MediaStore.Audio.Media.DATA },null,null,null);while (cursor.moveToNext()){//if (cursor.moveToFirst()) {int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
// String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME));String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));File file=new File(url);if(null!=file){files.add(file);MusciInfoBean musciInfoBean = new MusciInfoBean();musciInfoBean.id=id;musciInfoBean.name = file.getName();musciInfoBean.packageSize = file.length();musciInfoBean.path = file.getPath();//musciInfoBean.time = MusicFileUtils.getPlayDuration(file.getPath());musciInfoBean.time=MusicFileUtils.timeParse(duration);musciInfoBeans.add(musciInfoBean);}}try {cursor.close();}catch (Exception e){e.printStackTrace();}}//按时间升序排列
Collections.sort(musciInfoBeans, ((o1, o2) -> o2.time.compareTo(o1.time)));
展示的就不说了,就把上面的信息展示出来就行
播放
public void play(MusciInfoBean musciInfoBean) {try {if (musciInfoBean.path == null){return;}String filePath = musciInfoBean.path;if (filePath.isEmpty()) return;File file = new File(filePath);if (!file.exists()) {// 文件不存在,可以在这里处理错误return;}// 获取文件 MIME 类型String mimeType = getMimeType(file);// 检查 MIME 类型是否为视频类型if (!isAudioType(mimeType)) {// 不是音乐文件,可以在这里处理错误return;}// 创建 UriUri uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", file);// 创建 IntentIntent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(uri, mimeType);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 检查是否有应用可以处理此意图if (intent.resolveActivity(getPackageManager()) != null) {startActivity(intent);} else {Toast.makeText(this, "当前系统无可播放音乐的软件!", Toast.LENGTH_SHORT );}} catch (IllegalStateException e) {e.printStackTrace();}}private boolean isAudioType(String mimeType) {return mimeType.startsWith("audio/");}private String getMimeType(File file) {String extension = file.getName().substring(file.getName().lastIndexOf('.') + 1).toLowerCase();if ("mp3".equals(extension) || "wav".equals(extension) || "aac".equals(extension) ||"flac".equals(extension) || "ogg".equals(extension) || "m4a".equals(extension) ||"wma".equals(extension) || "aiff".equals(extension) || "amr".equals(extension)) {return "audio/" + extension;} else {return "audio/x-generic"; // 通用音频类型,可能不是最优解}}
删除
//子线程中使用
public void delFile(List<MusciInfoBean> list) {List<MusciInfoBean> files = list;for (MusciInfoBean appInfoBean : files) {File file = new File(appInfoBean.path);if (null != file) {file.delete();}}
}
文件
文件的含义就多了,像pdf,word,xml,json,txt,docx等等都可以算文件
先创建文件的bean对象来承载数据
/*** 文件 实体类* @property fileName String 文件名* @property isFile Boolean true---文件;false---文件夹* @property filePath String 文件路径* @property date String 格式化后的时间* @property originalDate String 原始时间(时间戳)* @property size String 格式化后的大小* @property originaSize Long 原始大小(byte)* @property folderLength Int 文件夹包含的文件个数* @property parent String? 父级所包含的选择文件个数(自定义文件获取不需要给该字段赋值)*/
//有些字段无实际意义,按需获取就行
data class ZFileBean(var fileName: String = "",var isFile: Boolean = true,var filePath: String = "",var date: String = "",var originalDate: String = "",var size: String = "",var originaSize: Long = 0L,var folderLength: Int = 0,var parent: String? = "",var folderName: String = "",var thumbPath: String = "",var fullPath: String = "",var imageCount: Int = 0,var isDelete:Boolean? = false, //是否选中删除var originalduration:Long =0, //原始视频的时长
) : Serializable ,Parcelable
获取文件对象列表
//这个方法其实可以获取到所有本地的文件,如视频、图片、音乐、压缩包、安装包等,只要传入不同的后缀数组就行val filefilterArray = arrayOf(DOC, DOCX, XLS, XLSX, PPT, PPTX, PDF, TXT, ZIP, JSON, XML) //还有补充的可以再加private fun getLocalData(filterArray: Array<String>): MutableList<ZFileBean> {val list = arrayListOf<ZFileBean>()var cursor: Cursor? = nulltry {val fileUri = MediaStore.Files.getContentUri("external")val projection = arrayOf(MediaStore.Files.FileColumns.DATA,MediaStore.Files.FileColumns.TITLE,MediaStore.Files.FileColumns.SIZE,MediaStore.Files.FileColumns.DATE_MODIFIED)val sb = StringBuilder()filterArray.forEach {if (it == filterArray.last()) {sb.append(MediaStore.Files.FileColumns.DATA).append(" LIKE '%.$it'")} else {sb.append(MediaStore.Files.FileColumns.DATA).append(" LIKE '%.$it' OR ")}}val selection = sb.toString()val sortOrder = MediaStore.Files.FileColumns.DATE_MODIFIEDval resolver = getContext()?.contentResolvercursor = resolver?.query(fileUri, projection, selection, null, sortOrder)if (cursor?.moveToLast() == true) {do {val path =cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA))val size =cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns.SIZE))val date =cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATE_MODIFIED))val fileSize = ZFileOtherUtil.getFileSize(size)val lastModified = ZFileOtherUtil.getFormatFileDate(date * 1000)if (size > 0.0) {val name = path.substring(path.lastIndexOf("/") + 1, path.length)list.add(ZFileBean(name,true,path,lastModified,date.toString(),fileSize,size))}} while (cursor.moveToPrevious())}} catch (e: Exception) {ZFileLog.e("根据特定条件 获取文件 ERROR ...")e.printStackTrace()} finally {cursor?.close()return list}}
展示
展示没啥说的,顶多根据文件的后缀名显示不同的图片就行
点击打开
在res中添加一个xml的文件夹,在xml文件夹中创建dn_file_paths.xml文件
<?xml version="1.0" encoding="utf-8"?>
<paths><files-pathname="files_path"path="." /><cache-pathname="cache_path"path="." /><external-pathname="external_path"path="." /><external-files-pathname="external_files_path"path="." /><external-cache-pathname="external_cache_path"path="." /><external-media-pathname="external_media_path"path="." /><!-- 添加共享目录--><external-pathname="external_files"path="." />
</paths>
在AndroidManifast.xml中注册内容提供商
<applicationandroid:allowBackup="true"><providerandroid:name="androidx.core.content.FileProvider"android:authorities="包名.provider"android:exported="false"android:grantUriPermissions="true"tools:replace="android:authorities"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/dn_file_paths"/></provider></application>
打开方法
private fun openFile(filePath: String) {if (filePath.isEmpty()) returnval file = File(filePath)if (!file.exists()) {// 文件不存在,可以在这里处理错误return}val mimeType = getMimeType(file)val uri = FileProvider.getUriForFile(this, "${this.packageName}.provider", file)val intent = Intent(Intent.ACTION_VIEW).apply {setDataAndType(uri, mimeType)addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)}// 检查是否有应用可以处理此意图if (intent.resolveActivity(packageManager) != null) {startActivity(intent)} else {// 没有应用可以处理此文件类型// 可以提示用户安装支持的应用程序}}private fun getMimeType(file: File): String {val extension = file.extension.toLowerCase()return when (extension) {"doc", "docx" -> "application/msword""xls", "xlsx" -> "application/vnd.ms-excel""ppt", "pptx" -> "application/vnd.ms-powerpoint""pdf" -> "application/pdf""txt" -> "text/plain""json" -> "application/json"else -> "*/*" // 通用类型,可能不是最优解}}
删除
//子线程中使用
public void delFile(List<ZFileBean> list) {List<ZFileBean> files = list;for (ZFileBean appInfoBean : files) {File file = new File(appInfoBean.filePath);if (null != file) {file.delete();}}
}
相关文章:
安卓 文件管理相关功能记录
文件管理细分为图片、视频、音乐、文件四类 目录 权限 静态声明权限 动态检查和声明权限方法 如何开始上述动态申请的流程 提示 图片 获取图片文件的对象列表 展示 删除 视频 获取视频文件的对象列表 获取视频file列表 按日期装载视频文件列表 展示 播放 删除…...
监控视频汇聚融合云平台一站式解决视频资源管理痛点
随着5G技术的广泛应用,各领域都在通信技术加持下通过海量终端设备收集了大量视频、图像等物联网数据,并通过人工智能、大数据、视频监控等技术方式来让我们的世界更安全、更高效。然而,随着数字化建设和生产经营管理活动的长期开展࿰…...
【jvm】主要参数
Java 虚拟机(JVM)有许多参数用于控制其行为和性能,下面是一些 主要的 JVM 启动参数,这些参数通常分为以下几类: 内存管理相关参数 这些参数主要用来配置 JVM 的内存分配策略、堆内存、栈内存等。 -Xms 设置 JVM 启动…...
在Ubuntu中配置mysql,并允许外部访问数据库
在虚拟机中安装 MySQL 并允许外部访问,可以按照以下步骤操作: 1. 更新系统包 首先,确保你的系统是最新的,使用以下命令更新包列表: sudo apt update sudo apt upgrade2. 安装 MySQL Server 安装 MySQL 服务&#x…...
Golang的向前兼容性和toolchain规则,Go1.21.0
在 Go 1.21 中,在工具上新增了两个变化:增强了向前兼容性;工具链管理。 向前兼容性 在以前的版本中,Go 工具链尝试编译依赖于新版本的代码时,可能会遇到兼容性问题。例如,如果你的代码依赖于 Go 1.18 引入…...
如何有效修复ffmpeg.dll错误:一站式解决方案指南
当您遇到提示“ffmpeg.dll文件丢失”的错误时,这可能导致相关的应用程序无法启动或运行异常。本文将详细介绍如何有效地解决ffmpeg.dll文件丢失的问题,确保您的应用程序能够恢复正常运行。 ffmpeg.dll是什么?有哪些功能? ffmpeg.…...
更频繁的 Android SDK 发布:更快的创新、更高的质量和更完善
Android 一直致力于让创新更快地进入用户手中。除了每年的平台发布之外,我们还投资了Project Treble、Mainline、Google Play 服务、每月安全更新和季度发布,为 Pixel Drops 提供支持。 未来,Android 将更频繁地发布 SDK,计划于 …...
远程连接:构建智能家居舒适生活
远程连接技术让智能家居从梦想照进现实,为人们构建了舒适便捷的生活环境。通过家庭网络与各种智能设备的远程连接,用户可以在外出时,使用手机 APP 轻松控制家中的灯光、窗帘、空调、电视等设备。 例如,在炎热的夏天,下…...
Python 爬取网页文字并保存为 txt 文件教程
引言 在网络数据获取的过程中,我们常常需要从网页中提取有用的文字信息。Python 提供了强大的库来帮助我们实现这一目标。本教程将以https://theory.gmw.cn/2023 - 08/31/content_36801268.htm为例,介绍如何使用requests库和BeautifulSoup库爬取网页文字…...
多协议视频监控汇聚/视频安防系统Liveweb搭建智慧园区视频管理平台
智慧园区作为现代化城市发展的重要组成部分,不仅承载着产业升级的使命,更是智慧城市建设的重要体现。随着产业园区竞争的逐渐白热化,将项目打造成完善的智慧园区是越来越多用户关注的内容。 然而我们往往在规划前期就开始面临众多难题&#…...
InnoDB 查询成本
1. 单表查询成本 连接查询总成本 IO 成本 CPU 成本对于 InnoDB 存储引擎来说,页是磁盘和内存之间交互的基本单位,设计MySQL的大叔规定读取一个页面花费的成本默认是 1.0,读取以及检测一条记录是否符合搜索条件的成本默认是 0.2。1.0、0.2 …...
C++的高精度减法 分步详解
高精度减法计算原理 在读小学时,我们做减法都采用竖式方法,如图 1 所示。 这样,我们可以写出两个整数相减的算法。 我们就可以用 C 语言来模拟这个竖式减法的过程。我们可以考虑利用 C 的数组来存储对应数据,假设用数组 A 存储被…...
linux cpu 管理
视频教程:ubuntu cpu 管理_哔哩哔哩_bilibili 概述 平均负载,CPU 使用率,CPU上下文 1 平均负载 #查看命令: rootzyb:~# uptime 18:21:47 up 1:09, 2 users, load average: 0.00, 0.00, 0.00 依次则是过去 1 分钟、5 分钟、1…...
大批量URL去重的架构设计(redis-bitmap+redisson)
1. 引言 什么是数据去重? 在大数据处理中,去重是指消除重复数据,只保留唯一的数据记录。 去重的重要性 提高数据处理效率,节省存储空间,提升数据分析的准确性。 常见的去重技术 基于哈希函数、布隆过滤器、位图等方法…...
WebGPU跨平台应用开发
对于 Web 开发人员来说,WebGPU 是一个 Web 图形 API,可提供对 GPU 的统一和快速访问。WebGPU 公开了现代硬件功能,并允许在 GPU 上进行渲染和计算操作,类似于 Direct3D 12、Metal 和 Vulkan。 虽然这是真的,但这个故事…...
Proteus(8.15)仿真下载安装过程(附详细安装过程图)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、Proteus是什么? 二、下载链接 三、下安装步骤 1.解压,有键管理员运行 2.点击Next,进行下一步 3.勾选I accept…&#…...
vlan和vlanif
文章目录 1、为什么会有vlan的存在2、vlan(虚拟局域网)1、vlan原理1. 为什么这样划分了2、如何实现不同交换机相同的vlan实现互访呢3、最优化的解决方法,vlan不同交换机4、vlan标签和vlan数据帧 5、vlan实现2、基于vlan的划分方式1、基于接口的vlan划分方式2、基于m…...
Unity开发哪里下载安卓Android-NDK-r21d
google的东西,居然是完全开源的 真的不是很多公司能做到,和那种伪搜索引擎是不同的 到底什么时候google才会开始造车 不过风险很多,最好不要合资,风险更大...
期末速成C++【初识C++】
目录 1.英文单词 2.C的特点 3.C对C语言的补充 3.1命名空间和域操作符 🎇3.2控制台输入输出 3.3类型增强 3.3.1const常变量 3.3.2const与指针 3.3.3布尔类型与枚举类型 3.4默认参数 🎇3.5函数重载 🎇引用 3.6.1引用做函数参数 …...
爬虫案例学习6
获取淘宝商品数据2024-12-18 参考学习: 大佬博客 视频教程 通过搜索发现,数据是通过发送请求过来的,不是静态存在源代码的 所以我们需要请求这个接口获取数据:比如标题,价格,图片等信息 https://h5api.m…...
28、论文阅读:基于像素分布重映射和多先验Retinex变分模型的水下图像增强
A Pixel Distribution Remapping and Multi-Prior Retinex Variational Model for Underwater Image Enhancement 摘要介绍相关工作基于模型的水下图像增强方法:无模型水下图像增强方法:基于深度学习的水下图像增强方法: 论文方法概述像素分布…...
[BJDCTF2020]ZJCTF,不过如此 1
[BJDCTF2020]ZJCTF,不过如此 1 打开实例发现代码审计 需要GET传入text和file参数,然后执行文件包含 text需要读取到I have a dream文本,这边采用data流进行绕过 ?textdata://,I have a dream&filenext.php成功绕过,接下来…...
Hive的in与not in 值中有null的时候注意事项,join where条件等问题
在进行hive SQL查询数据的时候,where条件中使用了in或者not in,但是该值内有null空。 这时,无论是in还是not in,空值都不会进入该条件内,但是使用not in的时候只是希望把自己不想要的数据给排除掉,这时会同…...
大语言模型画图(流程图、框架图)
第一步:向随意大语言模型,描述内容,推荐豆包 豆包 加上下面Prompt 通过Mermaid语法,描述上面流程图 第二步:将生成Mermaid输入流程图生成网站 中文Mermaid - 流程图、关系图在线画图、生成和编辑器...
Oracle创建逻辑目录
Oracle 在执行逻辑备份及还原时,需要用到逻辑目录。 本文就来简单介绍一下逻辑目录相关的操作,希望对大家有所帮助。 1.登录到Oracle数据库 使用具有足够权限的数据库用户登录到Oracle数据库。通常,这需要是管理员账号,如SYS…...
[react] 优雅解决typescript动态获取redux仓库的类型问题
store.getState()是可以获取总仓库的 先拿到函数的类型 再用ReturnType<T> 它是 TypeScript 中的一个内置条件类型,用于获取某个函数类型 T 的返回值类型 代码 // 先拿总仓库的函数类型type StatefuncType typeof store.getState;//再拿函数类型T的返回值类…...
python如何获取excel单元格文字是否加粗
是的,Python 可以获取 Excel 单元格中的文字是否加粗。通常,这需要使用 openpyxl 库,它允许你读取和写入 Excel 文件(.xlsx 格式)。 以下是一个示例代码,展示如何检查某个单元格的文字是否加粗:…...
我的性能优化经验
专业方向:App cpu/memory/gpu/流畅度/响应时间的优化,Anr,Framework CarPowerManagementService模块的(STR),从0~1完成性能监控体系搭建,完成3大版本迭代高质量性能交付 响应时间: …...
【Vulkan入门】16-IndexBuffer
TOC 先叨叨 上篇介绍了如何使用VertexBuffer传入顶点信息。两个多星期了我们一直在玩三个点,本篇介绍如何渲染更多的点。 在渲染前考虑一个问题,渲染一个三角形需要三个点,渲染两个相接的三角形需要几个点? 答案是6个点…...
如何通过HTTP API新建Collection
本文介绍如何通过HTTP API创建一个新的Collection。 前提条件 已创建Cluster:创建Cluster。 已获得API-KEY:API-KEY管理。 Method与URL HTTP POST https://{Endpoint}/v1/collections 使用示例 说明 需要使用您的api-key替换示例中的YOUR_API_KEY、…...
excel 使用vlook up找出两列中不同的内容
当使用 VLOOKUP 函数时,您可以将其用于比较两列的内容。假设您要比较 A 列和 B 列的内容,并将结果显示在 C 列,您可以在 C1 单元格中输入以下公式: 这个公式将在 B 列中的每个单元格中查找是否存在于 A 列中。如果在 A 列中找不到…...
实验16 循环神经网络(3)
目录 1.数据处理 1.1.数据集下载 1.2.数据加载 1.2.1读取数据 1.2.2词表转换 1.2.3封装数据 2. 模型构建 2.1汇聚层算子 2.2模型汇总 3. 模型训练 3.1模型训练 3.2绘制准确率和损失函数图像 4. 模型评价 5. 模型预测 6. 基于Torch的单向LSTM 6.1模型修改-只返…...
Docker 清理命令
Docker 清理命令 删除停止的容器:停止的容器仍然会占用磁盘空间,可以使用以下命令删除所有停止的容器: docker container prune删除无用的网络:Docker 创建的网络,如果不再使用,也可以被清理: d…...
游泳溺水识别数据集,对9984张原始图片进行YOLO,COCO JSON, VOC XML 格式的标注,平均识别率在91.7%以上
游泳溺水识别数据集: 对9984张原始图片进行YOLO,COCO JSON, VOC XML 格式的标注,平均识别率在91.7%以上 ,可识别泳池或者水库中是否有人溺水。 数据集分割 训练组98% 9818图片 有效集%…...
【计算机网络】应用层
1. 域名系统 DNS 1.1 域名系统概述 域名系统 DNS (Domain Name System) : 互联网使用的命名系统。 用来把人们使用的机器名字(域名)转换为 IP 地址。 为互联网的各种网络应用提供了核心服务。 域名采用层次树状结构的命名方法。 DNS 是…...
计算机组成原理的学习笔记(1)
学习笔记 前言 本文主要是对于b站尚硅谷的计算机组成原理的学习笔记,仅用于学习交流。 一、hello.c如何运行起来? 1. 预处理阶段 在编译 C 程序时,预处理器首先处理代码。预处理器会: 插入头文件:例如,…...
挑战一个月基本掌握C++(第六天)了解函数,数字,数组,字符串
一 C函数 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上ÿ…...
《薄世宁医学通识50讲》以医学通识为主题,涵盖了医学的多个方面,包括医学哲学、疾病认知、治疗过程、医患关系、公共卫生等
《薄世宁医学通识50讲》是一门由薄世宁医生主讲的医学通识课程,该课程旨在通过深入浅出的方式,向广大听众普及医学知识,提升公众对医学的认知和理解。 晓北斗推荐-薄世宁医学通识 以下是对该课程的详细介绍: 一、课程概述 《薄世…...
Golang囊地鼠gopher
开发知识点-golang 介绍红队专题-Golang工具Fscan简介主要功能ubuntu 安装windows 安装常用命令:项目框架源文件common目录Plugins目录Webscan目录入口点插件扫描类型爆破插件common.ScantypeWebtitle函数webpoc扫描POC 执行CEL-GO 实践CEL指纹识别免杀源码特征参考链接红队专…...
关于如何正确在测试用例中mock静态方法的问题
文章目录 情况一:希望在测试用例中直接执行静态方法的逻辑情况二:不希望在测试用例中执行静态方法的逻辑插桩方法坑1: 报错SubclassByteBuddyMockMaker原因与解决方案坑2:报错 the existing static mock registration must be deregistered原…...
深度学习0-前置知识
一、背景 AI最大,它的目的是通过让机器模仿人类进而超越人类; ML次之,它是AI的一个分支,是让机器模仿人类的一种方法。开发人员用大量数据和算法“训练”机器,让机器自行学会如何执行任务,它的成功取决于…...
web网页前后端交互方式
参考该文, 一、前端通过表单<form>向后端发送数据 前端是通过html中的<form>表单,设置method属性定义发送表单数据的方式是get还是post。 如使用get方式,则提交的数据会在url中显示;如使用post方式,提交…...
电商大数据的几种获取渠道分享!
在当今数字化时代,电商大数据已成为企业决策和运营的重要基础。如何高效地获取、分析和利用这些数据,对于提升电商企业的竞争力至关重要。本文将详细介绍几种电商大数据的获取渠道,帮助电商从业者更好地掌握数据资源,提升业务洞察…...
【图像配准】方法总结
图像配准(Image registration)就是将不同时间、不同传感器(成像设备)或不同条件下(天候、照度、摄像位置和角度等)获取的两幅或多幅图像进行匹配、叠加的过程,就是找到1幅图像像素到另1幅图像像素间的空间映射关系它已…...
SSM 超市管理系统
🥂(❁◡❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞 💖📕🎉🔥 支持我:点赞👍收藏⭐️留言📝欢迎留言讨论 🔥🔥&…...
【Yonghong 企业日常问题04】永洪BI可视化工具Linux部署全攻略(部署详解版)
文章目录 一、准备工作二、服务器环境配置2.1 安装JDK2.2 安装字体 三、产品安装四、相关资料 一、准备工作 在进行部署之前需要有以下几项工作准备: 1.产品安装包 2.对应版本的License 3.对应版本的JDK(推荐JDK11,产品v8版本以上需要JDK11以上版本) 4.…...
ubuntu下gdb调试ROS
参考: 使用VsCode进行ROS程序调试_ros vscode 调试-CSDN博客 https://blog.csdn.net/weixin_45031801/article/details/134399664?spm1001.2014.3001.5506 一、调试准备 1.1 CMakeLists改动 注释文件中的 set(CMAKE_BUILD_TYPE "Release") #构建类…...
scala中正则表达式的使用
正则表达式: 基本概念 在 Scala 中,正则表达式是用于处理文本模式匹配的强大工具。它通过java.util.regex.Pattern和java.util.regex.Matcher这两个 Java 类来实现(因为 Scala 运行在 Java 虚拟机上,可以无缝使用 Java 类库&…...
语音识别失败 chrome下获取浏览器录音功能,因为安全性问题,需要在localhost或127.0.0.1或https下才能获取权限
环境: Win10专业版 谷歌浏览器 版本 131.0.6778.140(正式版本) (64 位) 问题描述: 局域网web语音识别出现识别失败 chrome控制台出现下获取浏览器录音功能,因为安全性问题,需要在…...
前端本地数据存储方式有哪些
发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。 在前端开发中,本地数据存储是实现客户端数据持久化的关键技术。以下是几种常见的前端本地数据存储方式: …...