简世博客

一个简单的世界——博客空间,写了一些Android相关的技术文章,和一些点滴的想法

0%

在Flutter中,如果 PageView 嵌套 Swiper时,默认是很正常的,内层Swiper优先消费滚动事件。
但是当Swiper的layout设为自定义:SwiperLayout.CUSTOM 时,则会出现Swiper无法消费滚动事件的问题。

这个问题是因为PageView和Swiper的滚动发送了冲突导致的。

  1. 如果交互上可以接受的话,最简单的办法就是直接禁止外层PageView的滚动。
    把PageView的physics改为NeverScrollableScrollPhysics即可。

  2. 另一种方法,就是要改Swiper的实现代码了,flutter想改第三方库里的代码很简单,直接把代码拷出来修改即可。
    具体要改的地方,就是custom_layout.dart里的_buildAnimation方法,将其中的
    onPanStart, onPanEnd, onPanUpdate 三个方法,分别改为
    onHorizontalDragStart, onHorizontalDragEnd, onHorizontalDragUpdate 即可。

//原来的代码:
在这里插入图片描述
//修改后的代码:
在这里插入图片描述

onHorizontalDragStart, onHorizontalDragEnd, onHorizontalDragUpdate 三个方法,比对应的onPanStart, onPanEnd, onPanUpdate 三个方法的优先级更高,flutter会先判断onHorizontal…方法。

:w !sudo tee %
w !sudo tee %

---

在Linux或mac上工作的朋友很可能遇到过这样一种情况,当你用Vim编辑完一个文件时,运行:wq保存退出,突然蹦出一个错误:

E45: 'readonly' option is set (add ! to override)

这表明文件是只读的,按照提示,加上!强制保存::w!,结果又一个错误出现:

"readonly-file-name" E212: Can't open file for writing

文件明明存在,为何提示无法打开?这错误又代表什么呢?查看文档:help E212

For some reason the file you are writing to cannot be created or overwritten.
The reason could be that you do not have permission to write in the directory
or the file name is not valid.

原来是可能没有权限造成的。此时你才想起,这个文件需要root权限才能编辑,而当前登陆的只是普通用户,在编辑之前你忘了使用sudo来启动Vim,所以才保存失败。于是为了防止修改丢失,你只好先把它保存为另外一个临时文件temp-file-name,然后退出Vim,再运行sudo mv temp-file-name readonly-file-name覆盖原文件。

目录

  • 解决方案
    <ul><li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#vim%E4%B8%AD%E6%89%A7%E8%A1%8C%E5%A4%96%E9%83%A8%E5%91%BD%E4%BB%A4" id="markdown-toc-vim中执行外部命令">Vim中执行外部命令</a></li>
        <li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#%E5%91%BD%E4%BB%A4%E7%9A%84%E5%8F%A6%E4%B8%80%E7%A7%8D%E8%A1%A8%E7%A4%BA%E5%BD%A2%E5%BC%8F" id="markdown-toc-命令的另一种表示形式">命令的另一种表示形式</a></li>
        <li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#%E7%9A%84%E6%84%8F%E4%B9%89" id="markdown-toc-的意义">%的意义</a></li>
        <li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#tee%E7%9A%84%E4%BD%9C%E7%94%A8" id="markdown-toc-tee的作用">tee的作用</a></li>
        <li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E4%B9%8B%E5%90%8E" id="markdown-toc-命令执行之后">命令执行之后</a></li>
    </ul></li>
    <li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#%E6%9B%B4%E7%AE%80%E5%8D%95%E7%9A%84%E6%96%B9%E6%A1%88%E6%98%A0%E5%B0%84" id="markdown-toc-更简单的方案映射">更简单的方案:映射</a></li>
    <li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%80%9D%E8%B7%AF" id="markdown-toc-另一种思路">另一种思路</a>
    <ul><li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#%E9%87%8D%E5%AE%9A%E5%90%91%E7%9A%84%E9%97%AE%E9%A2%98" id="markdown-toc-重定向的问题">重定向的问题</a></li>
        <li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#%E9%87%8D%E5%AE%9A%E5%90%91%E6%96%B9%E6%A1%88" id="markdown-toc-重定向方案">重定向方案</a></li>
    </ul></li>
    <li><a href="http://feihu.me/blog/2014/vim-write-read-only-file/#%E5%86%99%E5%9C%A8%E7%BB%93%E5%B0%BE" id="markdown-toc-写在结尾">写在结尾</a></li>
    

