android 11及保存gif以及音频到本地

您好,我看到您的博客,android11保存相册和视频到相册,觉得很好,很厉害,但是还有一个问题就是,android 11及以上保存gif到相册,以及如何保存音频到本地啊,我看您的博客文章只有保存视频和图片。麻烦您了

在 Android 11 及以上版本,为了加强隐私保护,对应用访问外部存储进行了限制。可以通过 MediaStore API 来保存图片、视频和音频文件到相册或本地。

保存 GIF 到相册:

val resolver: ContentResolver = context.contentResolver
val imageCollection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

val imageDetails = ContentValues().apply {
    put(MediaStore.Images.Media.DISPLAY_NAME, "my_gif.gif")
    put(MediaStore.Images.Media.MIME_TYPE, "image/gif")
    put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
}

val imageUri = resolver.insert(imageCollection, imageDetails)
val outputStream = resolver.openOutputStream(imageUri!!)
outputStream?.let {
    // 将 gif 字节数组写入 outputStream
    it.write(gifBytes)
    it.close()
}

保存音频到本地:

val resolver: ContentResolver = context.contentResolver
val audioCollection = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

val audioDetails = ContentValues().apply {
    put(MediaStore.Audio.Media.DISPLAY_NAME, "my_audio.mp3")
    put(MediaStore.Audio.Media.MIME_TYPE, "audio/mpeg")
    put(MediaStore.Audio.Media.RELATIVE_PATH, Environment.DIRECTORY_MUSIC)
}

val audioUri = resolver.insert(audioCollection, audioDetails)
val outputStream = resolver.openOutputStream(audioUri!!)
outputStream?.let {
    // 将音频字节数组写入 outputStream
    it.write(audioBytes)
    it.close()
}

需要注意的是,保存文件到相册或本地时,需要在 AndroidManifest.xml 文件中添加对应的权限声明,例如:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

另外,Android 10 及以上版本还需要在代码中动态请求权限,才能访问外部存储。

保存GIF文件到相册

// 假设你已经有一个GIF文件的路径 gifFilePath
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "my_gif.gif");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/gif");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);

Uri imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

OutputStream outputStream;
try {
    outputStream = getContentResolver().openOutputStream(imageUri);
    InputStream inputStream = new FileInputStream(gifFilePath);
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, bytesRead);
    }
    inputStream.close();
    outputStream.close();
    // 刷新相册
    MediaScannerConnection.scanFile(this, new String[]{imageUri.getPath()}, null, null);
} catch (IOException e) {
    e.printStackTrace();
}

保存音频文件到本地

// 假设你已经有一个音频文件的路径 audioFilePath
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Media.DISPLAY_NAME, "my_audio.mp3");
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mpeg");
values.put(MediaStore.Audio.Media.RELATIVE_PATH, Environment.DIRECTORY_MUSIC);

Uri audioUri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);

