Flutter 开源项目(一) - 文件管理器

朋友们,好久不见,很久没有在掘金发布过文章了,大三的时间的确是非常匆忙,已经下定决心入坑Flutter行业的我也需要成天的学习 web 开发了。

声明

  • 仅以学习为目的,对于个人文章或者代码写的不好,导致你在某些工作上使用了我的代码而带来的负面效果,而我就需要承担一些人损失多少多少钱的责任这一说,我不赞同,如果有请绕道。

    写的烂的我在努力写好了。

  • 开源的库并没有到一个很稳定的版本,所以说都是仅供参考学习,我会持续对每一个开源库进行维护。

前言

我把我大学的前两年的绝大部分开发时间分给了我的个人项目魇·工具箱,由于其中的ROM工具是付费功能,起初我把各种各样的功能代码都写在一个项目中,从初学Flutter就开始了这个项目,项目中代码水平参差不齐,越到后面我发现维护越来越困难,代码虽然只有几w行,但对于个人来说几乎到了没法维护非付费功能的一个地步,也经历了很多次的重构,每次重构也必会代码大量的bug。

我希望把工具箱的各个功能进行模块化,能够无缝的集成在任何项目中,一方面也是方便个人维护,,而非付费的模块,我会依次开源到个人储存库。

先上开源地址吧

相关文章

Flutter开发文件管理器基本原理

开源列表

  • 文件管理器
  • 刷机工具
  • ADB 工具
  • 终端模拟器

支持功能列表

  • 文件选择
  • d
  • Apktool(仅限安卓)
  • 书签管理
  • 我也清楚作为一个文件管理器来说这样的功能是远远不够的,但是时间的确有限。
    目前个人会用到的仅有反编译与文件选择功能。

使用集成

就像下面这样简单。

相比原生文件选择器

  • 首先 app 的启动自身是具有一个独立的 uid/gid 的,所以由这个 app 启动的所有进程都是带有同样信息的,那么用dartapi始终拿不到
    高级权限,永远只能访问沙盒数据。
  • 由于我们不仅仅是需要有选择文件的功能,我们还需要扩展文件管理器的功能。

截图

开发遇到的问题

分享处理问题思路

Flutter 相关

其中遇到的两个问题就是,路由的管理与状态的管理

MateriapApp 带来的路由问题

我们通常的 app 的顶层会有一个 MaterialApp 组件来获得一个初始的路由,而如果不适用MaterialApp组件页面就会爆红很多组件,个别组件套上Material即可,其他的一些组件是一定要祖先节点有MaterialApp(或者CupertinoAppWidgetsApp),在写这个文件管理器我也同样给顶层加上了一个MaterialApp,在调试example也没有遇到任何的问题,但在集成到主项目后,路由的管理就会特别的麻烦。

场景如下:

在项目A中集成了BB在组件上套有MaterialApp,在A中的页面 push B,随后B项目内部调用Navigator.of(context).push
这个时候就会发现,B拿到的NavigatorBMaterialApp下的,这个时候我们按下返回键,最后执行的Navigator.of(context).pop()中的context是A中的,所以不管B路由了多少次,都会被整个pop掉。

这个问题的确是新手级别的,但是最初我的解决方向是在B上拦截返回按键,然后越处理越不对劲,所以我最后取消了组件的 MaterialApp。

页面级别的状态管理

我个人最初在组件的上层套上MaterialApp的原因就是,我需要用一个在MaterialApp上的Provider,这样一来我不管是单页面,还是路由,能轻易的通过Provider.of(context)拿到一个祖先节点的状态实例,但是由于上个问题带来的影响,我于是取消了MaterialApp,于是在页面内进行路由的时候,不管怎样我都不能通过Provider.of找到含有实例的祖先节点。

当时咨询到和想到的几个方法:

  • 拿实例的页面路由到第二个页面的时候将实例传进去。
  • context通过参数传到第二个页面。
  • 将数据存进单例。

    我最后放弃了这个思路,如果我还有n个页面需要路由,那么我需要给每个页面的构造函数添加一个参数,亦或者是通过路由参数的方法,这样不用更改构造函数,但是个人觉得获取路由参数是非常不可控的,即使我忘记传这个参数,在新页面获取理由参数的地方编辑器是不会提示出错的,只会在使用的时候才能看到错,我觉得这样不利于维护。

