【原】MediaScanner 扫描失败

本文探讨了在不同Android版本和路径下使用MediaScannerConnection.scanFile进行SDCard扫描的问题。发现在android2.3.3模拟器中,使用/sdcard路径扫描会失败且文件夹为空,而/android3.0真机上则是/mnt/sdcard路径扫描成功。

String[] paths = pathList.toArray(new String[pathList.size()]);
							String[] types = typeList.toArray(new String[typeList.size()]);
							MediaScannerConnection.scanFile(context, paths, types, null);

 android 2.3.3 模拟器  /sdcard 扫描失败 只有文件夹 文件为空

 android 2.3.3 模拟器  /mnt/sdcard 扫描成功

 

 android 3.0 真机  /sdcard 扫描失败 无文件夹 文件为空

 android 3.0 真机  /mnt/sdcard 扫描成功

 

try (FileOutputStream fos = new FileOutputStream(file)) { // 将字节数组解码为Bitmap Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); // 私有路径示例:/storage/emulated/0/Android/data/com.android.example.cameraappxjava/files/Pictures/pic_1735863161779.jpg // 关键:触发媒体扫描 MediaScannerConnection.scanFile( this, new String[]{file.getAbsolutePath()}, new String[]{"image/jpeg"}, new MediaScannerConnection.OnScanCompletedListener() { @Override public void onScanCompleted(String path, Uri uri) { if (uri != null) { Log.i(TAG, "已扫描到系统相册: " + uri.toString()); } else { Log.w(TAG, "扫描失败: " + path); } } }); } catch (IOException e) { e.printStackTrace(); }这代码这么写了以后日志: 保存图像: /storage/emulated/0/Android/data/com.android.example.cameraappxjava/files/Pictures/pic_1735885307925.jpg 01-03 01:21:48.243 12617 12760 I camera2api: 拍照完成,保存至: /storage/emulated/0/Android/data/com.android.example.cameraappxjava/files/Pictures/pic_1735885307925.jpg 01-03 01:21:48.243 12617 12760 W camera2api: 未找到完美匹配尺寸,使用默认 01-03 01:21:48.243 12617 12760 I camera2api: 选择预览尺寸: 4096x3072 01-03 01:21:48.243 12617 12760 I camera2api: 使用拍照尺寸: 4096x2304 01-03 01:21:48.287 12617 12843 W camera2api: 扫描失败: /storage/emulated/0/Android/data/com.android.example.cameraappxjava/files/Pictures/pic_1735885307925.jpg 01-03 01:21:48.379 12617 12760 I camera2api: 2.2 预览会话配置成功,这是什么问题
09-16
using System; using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; public class DownloadService : IDisposable { private readonly HttpClient _httpClient; private CancellationTokenSource _cts = new(); private bool _isPaused; private readonly ConcurrentDictionary<string, FileDownloadState> _downloadStates = new(); private long _totalBytesRead; private long _totalSize; private readonly ConcurrentDictionary<string, byte> _allTempFiles = new(); private readonly IConnectivity _connectivity; // 定义下载完成事件委托 public delegate void FileDownloadedHandler(string filename); public event FileDownloadedHandler OnFileDownloaded; // 新增重连状态标志 private bool _reconnectFlag; private bool _wasKickedOff; public DownloadService() { // 全局TCP/IP优化 ServicePointManager.DefaultConnectionLimit = 200; ServicePointManager.Expect100Continue = false; ServicePointManager.UseNagleAlgorithm = false; ServicePointManager.ReusePort = true; ServicePointManager.MaxServicePoints = 1000; _httpClient = new HttpClient(new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(5), PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), MaxConnectionsPerServer = 50, UseProxy = false, AutomaticDecompression = DecompressionMethods.All }); _httpClient.DefaultRequestHeaders.AcceptEncoding.ParseAdd("gzip, deflate, br"); _httpClient.DefaultRequestVersion = HttpVersion.Version20; _httpClient.Timeout = TimeSpan.FromMinutes(30); _httpClient.DefaultRequestHeaders.ConnectionClose = false; _connectivity = Connectivity.Current; _connectivity.ConnectivityChanged += OnConnectivityChanged; } private void OnConnectivityChanged(object sender, ConnectivityChangedEventArgs e) { if (_wasKickedOff) return; if (e.NetworkAccess != NetworkAccess.Internet) { Pause(); } else if (_isPaused) { _reconnectFlag = true; Resume(); } } public async Task DownloadAllAsync( Dictionary<string, string> files, IProgress<DownloadProgressReport> progress, int maxDegreeOfParallelism = 8) { var options = new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism, CancellationToken = _cts.Token }; _totalBytesRead = 0; int completedFiles = 0; await Parallel.ForEachAsync(files, options, async (file, ct) => { string fileName = file.Key; string url = file.Value; var savePath = Path.Combine(FileSystem.AppDataDirectory, fileName); var tempPath = Path.GetTempFileName(); _allTempFiles[tempPath] = 0; try { var state = new FileDownloadState(fileName, tempPath); _downloadStates[fileName] = state; long downloadedForFile = 0; if (File.Exists(tempPath)) { downloadedForFile = new FileInfo(tempPath).Length; } else if (File.Exists(savePath)) { downloadedForFile = new FileInfo(savePath).Length; } while (true) { try { using var headResponse = await _httpClient.SendAsync( new HttpRequestMessage(HttpMethod.Head, url), ct); var totalFileSize = headResponse.Content.Headers.ContentLength ?? 0; // 动态分块大小 (8-16MB) int chunkSize = Math.Max(8 * 1024 * 1024, (int)(totalFileSize / 8)); chunkSize = Math.Min(chunkSize, 16 * 1024 * 1024); var chunks = (int)Math.Ceiling((double)(totalFileSize - downloadedForFile) / chunkSize); await Parallel.ForEachAsync(Enumerable.Range(0, chunks), new ParallelOptions { MaxDegreeOfParallelism = 8 // 每个文件8线程 }, async (chunkIndex, ct) => { var start = downloadedForFile + chunkIndex * chunkSize; var end = Math.Min(start + chunkSize - 1, totalFileSize - 1); using var request = new HttpRequestMessage(HttpMethod.Get, url); request.Headers.Range = new RangeHeaderValue(start, end); using var response = await _httpClient.SendAsync( request, HttpCompletionOption.ResponseHeadersRead, ct); response.EnsureSuccessStatusCode(); // 使用1MB大缓冲区 var buffer = ArrayPool<byte>.Shared.Rent(1024 * 1024); try { using var contentStream = await response.Content.ReadAsStreamAsync(); using var fileStream = new FileStream( tempPath, FileMode.Append, FileAccess.Write, FileShare.Write, 1024 * 1024, FileOptions.Asynchronous | FileOptions.WriteThrough); int bytesRead; while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, ct)) > 0) { await fileStream.WriteAsync(buffer, 0, bytesRead, ct); downloadedForFile += bytesRead; Interlocked.Add(ref _totalBytesRead, bytesRead); progress?.Report(new DownloadProgressReport { TotalFiles = files.Count, CompletedFiles = completedFiles, DownloadedBytes = _totalBytesRead, TotalSize = _totalSize, CurrentFileName = file.Key, CurrentProgress = (double)downloadedForFile / totalFileSize }); } } finally { ArrayPool<byte>.Shared.Return(buffer); } }); File.Move(tempPath, savePath, true); _allTempFiles.TryRemove(tempPath, out _); Interlocked.Increment(ref completedFiles); OnFileDownloaded?.Invoke(fileName); break; } catch (OperationCanceledException) { throw; } catch (Exception ex) when (ex is HttpRequestException || ex is IOException) { if (_wasKickedOff) throw; await Task.Delay(5000, ct); } } } catch (OperationCanceledException) { } catch (Exception ex) { if (ex.Message.Contains("踢下线")) _wasKickedOff = true; } finally { if (!File.Exists(savePath)) { SafeDeleteFile(tempPath); } _allTempFiles.TryRemove(tempPath, out _); _downloadStates.TryRemove(fileName, out _); progress?.Report(new DownloadProgressReport { TotalFiles = files.Count, CompletedFiles = completedFiles, DownloadedBytes = _totalBytesRead, TotalSize = _totalSize }); } }); } public void Pause() => _isPaused = true; public void Resume() => _isPaused = false; public void Cancel() { _cts.Cancel(); foreach (var tempPath in _allTempFiles.Keys) { SafeDeleteFile(tempPath); } _allTempFiles.Clear(); _downloadStates.Clear(); _cts = new CancellationTokenSource(); _isPaused = false; _reconnectFlag = false; _wasKickedOff = false; _totalBytesRead = 0; } private void SafeDeleteFile(string path) { try { if (File.Exists(path)) File.Delete(path); } catch { // 忽略删除错误 } } public void Dispose() => _httpClient?.Dispose(); } public class DownloadProgressReport { public int TotalFiles { get; set; } public int CompletedFiles { get; set; } public int PrecalculatedFiles { get; set; } public long DownloadedBytes { get; set; } public long TotalSize { get; set; } public string CurrentFileName { get; set; } public double CurrentProgress { get; set; } public double OverallProgress => TotalSize > 0 ? (double)DownloadedBytes / TotalSize : 0; public int FileProgress => TotalFiles > 0 ? (CompletedFiles * 100) / TotalFiles : 0; } public class FileDownloadState { public string FileName { get; } public string TempPath { get; } public long DownloadedBytes { get; set; } public long TotalSize { get; set; } public double Progress => TotalSize > 0 ? (double)DownloadedBytes / TotalSize : 0; public FileDownloadState(string fileName, string tempPath) { FileName = fileName; TempPath = tempPath; } } 这段代码为什么下载成功后也写入缓存路径了,但是系统去读取路径时,无法读取,("/data/user/0/com.companyname.aitoy/files/theater02.mp4")这是路劲,确定手机是支持写入的,是代码的问题
最新发布
09-19
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值