OutputStream outputStream;
try {
    outputStream = getContentResolver().openOutputStream(audioUri);
    InputStream inputStream = new FileInputStream(audioFilePath);
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, bytesRead);
    }
    inputStream.close();
    outputStream.close();
    // 刷新媒体库
    MediaScannerConnection.scanFile(this, new String[]{audioUri.getPath()}, null, null);
} catch (IOException e) {
    e.printStackTrace();
}
  • 这篇文章讲的很详细,请看:Android 网络图片,gif 下载并保存到相册
  • 除此之外, 这篇博客: Android之仿美团加载数据帧动画中的 一:先来张效果图(这里是GIF动画,我就截屏的所有没有动画,实际是动的) 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 以下回答由chatgpt基于相关博客总结生成:

    我可以为您提供如下解决方案:

    1. 如何在Android 11及以上的系统中保存gif到相册:

    在Android 11及以上系统中,应用需要请求存储权限才能访问公共存储空间,可以使用MediaStore API将gif保存到相册。

    示例代码:

    private void saveGifToGallery(Context context, String filePath) {
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, "gif_image");
        values.put(MediaStore.Images.Media.DESCRIPTION, "Gif image saved from app");
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/gif");
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
    
        Uri external = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        ContentResolver resolver = context.getContentResolver();
    
        Uri insertUri = resolver.insert(external, values);
        if (insertUri != null) {
            try {
                OutputStream outputStream = resolver.openOutputStream(insertUri);
                InputStream inputStream = new FileInputStream(new File(filePath));
                byte[] buffer = new byte[1024];
                int read;
                while ((read = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, read);
                }
                inputStream.close();
                outputStream.flush();
                outputStream.close();
                Toast.makeText(context, "Gif saved to gallery", Toast.LENGTH_SHORT).show();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    1. 如何保存音频到本地:

    将音频保存到本地需要先请求存储权限,然后将音频数据写入到文件中。

    示例代码:

    private void saveAudioToLocalStorage(Context context, byte[] audioData) {
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    PERMISSION_REQUEST_CODE);
            return;
        }
    
        String fileName = "audio_record_" + System.currentTimeMillis();
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), fileName);
        try {
            FileOutputStream outputStream = new FileOutputStream(file);
            outputStream.write(audioData);
            outputStream.flush();
            outputStream.close();
            Toast.makeText(context, "Audio saved to local storage", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

android 在本地建立文件夹,并将图片和音频保存在文件夹下,
件路劲是我们之前创建文件的时候保存的,通过newFileName(),我们会得到我们所需要保存文件的文件路径和文件名,然后我们将文件名返回,通过方法setoutfile将文件输出即可,当然这可能会比较伤硬盘的,因为这是一个字节一个字节的读取,我们通过缓冲读取的效果会更好一些,这样我们的文件就被保存下来了。

 public void playRecord() {
    mediaRecorder = new MediaRecorder();
    // 第1步:设置音频来源(MIC表示麦克风)
    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    // 第2步:设置音频输出格式(默认的输出格式)
    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    // 第3步:设置音频编码方式(默认的编码方式)
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    // 第4步:指定音频输出文件
    mediaRecorder.setOutputFile(newFileName());
    // 第5步:调用prepare方法
    try {
        mediaRecorder.prepare();
    } catch (IllegalStateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    // 第6步:调用start方法开始录音
    mediaRecorder.start();
    }
 
    public String newFileName() {
    SharedPreferences filePath = getSharedPreferences("filepath", MODE_PRIVATE);
    mFileName = filePath.getString("path", null);
    String s = new SimpleDateFormat("yyyy-MM-dd hhmmss").format(new Date());
    return mFileName += "/music/" + s + ".3gp";
    }

图片都可以报错,gif也可以的啊,gif也是类似于图片的一种,动态图片而已。网络图片或gif,可以使用像OkHttp这样的工具进行下载。同样的,音频和视频也是一样的啊,只是文件后缀不同而已。要注意请求读写权限。具体的程序代码,你可以参考:
https://www.jianshu.com/p/e3e8fb749e9f

需要在AndroidManifest.xml 文件中添加读写外部存储的权限,然后在进行保存

引用chatgpt内容作答:
在 Android 11 及以上的设备上,保存 GIF 到相册需要使用 Android 10 引入的新的存储访问框架。同时,保存音频文件到本地可以使用标准的文件操作方法。下面是一个示例代码,演示如何保存 GIF 到相册以及如何保存音频到本地。

首先,确保在 AndroidManifest.xml 文件中添加以下权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

接下来,你可以使用以下代码保存 GIF 到相册:

import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.provider.MediaStore;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class GifUtils {

    public static void saveGifToGallery(Context context, Bitmap gifBitmap, String gifFileName) {
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, gifFileName);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/gif");
        values.put(MediaStore.Images.Media.WIDTH, gifBitmap.getWidth());
        values.put(MediaStore.Images.Media.HEIGHT, gifBitmap.getHeight());

        // 在 Android 10 及以上版本,使用 MediaStore API 保存到公共集合
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
            values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
            values.put(MediaStore.Images.Media.IS_PENDING, 1);

            // 保存到 MediaStore
            final android.content.ContentResolver resolver = context.getContentResolver();
            final android.net.Uri collection = MediaStore.Images.Media
                    .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            final android.net.Uri item = resolver.insert(collection, values);

            try {
                // 将 GIF 图片保存到 URI
                if (item != null) {
                    OutputStream out = resolver.openOutputStream(item);
                    gifBitmap.compress(Bitmap.CompressFormat.WEBP, 100, out);
                    out.flush();
                    out.close();

                    // 完成保存后,将 IS_PENDING 设置为 0
                    values.clear();
                    values.put(MediaStore.Images.Media.IS_PENDING, 0);
                    resolver.update(item, values, null, null);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            // 通知媒体库刷新
            MediaScannerConnection.scanFile(context, new String[]{item.toString()}, null, null);
        } else {
            // 在 Android 9 及以下版本,将 GIF 图片保存到指定文件夹
            File gifDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/GIFs");
            if (!gifDirectory.exists()) {
                gifDirectory.mkdirs();
            }

            File gifFile = new File(gifDirectory, gifFileName + ".gif");
            try {
                OutputStream out = new FileOutputStream(gifFile);
                gifBitmap.compress(Bitmap.CompressFormat.WEBP, 100, out);
                out.flush();
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

            // 通知媒体库刷新
            MediaScannerConnection.scanFile(context, new String[]{gifFile.getAbsolutePath()}, null, null);
        }
    }

    public static Bitmap createDummyGifBitmap(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawColor(Color.RED);
        return bitmap;
    }
}

上面的代码中,saveGifToGallery() 方法用于保存 GIF 到相册。如果设备的 Android 版本是 10 及以上,它使用 MediaStore API 将 GIF 保存到公共集合中,并设置 IS_PENDING 标志来表示此文件为待定状态。保存完成后,将 IS_PENDING 设置为 0,这样媒体库就可以正确识别文件。

如果设备的 Android 版本是 9 及以下,代码会将 GIF 图片保存到指定文件夹中,并使用 MediaScannerConnection 来通知媒体库刷新。

以下是保存音频文件到本地的示例代码:

import android.content.Context;
import android.media.MediaScannerConnection;
import android.os.Environment;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class AudioUtils {

    public static void saveAudioToLocalStorage(Context context, byte[] audioData, String audioFileName) {
        File audioDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/Audio");
        if (!audioDirectory.exists()) {
            audioDirectory.mkdirs();
        }

        File audioFile = new File(audioDirectory, audioFileName + ".mp3");
        try {
            OutputStream out = new FileOutputStream(audioFile);
            out.write(audioData);
            out.flush();
            out.close();

            // 通知媒体库刷新
            MediaScannerConnection.scanFile(context, new String[]{audioFile.getAbsolutePath()}, null, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上述代码中的 saveAudioToLocalStorage() 方法用于将音频数据保存为本地文件。它将音频数据写入到指定的文件夹中,并使用 MediaScannerConnection 来通知媒体库刷新,以便在其他应用程序中能够访问到该音频文件。
在使用上述代码保存文件之前,确保已经获取了适当的权限,如写入外部存储的权限(WRITE_EXTERNAL_STORAGE)以及在 AndroidManifest.xml 文件中声明这些权限。此外,还需要注意 Android 10 及以上版本与 Android 9 及以下版本之间的存储访问差异,以选择适当的保存方式。

采用chatgpt:
要在Android 11及更高版本中保存GIF到相册,您可以使用以下代码:

ContentResolver resolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "gifFileName.gif");
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/gif");
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "GIFs");

Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

try {
    OutputStream outputStream = resolver.openOutputStream(uri);
    // 将 GIF 数据写入 outputStream
} catch (IOException e) {
    e.printStackTrace();
}

这将在Pictures/GIFs目录下创建一个名为"gifFileName.gif"的GIF文件,并将其添加到媒体库中。

要将音频保存到本地,您可以使用以下代码:

String fileName = "audioFileName.mp3";
File file = new File(getExternalFilesDir(null), fileName);

try {
    FileOutputStream fos = new FileOutputStream(file);
    // 将音频数据写入 fos
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

这将在应用程序的外部存储路径中创建一个名为"audioFileName.mp3"的文件,并将音频数据写入该文件中。请注意,您需要在AndroidManifest.xml中声明WRITE_EXTERNAL_STORAGE权限才能访问外部存储器。

对于 Android 11 及更高版本,保存 GIF 到相册以及保存音频到本地的方法与保存图片和视频类似。下面是一个简单的示例代码:

  1. 保存 GIF 到相册:
// 在 AndroidManifest.xml 文件中添加 WRITE_EXTERNAL_STORAGE 权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

// 保存 GIF 到相册
String gifFilePath = "path_to_gif_file.gif";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "my_gif");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/gif");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
Uri gifUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try {
    OutputStream outputStream = getContentResolver().openOutputStream(gifUri);
    FileInputStream fileInputStream = new FileInputStream(gifFilePath);
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = fileInputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, bytesRead);
    }
    fileInputStream.close();
    outputStream.close();
    Toast.makeText(this, "GIF saved to gallery", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
    e.printStackTrace();
}
  1. 保存音频到本地:
// 在 AndroidManifest.xml 文件中添加 WRITE_EXTERNAL_STORAGE 权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

// 保存音频到本地
String audioFilePath = "path_to_audio_file.mp3";
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Media.DISPLAY_NAME, "my_audio");
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mpeg");
values.put(MediaStore.Audio.Media.RELATIVE_PATH, Environment.DIRECTORY_MUSIC);
Uri audioUri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
try {
    OutputStream outputStream = getContentResolver().openOutputStream(audioUri);
    FileInputStream fileInputStream = new FileInputStream(audioFilePath);
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = fileInputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, bytesRead);
    }
    fileInputStream.close();
    outputStream.close();
    Toast.makeText(this, "Audio saved to local storage", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
    e.printStackTrace();
}

以上示例代码中,我们使用 ContentValues 来设置保存文件的相关信息,如显示名称、MIME 类型和相对路径。然后,通过调用 getContentResolver().insert() 将文件插入到媒体库中,并获取对应的 URI。最后,使用输入流和输出流来复制文件内容并保存到指定位置。

请注意,在 Android 10 及更高版本中,需要在 AndroidManifest.xml 文件中添加适当的存储权限。此外,为了确保正确的文件保存和访问,请根据实际需求进行适当的错误处理和权限请求。

看到这么多人工智能的回答,我想说的是,如果不了解本质,再多的解答对题主来说都是枉然。
题目中描述的是保存一些文件到相册,其实我们只要知道相册的路径,不管什么文件都能保存到相册,只是相册不支持的格式可能无法打开,仅此而已。
安卓相册目录,在内部存储的DCIM中,只要申请到权限,什么文件都可以保存。
其绝对路径可以用以下方式得到

Environment.getExternalStorageDirectory() + "/DCIM"

这种路径下还可以新建自定义的文件夹,保存的文件打开相册之后会自动扫描并预览
安卓12一下添加并动态申请

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

安卓12及以上添加并跳转到设置

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />

至于如何动态申请或者跳转,相信网上一大把现成的代码,写的又漂亮又简介。

谁的博客