但这样操作过于繁琐。而且如果只是想暂存此文件,还需要接着修改,则希望保留Vim的工作状态,比如编辑历史,buffer状态等等,该怎么办?能不能在不退出Vim的情况下获得root权限来保存这个文件?

解决方案

答案是可以,执行这样一条命令即可:

:w !sudo tee %

接下来我们来分析这个命令为什么可以工作。首先查看文档:help :w,向下滚动一点可以看到:

							*:w_c* *:write_c*
:[range]w[rite] [++opt] !{cmd}
            Execute {cmd} with [range] lines as standard input
            (note the space in front of the '!').  {cmd} is
            executed like with ":!{cmd}", any '!' is replaced with
            the previous command |:!|.

The default [range] for the ":w" command is the whole buffer (1,$)

把这个使用方法对应前面的命令,如下所示:

:       w               !sudo tee %
|       |               |  |
:[range]w[rite] [++opt] !{cmd}

我们并未指定range,参见帮助文档最下面一行,当range未指定时,默认情况下是整个文件。此外,这里也没有指定opt

Vim中执行外部命令

接下来是一个叹号!,它表示其后面部分是外部命令,即sudo tee %。文档中说的很清楚,这和直接执行:!{cmd}是一样的效果。后者的作用是打开shell执行一个命令,比如,运行:!ls,会显示当前工作目录下的所有文件,这非常有用,任何可以在shell中执行的命令都可以在不退出Vim的情况下运行,并且可以将结果读入到Vim中来。试想,如果你要在Vim中插入当前工作路径或者当前工作路径下的所有文件名,你可以运行:

:r !pwd或:r !ls

此时所有的内容便被读入至Vim,而不需要退出Vim,执行命令,然后拷贝粘贴至Vim中。有了它,Vim可以自由的操作shell而无需退出。

命令的另一种表示形式

再看前面的文档:

Execute {cmd} with [range] lines as standard input

所以实际上这个:w并未真的保存当前文件,就像执行:w new-file-name时,它将当前文件的内容保存到另外一个new-file-name的文件中,在这里它相当于一个另存为,而不是保存。它将当前文档的内容写到后面cmd的标准输入中,再来执行cmd,所以整个命令可以转换为一个具有相同功能的普通shell命令:

$ cat readonly-file-name | sudo tee %

这样看起来”正常”些了。其中sudo很好理解,意为切换至root执行后面的命令,tee%是什么呢?

%的意义

我们先来看%,执行:help cmdline-special可以看到:

In Ex commands, at places where a file name can be used, the following
characters have a special meaning.  These can also be used in the expression
function expand() |expand()|.
    %	Is replaced with the current file name.		  *:_%* *c_%*

在执行外部命令时,%会扩展成当前文件名,所以上述的cmd也就成了sudo tee readonly-file-name。此时整个命令即:

$ cat readonly-file-name | sudo tee readonly-file-name

注意:在另外一个地方我们也经常用到%,没错,替换。但是那里%的作用不一样,执行:help :%查看文档:

Line numbers may be specified with:		*:range* *E14* *{address}*
    {number}	an absolute line number
    ...
    %		equal to 1,$ (the entire file)		  *:%*

在替换中,%的意义是代表整个文件,而不是文件名。所以对于命令:%s/old/new/g,它表示的是替换整篇文档中的old为new,而不是把文件名中的old换成new。

tee的作用

现在只剩一个难点: tee。它究竟有何用?维基百科上对其有一个详细的解释,你也可以查看man page。下面这幅图很形象的展示了tee是如何工作的:

tee的作用

ls -l的输出经过管道传给了tee,后者做了两件事,首先拷贝一份数据到文件file.txt,同时再拷贝一份到其标准输出。数据再次经过管道传给less的标准输入,所以它在不影响原有管道的基础上对数据作了一份拷贝并保存到文件中。看上图中间部分,它很像大写的字母T,给数据流动增加了一个分支,tee的名字也由此而来。

现在上面的命令就容易理解了,tee将其标准输入中的内容写到了readonly-file-name中,从而达到了更新只读文件的目的。当然这里其实还有另外一半数据:tee的标准输出,但因为后面没有跟其它的命令,所以这份输出相当于被抛弃。当然也可以在后面补上> /dev/null,以显式的丢弃标准输出,但是这对整个操作没有影响,而且会增加输入的字符数,因此只需上述命令即可。

