拓冰建站拓冰建站
首页 / 资讯中心 / 正文

url_launcher_android:Flutter 官方 Android 端 URL 启动器实现与源码解析

移动开发跨平台【免费下载链接】pluginsPlugins for Flutter maintained by the Flutter team项目地址https://gitcode.com/gh_mirrors/pl/plugins点击查看免费下载导读url_launcher_android是 Flutter 官方团队维护的联邦插件Federated Plugin体系中url_launcher的 Android 端实现负责在 Android 设备上完成tel:、mailto:、https:等 URL 的探测canLaunch与启动launch能力。本文将以该包的 README.md 为骨架结合仓库内 Dart 端实现、Java 源码、AndroidManifest 配置与测试用例完整讲解它的接入方式、Android 11 包可见性配置、MethodChannel 调用链以及内部 WebView 与 Intent 启动机制的底层原理帮助你从会用进阶到懂实现。一、认识 url_launcher_android一个被 endorsed 的联邦插件实现url_launcher_android在 README.md 中对自己的定位只有一句话它是url_launcher的 Android 实现。这句话背后对应着 Flutter 插件体系中的endorsed federated plugin被背书的联邦插件机制联邦插件Federated Plugin把一个插件的功能拆成「平台接口包 各平台实现包 端上 API 包」多个独立 pub 包。url_launcher家族正是典型例子共包括url_launcher端上 API、url_launcher_platform_interface平台接口、url_launcher_androidAndroid 实现、url_launcher_ios、url_launcher_linux、url_launcher_macos、url_launcher_web、url_launcher_windows等多个包。Endorsed被背书指端上 API 包在自己的pubspec.yaml中声明了默认实现。对开发者而言只需要依赖url_launcher一个包构建 Android 应用时url_launcher_android会被自动引入无需手工添加依赖。从 pubspec.yaml 可以看到这种关系是如何在声明层面建立的name: url_launcher_android version: 6.0.23 environment: sdk: 2.14.0 3.0.0 flutter: 3.0.0 flutter: plugin: implements: url_launcher # 声明本包实现 url_launcher platforms: android: package: io.flutter.plugins.urllauncher pluginClass: UrlLauncherPlugin dartPluginClass: UrlLauncherAndroid dependencies: flutter: sdk: flutter url_launcher_platform_interface: ^2.0.3implements: url_launcher是 endorsed 机制的关键声明它让 Flutter 工具在解析依赖时只要工程里存在url_launcher就自动把url_launcher_android作为 Android 端实现注入dartPluginClass: UrlLauncherAndroid则指定了 Dart 侧的实现类入口。二、快速开始在工程中接入由于 endorsed 机制接入步骤极其简单。在你的 Flutter 项目pubspec.yaml的dependencies中只需添加dependencies: url_launcher: ^6.1.0然后即可在 Dart 代码中直接使用端上 API。以 url_launcher 中的最小示例为基准import package:flutter/material.dart; import package:url_launcher/url_launcher.dart; final Uri _url Uri.parse(https://flutter.dev); Futurevoid _launchUrl() async { if (!await launchUrl(_url)) { throw Exception(Could not launch $_url); } }当你运行flutter build apk或flutter run时工具会自动解析url_launcher的 endorsed 依赖并把url_launcher_android编入 Android 工程随后UrlLauncherPlugin与UrlLauncherAndroid分别在原生与 Dart 两端注册生效。你不需要也不应该在自己的代码里直接import package:url_launcher_android/...。三、Android 关键配置Android 11 的包可见性queries声明这是 Android 平台上最容易踩坑、也最需要理解的一处配置。Android 11API 30开始引入了**包可见性Package Visibility**限制应用默认无法查询设备上安装了哪些应用、无法探测其他应用能否处理某个 Intent。这意味着如果你在 Android 11 上调用canLaunchUrl(sms:...)却不做任何声明它几乎总会返回false尽管launchUrl实际可以成功。解决办法是在应用自己的AndroidManifest.xml根元素下添加queries声明把传给canLaunchUrl的 URL scheme 逐一列出。参考 url_launcher 主包 README 中的示例!-- Provide required visibility configuration for API level 30 and above -- queries !-- If your app checks for SMS support -- intent action android:nameandroid.intent.action.VIEW / data android:schemesms / /intent !-- If your app checks for call support -- intent action android:nameandroid.intent.action.VIEW / data android:schemetel / /intent /queriesurl_launcher_android自己的示例工程 example/android/app/src/main/AndroidManifest.xml 给出了更贴近真实场景的写法——它同时声明了https用于浏览器打开和tel注意这里用的是android.intent.action.DIAL而非VIEW因为拨号场景通常由DIAL处理queries intent action android:nameandroid.intent.action.VIEW / data android:schemehttps / /intent intent action android:nameandroid.intent.action.DIAL / data android:schemetel / /intent /queries另外开发调试阶段需要在 Manifest 中保留android.permission.INTERNET权限Flutter 调试工具与热重载依赖它示例工程同样包含这一声明。注意queries声明解决的是能否查询到 handler的问题如果你的目标是直接launchUrl而不是canLaunchUrl缺失声明通常不影响打开系统浏览器或拨号应用但会影响你的 UI 前置判断逻辑。四、核心能力与 Dart 侧实现canLaunch / launch / closeWebViewurl_launcher_android的 Dart 端实现位于 lib/url_launcher_android.dart核心类UrlLauncherAndroid继承自UrlLauncherPlatform通过registerWith()把自己注册为平台接口的默认实例class UrlLauncherAndroid extends UrlLauncherPlatform { static void registerWith() { UrlLauncherPlatform.instance UrlLauncherAndroid(); } ... }它与原生端的通信全部经由一个固定名称的 MethodChannelconst MethodChannel _channel MethodChannel(plugins.flutter.io/url_launcher_android);Dart 侧共暴露三个能力分别对应三个通道方法Dart 方法通道方法参数说明canLaunch(url)canLaunchurl探测该 URL 是否有可处理的组件launch(url, ...)launchurl、useWebView、enableJavaScript、enableDomStorage、universalLinksOnly、headers发起打开动作closeWebView()closeWebView无关闭由插件打开的 WebView Activity其中launch的 Dart 签名完整列出了所有可选配置项见 url_launcher_android.dartFuturebool launch( String url, { required bool useSafariVC, // iOS 专用Android 忽略 required bool useWebView, // true 时在应用内 WebView 打开 required bool enableJavaScript,// 仅 useWebViewtrue 时生效 required bool enableDomStorage,// 仅 useWebViewtrue 时生效 required bool universalLinksOnly,// 仅 iOS 生效Android 忽略 required MapString, String headers, // 附加 HTTP 头会透传给 Intent String? webOnlyWindowName, // Web 专用 })一个值得注意的 Dart 侧设计canLaunch并非盲目转发而是内置了一个通用 URL 兜底探测逻辑。当特定 URL 探测失败、且其 scheme 是http或https时它会改为探测http(s)://flutter.dev再来一次Futurebool canLaunch(String url) async { final bool canLaunchSpecificUrl await _canLaunchUrl(url); if (!canLaunchSpecificUrl) { final String scheme _getUrlScheme(url); if (scheme http || scheme https) { return _canLaunchUrl($scheme://flutter.dev); } } return canLaunchSpecificUrl; }这样做是为了规避设备上注册了自定义 handler 导致canLaunch误报 false的场景该行为在 CHANGELOG.md 6.0.16 版本中引入。注意 scheme 提取刻意不用Uri而是手动截取:之前的部分因为 Android 对传入字符串的容忍度很高插件需要同样宽容地处理并非合法 URL的输入。五、原生侧实现原理从 MethodCall 到 Intent原生端的调用链分为三层全部位于android/src/main/java/io/flutter/plugins/urllauncher/目录下UrlLauncherPlugin插件入口UrlLauncherPlugin.java 实现了FlutterPlugin与ActivityAware两个接口。onAttachedToEngine时创建UrlLauncher并注册 method call handleronAttachedToActivity时把当前 Activity 注入UrlLauncher。这保证了在 add-to-app、Activity 重建、配置变更等场景下插件都能拿到正确的 Activity 引用。MethodCallHandlerImpl方法分发MethodCallHandlerImpl.java 以switch分发canLaunch/launch/closeWebView三个方法并负责把 Dart 传来的headersMap 转成 AndroidBundle。它定义了两种错误码NO_ACTIVITY当前没有前台 Activity无法启动 IntentACTIVITY_NOT_FOUND系统中没有任何 Activity 能处理该 URL 的 Intent等价于ActivityNotFoundException被捕获。UrlLauncher真正的执行者UrlLauncher.java 是核心逻辑所在我们逐段拆解canLaunch 的实现——构造一个ACTION_VIEWIntent 并交给PackageManager.resolveActivity解析boolean canLaunch(String url) { Intent launchIntent new Intent(Intent.ACTION_VIEW); launchIntent.setData(Uri.parse(url)); ComponentName componentName launchIntent.resolveActivity(applicationContext.getPackageManager()); if (componentName null) { return false; } else { return !{com.android.fallback/com.android.fallback.Fallback} .equals(componentName.toShortString()); } }这里额外排除com.android.fallback组件Android 上的一个兜底处理者避免把仅有兜底误判为确实可处理。launch 的实现——根据useWebView分支构造不同 IntentLaunchStatus launch(String url, Bundle headersBundle, boolean useWebView, boolean enableJavaScript, boolean enableDomStorage) { if (activity null) { return LaunchStatus.NO_ACTIVITY; } Intent launchIntent; if (useWebView) { launchIntent WebViewActivity.createIntent( activity, url, enableJavaScript, enableDomStorage, headersBundle); } else { launchIntent new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(url)) .putExtra(Browser.EXTRA_HEADERS, headersBundle); } try { activity.startActivity(launchIntent); } catch (ActivityNotFoundException e) { return LaunchStatus.ACTIVITY_NOT_FOUND; } return LaunchStatus.OK; }默认路径构造ACTION_VIEWIntentsetData放入 URL通过startActivity交给系统分派——打开浏览器、拨号、发短信等都由系统根据 URL scheme 决定目标应用自定义 headers 通过Browser.EXTRA_HEADERS附加到 Intent只有支持读取该 Extra 的浏览器/应用才会消费它们useWebViewtrue时则跳转到插件自带的WebViewActivity。closeWebView 的实现——通过系统广播通知 WebView 页面关闭void closeWebView() { applicationContext.sendBroadcast(new Intent(WebViewActivity.ACTION_CLOSE)); }WebViewActivity内部注册了一个监听ACTION_CLOSE的BroadcastReceiver收到广播即调用finish()。这解释了为何closeWebView能关闭由插件打开的 WebView 页面。六、WebViewActivity应用内打开网页的实现细节当launchUrl使用LaunchMode.inAppWebView对应useWebView: true时URL 会在插件自带的 WebViewActivity.java 中加载。该 Activity 已在插件自身的 AndroidManifest.xml 中注册activity android:nameio.flutter.plugins.urllauncher.WebViewActivity android:themeandroid:style/Theme.NoTitleBar.Fullscreen android:exportedfalse/注意exportedfalse——它只能由本应用内部启动外部应用无法直接唤起。onCreate中的关键流程对应 WebViewActivity.javafinal Intent intent getIntent(); final String url intent.getStringExtra(URL_EXTRA); final boolean enableJavaScript intent.getBooleanExtra(ENABLE_JS_EXTRA, false); final boolean enableDomStorage intent.getBooleanExtra(ENABLE_DOM_EXTRA, false); final Bundle headersBundle intent.getBundleExtra(Browser.EXTRA_HEADERS); final MapString, String headersMap extractHeaders(headersBundle); webview.loadUrl(url, headersMap); webview.getSettings().setJavaScriptEnabled(enableJavaScript); webview.getSettings().setDomStorageEnabled(enableDomStorage); webview.setWebViewClient(webViewClient); webview.getSettings().setSupportMultipleWindows(true); webview.setWebChromeClient(new FlutterWebChromeClient()); registerReceiver(broadcastReceiver, closeIntentFilter);值得记录的实现细节JS 与 DOM 存储默认关闭enableJavaScript、enableDomStorage默认均为false需要时通过launchUrl的对应参数显式开启页面内跳转留在 WebView 内部通过自定义WebViewClient.shouldOverrideUrlLoading把新 URL 继续用loadUrl在当前 WebView 中加载而不是跳到系统浏览器target_blank/window.open多窗口处理插件为修复内部 bug源码注释标注为 b/159892679默认开启setSupportMultipleWindows(true)并用自定义FlutterWebChromeClient.onCreateWindow拦截新窗口的 URL 到同一个 WebView 中加载返回键行为onKeyDown中优先执行webview.goBack()处理 WebView 内部历史栈而不是直接退出 Activity关闭机制通过ACTION_CLOSE广播触发finish()。七、支持的 URL scheme 与编码注意事项Android 平台支持哪些 scheme 并不由插件决定——URL 被原样交给系统ACTION_VIEW分派能打开什么取决于设备上安装了哪些应用。常见 scheme 及行为见 url_launcher 主包 READMEScheme示例行为https:URLhttps://flutter.dev在默认浏览器打开mailto:邮箱?subject...body...mailto:smithexample.org?subjectNews在默认邮件应用中写信tel:号码tel:1-555-010-999调用默认电话应用拨号sms:号码sms:5550101234在默认短信应用中发短信file:路径file:/home用默认关联应用打开文件/目录桌面平台支持编码建议非http/https的 scheme 构造查询参数时推荐使用query参数配合自定义encodeQueryParameters函数而不是Uri的queryParameters以避免空格被编码成的已知问题String? encodeQueryParameters(MapString, String params) { return params.entries .map((MapEntryString, String e) ${Uri.encodeComponent(e.key)}${Uri.encodeComponent(e.value)}) .join(); } final Uri emailLaunchUri Uri( scheme: mailto, path: smithexample.com, query: encodeQueryParameters(String, String{ subject: Example Subject Symbols are allowed!, }), ); launchUrl(emailLaunchUri);canLaunchUrl 的语义提醒canLaunchUrl返回false并不代表launchUrl一定失败如上述 Android 11 未配置queries、Web 平台、自定义 handler 等场景。因此文档建议能提供降级方案时优先直接调用launchUrl并处理失败分支而不是依赖canLaunchUrl去禁用 UI。例如一个发送反馈邮件按钮mailto探测失败时可以改走https网页表单。八、测试与验证如何确认实现行为url_launcher_android的测试覆盖了 Dart 与 Java 两个层面是理解实现行为的最好参照。Dart 侧test/url_launcher_android_test.dart通过 mock MethodChannel 验证参数转发其中几个用例直接印证了前面讲的实现细节checks a generic URL if an http URL returns false验证canLaunch(http://example.com/)失败后会自动再探测http://flutter.dev通道共被调用 2 次does not a generic URL if a non-web URL returns false验证sms:这类非 Web scheme 失败后不会触发兜底探测通道仅调用 1 次handles force WebView with javascript/handles force WebView with DOM storage验证useWebView、enableJavaScript、enableDomStorage参数的完整透传closeWebView calls through验证closeWebView无参数调用。Java 侧android/src/test/java/io/flutter/plugins/urllauncher/下的MethodCallHandlerImplTest.java与WebViewActivityTest.java则验证了方法分发、headers 到 Bundle 的转换、错误码以及WebViewActivity的 headers 提取逻辑。日常使用中验证配置是否生效的最快方式在 Android 11 模拟器/真机上调用canLaunchUrl(tel:123)若返回false先检查AndroidManifest.xml中是否缺少对应的queries声明。九、版本、环境与已知边界版本与环境当前仓库中url_launcher_android版本为6.0.23要求 Flutter3.0.0、Dart SDK2.14.0 3.0.0见 pubspec.yamlAndroid 支持范围url_launcher主包声明 Android 支持 SDK 16见 url_launcher READMEuseSafariVC与universalLinksOnly在 Android 上被忽略它们分别对应 iOS 的 SFSafariViewController 与 Universal Links 语义Dart 层虽会透传但 Android 原生UrlLauncher.launch并不消费这两个参数这一点从 UrlLauncher.java 的签名可以确认URL 必须合法可编码虽然插件对字符串容忍度高但官方仍建议使用Uri构造 URL避免历史上非法 URL 字符串导致的各类启动失败打开能力依赖已安装应用例如模拟器上通常没有默认邮件/电话应用mailto:、tel:可能无法启动这不是插件缺陷而是平台环境限制。十、总结url_launcher_android虽然 README 简短却是 Flutter 联邦插件体系在 Android 侧的一次教科书式实践通过implements: url_launcher声明实现关系通过plugins.flutter.io/url_launcher_android通道完成 Dart 与 Java 通信通过ACTION_VIEWIntent 复用系统能力通过WebViewActivity提供应用内网页加载并通过queries适配 Android 11 的包可见性约束。理解这些源码细节之后排查canLaunchUrl误报、自定义 header 不生效、WebView 无法开启 JS 等常见问题时你就能迅速定位到具体环节而不是停留在照着文档改的层面。赞分享移动开发跨平台【免费下载链接】pluginsPlugins for Flutter maintained by the Flutter team项目地址https://gitcode.com/gh_mirrors/pl/plugins点击查看免费下载相关推荐url_launcher_linux 深度解析Flutter 官方 Linux 端 URL 启动插件的 endorsed 联邦插件机制与底层实现url_launcher_linux 深度解析Flutter 官方 Linux 端 URL 启动插件的 endorsed 联邦插件机制与底层实现 导读 url跨平台移动开发UI组件开发工具url_launcher_ios 深度解析Flutter 官方 iOS 端 URL 启动插件的工作原理与配置指南url_launcher_ios 深度解析Flutter 官方 iOS 端 URL 启动插件的工作原理与配置指南 本文围绕 Flutter 官方插件 url_跨平台移动开发UI组件开发工具url_launcher_macosFlutter 联邦插件背书机制与 macOS 端 URL 启动实现深度解析url_launcher_macosFlutter 联邦插件背书机制与 macOS 端 URL 启动实现深度解析 url_launcher_macos 是 F跨平台移动开发UI组件开发工具上一篇Material-Dialogs输入与文件处理模块下一篇深入解析Lit-html现代Web组件模板引擎的革命创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门