我的近况

经常有朋友问我最近在忙什么,是不是发财了咋没有上班?

当然没有。去年初从上一家公司离职后,我一直靠做点小活赚钱。后来时常到外地,因为家里事情多以及强度大就不干了。自从10月之后就一直没有找工作,也没有接活,就在家发发呆,看看娃,学学以前想学的东西。

今年过完年之后,我就在各大程序员兼职平台上注册了账号,开始有意识的地接活,这个过程倒是挺有意思的。你需要对过去做个整理,对自己有个重新的认识,毕竟已经好久没有写过简历了。另外你需要对自己进行包装,尽可能让别人信任你,觉得你技术和人品的都还行,这个我也在探索之中。的确有人会来咨询,也会受到别人的否定,不过最重要的是我增加了与人的交流,交流越多,机会也就越多。

最近就有个做外包产品的机会,我试试看能不能接下来并且完成它。

继续阅读 →

LiveData – 数据监听神器

LiveData我认为是Jetpack构架组件里最有用一个。

以前做项目的时候,不同页面里会经常显示一些通用信息,比如当前登录用户的用户名和头像等。这些信息都保存在数据层,你的UI如果想及时响应数据的变化,那么必须想办法去监听数据的变化。假设你的N个页面都有显示头像,那么用户进入你的App的设置页面,更改了头像,然后显示了头像的这些页面是不是得及时改变?

我以前是这么做的。使用EventBus,在各个需要显示头像的Activity里注册事件,用户改完了头像,发送一个事件,然后各个页面自己去更新。

LiveData提供了一个简洁优雅的方式来完成这种通用需求。我们可以给LiveData注册观察者,一旦数据变化,就可以响应。配合LifeCycle使用,可以获取当前Activity的LifeCycle,当LifeCycle到达DESTROYED状态的时候,把观察者给删除。也即是说,你在Activity的onCreate开始监听数据之后,就不用操心再在onDestroy去写代码了,取消监听是自动的,你省事儿了。

(更多…)

继续阅读 →

ViewModel能替代onSaveInstanceState么?

ViewModel出现之前,处理横竖屏的最佳实践就是使用Activity的onSaveInstanceState()来保存Activity销毁之前的数据,在onCreate()或者onRestoreInstanceState()的时候取出数据。有了ViewModel之后,你可能会思考,这两种方式都有啥区别,是不是只用ViewModel就行了?

仁者见仁智者见智。这两种方式还是稍稍有些不一样的,我们简单做个回顾:

(更多…)

继续阅读 →

使用ViewModel存储Activity数据

ViewModel是用来存储UI相关的数据的,Activity在横竖屏变化的时候会被销毁,然后再重新创建,如果你把你的数据存储在Activity中,那么新建Activity的时候这些数据就没有了,用户就会很疑惑,屏幕旋转了下怎么界面的东西就没了?所以需要把界面相关的数据存储下来,下回接着用。使用ViewModel就可以做到这一点。

你可能会问,我的App只有竖屏是不是需要考虑这些了?

答案是否定的。这种Activity重新创建的情况有很多,只要发生了configuration changes,就会重建,横竖屏切换只是其中最典型的一种,其他还有十几种,比如字体变化和语言切换等,你总不可能每一种都不管吧,用户瞎操作出问题了咋办。

(更多…)

继续阅读 →

生命周期组件LifeCycle

我们知道Activity有着自己的生命周期,在不同的阶段有着不同的回调函数,比如onCreate、onResume等等。这些回调是非常常用的,因为你时常要利用它们完成一些事情,比如你如果用EventBus的话,可能需要分别在onCreate和onDestroy中注册和取消监听器。类似这样的操作多了去了,需要一个方便的工具来做这些操作,那就是LifeCycle组件

LifeCycle类把生命周期给抽象了出来,包含了不同的状态和事件,事件一来,状态就变,参考下图:

(更多…)

继续阅读 →

初识Android Jetpack

Android Jetpack就是谷歌提供的一堆帮助你加速开发的工具,你不必再去重复地写样板代码,对于经常使用的一些逻辑代码,Android Jetpack已经帮你实现了,你能把更多的时间放在业务逻辑上。

