修订时间:2022年10月9日
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT >= 19) {
handlePathOnKitKat(data);
} else {
handlePathBeforeKitKat(data);
}
}
}
private void handlePathBeforeKitKat(Intent data) {
Uri uri = data.getData();
String path = uri.getPath();
//somethings
}
@TargetApi(19)
private void handlePathOnKitKat(Intent data) {
String path = null;
Uri uri = data.getData();
if (DocumentsContract.isDocumentUri(this, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID + "=" + id;
path = getPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
} else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
path = getPath(contentUri, null);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
path = getPath(uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
path = uri.getPath();
}
//somethins
}
private String getPath(Uri uri, String selection) {
String path = null;
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
记录一下,免得到处找。
以上内容为android9之前可用,在android10之后对文件管理更加严格后,该方法不一定能获取路径,targetSDK为28
或者targerSDK为29
设置了android:requestLegacyExternalStorage="true"
也能用。
记录一下高版本对文件的处理。
获取文件:
- 通过
Context
的getFileDir()
、getCacheFile()
等方法获取应用内部数据,这时获取到的数据就是file本身,可直接获取到path信息。实际路径在data/data/packageName/xxx
里。 - 通过
Context
的getExternalFilesDir()
、getExternalCacheDir
等方法获取应用在外部存储器的内部数据,同样的也是获取file对象。实际路径在storage/sdcard0/Android/data/packageName/xxx
里。
以上两个方法均是获取的App的内部沙盒数据,那获取外部数据呢?
//androidx-appcompat高版本有registerForActivityResult,可通过该方法处理
//不再编写使用onActivityResult这些方法了,回调处理数据
//TakePicture是拍照用的,还有其他很多官方编写的使用,也可自定义
registerForActivityResult(ActivityResultContracts.TakePicture()) { b ->//b是成功与否
//因为是拍照,所以提前创建好了File,currentFilePath是已知的数据,目的是保存到相册
currentFilePath?.let {
if (b) {
//保存图片到相册
val typeUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL_PRIMARY
)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
//插入数据获取uri
contentResolver.insert(typeUri, ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "${getTime()}.jpeg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(
MediaStore.Images.Media.RELATIVE_PATH,
"${Environment.DIRECTORY_PICTURES}"
)
}
})?.let { uri ->
//根据api返回的uri,进行数据写入
var os: OutputStream? = null
var fis: FileInputStream? = null
try {
os = contentResolver.openOutputStream(uri)
fis = FileInputStream(File(path))
val bytes = ByteArray(4096)
var byte = -1
while (true) {
byte = fis.read(bytes)
if (byte == -1) {
break
}
os?.write(bytes)
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
os?.close()
fis?.close()
}
}
}
}
}.launch(
FileProvider.getUriForFile(
this,
"${packageName}.fileprovider", file//拍照自行创建好的文件
)
)
//获取非沙盒内文件
registerForActivityResult(ActivityResultContracts.GetContent()) {
it?.let { uri ->//这是系统返回的文件uri,我这是图片举例
val file = File(
getExternalFilesDir(
Environment.DIRECTORY_PICTURES
), UUID.randomUUID().toString().replace("-", "")
)
var fos: FileOutputStream? = null
var input: InputStream? = null
try {
//通过uri获取输入流,转存至app的沙盒文件里,从而获取文件path
input = contentResolver.openInputStream(uri)
fos = FileOutputStream(file)
val bytes = ByteArray(4096)
var byte: Int?
while (true) {
byte = input?.read(bytes)
if (byte == -1) {
break
}
fos.write(bytes)
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
input?.close()
fos?.close()
}
}
}.launch("image/*")//修改type即可获取对应的文件类型
小小的总结一下:获取文件,非app沙盒文件通过uri获取输入流转存到沙盒里,app沙盒文件直接获取file对象即可。
存储文件
存储在沙盒内就不用说了,正常的写入即可。这里讲讲非沙盒内存储。
//构造方法处理
registerForActivityResult(ActivityResultContracts.CreateDocument("image/jpg")){uri->
//在这里获取到了要存储的文件uri,系统已经创建好类型
//接着通过输入输出流即可
it?.let{
val os = contentResolver.openOutputStream(it)
....接着巴拉巴拉的数据
}
}.launch("title.jpg")//这里设置文件名