项目背景
项目需要从钉钉微应用跳转 WPS 打开 word 文档,但是 WPS 只提供了 StartActivity 方式携带参数跳转应用,deeplink 只能打开应用,而钉钉微应用只支持 deeplink ,所以需要搭建一个中间跳转的app工具。
Flutter
Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。
flutter使用dart语言,是将来谷歌新操作系统 Fuchsia 的主要构建方式,组件使用响应式框架构建,使用组件构建 UI ,组件可以改变状态,提供热加载,最大的特点是同时在iOS和Android系统中开发应用,支持混合开发。
获取 deeplink 路径
在flutter应用中导入 uni_links 包,接收deeplink:
在 pubspec.yaml 文件 dependencies 下增加 uni_links: ^0.1.4 ,记得执行 flutter packages get
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
class extends State<FirstPage> {
void initState() { ... getInitialUri().then((Uri url) { print('URL received: $url'); if (url != null && url.scheme == 'gsoft') { map.addAll(url.queryParameters); } }); ... super.initState(); } ... }
|
执行 getInitialUri 方法可以拿到 deeplink 的路径,获取传递的参数。
通过 StartActivity 方式启动WPS
flutter应用与原生之间交互需要通过插件模式:
定义 plugin,两边 plugin 名需要一致
flutter端:
1 2 3 4 5 6
|
class extends State<FirstPage> { ... const demoPlugin = const MethodChannel('wps.plugin'); demoPlugin.invokeMethod('interaction', map); ... }
|
android端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "wps.plugin";
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.equals("interaction")) { String fileUrl = call.argument("fileUrl"); Bundle bundle = new Bundle(); //打开文档参数-打开模式 Object openMode = call.argument("OpenMode"); if (openMode != null) { bundle.putString(WpsModel.OPEN_MODE, String.valueOf(openMode)); }
//打开文档参数-打开直接进入编辑模式 Object editMode = call.argument("EditMode"); if (editMode != null) { bundle.putBoolean("Edit Mode", Boolean.valueOf(editMode.toString())); }
//文档记录-删除使用记录 Object clearTrace = call.argument("ClearTrace"); if (clearTrace != null) { bundle.putBoolean("ClearTrace", Boolean.valueOf(clearTrace.toString())); }
//文档初始化参数-自动跳转 Object autoJump = call.argument("AutoJump"); if (autoJump != null) { bundle.putBoolean("AutoJump", Boolean.valueOf(autoJump.toString())); }
//修订相关参数-打开文档是否显示修订批注面板 Object showReviewingPaneRightDefault = call.argument("ShowReviewingPaneRightDefault"); if (showReviewingPaneRightDefault != null) { bundle.putBoolean("ShowReviewingPaneRightDefault", Boolean.valueOf(showReviewingPaneRightDefault.toString())); }
//修订相关参数-修订模式打开文档 Object enterReviseMode = call.argument("EnterReviseMode"); if (enterReviseMode != null) { bundle.putBoolean("EnterReviseMode", 大专栏 flutter实践 - plsyan class="keyword">Boolean.valueOf(enterReviseMode.toString())); }
//水印相关参数-设置水印文字内容 Object waterMaskText = call.argument("WaterMaskText"); if (waterMaskText != null) { bundle.putString("WaterMaskText", String.valueOf(waterMaskText)); }
//批注-批注的作者 Object userName = call.argument("UserName"); if (userName != null) { bundle.putString("UserName", String.valueOf(userName)); }
//文件保存时发送广播 bundle.putBoolean("SendSaveBroad", true);
Intent intent = new Intent(); File file = new File(fileUrl);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", file); intent.setData(contentUri); intent.putExtras(bundle);
intent.setAction(android.content.Intent.ACTION_VIEW); intent.setClassName(WpsModel.PackageName.PRO, WpsModel.ClassName.NORMAL); MainActivity.this.startActivity(intent);
result.success("success"); } else { result.notImplemented(); } } });
GeneratedPluginRegistrant.registerWith(this); }
}
|
WPS保存文件操作后广播
原生安卓端接收 WPS 广播后,传递给 flutter 处理。
android端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
... private static final String SAVED_CHANNEL = "saved.wps.plugin"; ... //接收wps广播 new EventChannel(getFlutterView(), SAVED_CHANNEL) .setStreamHandler(new EventChannel.StreamHandler() { private BroadcastReceiver savedReceiver;
@Override public void onListen(Object o, EventChannel.EventSink eventSink) { savedReceiver = createSavedReceiver(eventSink); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("cn.wps.moffice.broadcast.AfterSaved"); registerReceiver(savedReceiver, intentFilter); }
@Override public void onCancel(Object o) { unregisterReceiver(savedReceiver); savedReceiver = null; } }); ...
private BroadcastReceiver createSavedReceiver(final EventChannel.EventSink events) { return new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getBooleanExtra(SAVE_AS, false)) { events.success(intent.getStringExtra("CurrentPath")); } } }; }
|
flutter端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
static const fromAndroidPlugin = const EventChannel('saved.wps.plugin');
... void _fromAndroidPlugin() { fromAndroidPlugin .receiveBroadcastStream() .listen(_onFromAndroidEvent, onError: _onFromAndroidError); }
void _onFromAndroidEvent(Object event) { print("文件修改保存路径:" + event); }
void _onFromAndroidError(Object error) { print(error); } ...
|
- 干货 | 京东技术中台的Flutter实践之路
在 2019 年,Flutter 推出了多个正式版本,支持的终端越来越多,使用的项目也越来越多.Flutter 正在经历从小范围尝鲜到大面积应用的过程,越来越多的研发团队加入到 Flutter 的学习 ...
- Flutter实战:手把手教你写Flutter Plugin
前言 如果你对移动端有所关注,那么你一定会听说过Flutter.得益于Google,Flutter一经推出便得受到了广泛关注.很多开发者跃跃欲试,国内部分大厂,诸如美团.闲鱼等团队已经开始了Flutt ...
- Flutter混合栈的管理
Flutter出现的目的旨在统一Android/IOS两端编程,因此完全基于Flutter开发的App,只需提供一个包含FlutterView的页面,后续页面增加/删除/跳转均在FlutterView ...
- Flutter 中文文档网站 flutter.cn 正式发布!
在通常的对 Flutter 介绍中,最耳熟能详的是下面四个特点: 精美 (Beautiful):充分的赋予和发挥设计师的创造力和想象力,让你真正掌控屏幕上的每一个像素. ** 极速 (Fast)**: ...
- 微信公众号【阿里技术(ali_tech)】历史文章整理
简介 来自微信公众号: ali_tech 阿里巴巴官方技术号,关于阿里的技术创新均呈现于此. 本内容来自微信公众号的分享,最后更新时间2019-10-26,请关注对应公众号接收最新分享,定期同步地址: ...
- Flutter 实现原理及在马蜂窝的跨平台开发实践
一直以来,跨平台开发都是困扰移动客户端开发的难题. 在马蜂窝旅游 App 很多业务场景里,我们尝试过一些主流的跨平台开发解决方案, 比如 WebView 和 React Native,来提升开发效率和 ...
- Flutter的原理及美团的实践
导读 Flutter是Google开发的一套全新的跨平台.开源UI框架,支持iOS.Android系统开发,并且是未来新操作系统Fuchsia的默认开发套件.自从2017年5月发布第一个版本以来,目前 ...
- Flutter混合工程改造实践
背景 6月下旬,我们首次尝试用Flutter开发AI拍app.开发的调研准备阶段没有参考业界实践,导致我们踩到一些填不上的坑.在这些坑中,最让我感到棘手的是Flutter和原生页面混合栈管理的问题. ...
- Flutter 开发入门实践
前言: Flutter 是 Google 推出的跨平台解决方案, 开发语言:Dart 优势: 劣势: 学习推荐: 官方网站:https://flutter.io/ 书籍:<Flutter技术入门 ...
随机推荐
- [代码审计]PCWAP
为什么想要审计这套源码呐?之前看到某大佬在做反钓鱼网站的时候,发现钓鱼网站的后台用的就是PCWAP,所以我觉得有必要审计一下,顺便记录,打击网络犯罪! 0x00 PCAWAP: PCWAP手机网站建站 ...
- CPython中的GIL
GIL:全局解释器锁(cpython中) GIL产生的背景,由于C语言底层原因,CPpython中多线程运行,每个线程都需要申请全局资源,但是Cpython并不能应对所有线程同时的资源请求,为防止发生 ...
- Maven--设置Http代理
<settings> ... <proxies> <proxy> <id>my-proxy</id> <active>true& ...
- java常见的 http 请求库比较
java常见的http请求库有httpclient,RestTemplate,OKhttp,更高层次封装的 feign.retrofit 1.HttpClient HttpClient:代码复杂,还得 ...
- sol - 0x63
[例题]巡逻 注意到K只能是1或2,也就是说只能建0/1/2条新道路 我们分类讨论 当修建0条新道路的时候, 执行遍历会恰好遍历到每条边2次,答案为2*(n-1) 当修建1条新道路的时候, 我们设新道 ...
- Laravel常见问题总结
1.Whoops, looks like something went wrong. 一般报这个问题是由于复制框架文件时没有把相应的env (隐藏文件) 复制 导致新复制的框架没有配置选项 解决方法: ...
- 用最小的空间复杂度找出一个长度为n的数组且数据中的元素是[0,n-1]中任一个重复的数据。
用最小的空间复杂度找出一个长度为n的数组且数据中的元素是[0,n-1]中任一个重复的数据. 比如:[1, 2, 3, 3, 2, 2, 6, 7, 8, 9] 中 2 or 3 分析:这道题目,实现比 ...
- Java多线程常见概念
进程和线程的区别 进程是资源分配的最小单位,线程是CPU调度的最小单位 线程不能看做独立应用,而进程可以 进程有独立的地址空间,互相不影响,线程只是进程的不同执行路径 线程没有独立的地址空间,多进程的 ...
- 求Fibonacii数列的第40个数
public class Fibonacii{ public int m1(int n){ if(n == 1||n == 2){ return 1; } return m1(n-1) + m1(n- ...
- Matlab高级教程_第二篇:一个简单的混编例子
1. 常用的混编是MATLAB和VS两个编辑器之间的混编方式. 2. 因为MATLAB的核是C型语言,因此常见的混编方式是MATLAB和C型语言的混编. 3. 这里介绍一个简单的MATLAB语言混编成 ...