Android Jetpack并不是全新的,他将我们所熟知的工具都整合到一起,比如AppCompat、LiveData、DataBinding等。又增加了一些工具比如WorkManager、Paging和Navigation等。

(更多…)

继续阅读 →

我又回来啦

前一段时间,因为备案取消了,所以我的博客一直不能访问。有朋友反馈我博客挂了我还很欣慰,原来写的东西还有人看。前天下了决心,准备把它迁回来。

2010年的时候我搭建了这个博客,当时和一个朋友在华域合租了虚拟主机,我主要写些竞赛题解什么的。没想到现在都快9年了,看了数据,总共写了96篇,收到评论506条,PV大概有11.3万,UV有个4.5万,Adsense有个六七刀😂。应该是帮助了不少人的。对于我也算是一笔财富,希望能坚持下去,来个20年什么的。

话说上一篇文章还是15年5月发布的,中间隔了实在太久,跟许多早些写博客的朋友一样,心思都花费在了日常工作事务上,有了小孩儿之后,更是把这块儿地给荒废了。刚才检查了友情链接,90%都访问不了了,全都删掉算了。又检查了谷歌和百度,之前的收录的链接全都没有了,真是惨😞,我还得慢慢来。

前天在阿里云上配了个最低配的机器,直接买了三年,折扣完也就比虚拟主机贵了两杯咖啡。昨天下午提交了备案信息,今天下午就收到了管局审核通过的短信,是在太快了,大学备案的时候,还是跑到城市的另一边,填表、拍照、审核流程麻烦得很。机器所在机房就在杭州,我立马配置了https和http2,打开页面嗖嗖的,再也不用看着菊花转了,也不用再忍受卡顿的ssh连接了。

接下来我就多写一点东西吧,希望还能有人看😁

继续阅读 →

LauncherRootView和DragLayer的布局过程

(系列文章点这里)

话说讲解Launcher3的布局过程真是要耗费一番功夫了,因为他的过程很难理解, 一般来说我们普通应用的布局过程其实都是按照系统自带的ViewGroup来的,如果自定义了一些布局方法,开发者在考虑xml标记的布局文件的时候,还要考虑运行时的一些动态变化。

在接着往下读之前,你最好先去了解一下View的绘制流程。

在主布局launcher.xml中, 最外层是LauncherRootView。

我们发现它只是重写了一个方法, View.fitSystemWindow(rect), 那这个方法是干什么的呢?查了官方文档,还是看不懂他到底是做什么的,但是倒是会用了。如果你的View在status bar或者navigation bar之下,那么这个方法会传递进去一个参数,说明这个View的四周有多少是被status bar或者navigation bar挡住了的。这个方法的会调用父类的setInsets()方法,我们来看看父类InsettableFrameLayout。

InsettableFrameLayout继承了FrameLayout, 并且实现了父类的一些方法,总的来说,他的目的就是给子View都设定一个Margin值,防止被status bar或者navigation bar挡住。如果一个View已经是Insettable的了,那么就忽略他。在Launcher3中,Workspace, DragLayer和AppsCustomizeTabHost都实现了这个接口,那么他们自身就不会被加Margin, 而搜索栏和Hotseat都没有实现这个接口,那么说明他们在被加到父亲上得时候会被设一个Margin,最后的到得效果就是搜索栏刚好在status bar之下(gravity为top,topmargin是status bar的高度),Hotseat刚好在navigation bar的上方(gravity为bottom,bottomMargin为navigation bar的高度)。

LauncherRootView的实现已经很明了了,他的孩子只有DragLayer,DragLayer实现了Insettable接口, 所以他不会加上margin。DragLayer其实也是InsettableFrameLayout的子类, DragLayer的孩子有Workspace,Hotseat,AppsCustomizeTabHost 和 SearchDropTargetBar等等 。

DragLayer的布局过程稍微复杂一点,不过还好。DragLayer自己定义了一个LayoutParams, 在FrameLayout原有的LayoutParams基础之上,添加了几个参数,x,y和customPosition。在DragLayer中添加孩子的时候,都会自来于提供的LayoutParams来计算View的布局位置。

我们来看下DragLayer重写的onLayout方法[如果还不清楚这个方法是干啥的话,赶快搜一搜]:

