我正在构建一个Flutter应用程序,该应用程序本质上是从云中获取数据的。 数据类型各不相同,但它们通常是图像,pdf,文本文件或存档文件(zip文件)。
现在,我想触发一个隐式意图,以便用户可以选择自己喜欢的应用程序来处理有效负载。
我已经搜索了答案,并且尝试了以下路线:
3号路由并不是我真正想要的,因为它使用的是平台的“共享”机制(即在Twitter上发布/发送给联系人),而不是打开有效负载。
1号线和2号线的运作方式...以一种不稳定,怪异的方式工作。我稍后再解释。
这是我的代码流:
import 'package:url_launcher/url_launcher.dart';
// ...
// retrieve payload from internet and save it to an External Storage location
File payload = await getPayload();
String uriToShare = samplePayload.uri.toString();
// at this point uriToShare looks like: 'file:///storage/emulated/0/jpg_example.jpg'
uriToShare = uriToShare.replaceFirst("file://", "content://");
// launch url
if (await canLaunch(uriToShare)) {
await launch(uriToShare);
} else {
throw "Failed to launch $uriToShare";
以上代码使用的是url_launcher
插件。如果我使用的是android_intent
插件,那么最后几行代码将变为:
// fire intent
AndroidIntent intent = AndroidIntent(
action: "action_view",
data: uriToShare,
);
await intent.launch();
将文件保存到外部目录的所有工作(我可以在运行代码后确认文件是否存在)
当我尝试共享URI时,事情变得很奇怪。 我已经在3种不同的手机上测试了这段代码。其中之一(三星Galaxy S9)将抛出此异常:
I/io.flutter.plugins.androidintent.AndroidIntentPlugin(10312): Sending intent Intent { act=android.intent.action.VIEW dat=content:///storage/emulated/0/jpg_example.jpg }
E/MethodChannel#plugins.flutter.io/android_intent(10312): Failed to handle method call
E/MethodChannel#plugins.flutter.io/android_intent(10312): java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.VIEW dat=content:///storage/emulated/0/jpg_example.jpg cmp=com.google.android.gm/.browse.TrampolineActivity } from ProcessRecord{6da6f74 10312:com.safe.fmeexpress/u0a218} (pid=10312, uid=10218) requires com.google.android.gm.permission.READ_GMAIL
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.os.Parcel.readException(Parcel.java:1959)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.os.Parcel.readException(Parcel.java:1905)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.app.IActivityManager$Stub$Proxy.startActivity(IActivityManager.java:4886)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.app.Instrumentation.execStartActivity(Instrumentation.java:1617)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.app.Activity.startActivityForResult(Activity.java:4564)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.app.Activity.startActivityForResult(Activity.java:4522)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.app.Activity.startActivity(Activity.java:4883)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.app.Activity.startActivity(Activity.java:4851)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at io.flutter.plugins.androidintent.AndroidIntentPlugin.onMethodCall(AndroidIntentPlugin.java:141)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:191)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at io.flutter.view.FlutterNativeView.handlePlatformMessage(FlutterNativeView.java:152)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.os.MessageQueue.next(MessageQueue.java:325)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.os.Looper.loop(Looper.java:142)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at android.app.ActivityThread.main(ActivityThread.java:6938)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
E/MethodChannel#plugins.flutter.io/android_intent(10312): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
我不知道cmp=com.google.android.gm/.browse.TrampolineActivity
此异常仅发生在Galaxy S9中。 另外两部手机没有给我这个问题。 他们确实启动了文件uri,有人问我如何打开文件,但没有提供任何图像处理应用程序(例如Gallery,QuickPic或Google Photos)。
只需澄清一下,url_launcher
和android_intent
路线都会得出完全相同的结果。
感觉我在这里错过了一步。谁能指出我做错了什么?我是否必须开始使用平台渠道来完成此任务?
我为什么要做一些澄清:
android.os.FileUriExposedException
android_intent
尚无法设置意图标志)答案 0 :(得分:3)
很难使其正常工作。以下是一些提示,这些提示帮助我从Flutter启动ACTION_VIEW
意图,并使用Flutter下载文件。
1)在Android清单中注册FileProvider
:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.myapp.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
provider_paths.xml
:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="." />
</paths>
2)创建一个提供2种方法的自定义平台渠道(以下代码为Kotlin):
getDownloadsDir
:应返回放置下载文件的数据目录。试试这个:
val downloadsDir = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).path
result.success(downloadsDir);
previewFile
,它带有2个字符串参数:path
(在Dart中为File.path
)和type
(例如“ application / pdf”):
val file = File(path);
val uri = FileProvider.getUriForFile(this, "com.example.myapp.provider", file);
val viewFileIntent = Intent(Intent.ACTION_VIEW);
viewFileIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION);
viewFileIntent.setDataAndType(uri, type);
try {
startActivity(viewFileIntent);
result.success(null);
} catch (e: ActivityNotFoundException) {
result.error(...);
}
最重要的部分是创建FileProvider
。 url_launcher和android_intent无法使用,您必须创建自己的平台频道。您可以更改下载路径,但同时还必须找到正确的提供者/权限设置。
也可以在iOS上进行这项工作,但这超出了这个问题的范围。
如果您使用的是image_picker插件:
FileProvider
与image_picker(0.4.6)插件的当前版本冲突,很快就会发布修复程序。
答案 1 :(得分:2)
自从这个问题得到解答后,一个优秀的 flutter 插件 open_file 已经发布,解决了这个跨平台的问题。
当与 path_provider 结合使用时 - 在以下下载到 getTemporaryDirectory()
的示例中,这会在 iOS 和 Android 上打开给定的 URL/文件名以及关联的应用程序(如果已下载,则使用本地文件):< /p>
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:open_file/open_file.dart';
Future<String> download(String url, String filename) async {
String dir = (await getTemporaryDirectory()).path;
File file = File('$dir/$filename');
if (await file.exists()) return file.path;
await file.create(recursive: true);
var response = await http.get(url).timeout(Duration(seconds: 60));
if (response.statusCode == 200) {
await file.writeAsBytes(response.bodyBytes);
return file.path;
}
throw 'Download ${url} failed';
}
void downloadAndLaunch(String url, String filename) {
download(url, filename).then((String path) {
OpenFile.open(path);
});
}