命令执行之后

运行完上述命令后,会出现下面的提示:

W12: Warning: File "readonly-file-name" has changed and the buffer was changed in Vim as well
See ":help W12" for more info.
[O]K, (L)oad File:

Vim提示文件更新,询问是确认还是重新加载文件。建议直接输入O,因为这样可以保留Vim的工作状态,比如编辑历史,buffer等,撤消等操作仍然可以继续。而如果选择L,文件会以全新的文件打开,所有的工作状态便丢失了,此时无法执行撤消,buffer中的内容也被清空。

更简单的方案:映射

上述方式非常完美的解决了文章开始提出的问题,但毕竟命令还是有些长,为了避免每次输入一长串的命令,可以将它映射为一个简单的命令加到.vimrc中:

" Allow saving of files as sudo when I forgot to start vim using sudo.

cmap w!! w !sudo tee > /dev/null %

这样,简单的运行:w!!即可。命令后半部分> /dev/null在前面已经解释过,作用为显式的丢掉标准输出的内容。

另一种思路

至此,一个比较完美但很tricky的方案已经完成。你可能会问,为什么不用下面这样更常见的命令呢?这不是更容易理解,更简单一些么?

:w !sudo cat > %

重定向的问题

我们来分析一遍,像前面一样,它可以被转换为相同功能的shell命令:

$ cat readonly-file-name | sudo cat > %

这条命令看起来一点问题没有,可一旦运行,又会出现另外一个错误:

/bin/sh: readonly-file-name: Permission denied

shell returned 1

这是怎么回事?不是明明加了sudo么,为什么还提示说没有权限?稍安勿躁,原因在于重定向,它是由shell执行的,在一切命令开始之前,shell便会执行重定向操作,所以重定向并未受sudo影响,而当前的shell本身也是以普通用户身份启动,也没有权限写此文件,因此便有了上面的错误。

重定向方案

这里介绍了几种解决重定向无权限错误的方法,当然除了tee方案以外,还有一种比较方便的方案:以sudo打开一个shell,然后在该具有root权限的shell中执行含重定向的命令,如:

:w !sudo sh -c 'cat > %'

可是这样执行时,由于单引号的存在,所以在Vim中%并不会展开,它被原封不动的传给了shell,而在shell中,一个单独的%相当于nil,所以文件被重定向到了nil,所有内容丢失,保存文件失败。

既然是由于%没有展开导致的错误,那么试着将单引号'换成双引号"再试一次:

:w !sudo sh -c "cat > %"

成功!这是因为在将命令传到shell去之前,%已经被扩展为当前的文件名。有关单引号和双引号的区别可以参考这里,简单的说就是单引号会将其内部的内容原封不动的传给命令,但是双引号会展开一些内容,比如变量,转义字符等。

当然,也可以像前面一样将它映射为一个简单的命令并添加到.vimrc中:

" Allow saving of files as sudo when I forgot to start vim using sudo.

cmap w!! w !sudo sh -c "cat > %"

注意:这里不再需要把输出重定向到/dev/null中。

写在结尾

至此,借助Vim强大的灵活性,实现了两种方案,可以在以普通用户启动的Vim中保存需root权限的文件。两者的原理类似,都是利用了Vim可以执行外部命令这一特性,区别在于使用不同的shell命令。如果你还有其它的方案,欢迎给我留言。

无源码调试android应用,网传三种办法:

1 Xposed 有一个插件 XInstaller 可以实现对任意进程进行调试

2 将ro.debuggable值改为1

3 将应用反编译后,修改 AndroidManifest.xml 将 android:debuggable 设置为 true,再重新签名

第一种方法最简单;

第二种方法很麻烦,由于ro.debuggable是只读的,要修改好像是需要刷boot.img修改 init.prop,具体我没操作过,不过可以在网上找现成的刷机包,鉴于我本身就在手机公司工作,所以这个方法对我来说最方便,自己build一个整机包即可;

第三种需要反编译,十分困难,伴随着很多不可预知的问题。

emacs操作简介

文件编辑

emacs是由GNU组织的创始人Richard Stallman开发的一个功能强大的全屏文本编辑器,它支持多种编程语言,具有很多优良的特性。有众多的系统管理员和软件开发者使用emacs。

