谷歌对权限的要求日益严格,READ_MEDIA_IMAGES/WRITE_EXTERNAL_STORAGE/READ_MEDIA_VIDEOS这三个权限,必须是APP的核心功能代码才可以申请,如果只是某一个功能使用,谷歌要求使用Android's Photo Picker,否则APP上传GooglePlay时会被拒绝:
解决方法 :
从Android13版本开始,Android系统引入了系统级的照片选择器Photo Picker,无需申请权限即可使用;在Android13以下的版本,可以直接启用系统相册来选择图片和视频。
因为Android系统是在Android13版本之后才引入Photo Picker的,因此我们需要解决版本兼容性问题 :
- 在Android13及以上版本,使用Photo Picker,使用Intent: MediaStore.ACTION_PICK_IMAGES来启动照片选择器。
- 在Android13以下的版本,使用自定义图片选择器,或者通过Intent.ACTION_GET_CONTENT,并设置类型"image/或video/" 来启动系统相册。
- 在Android13及以上的版本,使用MediaStore.EXTRA_PICK_IMAGES_MAX来设置选择图片或视频的最大数量; Android13以下的版本,使用extra: Intent.EXTRA_ALLOW_MULTIPLE 来允许多选 编写如下工具类 :
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.util.Log;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import java.util.ArrayList;
import java.util.List;
/**
* Android Photo Picker 工具类
* 兼容 Android 13+ 的官方 Photo Picker 和低版本的传统文件选择
*/
public class PhotoPickerUtil {
private static final String TAG = "PhotoPickerUtil";
// 请求代码
public static final int REQUEST_CODE_PICK_IMAGES = 1001;
public static final int REQUEST_CODE_PICK_VIDEOS = 1002;
public static final int REQUEST_CODE_PICK_MEDIA = 1003;
// 结果键值
public static final String EXTRA_SELECTED_URIS = "selected_uris";
public static final String EXTRA_ERROR_MESSAGE = "error_message";
private ActivityResultLauncher<Intent> mediaPickerLauncher;
private PhotoPickerCallback callback;
private final Context context;
private int maxSelection = 1;
private boolean allowMultiple = false;
private String[] mimeTypes = {"image/*", "video/*"};
public interface PhotoPickerCallback {
void onMediaSelected(List<Uri> uris);
void onError(String errorMessage);
}
public PhotoPickerUtil(Context context) {
this.context = context;
initActivityResultLauncher();
}
/**
* 初始化 Activity Result Launcher
*/
private void initActivityResultLauncher() {
if (context instanceof FragmentActivity) {
FragmentActivity activity = (FragmentActivity) context;
mediaPickerLauncher = activity.registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
handleActivityResult(result.getResultCode(), result.getData());
}
});
}
}
/**
* 设置回调
*/
public void setCallback(PhotoPickerCallback callback) {
this.callback = callback;
}
/**
* 设置最大选择数量
*/
public void setMaxSelection(int maxSelection) {
this.maxSelection = maxSelection;
this.allowMultiple = maxSelection > 1;
}
/**
* 设置 MIME 类型
* @param mimeTypes 如 {"image/*"}, {"video/*"}, {"image/*", "video/*"}
*/
public void setMimeTypes(String[] mimeTypes) {
this.mimeTypes = mimeTypes;
}
/**
* 打开媒体选择器
*/
public void openMediaPicker() {
if (isPhotoPickerAvailable()) {
// Android 13+ 使用官方 Photo Picker
openSystemPhotoPicker();
} else {
// 低版本使用传统 Intent
openLegacyMediaPicker();
}
}
/**
* 检查是否支持官方 Photo Picker
*/
private boolean isPhotoPickerAvailable() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
}
/**
* 打开系统 Photo Picker (Android 13+)
*/
private void openSystemPhotoPicker() {
try {
Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
// 设置多选
if (allowMultiple && maxSelection > 1) {
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxSelection);
}
// 设置 MIME 类型过滤
if (mimeTypes != null && mimeTypes.length > 0) {
if (mimeTypes.length == 1) {
intent.setType(mimeTypes[0]);
} else {
// 多类型选择
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
}
}
if (mediaPickerLauncher != null) {
mediaPickerLauncher.launch(intent);
} else if (context instanceof Activity) {
((Activity) context).startActivityForResult(intent, REQUEST_CODE_PICK_MEDIA);
}
} catch (Exception e) {
Log.e(TAG, "打开系统 Photo Picker 失败", e);
// 降级到传统方式
openLegacyMediaPicker();
}
}
/**
* 打开传统媒体选择器 (Android 12 及以下)
*/
private void openLegacyMediaPicker() {
try {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// 设置 MIME 类型
if (mimeTypes != null && mimeTypes.length == 1) {
intent.setType(mimeTypes[0]);
} else {
intent.setType("*/*");
if (mimeTypes != null && mimeTypes.length > 1) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
}
}
// 设置多选
if (allowMultiple) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
// 创建选择器 Intent
Intent chooser = Intent.createChooser(intent, "选择图片或视频");
if (mediaPickerLauncher != null) {
mediaPickerLauncher.launch(chooser);
} else if (context instanceof Activity) {
((Activity) context).startActivityForResult(chooser, REQUEST_CODE_PICK_MEDIA);
}
} catch (Exception e) {
Log.e(TAG, "打开传统媒体选择器失败", e);
if (callback != null) {
callback.onError("无法打开媒体选择器: " + e.getMessage());
}
}
}
/**
* 处理选择结果
*/
private void handleActivityResult(int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK || data == null) {
if (callback != null) {
callback.onError("用户取消选择或选择失败");
}
return;
}
try {
List<Uri> selectedUris = new ArrayList<>();
// 处理多选结果
if (data.getClipData() != null) {
int count = Math.min(data.getClipData().getItemCount(), maxSelection);
for (int i = 0; i < count; i++) {
Uri uri = data.getClipData().getItemAt(i).getUri();
if (uri != null) {
selectedUris.add(uri);
}
}
}
// 处理单选结果
else if (data.getData() != null) {
selectedUris.add(data.getData());
}
if (selectedUris.isEmpty()) {
if (callback != null) {
callback.onError("未选择任何文件");
}
} else {
if (callback != null) {
callback.onMediaSelected(selectedUris);
}
}
} catch (Exception e) {
Log.e(TAG, "处理选择结果失败", e);
if (callback != null) {
callback.onError("处理文件时出错: " + e.getMessage());
}
}
}
/**
* 在 Activity 或 Fragment 的 onActivityResult 中调用
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_PICK_MEDIA) {
handleActivityResult(resultCode, data);
}
}
/**
* 释放资源
*/
public void release() {
callback = null;
}
}
使用方法:
public class MainActivity extends AppCompatActivity {
private PhotoPickerUtil photoPickerUtil;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化 Photo Picker
photoPickerUtil = new PhotoPickerUtil(this);
photoPickerUtil.setCallback(new PhotoPickerUtil.PhotoPickerCallback() {
@Override
public void onMediaSelected(List<Uri> uris) {
// 处理选择的媒体文件
for (Uri uri : uris) {
Log.d("PhotoPicker", "Selected URI: " + uri.toString());
// 在这里显示图片或视频
}
}
@Override
public void onError(String errorMessage) {
Toast.makeText(MainActivity.this, errorMessage, Toast.LENGTH_SHORT).show();
}
});
// 设置最多选择 5 个文件
photoPickerUtil.setMaxSelection(5);
// 只选择图片
// photoPickerUtil.setMimeTypes(new String[]{"image/*"});
// 只选择视频
// photoPickerUtil.setMimeTypes(new String[]{"video/*"});
// 选择图片和视频(默认)
photoPickerUtil.setMimeTypes(new String[]{"image/*", "video/*"});
// 打开选择器
findViewById(R.id.btn_pick_media).setOnClickListener(v -> {
photoPickerUtil.openMediaPicker();
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 如果使用传统 onActivityResult 方式
photoPickerUtil.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (photoPickerUtil != null) {
photoPickerUtil.release();
}
}
}