Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/flutter/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
颤振:如何让外部应用程序打开文件(如Android的隐式意图)?_Android_Flutter - Fatal编程技术网

颤振:如何让外部应用程序打开文件(如Android的隐式意图)?

颤振:如何让外部应用程序打开文件(如Android的隐式意图)?,android,flutter,Android,Flutter,我正在构建一个Flutter应用程序,它基本上是从云端获取数据。 数据类型各不相同,但它们通常是图像、pdf、文本文件或存档(zip文件) 现在我想激发一个隐含的意图,这样用户就可以选择他们喜欢的应用来处理负载 我搜索了答案,并尝试了以下途径: url_启动器插件(如中所建议) 安卓意图 共享插件 路由#3并不是我真正想要的,因为它使用平台的“共享”机制(即在Twitter上发帖/发送给联系人),而不是打开有效负载 路线1和2有点工作了。。。以一种不稳定、奇怪的方式。我稍后再解释 以下是我的代码

我正在构建一个Flutter应用程序,它基本上是从云端获取数据。 数据类型各不相同,但它们通常是图像、pdf、文本文件或存档(zip文件)

现在我想激发一个隐含的意图,这样用户就可以选择他们喜欢的应用来处理负载

我搜索了答案,并尝试了以下途径:

  • url_启动器插件(如中所建议)
  • 安卓意图
  • 共享插件
  • 路由#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\u启动器
    插件。如果我使用的是
    android\u intent
    plugin,那么最后一行代码将变成:

    // 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

    这个例外只发生在星系S9中。 另外两部手机没有给我这个问题。 他们确实启动了文件uri,有人问我如何打开文件,但没有提供任何图像处理应用程序(如Gallery、QuickPic或谷歌照片)

    只是澄清一下,
    url\u launcher
    android\u intent
    路由都会导致完全相同的结果

    我觉得我在这里少了一步。有人能指出我做错了什么吗?我是否必须开始使用平台渠道来实现这一点

    关于我为什么这样做的一些澄清:

    • 已将uri类型从file://转换为content://因为我得到的是
      android.os.FileUriExposedException
    • 将临时文件存储在外部存储器中,因为我还不想处理授予URI读取权限的问题。(我试过了,但是android\u intent还没有设置intent标志的方法)

      • 很难让它正常工作。下面是一些提示,它们帮助我从Flatter启动
        ACTION\u VIEW
        intents,并使用Flatter下载的文件

        1) 在android清单中注册
        文件提供程序

        <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>
        
        2) 创建一个提供2种方法的自定义平台频道(下面的代码是Kotlin):

        getDownloadsDir
        :应返回下载文件所在的数据目录。试试这个:

        val downloadsDir = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).path
        result.success(downloadsDir);
        
        previewFile
        接受两个字符串参数:
        path
        File.path
        在Dart中)和
        类型(例如“application/pdf”):

        最重要的部分是创建
        文件提供程序
        。url_启动器和android_意图将不起作用,您必须创建自己的平台频道。您可以更改下载路径,但还必须找到正确的提供程序/权限设置

        在iOS上实现这一点也是可能的,但超出了这个问题的范围


        如果您正在使用图像选择器插件:


        FileProvider
        与当前版本的image_picker(0.4.6)插件冲突,很快将发布一个修复程序。

        这一问题得到解决后,发布了一个优秀的颤振插件,解决了这个跨平台问题

        在以下示例中,当与-一起下载到
        getTemporaryDirectory()
        时,将打开一个给定的url/文件名以及iOS和Android上的关联应用程序(如果已下载,则使用本地文件):

        导入'dart:io';
        将“package:http/http.dart”导入为http;
        导入“package:path_provider/path_provider.dart”;
        导入“package:open_file/open_file.dart”;
        未来下载(字符串url、字符串文件名)异步{
        String dir=(wait getTemporaryDirectory()).path;
        File File=File(“$dir/$filename”);
        如果(wait file.exists())返回file.path;
        wait file.create(递归:true);
        var response=wait http.get(url).timeout(持续时间(秒:60));
        如果(response.statusCode==200){
        wait file.writeAsBytes(response.bodyBytes);
        返回file.path;
        }
        抛出“下载${url}失败”;
        }
        无效下载和启动(字符串url、字符串文件名){
        下载(url,文件名)。然后((字符串路径){
        OpenFile.open(路径);
        });
        }
        
        你说得对。主要的问题是,通过Intent将文件共享给其他应用程序变得非常乏味,而不管文件的位置如何(不管文件是位于应用程序的本地目录还是外部共享目录,如“下载”),我只是在Flatter中遇到了这个确切的要求,我想知道,自2018年8月提出这个问题以来,情况是否发生了一些变化?还是平台渠道仍然是实现这一目标的唯一途径?干杯。这仍然是路要走。也许现在有更好的插件,但这些插件会做完全相同的事情。@Kagawa你找到解决方案了吗?或者上面的方法是目前唯一的方法?使用这个插件:这个方法支持打开“epub”文件吗?比如书籍应用程序(iOS)或Play Books应用程序(谷歌)要打开文件吗?我只使用pdf文件进行了测试,但只要有一个相关的应用程序,它就应该适用于所有类型的文件,包括epub。
        val downloadsDir = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).path
        result.success(downloadsDir);
        
        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(...);
        }
        
        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);
          });
        }