语法

emacs(选项)(参数)

选项

+<行号>:启动emacs编辑器,并将光标移动到制定行号的行;
-q:启动emacs编辑器,而不加载初始化文件;
-u<用户>:启动emacs编辑器时,加载指定用户的初始化文件;
-t<文件>:启动emacs编辑器时,把指定的文件作为中端,不适用标准输入(stdin)与标准输出(stdout);
-f<函数>:执行指定lisp(广泛应用于人工智能领域的编程语言)函数;
-l<lisp代码文件>:加载指定的lisp代码文件;
-batch:以批处理模式运行emacs编辑器。

参数

文件:指定要编辑的文本文件。

emacs命令操作大全

基本命令

C-x C-c : 退出Emacs
C-x C-f : 打开一个文件,如果文件不存在,则创建一个文件
C-g : 取消未完成的命令

编辑

C-z (redefined): Undo;原来C-z是挂起Emacs(然后用fg命令调出);C-x u 是默认的命令; 移动一下光标,再C-z就可以redo
M-d : 删除光标后的词语

移动光标

C-v : 向前翻页
M-v : 向后翻页
M-r : 将光标移动到屏幕中间那行
C-a : 移到行首
M-a : 移到句首,从行首到句首之间可能有空格
C-e : 移到行尾
M-e : 移到句尾
M-{ : 向上移动一段
M-} : 向下移动一段
C-right : 向前移动一个单词
C-left : 向后移动一个单词
C-up : 向前移动一段
C-down : 向后移动一段
M-< : 移到整个文本开头
M-> : 移到整个文本末尾
C-u 数字 命令 : 执行多次(数字表示次数)该命令;"M-数字 命令" 也可以
M-x goto-line : 移动到某一行
C-l : 重绘屏幕,效果就是当前编辑行移动窗口中央

Buffer 相关

C-x k : 关闭当前buffer
C-x b : 切换到前一个编辑的buffer
C-x C-b : 列出当前所有buffer
C-x C-s : 保存当前buffer
C-x s : 保存所有未保存的buffer,会提示你是否需要保存
C-x C-w : 文件另存为

拷贝与粘贴

M-space (redefined): 设置mark; C-@ 是默认命令
C-w (redefined) : 剪切一块区域;如果没有设置mark,则是剪切一行
M-w (redefined) : 拷贝一块区域;如果没有设置mark, 则是拷贝一行
C-k : 从当前位置剪切到行尾
C-y : 粘贴
M-y : 用C-y拉回最近被除去的文本后,换成 M-y可以拉回以前被除去的文本。键入多次的M-y可以拉回更早以前被除去的文本。
C-x r k : 执行矩形区域的剪切
C-x r y : 执行矩形区域的粘贴
窗口操作
C-x 0 : 关闭当前窗口
C-x 1 : 将当前窗口最大化
C-x 2 : 垂直分割窗口
C-x 3 : 水平分割窗口
M-o (redefined) : 在窗口之间切换; C-x o 是默认命令
C-x 5 1/2/3/0 : 对frame类似的操作
C-x < : 窗口内容右卷
C-x > : 窗口内容左卷(这两个命令在垂直分割窗口后比较有用)
(C-u) C-x ^ : 加高当前窗口,如果有C-u,则每次加高4行
(C-u) C-x } : 加宽当前窗口
(C-u) C-x { : 压窄当前窗口
ESC C-v : 在其它窗口进行卷屏操作

搜索和替换

C-s : 向前搜索(增量式搜索);连续C-s,跳到下一个搜索到的目标
C-s RET : 普通搜索
C-r : 向前搜索
C-s RET C-w : 按单词查询
M-% : 查询替换,也就是替换前会询问一下
M-x replace-string : 普通替换

Tags

M-! etags .c .h : 创建TAGS文件
M-. : 跳到tag所在位置
M-x list-tags : 列出tags

书签

C-x r m : 设置书签bookmark
C-x r b : 跳到bookmark处

帮助

C-h ? : 查看帮助信息
C-h f : 查看一个函数
C-h v : 查看一个变量
C-h k : 查看一个键绑定 (C-h c 也是查看键绑定,但是信息较简略)
C-h C-f : 查看一个函数的info,非常有用
C-h i : 看Info

其它

C-M-\ : 对选中区域,按照某种格式(比如C程序)进行格式化
C-x h : 全部选中
M-! : 执行外部shell命令
M-x shell : 模拟shell的buffer
M-x term : 模拟terminal, C-c k 关闭terminal
C-x C-q : 修改buffer的只读属性

 

想要实现一个可拖动的悬浮窗,原理上非常简单:根据action_move时的event坐标偏移,去修改view的位置即可。
但是实际实现上踩了个小坑。

详细踩坑:

平时获取event的坐标,都是用event.getX() 和 event.getY() ,但是实操下来发现,这样会导致拖动效果不跟手, 拖动的位置比手势移动的位置要小,并且会有明显的位置抖动。

在这个拖动的场景下,其实应该使用 event.getRawX() 和 event.getRawY() 来获取坐标。

event.getX() 和 event.getRawX() 的区别:

1
2
3
4
5
6
7
8
9
/**
* {@link #getX(int)} for the first pointer index (may be an
* arbitrary pointer identifier).
*
* @see #AXIS_X
*/
public final float getX() {
return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* Returns the original raw X coordinate of this event. For touch
* events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views.
*
* @see #getX(int)
* @see #AXIS_X
*/
public final float getRawX() {
return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}

getRawX()使用的是相对屏幕的坐标,而 getX()使用的是相对view的坐标,在view本身随手势移动的时候,这个相对view的坐标就会发生错误的偏移,导致拖动效果不跟手。

实际代码

1
2
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
binding.root.setOnTouchListener(object : View.OnTouchListener {
var xDown = 0f
var yDown = 0f

var interactorTranslationXWhenDown = 0f
var interactorTranslationYWhenDown = 0f

override fun onTouch(v: View?, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
xDown = event.rawX
yDown = event.rawY
interactorTranslationXWhenDown = interactorTranslationX
interactorTranslationYWhenDown = interactorTranslationY
}
MotionEvent.ACTION_MOVE -> {
val dx = event.rawX - xDown
val dy = event.rawY - yDown
interactorTranslationX =
interactorTranslationXWhenDown + dx
interactorTranslationY =
interactorTranslationYWhenDown + dy
Timber.e("dx:${dx} dy:${dy}")
updateTranslation()
}
else -> {}
}
return false
}

})

