最近半年做了什么呢

回顾自己过去半年,很揪心,折腾了挺多东西,但实际上似乎什么事都没干成。下面罗列下自己做了什么。

Flutter

看到日渐火热的新东西,很开心地去折腾了。期间主要看了三个项目:
官方Gallery
俄罗斯方块游戏(代码风格特别好)
每日清单Todo
个人感觉,flutter构建页面的思维是比原生Android舒服很多的,其中令我感触最深的有两个例子:

动画的实现.

Tween思想实现一个动画,假设我们需要一个移动动画,我们只需要定义一个起点,终点,以及一个时间插值器,如此,便可完成了。
相比于原生,动画实现的编码思维用的面向过程,但在flutter中,我们是面向概念的,或者说面向对象的。
这种思维方式的转变,我们可以站在更高层次来思考。

页面路由的使用

在前端中,页面路由是很常见的开发工作了,flutter引用了web端的路由方式。使用之后,难免有些许嫌弃原生的实现方式了。 Android中,我们想要监听系统的一些事件,通常是在相应的生命周期方法中,加入自己相应的处理逻辑。相应的,我们在跳转页面返回时, 也沿用了这个方式,在onActivityResult中处理页面返回值。

这里,我认为最大的问题是,启动页面和处理页面返回值的逻辑分开了,本该连贯的思路分裂开,对于代码的书写和后期查看维护,添加了难度。

我想这个问题是原生普遍存在的,故出现RxActivity,RxPermission,RxPickImage这样的库,它们存在都是为了使一个套处理逻辑尽可能的内聚。 个人认为这些思想是进步的,终将会该替代原先的思维。Flutter作为新生事物,在框架层面引入这些思想,使用上确实令人舒服。


刷题LeetCode

近一个月来坚持着每日一题的任务,目前刷了133道,前期很多题目根本没有思路,直接看题解了,现在也就会一些简单的套路题吧。

有什么体会呢? 了解一些常见的概念,动态规划,双指针,滑动窗口,二叉树操作等等,对于边界处理也有点经验,感觉学到的东西蛮多的,有种当年高中数学题的感觉,评论区也挺有趣的,膜拜各路大佬。

记得毕业时(两年过去了)有个大佬跟我说了一道题,使用最小花费爬楼梯 ,当时,第一次听说LeetCode刷题,听着迷迷糊糊的。后来,去面试一家小公司,恰好问了这道题,自己也没有做出来,现在想,要是当时准备准备就好了。

这道题是这样子的。 问:每个台阶有一个数值,代表着站在上面消耗的体力,以数组costs表示,人每次可以选择跳跃1个或2个台阶,问越过所有的台阶最小的代价是多少?

要想跳到第n阶,即站在第n个台阶,之前必须经过第n-1或n-2台阶,定义f(i)为跳到第i阶的最小代价,而f(i) = min( f(i-1), f(i-2) ) 故问题求的问题就是求f(n),使用递归

1
2
3
4
5
6
7
8
9
fun f(i){
if(i==0){
return costs[0]
}
if(i==1){
return min(costs[0],costs[1])
}
return min(f(i-2)+costs[i], f(i-1))
}

这是最朴实的方法,方法调用栈的高度会达到O(n),而且我们求f(n)时,递归求f(n-2)和f(n-1),而f(n-1)又会求f(n-2),这样,我们会发现f(n-2)调用两次,而f(n-3) 调用了3次,以此类推,后面都会进行重复的不必要计算。

故,我们可以引用备忘录或缓存的避免重复计算这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun f(i,map){
if(i==0){
return costs[0]
}
if(i==1){
return min(costs[0],costs[1])
}
//若是已经求过f(i),则直接从缓存取出
if(mpa.contains(i)){
return map[i]
}
val res = min(f(i-2)+costs[i], f(i-1))
//将结果放入缓存
res[i] = res;
return res;
}

进一步的,目前我们解决问题的思路是,以求f(n),作为我们的最终问题,然后将当前问题划分两个小问题f(n-2),f(n-1),之后一步步将问题分解成我们能够求的问题,即递归的边界f(0)和f(1) 。可以说,这是递归的思路,相应的,还有迭代的思路。如下

1
2
3
4
5
6
7
8
9
10
11
fun f(i){
val dp = IntArray(n+1) //定义dp[i]
dp[0] = costs[0];
dp[1] = min(costs[0],costs[1]);

for(i in 2..n+1){
d[i] = min( f(n-2)+costs[i], dp[i-1]);
}
}
// 结果
return dp[n+1]

迭代,也是经典动态规划过程,每个子问题求解都要依赖上一个状态的结果,依次迭代,最终得出我们目标问题的答案。这里还有比较玄乎的称呼(对于我来说,有待理解体会),自下而上思路,而递归就是自上而下。

Android原生

Xposed

迫于好奇心,初略了解了下这个框架。

原理,通过替换系统内app_process文件,使得Zygote进程启动时加载XposedBridge的jar包,而之后所有的进程都是从Zygote进程孵化出来的,之后所有App的方法调用都可以添加监听,插入自己所需的逻辑。

原理还是一知半解,想起来了以前看过的AOP面向切面,找到一个固定的方法,通过反射在方法调用前处理入参,加入或改变方法内的逻辑,改变方法返回值等等,也叫做hook某个方法。在实际写自己的模块时,找到hook点是挺关键的。

后来为什么不更新了呢?

在Xposed仅支持7.0之前的版本,作者在xda论坛上声称,由于7.0之后采用aot技术,ahead of runtime,在程序执行前就将字节码转换为与平台有关的二进制代码,故我们不能在java运行时获取到相应的hook点。后来也有一些库将aot直接禁用来达到继续hook的目的,但这也使得程序性能降低。

Android嵌套滑动机制

Android在5.0引入了嵌套滑动,来实现在嵌套滑动中处理。

在事件分发中,在一套流程中(down开始,up结束),选择消费的View只能有一个,不能存在子View消费一半的滑动距离后,然后又转移给父级View消费。而这个机制就是为解决这类问题的,应用这个机制可以将TabLayout作为一个粘性头部。

其大概用法跟很多XXHelper思路一样,询问你要不要处理,处理逻辑,处理完后要不要继续传给后者。

另外CoordinatorLayout中的Behavior也是结合该机制一起使用的。

夭折的小项目

有想法做一个自己的项目,情侣卡片。

初衷是服务情侣之间的比较浪漫承诺,比如答应一段大餐,7天不准吵架,允许打1小时游戏,等等。

个人觉得挺好玩的,也看到知乎有个一些情侣确实用纸片卡片这么玩。

然后我就动手了

  • 实现了卡片滑动效果
  • 编辑卡片包括设置卡片颜色,或选择背景图片,输入文字
  • 全局使用自定义的卡通文字
  • 使用Room来保存数据
  • 将卡片发送给对方
  • UI设计(太丑了)

如何将卡片发送给对方呢?

第一个想法是使用局域网内互传,但想到很多异地恋的,否决。

故想到用google的Firebase套件,因为自己学Flutter时敲过一个投票项目,换成Kotlin原生时,发现有挺多问题,卡着这个地方了。