项目背景
项目需要从钉钉微应用跳转 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
| 12
 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端:
| 12
 3
 4
 5
 6
 | class  extends State<FirstPage> {...
 const demoPlugin = const MethodChannel('wps.plugin');
 demoPlugin.invokeMethod('interaction', map);
 ...
 }
 | 
 
android端:
| 12
 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";
    @Overrideprotected 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端:
| 12
 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端:
| 12
 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技术入门 ... 
随机推荐
	
									- Java架构师笔记-你必须掌握的 21 个 Java 核心技术!(干货)
			闲来无事,师长一向不(没)喜(有)欢(钱)凑热闹,倒不如趁着这时候复盘复盘.而写这篇文章的目的是想总结一下自己这么多年来使用java的一些心得体会,希望可以给大家一些经验,能让大家更好学习和使用Jav ... 
- Java类只加载一次的情况
			一个类只加载一次: 调用Java命令. 创建对象时 访问静态成员时 Class.forName("包名.类名") 
- Django_JavaScript
			JavaScript是什么 JavaScript是一种运行在客户端(浏览器)的编程语言,用来给网页添加动态功能. JavaScript的作用 最初目的 为了处理表单的验证操作 现在广泛的应用场景 网页 ... 
- 吴裕雄--天生自然python Google深度学习框架:图像识别与卷积神经网络
			
			
- Sex linkage
			I.8 Sex linkage 单倍体:性别决定基因(S\s)和与性别决定基因连锁的等位基因(A\a)存在于同一套遗传物质上,其配子结合和减数分裂图示如下: 如果性别是由染色体区域决定的,自然选择会避 ... 
- git clone 拉取github上面的代码报错:fatal: Authentication failed for  xxx解决
			1.打开git bash,输入密码:git config --system --unset credential.helper2.结果报错:error: could not lock config f ... 
- 2019-ECfinal-M题-value
			题目传送门 sol:每个下标都有选和不选两种情况,所以总方案数是$2^{n}$,在$n$最大是$100000$的情况下不符合要求.可以这样想,假设$i^{p}=k$有符合题目要求的解,还有一个整数$j ... 
- 2018 ACM-ICPC 宁夏 C.Caesar Cipher(模拟)
			In cryptography, a Caesar cipher, also known as the shift cipher, is one of the most straightforward ... 
- ACM-ICPC Nanjing Onsite 2018  I. Magic Potion
			题意:类似二分图匹配给的题目,不过这次在这里给出了k,表示没人可以再多一次匹配机会,这次匹配不能用上一次被匹配的对象 分析:不能用匈牙利做俩次匹配,因为俩次的最大匹配并不等价于总和的匹配,事实证明,你 ... 
- python编程练习题目
			github上面的一个项目,分为level1,level2,level3 三个等级的难度. 题目地址 一部分中文翻译 python教程 剑指offer,python3实现 python进阶 练习题1: ...