注:如果想写的简单一点,也可以直接使用

1
2
interactorTranslationX = event.rawX
interactorTranslationY = event.rawY

这样也能实现基本的拖动效果,只是view会跳到手指右下角的位置来拖动,有点难用。

ContentProvider的onCreate的调用时机介于Application的attachBaseContext和onCreate之间。

利用这个机制,可以在ContentProvider的onCreate中做某些初始化逻辑,这样写的sdk,就不需要app方主动调用初始化也可以完成自动初始化。

1
2
3
4
5
<provider
android:name="com.xxx.xxxProvider"
android:authorities="${applicationId}"
android:multiprocess="true"
android:exported="false"/>
1
2
3
4
5
@Override
public boolean onCreate() {
XXX.init();
return true;
}

查看原有VM实例信息

首先打开https://console.cloud.google.com/compute/instances查看我们GCP中的VM实例
在这里插入图片描述
点击实例名进入详情
可以看到我们的CPU 内存 磁盘 等信息。
在这里插入图片描述
在这里插入图片描述
ssh上去,用df和lsblk查看的输出如下。
在这里插入图片描述

创建快照

在调整配置之前,先创建快照。以备不测。https://console.cloud.google.com/compute/snapshots
创建了快照,之后就能随时通过这个快照创建出实例。
在这里插入图片描述

调整磁盘大小

点击磁盘的名称来进入磁盘的详情页
在这里插入图片描述
点击修改,然后输入要改变成的磁盘大小(本例中从10G 改为20G),保存。
在这里插入图片描述
这时,用df查看磁盘信息没有任何变化。用lsblk查看,分区中表中sda磁盘的大小已经变为了20G,但是sda1分区还是只有10G。
在这里插入图片描述
根据GCP文档中所说。

使用最新版本公用映像的实例可以在系统重新启动后自动调整其分区和文件系统的大小。SUSE Linux Enterprise Server (SLES) 公共映像是唯一不支持此功能的映像。

我使用的是Ubuntu 16.04.5的公共映像,所以可以使用这个特性,直接sudo reboot重启VM实例。