最后解决方法:
通过单例的方式,在第一个页面拿到状态实例的时候将实例保存在单例,第二个页面直接通过单例取数据,通过addListinerremoveListiner的方式来同步UI的更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Global {
factory Global() => _getInstance();
Global._internal() {
environment = PlatformUtil.environment();
themeFollowSystem = true;
}
// 由于使用的是页面级别的Provider,所以push后的context会找不到Provider的祖先节点
ProcessState processState;
String _documentsDir;
static Global get instance => _getInstance();
static Global _instance;

static Global _getInstance() {
_instance ??= Global._internal();
return _instance;
}
Future<void> initGlobal() async {}
static String get documentsDir => instance._documentsDir;
}

Apktool移植到安卓

这虽然不是处理耗时最久的问题,但对当时的我,是最难的问题。
整个处理都在一年前了,有些想不太清了。

在次之前已经有个比较出名的app->Apktool,名字就叫这个,我从安卓2.1的时候就在使用这个app,出自国外的大佬。
Apktool App的思路:交叉编译java环境, 随后的所有操作,打开一个Linux Shellrun apktool.jar就行了,

缺点:jdk的集成导致数据直接上100m,这也是我最不想看到的,作为一个dart package,我肯定不希望别人集成后直接app1个G是吧。

个人尝试过的思路

将apktool.jar 转换为 dex

理由:dex 文件可直接在安卓端启动,利用dalvikvmapp_process

这是我在看了很多优秀的开源库的做法,往往想要安卓设备上运行java代码,通常就是将.jar文件转换成.dex
二者区别

  • jar 内是.class文件,java 虚拟机的字节码。
  • dex 反编译得到.smali文件,是安卓虚拟机的字节码

    dalvik虚拟机是基于寄存器的,而jvm是基于栈的.

首先在转换上就遇到了非常多的问题,对dx命令的使用,因为还有一个叫dx8的工具,二者是不一样的,主要是要加上一堆的参数,-h只能知道参数是干嘛的,比如你知道-api是指定android 版本,但你不知道你是这个参数不对转换出错,也不知道具体要调成多少才能转换成功,
最后总算是转换成功了。

  • 反编译正常,但效率低下(相比Apktool x这个app)
  • 回编译卡住,原因未知,试过logcat,始终没有找到原因。

    其实单从第一条来看我就已经放弃这个思路了。

在安卓项目中引入 apktool
gradle中引入
我当时就想,一些平台的SDK也是.jar,里面也是.class,引入后也能直接打包进app内的dex下,而且在java就能直接调用。
apktool.jar是一个java的命令行工具,之所以能成为命令行工具是指定的类带有main函数,我以同样的方式引入后直接调main函数不就行了。

反手gradle引入 -> 报错一气呵成。

1
2
3
4
dependencies {
implementation 'org.smali:smali:2.3.1'
implementation 'org.smali:baksmali:2.3.1'
}

上面还有一行是apktool,现在找不到地址了,然后就以这样的方式引入后,构建的时候,报错一堆。具体是直接 implementation 引入还是下载了 jar 再引入记不清了。
我当时就奇了怪了,都是java,没理由啊。

随后 clone 下apktool的所有代码,将代码 copy 到项目,ok,爆红的地方终于找到了,apktool.jar在处理apk内的.9图片的时候,用了awt这个包,这个包在安卓的sdk上是没有的,然后就是大量的代码修改,将awt处理图片的地方全部换成Bitmap,对于当时的我,实在吃力,起码花了几周的时间。

ls 输出的解析

在各个平台可能会遇到ls命令版本不一致,或者输出不一致的问题,就会直接导致文件列表信息获取失败,但在我测试的过程中,这个都能找到规律和处理的方法。

改进方法。

其实其中有的问题都是两年前处理的了,两年前我才大一上,当时还没接触技术圈子,成天自己闷头写代码。只是很多时候回想起来,这一路还是经历过一些东西,现在都还记得。

作者

梦魇兽

发布于

2020-11-15

更新于

2023-03-11

许可协议

评论