Android 共享存儲(chǔ)空間
訪問(wèn)共享存儲(chǔ)空間中的媒體文件
1、MediaStore概述
MediaStore是android系統(tǒng)提供的一個(gè)多媒體數(shù)據(jù)庫(kù),專門用于存放多媒體信息的,通過(guò)ContentResolver即可對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作。
- MediaStore.Files: 共享的文件,包括多媒體和非多媒體信息;
- MediaStore.Audio: 存放音頻信息;
- MediaStore.Image: 存放圖片信息;
- MediaStore.Vedio: 存放視頻信息;
每個(gè)內(nèi)部類中都又包含了Media,Thumbnails和相應(yīng)的MediaColumns,分別提供了媒體信息,縮略信息和操作字段。
(1)MediaStore.Images.Media的使用——MediaStore.Images.Media.EXTERNAL_CONTENT_URI
(2)MediaStore.Video.Media的使用——MediaStore.Video.Media.EXTERNAL_CONTENT_URI
(3)MediaStore.Audio.Media的使用——MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
(4)MediaStore.Downloads的使用——MediaStore.Downloads.getContentUri(“external”)
(5)MediaStore.Files的使用——MediaStore.Files.getContentUri(“external”)
2、示例
1、Android保存圖片到相冊(cè),兼容Android10及以上版本:
// 保存bitmap到相冊(cè),并兼容AndroidQ
public static boolean saveBitmap(Context context, Bitmap bitmap) {
if (bitmap == null) {
return false;
}
boolean isSaveSuccess;
if (Build.VERSION.SDK_INT < 29) {
isSaveSuccess = saveImageToGallery(context, bitmap);
} else {
isSaveSuccess = saveImageToGalleryQ(context, bitmap);
}
return isSaveSuccess;
}
/**
* android 10 以下版本
*/
private static boolean saveImageToGallery(Context context, Bitmap image) {
// 首先保存圖片
String storePath = AssetPathUtil.INSTANCE.getBitmapFileDir();
File appDir = new File(storePath);
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = getCharacterAndNumber() + ".png";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
// 通過(guò)io流的方式來(lái)壓縮保存圖片
boolean isSuccess = image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
// 保存圖片后發(fā)送廣播通知更新數(shù)據(jù)庫(kù)
Uri uri = Uri.fromFile(file);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
if (isSuccess) {
return true;
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* android 10 以上版本
*/
private static boolean saveImageToGalleryQ(Context context, Bitmap image) {
long mImageTime = System.currentTimeMillis();
String mImageFileName = getCharacterAndNumber() + ".png";
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.RELATIVE_PATH, AssetPathUtil.INSTANCE.getBitmapFileDir());
values.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName);
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000);
values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000);
values.put(MediaStore.MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
values.put(MediaStore.MediaColumns.IS_PENDING, 1);
ContentResolver resolver = context.getContentResolver();
final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try {
OutputStream out = resolver.openOutputStream(uri);
if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
return false;
}
values.clear();
values.put(MediaStore.MediaColumns.IS_PENDING, 0);
values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
resolver.update(uri, values, null, null);
} catch (Exception e) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
resolver.delete(uri, null);
}
return false;
}
return true;
}
2、刪除相冊(cè)資源(Kotlin):文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-507844.html
/**
* 保存bitmap到相冊(cè),并兼容AndroidQ
*/
fun getBitmapFileDir(): String {
return if (Build.VERSION.SDK_INT < 29) {
// android 10 以下版本
Environment.getExternalStorageDirectory().absolutePath + File.separator + BITMAP_FILE_DIRECTORY
} else {
Environment.DIRECTORY_PICTURES + File.separator + BITMAP_FILE_DIRECTORY
}
}
/**
* 刪除保存在【相冊(cè)指定目錄】中的圖片
*/
fun deleteBitmapFileDir(context: Context) {
if (Build.VERSION.SDK_INT < 29) {
// android 10 以下版本
val path = getBitmapFileDir()
FileUtils.deleteAllInDir(path)
} else {
deleteImages(context)
}
}
/**
* android 10及以上版本通過(guò)ContentResolver刪除指定的目錄
*/
private fun deleteImages(context: Context) {
var relativePath = getBitmapFileDir()
//判斷是否有加斜杠
if (!relativePath.endsWith("/")) {
relativePath += File.separator
}
val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.RELATIVE_PATH)
val selection = "${MediaStore.Images.Media.RELATIVE_PATH}=?"
val selectionArgs = arrayOf(relativePath)
context.contentResolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, arrayOf(relativePath))
}
3、MediaStore 擴(kuò)展方法:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-507844.html
/**
* 和媒體相關(guān)的工具類
*/
public class MediaUtils {
/**
* 每頁(yè)獲取的媒體數(shù)據(jù)量(分頁(yè)加載解決相冊(cè)卡頓問(wèn)題)
* */
private static final int PAGE_SIZE = 1000;
/**
* 添加到媒體數(shù)據(jù)庫(kù)
* Add to media database
*/
public static Uri saveVideo2Album(String videoPath, int videoWidth, int videoHeight,
int videoTime) {
File file = new File(videoPath);
if (file.exists()) {
Uri uri = null;
long dateTaken = System.currentTimeMillis();
ContentValues values = new ContentValues(11);
// 路徑;
values.put(MediaStore.Video.Media.DATA, videoPath);
// 標(biāo)題;
values.put(MediaStore.Video.Media.TITLE, file.getName());
// 時(shí)長(zhǎng)
values.put(MediaStore.Video.Media.DURATION, videoTime * 1000);
// 視頻寬
values.put(MediaStore.Video.Media.WIDTH, videoWidth);
// 視頻高
values.put(MediaStore.Video.Media.HEIGHT, videoHeight);
// 視頻大小;
values.put(MediaStore.Video.Media.SIZE, file.length());
// 插入時(shí)間;
values.put(MediaStore.Video.Media.DATE_TAKEN, dateTaken);
// 文件名;
values.put(MediaStore.Video.Media.DISPLAY_NAME, file.getName());
// 修改時(shí)間;
values.put(MediaStore.Video.Media.DATE_MODIFIED, dateTaken / 1000);
// 添加時(shí)間;
values.put(MediaStore.Video.Media.DATE_ADDED, dateTaken / 1000);
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
ContentResolver resolver = Utils.getApp().getContentResolver();
if (resolver != null) {
try {
uri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
} catch (Exception e) {
e.printStackTrace();
uri = null;
}
}
if (uri == null) {
MediaScannerConnection.scanFile(Utils.getApp(), new String[]{videoPath}, new String[]{"video/*"}, new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
}
});
}
return uri;
}
return null;
}
/**
* Add video record by android_Q api.
* AndroidQ以上,增加視頻文件記錄
*
* @param context 上下文,the context
* @param fileName 視頻文件名稱 the video file name
* @param fileType 視頻文件類型 the video file type
* @param relativePath 相對(duì)路徑 the relative path
* @param duration 文件時(shí)長(zhǎng),單位是毫秒The duration of the file, in milliseconds.
* @return String 類型的Uri. The String of Uri
*/
public static String addVideoRecord_Q(Context context, String fileName, String fileType,
String relativePath, long duration) {
if (!AndroidVersionUtils.isAboveAndroid_Q()) {
return null;
}
if (TextUtils.isEmpty(relativePath)) {
return null;
}
relativePath = "Movies/" + relativePath;
ContentResolver resolver = context.getContentResolver();
//設(shè)置文件參數(shù)到ContentValues中
ContentValues values = new ContentValues();
//設(shè)置文件名
values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);
//設(shè)置文件描述,這里以文件名代替
values.put(MediaStore.Video.Media.DESCRIPTION, fileName);
//設(shè)置文件類型為image/*
values.put(MediaStore.Video.Media.MIME_TYPE, "video/" + fileType);
values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
values.put(MediaStore.Video.Media.DATE_MODIFIED, System.currentTimeMillis() / 1000);
values.put(MediaStore.Video.Media.DURATION, duration);
//注意:MediaStore.Images.Media.RELATIVE_PATH需要targetSdkVersion=29,
//故該方法只可在Android10的手機(jī)上執(zhí)行
values.put(MediaStore.Video.Media.RELATIVE_PATH, relativePath);
//EXTERNAL_CONTENT_URI代表外部存儲(chǔ)器
Uri external = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
//insertUri表示文件保存的uri路徑
Uri insertUri = resolver.insert(external, values);
return String.valueOf(insertUri);
}
/**
* Delete video record by android_Q api
* AndroidQ以上刪除視頻記錄
*
* @param context 上下文,the context
* @param uri 文件的uri the uri of file
* @return 刪除的數(shù)量,如果是0,代表刪除失敗 The number of deletions. If it is 0, it means the deletion failed
*/
public static int deleteVideoRecord_Q(Context context, Uri uri) {
context = context.getApplicationContext();
ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null) {
return 0;
}
Cursor cursor = null;
String column = MediaStore.MediaColumns._ID;
int fileID = -1;
try {
cursor = contentResolver.query(uri, new String[]{column}, null, null,
null);
if (cursor != null && cursor.moveToFirst()) {
int column_index = cursor.getColumnIndexOrThrow(column);
fileID = cursor.getInt(column_index);
}
} catch (Exception e) {
LogUtils.e(e);
} finally {
if (cursor != null) {
cursor.close();
}
}
if (fileID >= 0) {
return contentResolver.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, column + "=?", new String[]{String.valueOf(fileID)});
}
return 0;
}
/**
* 獲取媒體數(shù)據(jù)列表
* Gets a list of media data
*
* @param type TYPE_VIDEO = 0; 視頻 TYPE_PHOTO = 1;圖片;YPE_ALL = 2;圖片和視頻
*/
public static void getMediaList(final int type, final String[] filter, final MediaCallback callback, int mCurrentPage) {
ThreadUtils.getCachedPool().execute(new Runnable() {
@Override
public void run() {
final List<MediaData> dataList = new ArrayList<>();
if (type == MediaData.TYPE_ALL) {
Cursor cursor = getMediaCursor(MediaData.TYPE_PHOTO, mCurrentPage);
if (cursor != null) {
createMediaData(cursor, filter, dataList, false);
cursor.close();
}
cursor = getMediaCursor(MediaData.TYPE_VIDEO, mCurrentPage);
if (cursor != null) {
createMediaData(cursor, filter, dataList, true);
cursor.close();
}
} else {
Cursor cursor = getMediaCursor(type, mCurrentPage);
if (cursor != null) {
createMediaData(cursor,filter, dataList, type == MediaData.TYPE_VIDEO);
cursor.close();
}
}
if (callback != null) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
callback.onResult(dataList);
}
});
}
}
});
}
@SuppressLint("InlinedApi")
private static Cursor getMediaCursor(int type, int mCurrentPage) {
String[] projection = null;
Uri uri = null;
String order = null;
if (type == MediaData.TYPE_VIDEO) {
projection = new String[]{MediaStore.Video.Thumbnails._ID
, MediaStore.Video.Media._ID
, MediaStore.Video.Thumbnails.DATA
, MediaStore.Video.Media.DURATION
, MediaStore.Video.Media.SIZE
, MediaStore.Video.Media.DATE_ADDED
, MediaStore.Video.Media.DISPLAY_NAME
, MediaStore.Video.Media.DATE_MODIFIED};
uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
order = MediaStore.Video.Media.DATE_ADDED;
} else if (type == MediaData.TYPE_PHOTO) {
projection = new String[]{
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Thumbnails.DATA,
MediaStore.MediaColumns.DISPLAY_NAME
};
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
order = MediaStore.Images.Media.DATE_ADDED;
}
if (projection == null) {
return null;
}
// 兼容折疊屏,在Android R及以上手機(jī),order中禁止了LIMIT關(guān)鍵字,所以在這里做了適配
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
Bundle bundle = new Bundle();
bundle.putInt(ContentResolver.QUERY_ARG_OFFSET, mCurrentPage * PAGE_SIZE);
bundle.putInt(ContentResolver.QUERY_ARG_LIMIT, PAGE_SIZE);
bundle.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, new String[]{order + " DESC"});
return Utils.getApp().getContentResolver().query(uri, projection, bundle, null);
} else {
String sortOrder = order + " DESC LIMIT " + PAGE_SIZE + " OFFSET " + mCurrentPage * PAGE_SIZE;
return Utils.getApp().getContentResolver().query(uri, projection, null, null, sortOrder);
}
}
/**
* 創(chuàng)建媒體實(shí)體類并添加到集合中
* Create a media entity class and add it to the collection
*/
@SuppressLint("InlinedApi")
private static void createMediaData(Cursor cursor, String[] filter, List<MediaData> list, boolean isVideo) {
if (cursor != null) {
String mediaId;
String mediaDate;
String mediaThumbnails;
String mediaDisplayName;
if (isVideo) {
mediaId = MediaStore.Video.Media._ID;
mediaDate = MediaStore.Video.Media.DATE_ADDED;
mediaThumbnails = MediaStore.Video.Thumbnails.DATA;
mediaDisplayName = MediaStore.Video.Media.DISPLAY_NAME;
} else {
mediaId = MediaStore.Images.Media._ID;
mediaDate = MediaStore.Images.Media.DATE_ADDED;
mediaThumbnails = MediaStore.Images.Thumbnails.DATA;
mediaDisplayName = MediaStore.Images.Media.DISPLAY_NAME;
}
while (cursor.moveToNext()) {
int videoId = cursor.getInt(cursor.getColumnIndex(mediaId));
String absolutePath = cursor.getString(cursor.getColumnIndex(mediaThumbnails));
String path = AndroidVersionUtils.isAboveAndroid_Q()? AndroidVersionUtils.getRealPathAndroid_Q(Uri.parse(absolutePath)): absolutePath;
String displayName = cursor.getString(cursor.getColumnIndex(mediaDisplayName));
int timeIndex = cursor.getColumnIndex(mediaDate);
long date = cursor.getLong(timeIndex) * 1000;
if (fileIsValid(path)) {
if (!TextUtils.isEmpty(absolutePath)) {
int lastDot = absolutePath.lastIndexOf(".");
String type = absolutePath.substring(lastDot + 1);
if (!TextUtils.isEmpty(type)) {
type = type.toLowerCase();
if (type.equals("mpg") || type.equals("mkv")) {
continue;
}
//過(guò)濾文件類型
boolean isFilter = false;
if (filter != null && filter.length > 0) {
for (int i = 0; i < filter.length; i++) {
String filterItem = filter[i];
if (StringUtils.equals(filterItem, type)) {
isFilter = true;
break;
}
}
}
if (isFilter) {
continue;
}
}
}
MediaData mediaData = new MediaData()
.setId(videoId)
.setPath(path)
.setDate(date)
.setDisplayName(displayName);
if (isVideo) {
mediaData.setType(MediaData.TYPE_VIDEO)
.setDuration(cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.DURATION)));
} else {
mediaData.setType(MediaData.TYPE_PHOTO);
}
if (isGif(path)){
mediaData.setIsGif(true);
mediaData.setType(MediaData.TYPE_VIDEO);
}
list.add(mediaData);
}
}
}
}
/**
* 判斷是否是gif圖片
* Is gif boolean.
*
* @param path the path
* @return the boolean
*/
public static boolean isGif(String path) {
String fileName = FileUtils.getFileSuffix(path);
if (!TextUtils.isEmpty(fileName) && "GIF".equals(fileName.toUpperCase())) {
return true;
}
return false;
}
private static boolean fileIsValid(String filePath) {
if (FileUtils.isAndroidQUriPath(filePath)) {
return true;
}
File file = FileUtils.getFileByPath(filePath);
return file != null && file.exists();
}
public interface MediaCallback {
void onResult(List<MediaData> dataList);
}
}
到了這里,關(guān)于Android保存圖片到相冊(cè),兼容Android10及以上版本的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!