再次查看,磁盘信息已经发生变化。非常方便。
在这里插入图片描述
如果是不能自动进行文件系统扩容的系统,可参照https://cloud.google.com/compute/docs/disks/add-persistent-disk#resize_partitions进行文件系统配置

修改cpu和内存

照例先ssh上去,看一下原有的配置,使用lscpu来查看cpu信息,使用free查看内存信息。
在这里插入图片描述

修改cpu和内存,需要虚拟机处于关机状态,首先点击停止来关机。
在这里插入图片描述
然后点击修改。我这里cpu改为第二代,2个vCPU,2G内存。
在这里插入图片描述
修改完成后,点击启动来重新开机。

再次用lscpu和free命令来查看配置,已经能看到修改已经生效了。
在这里插入图片描述

修改IP地址

查看VM实例的地方,就可以看到当前IP地址
在这里插入图片描述
要修改IP地址的话,需要到导航菜单里,点击”VPC网络“下的”外部IP地址“标签。
在这里插入图片描述
点击更改。
在这里插入图片描述
在弹出的弹框里,把关联的VM实例选择为”无“,点击确定。
在这里插入图片描述
然后点击释放静态地址。
在这里插入图片描述

使用新的地址,要点击”保留静态地址“。随便自定义一个名称,然后选择VM实例所在的区域,再在附加目标里选择VM实例,保存即可。
在这里插入图片描述

这就完成了新IP地址的分配,绑定。

最近找了几个比较大的热点分析平台,试用对比了一下。

  1. 谷歌趋势https://trends.google.com/trends/?geo=CN
  2. 蝉大师https://www.chandashi.com/aso/keywordrank.html
  3. 百度搜索风云榜http://top.baidu.com/?vit=1&fr=toppopulation
  4. 微博received排行榜http://www.sina.com.cn/mid/search-list.shtml

谷歌和禅大师非常好用,能获取到一些有用的信息。而百度和微博的热词平台,完全看不到有用信息!

谷歌趋势

我想找一下app的热点信息,就直接在搜索框里选输入app来搜索。
就会列出来相关的主题和搜索词。
https://trends.google.com/trends/explore?date=now%207-d&geo=CN&q=app
谷歌趋势app热点
这里要注意选一下搜索的地区和时间,还有热门或搜索量上升的切换。
地区我会在中国和香港两个区域都搜索看看,感觉香港的热点变化更敏感一点。
时间如果选的太短,就很难看出搜索量上升,如果时间选的太长,就找不到最近的热点信息,我觉得7天的时间范围比较合适。

比如上面我搜索了七天里中国搜索量上升的内容,最近两天火爆的ZAO赫然在列,说明还是这个平台还是很准的。

谷歌趋势 Google Trends 的7个使用方法,很多人都不知道

蝉大师

蝉大师的使用就更方便了,直接能爬取应用商店内的信息,相当强大,又很直观。
https://www.chandashi.com/aso/keywordrank.html

蝉大师应用榜单
只是这里的都是应用商店内已有的内容,想要抓机会、找蓝海就不行了。

不过蝉大师也提供的实时热搜的功能,并且能看到对应的应用, 从里面找到目前还没有精准对应的应用,也还是有一部分市场空间的。
https://www.chandashi.com/aso/hotsearch.html
蝉大师实时热搜

百度搜索风云榜

http://top.baidu.com/?vit=1&fr=toppopulation

看似内容很多,但是根本找不到有价值的信息……
百度搜索风云榜

微博热搜

http://www.sina.com.cn/mid/search-list.shtml
看起来内容都挺准的,但是受微博本身内容的影响,大都是一些娱乐相关的,可能娱记会觉得很好,但是对于我这种做技术的来说,真是完全不感兴趣了。
微博热搜

最近发现一个匿名免费的云盘平台 anonfile。感觉说不定什么时候可能就用得着,在这里记录一下。
https://anonfile.com/

匿名上传您的文件,并在AnonFiles上免费上传
我们为您提供20 GB的文件大小限制和无限带宽

虽说官网写的是20GB的大小限制,但是既然是匿名的,那就没办法统计每个用户上传的大小,所以我猜这个20GB的限制,应该也是不存在的。
唯一的缺点就是网速太慢,即使挂上梯子也没快多少。

截图

这个网站还有面向开发者的上传api。

我试了一下,非常简单.

