flutter随手记
Show a snack bar:
1 | ScaffoldMessenger.of(context) |
弹窗:
showModalBottomSheet
Show a snack bar:
1 | ScaffoldMessenger.of(context) |
弹窗:
showModalBottomSheet
A while ago, I tried setting up the Flutter environment and had a brief experience with it. After a long break, I recently became interested again and wanted to see what changes Flutter has undergone.
flutter doctor without any issues.flutter clean to clear the cache, but the problem persisted ❌.flutter pub cache clean to clear the dependency cache, but the problem remained ❌.flutter run -v to view the output, I noticed that it got stuck at the line Connecting to service protocol:http://127.0.0.1:57071/0dqLN-ZPpFk=/. I suspected an issue with port 57071, so I ran lsof -i:57071 to check the process ID occupying that port and then killed the process using kill <pid>. However, the issue persisted, and I realized that the service changed ports every time, so the problem wasn’t related to that ❌.Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source!. I remembered that I had changed the source due to a poor network environment in the past. Could it be an issue with this source? I quickly checked the ~/.zshrc file and found the following configuration:1 | export PUB_HOSTED_URL=https://pub.flutter-io.cn |
In summary, one should not easily trust third-party sources. Configuring the network environment correctly is always the first step in setting up the environment.
After searching on Stack Overflow for a while, I couldn’t find any answers pointing to the source. I’m documenting this experience here, hoping it can help others facing the same issue. ♥️
If the above solution doesn’t resolve your problem, you can refer to:
https://stackoverflow.com/questions/75665760/flutter-run-stuck-on-launching
https://stackoverflow.com/questions/68698437/flutter-app-stuck-at-installing-with-white-screen
很久之前尝试配置过一次flutter环境,体验了一下。很久没用了,最近有了兴趣,又想看下flutter有什么变化。
flutter doctor无异常项flutter clean清除缓存,问题没有解决❌flutter pub cache clean清除依赖缓存,问题没有解决❌flutter run -v查看输出信息,发现最后是卡在Connecting to service protocol:http://127.0.0.1:57071/0dqLN-ZPpFk=/这一行了,怀疑是57071这个端口有问题,运行lsof -i:57071查看占用这个端口的进程的pid,然后运行kill <pid>杀掉这个进程。再次运行发现还是不行,并且发现这个service每次都会换个端口,问题应该不是在这。❌Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source!,想起来以前因为网络环境不好,换过源,不会是这个源有问题吧,于是赶紧查看~/.zshrc文件,果然在里面发现了这个配置1 | export PUB_HOSTED_URL=https://pub.flutter-io.cn |
总结,不能轻易相信第三方源,配置好网络环境永远是配置环境的第一步
stackoverflow上查了半天,没有一个将问题指向源的,在此做下记录,希望能帮助到遇到同样问题的人。♥️
如果上面没有解决你的问题,可以参考:
https://stackoverflow.com/questions/75665760/flutter-run-stuck-on-launching
https://stackoverflow.com/questions/68698437/flutter-app-stuck-at-installing-with-white-screen
工作后很难过的发现自己学了那么多年的英语正在一点点的还给老师。日常工作之余会每天记录几个遇到的生疏单词,本篇主要记录下我平时背过的单词,以便日后查询。本文不会给出释义。
由于19年的单词都是抄在纸上的,未能按天归类,20年2月26号开始按天归类。
操作前,建议备份一下当前的分支,或者切换到新的分支进行操作。
首先,你需要找到分支或者提交的SHA-1值,如果你已经知道丢失分支的SHA-1值,可跳过这个步骤。
通过git reflog命令查找对git作出过的修改,可以查询到HEAD的变更记录,如commit, rebase, checkout, reset等。示例如下:
1 | ># git reflog |
第一列就是所在提交的SHA-1值
如果通过reflog无法找到丢失的分支或者提交,那你还可以通过git fsck命令查找git数据库中所有丢失的分支信息:
1 | git fsck --full --no-reflogs --unreachable --lost-found | grep commit | cut -d\ -f3 | xargs -n 1 git log -n 1 --pretty=oneline > ~/lost-found.txt |
由于输出的结果通常较大,我们一般将命令的输出重定向到一个文本文件中进行查看,其输出的结果如下所示:
1 | e005fc257bb96252e9bc84deb255bba28f829e63 --story=854880635 【技术优化】升级gradle: 修复打包无产物 |
然后我们就可以通过提交的名字或者通过git log -p <commit>查看提交的内容或git cat-file -p <commit>查看提交的信息来确定我们要恢复的提交,确定了要恢复的提交后,复制它的SHA-1值备用。
如果你要恢复的是分支,使用类似于下面的命令:
1 | git checkout -b <new branch name> <SHA-1> |
如果你要恢复的是提交,那么可以直接:
1 | git reset --hard <SHA-1> |
有时候,在开发过程中,可能被产品或设计打断,或者前期考虑不够严谨导致后面补充了过多的修补提交,这时候可以通过Rebase的交互模式,对提交历史进行“美化”。
例如下面一个提交记录:
1 | --story=854849047(d01f70f39)【技术优化】fresco图片库更新 hypertian* 2019-05-23 13:12 |
可以看出,UI修改和逻辑修改提交了多次,这个时候如果把两个UI走查的提交合并,把两个补充逻辑的提交合并,提交历史看起来会清楚干净很多,于是,我们使用rebase操作来整理提交记录:
找到要修改提交的上一条提交的SHA值,如上面例子,要对下面四个提交进行修改,则需要找到”fresco图片库更新”这个提交的SHA值:d01f70f39,然后运行:
1 | git rebase -i d01f70f3 |
就进入到下面的页面:
1 | pick 39741ee1a --story=64746051 【个人中心】增加开机兴趣选择修改: UI走查修改1 |
每次提交按照时间升序排列,也就是最上面的提交是最早的提交。每个提交前面是要进行修改的命令,下面列出了所有可用的命令的名字及其功能,默认是pick,也就是保留这条提交,什么也不做的意思。这里解释一下几个常用的命令:
reword:重命名提交信息
edit:编辑提交的内容,可以用于添加或移出提交的文件
squash:将当前提交和前一条提交合并,使用后,会跳到另一个页面,编辑合并后的提交信息
fixup:将当前提交和前一条提交合并,使用后,丢弃当前的提交信息,直接以上一条提交信息为合并后的提交信息
drop:删除当前的提交
⚠️ 需要注意的是这句话:
These lines can be re-ordered; they are executed from top to bottom.
意思是说,改变上面一行行的提交记录的顺序是可以改变最终的提交顺序的,不过需要注意的是,改变顺序可能会带来conflict,如果遇到冲突,需要解决冲突后才能继续Rebase。
可以看下视频中的演示消化理解
rebase interactively如果你rebase结束后发现并不是自己想要的结果,这个时候怎么恢复到rebase之前呢?很简单,运行下下面这个命令即可:
git reset --hard ORIG_HEAD
In case ORIG_HEAD is no longer useful, you can also use the branchName@{n} syntax, where n is the nth prior position of the branch pointer. So for example, if you rebase featureA branch onto your master branch, but you don’t like the result of the rebase, then you can simply do git reset --hard featureA@{1} to reset the branch back to exactly where it was before you did the rebase. You can read more about the branch@{n} syntax at the official Git docs for revisions.
既然Rebase命令可以删除和编辑历史提交,那同样也有一个重要的原则:
永远不要对已经推到主干分支服务器或者团队其他成员的提交进行Rebase,我们选择Rebase范围应该在自己本地工作范围内。
否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。
[1] https://www.internalpointers.com/post/squash-commits-into-one-git
将Kotlin版本从1.1.2-4升级到1.3.11后,发现打的release包开机就会崩溃:崩溃日志为
1 | java.lang.RuntimeException: Unable to instantiate application com.tencent.sigma.patch.HotPatchApplication: java.lang.ClassNotFoundException: Didn't find class "com.tencent.sigma.patch.HotPatchApplication" on path: DexPathList[[zip file "/data/app/com.tencent.dreamreader-P2-qUIVdDrdEOhONvRwOtw==/base.apk"],nativeLibraryDirectories=[/data/app/com.tencent.dreamreader-P2-qUIVdDrdEOhONvRwOtw==/lib/arm, /data/app/com.tencent.dreamreader-P2-qUIVdDrdEOhONvRwOtw==/base.apk!/lib/armeabi, /system/lib, /vendor/lib]] |
乍一看,以为是HotPatchApplication被混淆的原因导致Class Not Found,于是尝试keep住HotPatchApplication这个类,但是发现问题依旧。于是又怀疑是前不久升级gradle对分包造成了影响,对apk中的dex文件进行分析,发现HotPatchApplication这个类稳稳的躺在第一个dex文件中。
后来又进行了以下的尝试:
1.尝试按顺序更改Kotlin版本,最后,当Kotlin版本为1.3.0时,发现问题出现了。
2.另一个线索是当我打开Proguard时,那就是minifyEnable true,崩溃就出现了。
经过以上排查,基本确定了问题出在Kotlin和混淆的身上。最后发现上述日志中有一行:
1 | Suppressed: java.io.IOException: Failed to open dex files from /data/app/com.tencent.dreamreader-P2-qUIVdDrdEOhONvRwOtw==/base.apk because: Failure to verify dex file '/data/app/com.tencent.dreamreader-P2-qUIVdDrdEOhONvRwOtw==/base.apk': Out-of-order annotation_element name_idx: 7150 then 7150 |
这个报错是第一次见到,上网搜了下,最终定位在混淆时的一个参数身上:-overloadaggressively
ProGuard官网解释:
-overloadaggressively
Specifies to apply aggressive overloading while obfuscating. Multiple fields and methods can then get the same names, as long as their arguments and return types are different, as required by Java bytecode (not just their arguments, as required by the Java language). This option can make the processed code even smaller (and less comprehensible). Only applicable when obfuscating.
可以看出,开启该选项可能把不同的方法或者变量混淆成相同的名字,这样会尽可能的减小代码体积。但是由于过于激进,造成了dex文件中有重复annotation_element,这个问题在编译和打包时并不会报错,只有在开启软件时才会发现。
至于为什么升级了Kotlin才有这个问题,猜测原因是新版本的kotlin中加入了Keep注释与Java的Keep注释同名造成的。
解决办法:
去掉混淆文件(proguard.txt)中的-overloadaggressively
参考:
ViewPager2是Google于2019年2月7号发布的ViewPager的升级版本,目前最新版本是1.0.0-alpha01。ViewPager2主要解决了传统ViewPager中的几个问题:
查看ViewPager2内部代码可以看出,其实现原理是在其内部封装了一个RecyclerView,利用LinearLayoutManager+PagerSnapHelper实现了ViewPager的滚动效果以及对纵向滚动的支持。
1 | public class ViewPager2 extends ViewGroup { |
由于直接使用的RecycleView实现,一些ViewPager的特性没有得到完全实现,官方指出了下面几个问题:
其使用方法与传统ViewPager类似:
1.在模块gradle中添加依赖:
1 | implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha01' |
2.在Layout文件中布局,这里可以设置滚动方向:
1 | <androidx.viewpager2.widget.ViewPager2 |
3.创建Adapter,ViewPager2使用FragmentStateAdapter来实现Fragment的管理:
1 | public class ViewPager2Adapter extends FragmentStateAdapter { |
4.应用Adapter:
1 | viewPager2 = findViewById(R.id.viewpager2); |
ViewPager2具有原生支持垂直方向滑动的特性,但是由于目前FragmentStateAdapter还有稳定性问题,以及设置page transformer时,不支持设置绘制顺序,在实际应用时还需要多进行修改和测试。建议等官方推出稳定版本后,再考虑接入。
参考:
[1] https://developer.android.com/jetpack/androidx/releases/viewpager2
[2] https://developer.android.google.cn/reference/androidx/viewpager2/adapter/FragmentStateAdapter
[3] https://developer.android.google.cn/reference/androidx/viewpager2/widget/ViewPager2
如果说使用快捷键是程序员的刀🔪,那灵活的使用代码模版就应该是程序员的剑。
这里说的模版(Templates),是指在使用开发创建类文件,甚至是某些代码块时,IDE自动按照规定的格式创建出类或代码的功能。如果类中有大量相似代码,使用模版可以极大的提高开发效率,降低出错概率。
下面我们看一下如何在Android Studio上使用模版,我将讲述三种模版的创建,分别是:
java中经常需要定义这样的静态常量:
1 | private static final int DEFAULT_VALUE = 1; |
使用Live Templates后,就可以直接输入const就可以快速定义静态常量:

其设置方法是,打开File->Setting(⌘+,),搜索“Live Templates”,打开如下的界面:

其中,1指的是缩写和该缩写的描述;2中输入的是模版的内容;3中可以指定该模版应用生效的语言和场所,例如,可以限制该模版只应用到Java语言定义变量(declaration)的时候; 4可以将模版中变化的部分定义为变量,如上图中的${name}和${value}
上面例子中的const是默认的模版,你可以点击加号添加自己的模版,例如为kotlin定义一个tag常量的模版:

RemoteView是一个提供跨进程控制的View,主要用在通知栏或者小部件的开发上。例如音乐类APP自定义的通知栏样式就是通过RemoteView实现的。如果你之前没有听说过RemoteView,可以在这里简单了解一下:
https://www.jianshu.com/p/23041852bd85
RemoteView中可以使用的布局和控件是受限制的,能用的布局有:
AdapterViewFlipperFrameLayoutGridLayoutGridViewLinearLayoutListViewRelativeLayoutStackViewViewFlipper可以用的控件有:
AnalogClockButtonChronometerImageButtonImageViewProgressBarTextClockTextView最近开发了一个音乐类的App,播放音乐时会在通知栏常驻一个自定义样式的通知,其中通知栏有一个头像是通过setImageViewBitmap(int viewId, Bitmap bitmap)方法进行设置的:
1 | RemoteViews mRemoteView; // 全局变量 |
后来发现如果App连续放歌在3个小时左右时就会OOM崩掉,通过Profile检查内存后,发现是头像的bitmap没有销毁导致的,但是这里的bitmap对象每次使用完都会recycle掉,为什么还会内存泄漏呢?经过一番排查,发现是使用同一个RemoteVIew对象setImageViewBitmap(R.id.cover, bitmap)导致的。
在RemoteView的源码中我们可以看到一个mActions变量,这是一个Action的列表:
1 | /** |
而Action是其内部定义的一个可序列化的类:
1 | private abstract static class Action implements Parcelable |
通过跟踪setImageViewBitmap(int viewId, Bitmap bitmap)中bitmap的去向,发现最终调用了setBitmap()方法:
1 | public void setBitmap(int viewId, String methodName, Bitmap value) { |
可以看出,每次setImageViewBitmap(),都会将Bitmap做成一个BitmapReflectionAction,并添加到mActions列表里,这里的BitmapReflectionAction是继承Action的一个可序列化的类,Bitmap在里面作为被序列化成了一组值最终存到了mActions列表中。在源码中,mActions列表只看到有添加操作,并没有看到remove或者clear操作,导致了内存泄漏。
不要复用RemoteView,更新通知栏icon时,new一个新的RemoteView给NotificationCompat.Builder
So internally RemoteViews is simply a set of actions that are
“serialized” and sent to another process. Each time you make a call
to something like setDouble(), you’re adding an additional action to
RemoteViews’ internal list.Because there isn’t a way of clearing these actions from a RemoteViews
object, all of your successive setImageViewBitmap() calls, along with
their Bitmaps, remain in the internal list, and are actually
“serialized” and applied each time your send it. :(
In this case it’s best to just create a new RemoteViews object every time.
Update your browser to view this website correctly.&npsb;Update my browser now