使用 File 对象在 Android (API > 24) 中捕获和保存视频? [英] Capturing and Saving Videos in Android (API > 24) using File object?
问题描述
我想录制一段视频,然后在录制 5 秒后保存.我有以下代码在 API < 上运行良好24,但是对于 API > 24 我得到错误.
代码:
public void startRecording(){文件 mediaFile = 新文件(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)+ "/myvideo.mp4");意图意图 = 新意图(MediaStore.ACTION_VIDEO_CAPTURE);intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT,5);fileUri = Uri.fromFile(mediaFile);intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);startActivityForResult(意图,VIDEO_CAPTURE);}protected void onActivityResult(int requestCode,int resultCode, 意图数据) {如果(请求代码 == VIDEO_CAPTURE){如果(resultCode == RESULT_OK){Toast.makeText(this, "视频已保存到:\n" +data.getData(), Toast.LENGTH_LONG).show();} else if (resultCode == RESULT_CANCELED) {Toast.makeText(this, "视频录制取消.",Toast.LENGTH_LONG).show();} 别的 {Toast.makeText(this, "录制视频失败",Toast.LENGTH_LONG).show();}}}
错误:
2019-10-08 01:15:43.483 21573-21573/com.mobilecomputing.learn2sign E/AndroidRuntime:致命异常:main进程:com.mobilecomputing.learn2sign,PID:21573android.os.FileUriExposedException:file:///storage/emulated/0/myvideo.mp4 通过 ClipData.Item.getUri() 暴露在应用程序之外在 android.os.StrictMode.onFileUriExposed(StrictMode.java:1978)在 android.net.Uri.checkFileUriExposed(Uri.java:2371)在 android.content.ClipData.prepareToLeaveProcess(ClipData.java:963)在 android.content.Intent.prepareToLeaveProcess(Intent.java:10228)在 android.content.Intent.prepareToLeaveProcess(Intent.java:10213)在 android.app.Instrumentation.execStartActivity(Instrumentation.java:1854)在 android.app.Activity.startActivityForResult(Activity.java:4599)在 androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:676)在 android.app.Activity.startActivityForResult(Activity.java:4557)在 androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:663)在 com.mobilecomputing.learn2sign.PlayHelpVideo.startRecording(PlayHelpVideo.java:125)在 com.mobilecomputing.learn2sign.PlayHelpVideo$1.onClick(PlayHelpVideo.java:46)在 android.view.View.performClick(View.java:6669)在 android.view.View.performClickInternal(View.java:6638)在 android.view.View.access$3100(View.java:789)在 android.view.View$PerformClick.run(View.java:26145)在 android.os.Handler.handleCallback(Handler.java:873)在 android.os.Handler.dispatchMessage(Handler.java:99)在 android.os.Looper.loop(Looper.java:193)在 android.app.ActivityThread.main(ActivityThread.java:6898)在 java.lang.reflect.Method.invoke(Native Method)在 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
我检查过,错误是由于 File 对象的语法,API > 24 不支持.
虽然我可以找到其他可以工作的代码,但我很好奇这个代码是否有一个小调整,可以使它也适用于 API > 24.也许在同一行.
有人可以帮我吗?
我试过:https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en 但这样做会使应用程序在 API 22 上崩溃,因为嗯.
更新:
根据回复的答案发布更改,代码适用于 API <24 和更少,而不会正确崩溃.但是在 API > 24 上虽然没有崩溃,但是 createImageFile() 函数抛出异常:
私有文件 createImageFile() 抛出 IOException {//创建图片文件名String imageFileName = "myvideo";文件存储目录 = 新文件(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "相机");文件图像 = File.createTempFile(imageFileName,/* 前缀 */".mp4",/* 后缀 */storageDir/* 目录 */);//保存文件:用于 ACTION_VIEW 意图的路径Log.v("myTag","FAB 创建图像");mCurrentPhotoPath = "file:" + image.getAbsolutePath();返回图像;}
这是从以下函数调用的:
public void startRecording(){Log.v("myTag","FAB 录音");文件 mediaFile = null;尝试 {mediaFile = createImageFile();} catch (IOException ex) {Log.v("myTag","异常");返回;}意图意图 = 新意图(MediaStore.ACTION_VIDEO_CAPTURE);intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT,5);//fileUri = Uri.fromFile(mediaFile);fileUri = FileProvider.getUriForFile(PlayHelpVideo.this,BuildConfig.APPLICATION_ID + ".provider",媒体文件);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);startActivityForResult(意图,VIDEO_CAPTURE);}
您可以使用 FileProvider
类来授予对特定文件或文件夹的访问权限,以便其他应用程序可以访问它们.创建您自己的继承 FileProvider
的类,以确保您的 FileProvider
不会与此处所述的导入依赖项中声明的 FileProviders
冲突.>
将 file://
URI 替换为 content://
URI 的步骤:
添加一个扩展 FileProvider 的类
公共类 GenericFileProvider 扩展 FileProvider {}
<块引用>
在 AndroidManifest.xml
下添加 FileProvider
标签
标签.指定唯一的权限android:authorities
属性避免冲突,导入依赖项可能指定 ${applicationId}.provider
和其他常用权限.
<元数据android:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/provider_paths"/></提供者></应用程序></清单>
然后在 res/xml
文件夹中创建一个 provider_paths.xml
文件.如果文件夹不存在,则可能需要创建该文件夹.该文件的内容如下所示.它描述了我们希望共享对根文件夹 (path=".")
中名为 external_files 的外部存储的访问权限.
最后一步是更改下面的代码行
fileUri = Uri.fromFile(mediaFile);
到
fileUri= FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", mediaFile);
I want to record a video and then save it once it is done recording for 5 seconds. I have the following code which works fine on API < 24, however for API > 24 I get the error.
Code:
public void startRecording()
{
File mediaFile = new
File(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
+ "/myvideo.mp4");
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT,5);
fileUri = Uri.fromFile(mediaFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(intent, VIDEO_CAPTURE);
}
protected void onActivityResult(int requestCode,
int resultCode, Intent data) {
if (requestCode == VIDEO_CAPTURE) {
if (resultCode == RESULT_OK) {
Toast.makeText(this, "Video has been saved to:\n" +
data.getData(), Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "Video recording cancelled.",
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Failed to record video",
Toast.LENGTH_LONG).show();
}
}
}
Error:
2019-10-08 01:15:43.483 21573-21573/com.mobilecomputing.learn2sign E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mobilecomputing.learn2sign, PID: 21573
android.os.FileUriExposedException: file:///storage/emulated/0/myvideo.mp4 exposed beyond app through ClipData.Item.getUri()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1978)
at android.net.Uri.checkFileUriExposed(Uri.java:2371)
at android.content.ClipData.prepareToLeaveProcess(ClipData.java:963)
at android.content.Intent.prepareToLeaveProcess(Intent.java:10228)
at android.content.Intent.prepareToLeaveProcess(Intent.java:10213)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1854)
at android.app.Activity.startActivityForResult(Activity.java:4599)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:676)
at android.app.Activity.startActivityForResult(Activity.java:4557)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:663)
at com.mobilecomputing.learn2sign.PlayHelpVideo.startRecording(PlayHelpVideo.java:125)
at com.mobilecomputing.learn2sign.PlayHelpVideo$1.onClick(PlayHelpVideo.java:46)
at android.view.View.performClick(View.java:6669)
at android.view.View.performClickInternal(View.java:6638)
at android.view.View.access$3100(View.java:789)
at android.view.View$PerformClick.run(View.java:26145)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
I checked and the error is because of the Syntax of File object and that it is not supported on API > 24.
Although I could find other codes which were working I am curious as to if this code has a minor tweak which could make it work for API > 24 too. Perhaps something on the same line.
Can anyone help me on this ?
Edit:
I have tried: https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en but doing this makes application crash on API 22 as well.
Update:
Post making changes per one of the replied answers, code works on API < 24 and less without crashing properly. However on API > 24 although it doesnt crash, but createImageFile() function throws an exception:
private File createImageFile() throws IOException {
// Create an image file name
String imageFileName = "myvideo";
File storageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM), "Camera");
File image = File.createTempFile(
imageFileName, /* prefix */
".mp4", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
Log.v("myTag","FAB create image");
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
return image;
}
This is called from the below function:
public void startRecording()
{
Log.v("myTag","FAB recording");
File mediaFile = null;
try {
mediaFile = createImageFile();
} catch (IOException ex) {
Log.v("myTag","Exception");
return;
}
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT,5);
//fileUri = Uri.fromFile(mediaFile);
fileUri = FileProvider.getUriForFile(PlayHelpVideo.this,
BuildConfig.APPLICATION_ID + ".provider",
mediaFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(intent, VIDEO_CAPTURE);
}
You can use FileProvider
class to give access to the particular file or folder to make them accessible for other apps. Create your own class inheriting FileProvider
in order to make sure your FileProvider
doesn't conflict with FileProviders
declared in imported dependencies as described here.
Steps to replace file://
URI with content://
URI:
Add a class extending FileProvider
public class GenericFileProvider extends FileProvider {}
Add a FileProvider
<provider>
tag inAndroidManifest.xml
under<application>
tag. Specify a unique authority for theandroid:authorities
attribute to avoid conflicts, imported dependencies might specify${applicationId}.provider
and other commonly used authorities.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
<application
...
<provider
android:name=".GenericFileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>
Then create a provider_paths.xml
file in res/xml
folder. Folder may be needed to created if it doesn't exist. The content of the file is shown below. It describes that we would like to share access to the External Storage at root folder (path=".")
with the name external_files.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
The final step is to change the line of code below in
fileUri = Uri.fromFile(mediaFile);
to
fileUri= FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", mediaFile);
这篇关于使用 File 对象在 Android (API > 24) 中捕获和保存视频?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!