1
2
curl -F "file=@Downloads/timg.jpeg" https://anonfile.com/api/upload
{"status":true,"data":{"file":{"url":{"full":"https://anonfile.com/U4WbBb44n7/1_jpg","short":"https://anonfile.com/U4WbBb44n7"},"metadata":{"id":"U4WbBb44n7","name":"1.jpg","size":{"bytes":37201,"readable":"37.2 KB"}}}}}%

打开full或者short对应的链接就能访问:https://anonfile.com/v0XbBb47n3/timg_jpeg

API文档原文如下:

API Documentation
Want to upload straight to your account using our API? Please login for account API keys and more information on how to upload files to your account using our API.

UPLOAD
You send us files with POST and we will store them and return an url for you in JSON.

1
2
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
Request Example
curl -F "[email protected]" https://anonfile.com/api/upload
Successful Response
{
"status": true,
"data": {
"file": {
"url": {
"full": "https://anonfile.com/u1C0ebc4b0/file.txt",
"short": "https://anonfile.com/u1C0ebc4b0"
},
"metadata": {
"id": "u1C0ebc4b0",
"name": "file.txt",
"size": {
"bytes": 6861,
"readable": "6.7 KB"
}
}
}
}
}
Error Response
{
"status": false,
"error": {
"message": "The file is too large. Max filesize: 5 GiB",
"type": "ERROR_FILE_SIZE_EXCEEDED",
"code": 31
}
}
Error Codes & Types
(10) ERROR_FILE_NOT_PROVIDED
(11) ERROR_FILE_EMPTY
(12) ERROR_FILE_INVALID
(20) ERROR_USER_MAX_FILES_PER_HOUR_REACHED
(21) ERROR_USER_MAX_FILES_PER_DAY_REACHED
(22) ERROR_USER_MAX_BYTES_PER_HOUR_REACHED
(23) ERROR_USER_MAX_BYTES_PER_DAY_REACHED
(30) ERROR_FILE_DISALLOWED_TYPE
(31) ERROR_FILE_SIZE_EXCEEDED
(32) ERROR_FILE_BANNED
(40) STATUS_ERROR_SYSTEM_FAILURE
INFO
You can send us a GET request to grab info about a file. We respond with a JSON array.
Hint: Use the "status" key (true/false) if you want to check if a file exists.
Also, if the requested file does not exist, the response will be sent as a 404.

The URL format is:

https://anonfile.com/api/v2/file/{id}/info
Example Request
curl https://anonfile.com/api/v2/file/u1C0ebc4b0/info
INFO Response
{
"status": true,
"data": {
"file": {
"url": {
"full": "https://anonfile.com/u1C0ebc4b0/file.txt",
"short": "https://anonfile.com/u1C0ebc4b0"
},
"metadata": {
"id": "u1C0ebc4b0",
"name": "file.txt",
"size": {
"bytes": 6861,
"readable": "6.7 KB"
}
}
}
}
}
Info Error Response
{
"status": false,
"error": {
"message": "The file you are looking for does not exist.",
"type": "FILE_NOT_FOUND",
"code": 404
}
}

  • 另外还有个它的关联网站,写着”Visit our friends: BayFiles“。看起来和anonfile除了样式上不一样以外,其他全都一样,可能只是改了下css就发布到另一个服务器上了。

我们在使用git中,经常会遇到这样的情况:

git仓库中有某个必要的配置文件,这个文件应该存在,但是在每个开发人员的本地都要对他进行修改。这就导致这个文件要被反复修改,容易冲突。要想不冲突,就要每个人提交前,都刻意不提交该文件,很是麻烦。

一般我们这时候就会想到,用.gitignore来忽略这个文件,但是该文件是已经提交过的,并且对于工程是必要的,应该保存在git中的。.gitignore对于这种已提交过的文件是无能为力的。

 

这时候,就需要使用` git update-index --skip-worktree `命令了。

该命令的作用是,让git在搜索文件列表时,忽略某个文件,这样该文件即使有修改,git也不会关心。

#例子:
git update-index --skip-worktree web/main.dart

使用这个命令,时间久了,可能会忘记自己忽略过哪些文件,这时候可以使用` git ls-files -v . | grep "^S" `命令找出来忽略过的文件。

#例子:
git ls-files -v . | grep "^S"

#输出:S web/main.dart

不想继续忽略该文件时,使用` git update-index --no-skip-worktree `命令,来让git不再忽略该文件。

#例子:
git update-index --no-skip-worktree web/main.dart