2015-05-20_17-47-41

刚开始先按照FrameLayout的方式进行布局,然后,遍历所有的孩子的LayoutParams, 如果这个孩子的LayoutParams参数中含有的customPosition参数为true,那么就会使用参数里执行的x坐标和y坐标。

为什么需要这样的功能呢?比如说我们在拖拽一个图标的时候,这个图标其实是放在了DragLayer里的[drag layer,拖拽层嘛],如果给拖拽的图标分配了一个自定义位置的布局参数,那么布局完之后就可以显示在指定位置了。拖拽一个AppWidget和显示文件夹的时候也是同样道理。

理解了Launcher最外层的两个ViewGroup[LauncherRootView,DragLayer]的布局方式之后,相信你了解的更深入了一些,同学还学了自定义ViewGroup的时候如何自定义LayoutParams布局参数。再往Launcher的底层看得话就是Workspace,CellLayout了,他们会更为复杂,接下来的博文会介绍到。

继续阅读 →

IconCache原理

(系列文章点这里)

在Launcher3中,IconCache从名字上来看就是用来做应用图标缓存的,以前没有在意过这个类,最近刚好需要研究下,就做个笔记。

IconCache不仅会保存图标,还会保存应用的title, 我们可以看到缓存都是保存在mCache中的,他是个HashMap,键是ComponentName和UserHandleCompat组成的对象, 值是一个对象CacheEntry, CacheEntry的三个成员是图标,标题和内容描述[其实我也搞不懂]。

在Launcher启动的时候, 会在LauncherApplication中初始化LauncherAppState,而他会初始化IconCache对象, 刚开运行的时候IconCache肯定是空的, 只有在Launcher.java 这个Activity启动之后才会有缓存。在Launcher.onCreate()方法中,执行了IconCache的flushInvalidIcons, 会清空现有的缓存,如果Activity的方向发生了改变[即图标的大小发生了变化],原来的缓存可能都没有用了。那什么时候IconCache会被完全清空呢, 也就是图标和文字无效的时候,在用户的语言发生了变化之后会被清空,因为获取的图标和title很可能已经不同了。

下面来看图片是怎么加进去的。添加图标最关键的方法是IconCache.cacheLocked(), 这个方法会获取某个Component[Activity]的图标和标题,并存入mCache中, 我们看到这个方法名最后是“Locked”表明,方法调用的时候已经保证是线程安全的,可以看到IconCache.cacheLocked()每个调用的方法都有关键字synchronized。

最后IconCache还会生成一个系统默认图标,如果一个Activity由于某种原因我们没有找到对应的图标, 那么IconCache会返回一个默认图标, 这个图标是从系统取到的资源, 资源id是android.R.mipmap.sym_def_app_icon, 不同的系统这个资源对应的图标是不一样的, 比如4.4 和 5.1的系统默认图标就是不一样的。

好了IconCache需要说的就这么多。

继续阅读 →

招个Launcher开发

话说公司要开始Launcher产品的开发了,不过目前开发人员有限,鉴于我博客80%的流量都来自于Launcher3相关的关键词,招聘信息就发在这里好了。

先说下我们公司的情况,公司于去年10月成立,目前有5个人(2个Android,一个Web,一个实习,一个boss),团队很作目前比较顺畅。先期项目是一个广告插件,现在已经基本完成,月盈利可以达到百万,目前由另外一位android开发维护中。

母公司是boss之前的创业公司,具有很强的盈利能力,每天可以达到50万左右的app推广能力,可以说Launcher做出来之后不愁用户量上不去。

Launcher产品的目标是要作为一个“内容分发中心”,这个内容可能是影视,音乐,小说等等,初期用户目标是三四线城市的用户。当然,盈利性保证的前提就是Launcher这个产品要好用,用户喜欢,如果你要加入我们的团队,这个就是我们要考虑并完成目标。

职位描述我也不想一条一条列出来了,大概就是要么很厉害,要么参与开发过大量用户的app,要么熟悉Launcher。只要你对Launcher产品有想法,都可以发封邮件过来我们聊一聊,“志同道合”一定会使合作十分愉快的。

就这样

继续阅读 →