我正在开发一个Flutter应用程序,基本上是从云端获取数据,数据类型各不相同,但通常是图像、pdf、文本文件或存档(zip文件)。
现在我想触发一个隐式Intent,这样用户就可以选择他们喜欢的应用来处理有效负载。
我一直在寻找答案,并尝试了以下途径:
- url_launcher插件(如How to open an application from a Flutter app?中所建议)
- android意图
1.共享插件
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)
我不知道intent是如何被cmp=com.google.android.gm/.browse.TrampolineActivity
污染的
这个例外只发生在Galaxy S9上。另外两款手机没有给予我这个问题。它们确实启动了文件uri,并问我如何打开文件,但没有提供任何图像处理应用程序(如Gallery、QuickPic或Google相册)。
需要说明的是,url_launcher
和android_intent
路由会导致完全相同的结果。
“我感觉自己好像少了一个步骤,谁能指出我做错了什么?我是不是要开始使用平台渠道来完成这个任务?”
关于我为什么这么做的一些说明:
- 将uri类型从file://转换为content://,因为我正在获取
android.os.FileUriExposedException
- 将临时文件存储在外部存储中,因为我还不想处理授予URI读取权限的问题。(我尝试过,但
android_intent
还没有设置意图标志的方法)
2条答案
按热度按时间58wvjzkj1#
很难让它正常工作。这里有一些提示,帮助我从Flutter启动
ACTION_VIEW
intent,使用Flutter下载的文件。1)在android清单中注册一个
FileProvider
:provider_paths.xml
:2)创建一个提供2种方法的自定义平台通道(下面的代码是Kotlin):
getDownloadsDir
:应返回放置下载文件的数据目录。请尝试以下操作:**
previewFile
**接受2个字符串参数:path
(Dart中的File.path
)和type
(例如“应用程序/pdf”):最重要的部分是创建
FileProvider
. url_launcher和android_intent将不起作用,你必须创建自己的平台频道。你可以更改下载路径,但之后你还必须找到正确的提供商/权限设置。在iOS上实现这一点也是可能的,但超出了这个问题的范围。
如果您使用的是image_picker插件:
FileProvider
与当前版本的image_picker(0.4.6)插件冲突,将很快发布修复程序。1sbrub3j2#
由于这个问题得到了解答,一个优秀的Flutter插件open_filex已经发布,解决了这个跨平台。
当与path_provider结合使用时-在以下下载到
getTemporaryDirectory()
的示例中,这将在iOS和Android上打开一个给定的url/文件名以及关联的应用程序(如果已经下载,则使用本地文件):--编辑:--
将引用从
open_file
更改为open_filex
,因为原始软件包在Android上默认要求REQUEST_INSTALL_PACKAGES权限。这将阻止从2022年9月起在Google Play中进行审核。