Android 电视盒子可用的应用备份

用盒子也已经很多年了,几年来家里,自己用,也积累了一些常用的应用。这两天又拿到了 T1 盒子,又才想起来整理这样一份单子,这样不用每一次都一遍一遍的尝试了。记得以前 VST ,泰捷视频都还很不错的时候,再后来广电发了禁令,再后来这片市场混乱发展,各家大型网站优酷,爱奇艺又不敢公开大搞,却又在背后偷偷摸摸。再到现在几乎被什么芒果,CIBN垄断,内容没什么可看,却什么都要收费。我始终抱有一个观点,如果电视盒子这一块开放发展,国内的厂家完全能够占领全世界的盒子市场,好几年前用的 Android 盒子就已经能够满足我的大部分需求,并且应用设计也早 Google 自己推出 Android TV 盒子以及规范 Android TV 应用好多年。可惜这一块市场被一道禁令打到了地下。

Android 盒子安装应用的方法,大概可以分为这几个:

  • 局域网拷贝 APK
  • 用 U 盘
  • 沙发管家,或者当贝市场中安装
  • 局域网 adb 安装

当然对于一个新的设备,通过 adb 安装一个应用市场,然后通过应用市场下载其他应用是最简单的方式。

市场

Apple 有 App Store, Google 有 Play Store,电视盒子直到现在依然还在乱斗:

  • 奇珀市场 http://down.7po.com/
  • 沙发管家 http://www.shafa.com/
  • 当贝市场 智能电视应用市场

地址就不都给了,Google 搜一下很快。

直播

电视直播的应用,以前用 VST 和 泰捷还行,不过现在已经废了,然后现在 HDP 做的也还不错。

2018年3月3号更新,发现了一款叫做 超级直播 的应用,非常好用。使用超级直播的时候,在播放页面点击设置,然后选择 “二维码扫一扫开启更多功能”,并在二维码显示之后,连续多次点击出现的二维码多次,即可解锁隐藏功能。隐藏功能开启6000以后的频道,包括可以查看多个台湾、香港的频道,频道范围在 6xxx 可能上下有些偏差,65xx 开始会有抢先观看的电影。

一下排名按照易用程度:

  • 超级直播
  • HDP直播
  • 直播狗
  • 小薇直播
  • 电视家3.0

放一张截图

斐讯t1_直播

网络视频

网络视频是我用的最多的了,我本人用 哔哩哔哩 最多

  • bilibili
  • 银河奇异果
  • 云视听极光
  • CIBN 高清影视
  • CIBN 聚精彩
  • VST
  • 泰捷

本地视频

盒子带 samba ,能读局域网内 samba 共享的视频,那就需要一个本地播放器,最好支持的解码格式越多越好,在 Android 手机上我买过 MX Player Pro,不过免费的 KMPlayer 也不错。

  • ES 文件管理器
  • 小白文件管理器
  • MX Player Pro

系统工具

说到乐播投屏这个应用,还是我去实体店,然后有一个店员向我展示一个只有魔方大小的投影仪时,用的应用,将手机的屏幕投影到投影仪上,我就记住了这个投屏应用,回来发现,iOS 投屏还是不错的。

  • 乐播投屏
  • 悟空遥控

在 Android TV 上使用 YouTube

今天突然想到,我平时看的最多的 YouTube ,是有电视版的啊,最近又把路由器更新了一下,局域网使用 YouTube 完全没问题啊,然后就下载了几个 YouTube for TV 的应用,发现只有下面这一个不需要依赖 Play Service,然后,在侧边栏设置中,有一个关联码,和手机关联,然后就可以非常轻松的将手机上的 YouTube 视频投送到电视盒子上,然后再到投影仪上。太舒服了。

YouTube for TV

版本:1.12.10 发布时间:2018-01-12

下载地址: https://apkpure.com/youtube-tv-watch-record-live-tv/com.google.android.apps.youtube.unplugged


2018-01-13 android , tv , adb , packages

okhttp 使用

OkHttp是一个非常高效的HTTP客户端,默认情况下:

  • 支持HTTP/2,允许对同一主机的请求共用一个套接字。
  • 如果HTTP/2 不可用,连接池会减少请求延迟。
  • 透明的GZIP可以减少下载流量。
  • 响应的缓存避免了重复的网络请求。

同步 GET 方法

阻塞请求

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    Headers responseHeaders = response.headers();
    for (int i = 0; i < responseHeaders.size(); i++) {
      System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
    }

    System.out.println(response.body().string());
}

异步 GET 请求

响应可读时回调 Callback 接口,读取响应时阻塞当前线程

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();

    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Request request, Throwable throwable) {
        throwable.printStackTrace();
      }

      @Override public void onResponse(Response response) throws IOException {
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

        Headers responseHeaders = response.headers();
        for (int i = 0; i < responseHeaders.size(); i++) {
          System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
        }

        System.out.println(response.body().string());
      }
    });
}

POST 提交信息

public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    String postBody = ""
        + "Releases\n"
        + "--------\n"
        + "\n"
        + " * _1.0_ May 6, 2013\n"
        + " * _1.1_ June 15, 2013\n"
        + " * _1.2_ August 11, 2013\n";

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}

流方式提交请求体

流的方式提交请求体

public static final MediaType MEDIA_TYPE_MARKDOWN
      = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    RequestBody requestBody = new RequestBody() {
      @Override public MediaType contentType() {
        return MEDIA_TYPE_MARKDOWN;
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        sink.writeUtf8("Numbers\n");
        sink.writeUtf8("-------\n");
        for (int i = 2; i <= 997; i++) {
          sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
        }
      }

      private String factor(int n) {
        for (int i = 2; i < n; i++) {
          int x = n / i;
          if (x * i == n) return factor(x) + " × " + i;
        }
        return Integer.toString(n);
      }
    };

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(requestBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}

POST 提交文件

POST 方式提交文件

public static final MediaType MEDIA_TYPE_MARKDOWN
  = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    File file = new File("README.md");

    Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}

提交表单

public void run() throws Exception {
    RequestBody formBody = new FormEncodingBuilder()
        .add("search", "Jurassic Park")
        .build();
    Request request = new Request.Builder()
        .url("https://en.wikipedia.org/w/index.php")
        .post(formBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}

分块POST提交

private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
    // Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
    RequestBody requestBody = new MultipartBuilder()
        .type(MultipartBuilder.FORM)
        .addPart(
            Headers.of("Content-Disposition", "form-data; name=\"title\""),
            RequestBody.create(null, "Square Logo"))
        .addPart(
            Headers.of("Content-Disposition", "form-data; name=\"image\""),
            RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
        .build();

    Request request = new Request.Builder()
        .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
        .url("https://api.imgur.com/3/image")
        .post(requestBody)
        .build();

    Response response = client.newCall(request).execute();
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    System.out.println(response.body().string());
}

2018-01-12 okhttp

每天学习一个命令:awk 处理文本

awk 是一个强大的文本分析工具,它是 Linux 中功能强大的数据处理引擎之一,可能这么说非常抽象,可以理解 awk 非常轻松的处理比如每行都是相同格式的文本,比如日志,cvs 格式等等。相对于 grep 的查找,sed 的编辑,awk 在其对数据分析并生成报告时,显得尤为强大。本文只是简单的入门介绍,这个工具是什么,能够做什么,能够做到什么复杂的事情,更多强大的功能只能在日常使用中慢慢体会了。

当使用 awk 命令处理一个或者多个文件时,它会依次读取文件的每一行内容,然后对其进行处理,awk 命令默认从 stdio 标准输入获取文件内容,awk 使用一对单引号来表示一些可执行的脚本代码,在可执行脚本代码里面,使用一对花括号来表示一段可执行代码块,可以同时存在多个代码块。awk 的每个花括号内同时可以有多个指令,每一个指令用分号分隔,awk 其实就是一个脚本编程语言。

awk 命令和 sed 命令结构相同,通常情况下,awk 将每个输入行解释为一条记录而每一行中的内容(由空格或者制表符分隔)解释为每一个字段,一个或者多个连续空格或者制表符看做定界符。awk 中 $0 代表整个记录。

awk ' /MA/ { print $1 }' list

解释:打印包含 MA 的行中的第一个单词。再举一个具体的例子,比如

echo 'this is one world\nthat is another world' | awk '{print $1}'

那么输出就是 awk 处理之后的每一行第一个字符也就是

this
that

基本格式

awk 命令的基本格式

awk [options] 'script' file

options 这个表示一些可选的参数选项,script 表示 awk 的可执行脚本代码(一般被{} 花括号包围),这个是必须的。file 这个表示 awk 需要处理的文件,注意需要是纯文本文件(意味着 awk 能够处理)。

awk 自定义分隔符

之前提到的,awk 默认的分割符为空格和制表符,awk 会根据这个默认的分隔符将每一行分为若干字段,依次用 $1, $2,$3 来表示,可以使用 -F 参数来指定分隔符

awk -F ':' '{print $1}' /etc/passwd

解释:使用 -F 来改变分隔符为 : ,比如上面的命令将 /etc/passwd 文件中的每一行用冒号 : 分割成多个字段,然后用 print 将第 1 列字段的内容打印输出

在 awk 中同时指定多个分隔符,比如现在有这样一个文件 some.log 文件内容如下

Grape(100g)1980
raisins(500g)1990
plum(240g)1997
apricot(180g)2005
nectarine(200g)2008

现在我们想将上面的 some.log 文件中按照 “水果名称(重量)年份” 来进行分割

$ awk -F '[()]' '{print $1, $2, $3}' some.log
Grape 100g 1980
raisins 500g 1990
plum 240g 1997
apricot 180g 2005
nectarine 200g 2008

-F 参数中使用一对方括号来指定多个分隔符,awk 处理 some.log 文件时就会使用 “(“ 或者 “)” 来对文件的每一行进行分割。

awk 内置变量的使用

awk 除了 $ 和数字表示字段还有一些其他的内置变量:

  • $0 这个表示文本处理时的当前行,$1 表示文本行被分隔后的第 1 个字段列,$2 表示文本行被分割后的第 2 个字段列,$3 表示文本行被分割后的第 3 个字段列,$n 表示文本行被分割后的第 n 个字段列
  • NR 表示文件中的行号,表示当前是第几行
  • NF 表示文件中的当前行被分割的列数,可以理解为 MySQL 数据表里面每一条记录有多少个字段,所以 $NF 就表示最后一个字段,$(NF-1) 就表示倒数第二个字段
  • FS 表示 awk 的输入分隔符,默认分隔符为空格和制表符,可以对其进行自定义设置
  • OFS 表示 awk 的输出分隔符,默认为空格,也可以对其进行自定义设置
  • FILENAME 表示当前文件的文件名称,如果同时处理多个文件,它也表示当前文件名称
  • RS 行分隔符,用于分割行,默认为换行符
  • ORS 输出记录的分隔符,默认为换行符

比如我们有这么一个文本文件 fruit.txt 内容如下,用它来演示如何使用 awk 命令工具

peach    100   Mar  1997   China
Lemon    150   Jan  1986   America
Pear     240   Mar  1990   Janpan
avocado  120   Feb  2008   china

awk '{print $0}' fruit.txt   # 表示打印输出文件的每一整行的内容
awk '{print $1}' fruit.txt   # 表示打印输出文件的每一行的第 1 列内容
awk '{print $1, $2}' fruit.txt

文件的每一行的每一列的内容除了可以用 print 命令打印输出以外,还可以对其进行赋值

awk '{$2 = "***"; print $0}' fruit.txt

上面的例子就是表示通过对 $2 变量进行重新赋值,来隐藏每一行的第 2 列内容,并且用星号 * 来代替其输出

在参数列表中加入一些字符串或者转义字符之类的东东

awk '{print $1 "\t" $2 "\t" $3}' fruit.txt

像上面这样,你可以在 print 的参数列表中加入一些字符串或者转义字符之类的东东,让输出的内容格式更漂亮,但一定要记住要使用双引号。

awk 内置 NR 变量表示每一行的行号

awk '{print NR "\t" $0}' fruit.txt

1   peach    100   Mar  1997   China
2   Lemon    150   Jan  1986   America
3   Pear     240   Mar  1990   Janpan
4   avocado  120   Feb  2008   china

awk 内置 NF 变量表示每一行的列数

awk '{print NF "\t" $0}' fruit.txt

5   peach    100   Mar  1997   China
5   Lemon    150   Jan  1986   America
5   Pear     240   Mar  1990   Janpan
5   avocado  120   Feb  2008   china

awk 中 $NF 变量的使用

awk '{print $NF}' fruit.txt

上面这个 $NF 就表示每一行的最后一列,因为 NF 表示一行的总列数,在这个文件里表示有 5 列,然后在其前面加上 $ 符号,就变成了 $5 ,表示第 5 列

awk '{print $(NF - 1)}' fruit.txt

1997
1986
1990
2008

上面 $(NF-1) 表示倒数第 2 列, $(NF-2) 表示倒数第 3 列,依次类推。

awk 'NR % 6'        # 打印出了 6 倍数行之外的其他行
awk 'NR > 5'        # 打印第 5 行之后内容,类似 `tail -n +6` 或者 `sed '1,5d'`
awk 'NF >= 6'       # 打印大于等于 6 列的行
awk '/foo/ && /bar/'    # 打印匹配 `/foo/` 和 `/bar/` 的行
awk '/foo/ && !/bar/'   # 打印包含 `/foo/` 不包含 `/bar/` 的行
awk '/foo/ || /bar/'    # 或
awk '/foo/,/bar/'       # 打印从匹配 `/foo/` 开始的行到 `/bar/` 的行,包含这两行

awk 内置函数

awk 还提供了一些内置函数,比如

  • toupper() 用于将字符转为大写
  • tolower() 将字符转为小写
  • length() 长度
  • substr() 子字符串
  • sin() 正弦
  • cos() 余弦
  • sqrt() 平方根
  • rand() 随机数

更多的方法可以参考 man awk

awk 同时处理多个文件

awk '{print FILENAME "\t" $0}' demo1.txt demo2.txt

当你使用 awk 同时处理多个文件的时候,它会将多个文件合并处理,变量 FILENAME 就表示当前文本行所在的文件名称。

BEGIN 关键字的使用

在脚本代码段前面使用 BEGIN 关键字时,它会在开始读取一个文件之前,运行一次 BEGIN 关键字后面的脚本代码段, BEGIN 后面的脚本代码段只会执行一次,执行完之后 awk 程序就会退出

awk 'BEGIN {print "Start read file"}' /etc/passwd

awk 脚本中可以用多个花括号来执行多个脚本代码,就像下面这样

awk 'BEGIN {print "Start read file"} {print $0}' /etc/passwd

END 关键字使用方法

awk 的 END 指令和 BEGIN 恰好相反,在 awk 读取并且处理完文件的所有内容行之后,才会执行 END 后面的脚本代码段

awk 'END {print "End file"}' /etc/passwd
awk 'BEGIN {print "Start read file"} {print $0} END {print "End file"}' /etc/passwd

在 awk 中使用变量

可以在 awk 脚本中声明和使用变量

awk '{msg="hello world"; print msg}' /etc/passwd

awk 声明的变量可以在任何多个花括号脚本中使用

awk 'BEGIN {msg="hello world"} {print msg}' /etc/passwd

在 awk 中使用数学运算,在 awk 中,像其他编程语言一样,它也支持一些基本的数学运算操作

awk '{a = 12; b = 24; print a + b}' company.txt

上面这段脚本表示,先声明两个变量 a = 12 和 b = 24,然后用 print 打印出 a 加上 b 的结果。

请记住 awk 是针对文件的每一行来执行一次单引号 里面的脚本代码,每读取到一行就会执行一次,文件里面有多少行就会执行多少次,但 BEGIN 和 END 关键字后脚本代码除外,如果被处理的文件中什么都没有,那 awk 就一次都不会执行。

awk 还支持其他的数学运算符

+ 加法运算符
- 减法运算符
* 乘法运算符
/ 除法运算符
% 取余运算符

在 awk 中使用条件判断

比如有一个文件 company.txt 内容如下

yahoo   100 4500
google  150 7500
apple   180 8000
twitter 120 5000

如果要判断文件的第 3 列数据,也就是平均工资小于 5500 的公司,然后将其打印输出

awk '$3 < 5500 {print $0}' company.txt

上面的命令结果就是平均工资小于 5500 的公司名单,$3 < 5500 表示当第 3 列字段的内容小于 5500 的时候才会执行后面的 {print $0} 代码块

awk '$1 == "yahoo" {print $0}' company.txt

awk 还有一些其他的条件操作符如下

< 小于
<= 小于或等于
== 等于
!= 不等于
> 大于
>= 大于或等于
~ 匹配正则表达式
!~ 不匹配正则表达式

使用 if 指令判断来实现上面同样的效果

awk '{if ($3 < 5500) print $0}' company.txt

上面表示如果第 3 列字段小于 5500 的时候就会执行后面的 print $0

在 awk 中使用正则表达式

比如现在我们有这么一个文件 poetry.txt 内容如下:

This above all: to thine self be true
There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow
No matter how dark long, may eventually in the day arrival

使用正则表达式匹配字符串 “There” ,将包含这个字符串的行打印并输出

awk '/There/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow

使用正则表达式配一个包含字母 t 和字母 e ,并且 t 和 e 中间只能有任意单个字符的行

awk '/t.e/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow
No matter how dark long, may eventually in the day arrival

如果只想匹配单纯的字符串 “t.e”, 那正则表达式就是这样的 /t.e/ ,用反斜杠来转义 . 符号 因为 . 在正则表达式里面表示任意单个字符。

使用正则表达式来匹配所有以 “The” 字符串开头的行

awk '/^The/{print $0}' poetry.txt

在正则表达式中 ^ 表示以某某字符或者字符串开头。

使用正则表达式来匹配所有以 “true” 字符串结尾的行

awk '/true$/{print $0}' poetry.txt

在正则表达式中 $ 表示以某某字符或者字符串结尾。

awk '/m[a]t/{print $0}' poetry.txt

No matter how dark long, may eventually in the day arrival

上面这个正则表达式 /m[a]t/ 表示匹配包含字符 m ,然后接着后面包含中间方括号中表示的单个字符 a ,最后包含字符 t 的行,输出结果中只有单词 “matter” 符合这个正则表达式的匹配。因为正则表达式 [a] 方括号中表示匹配里面的任意单个字符。

继续上面的一个新例子如下

awk '/^Th[ie]/{print $0}' poetry.txt

这个例子中的正则表达式 /^Th[ie]/ 表示匹配以字符串 “Thi” 或者 “The” 开头的行,正则表达式方括号中表示匹配其中的任意单个字符。

再继续上面的新的用法

awk '/s[a-z]/{print $0}' poetry.txt

正则表达式 /s[a-z]/ 表示匹配包含字符 s 然后后面跟着任意 a 到 z 之间的单个字符的字符串,比如 “se”, “so”, “sp” 等等。

正则表达式 [] 方括号中还有一些其他用法比如下面这些

[a-zA-Z]  表示匹配小写的 a 到 z 之间的单个字符,或者大写的 A 到 Z 之间的单个字符
[^a-z]    符号 `^` 在方括号里面表示取反,也就是非的意思,表示匹配任何非 a 到 z 之间的单个字符

正则表达式中的星号 * 和加号 + 的使用方法,* 表示匹配星号前字符串 0 次或者多次,+ 和星号原理差不多,只是加号表示任意 1 个或者 1 个以上,也就是必须至少要出现一次。

正则表达式问号 ? 的使用方法,正则中的问号 ? 表示它前面的字符只能出现 0 次 或者 1 次。

正则表达式中的 {} 花括号用法,花括号 {} 表示规定它前面的字符必须出现的次数,像这个 /go{2}d/ 就表示只匹配字符串 “good”,也就是中间的字母 “o” 必须要出现 2 次。

正则表达式中的花括号还有一些其他的用法如下

/go{2,10}d/   表示字母 "o" 只能可以出现 2 次,3 次,4 次,5 次,6 次 ... 一直到 10 次
/go{2,}d/     表示字母 "o" 必须至少出现 2 次或着 2 次以上

正则表达式中的圆括号表示将多个字符当成一个完整的对象来看待。比如 /th(in){1}king/ 就表示其中字符串 “in” 必须出现 1 次。而如果不加圆括号就变成了 /thin{1}king/ 这个就表示其中字符 “n” 必须出现 1 次。

一些组合使用

使用 awk 过滤 history 输出,找到最常用的命令

history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head

过滤文件中重复行

awk '!x[$0]++' <file>

将一行长度超过 72 字符的行打印

awk 'length>72' file

查看最近哪些用户使用系统

last | grep -v "^$" | awk '{ print $1 }' | sort -nr | uniq -c

假设有一个文本,每一行都是一个 int 数值,想要计算这个文件每一行的和,可以使用

awk '{s+=$1} ENG {printf "%.0f", s}' /path/to/file

reference

  • http://www.ruanyifeng.com/blog/2018/11/awk.html

2018-01-12 linux , command , awk , ed , editor

斐讯 T1 盒子去除广告

斐讯投资送硬件这路子是着魔了,所以在斐讯 K2P 之后又入手了这个 T1 的盒子,配置 2G RAM,16G Sdcard,配置还不错。接口有 USB,网口,HDMI 口,还有 一个 AV 输出口,日常使用是没有任何问题的,初尝试一下非常流畅,不过让我不爽的是,第一次进入竟然需要验证手机,说是 CIBN 盒子的验证,如果不注册还不让进入。

配置

CPU:Amlogic S912 八核 Cortex-A53 CPU up to 2.0GHz GPU:ARM Mali-T820MP3 GPU up to 750MHz

2G RAM+16G ROM,双频 WIFI,且支持 ac,但只是单天线

删除不需要的应用

首先,进入盒子的设置 – 高级 – 远程调试打开,然后下载 adb, 开发过 Android 的人,或者大概熟悉 Android 的人一定用过这个命令,我之前也写过文章,总结 adb 常用的命令

将盒子通电,将盒子联网(可以使用网线,也可以使用无线网络),只要能够在局域网中有一个 IP 地址,然后进路由器里记录,找到盒子的 IP(192.168.x.x)地址。

adb connect 192.168.x.x        (盒子 IP)
adb shell
pm list packages # 可以用来查看安装的应用列表
pm uninstall -k --user 0 com.feixun.dangbeimarket     #自带 cibn 当贝市场
pm uninstall -k --user 0 com.tianci.ad                #广告
pm uninstall -k --user 0 com.feixun.qiyiguo           #自带爱奇艺
pm uninstall -k --user 0 cn.cibn.health               #健康中国
pm uninstall -k --user 0 com.pptv.tvsports.preinstall #CIBN 体育
pm uninstall -k --user 0 com.phicomm.phiweather       #天气
pm uninstall -k --user 0 com.pplive.atv               #聚精彩
pm uninstall -k --user 0 cn.cibntv.ott                #CIBN 高清影视
pm uninstall -k --user 0 com.phicomm.update           #斐讯 OTA 升级
pm uninstall -k --user 0 com.phicomm.datatracker      #斐讯数据收集

如果想要恢复最原始的状态,在设置选项里,恢复出厂设置,还是最原来的状态。

添加过滤规则

如果路由器支持过滤广告,可以添加如下两条规则:

asimgs.cp61.ott.cibntv.net
hoisin.coocaatv.com

安装应用

简单的方法,只通过遥控器操作,先用系统自带的当贝市场,找到“视频加速器”这个应用,安装然后运行,就会在视频加速器运行界面的下方,看到有“当贝市场”,再安装这个当贝市场。

如果你有需要安装本地文件的 APK 安装包,有两种方式,推荐使用 adb 方式安装,先将想要安装的 apk,下载到本地文件,然后 adb 连接盒子

adb connect <ip>
adb install /path/to/app.apk   # 安装本地路径下的应用

直接将本地的应用安装到盒子。

如果安装不成功,记得禁用安装验证,在 adb 连接的情况下

settings put global package_verifier_enable 0

或者用“悟空遥控器”这个应用,当贝市场里可以搜索它来安装,但也要在手机上下载安装“悟空遥控器”APP,里面有个“本地 APK 推送”,用它就可以安装你想要装的应用了,只要有 APK 安装包。

最后要强烈推荐一个投屏 App,乐播投屏,因为斐讯 T1 并没有 DLNA 的功能,但幸而只要一个 App 就能够解决,这样就能够将手机爱奇艺上的内容快速的投屏到盒子上。

斐讯 t1_home_screen

su 密码

恩山最近的帖子更新了 T1 su 的密码,记录一下

31183118

最后这篇文章 是我使用过并且觉得不错的 Android TV 的应用,可以参考。

reference


2018-01-10 android , box , adb , apk , ads , phicomm , 斐讯 , tv , 电视盒子

Linux 下查看内存使用

在 Linux 下,命令行就是一切,GUI 不是任何时候都可用的,所以掌握一定的常用命令,能够方便日常使用,比如查看进程,查看内存占用,等等,这篇文章就总结了一下 Linux 下查看当前系统占用内存的命令。

free

free 命令是最常用的查看系统使用内存及剩余可用内存的命令。free 命令能够显示系统使用和可用的物理内存,同时能够显示 swap 分区内容使用量。free 命令也能够显示被 kernel 使用的 buffers 和 caches。free 命令通过解析 /proc/meminfo 来收集信息。

free -m
              total        used        free      shared  buff/cache   available
Mem:          15911        8803        1100         886        6007        3750
Swap:         16246        3100       13146

-m 选项表示使用 MB 来显示数据,上面表示总共 15911 MB 内存,使用了 8803 M 内存。

A buffer is something that has yet to be “written” to disk. A cache is something that has been “read” from the disk and stored for later use.

上面的解释非常清晰, buffer 用于存放要输出到 disk 的数据,cache 是存放 disk 上读取的数据,这两者都为了提高 IO 性能由 OS 控制管理的。

还有一些其他比较有用的参数,比如 free -h 显示比较可读 h for human,还有 free -g 使用 GB 为单位显示信息。

/proc/meminfo

第二种方法就是直接读取 /proc/meminfo 文件, /proc 文件系统并不是真实的文件系统,不真正包含文件,他是虚拟文件系统,存储的是当前内核运行状态的一系列特殊文件。

运行命令 cat /proc/meminfo 检查其中的 MemTotal, MemFree, Buffers, Cached, SwapTotal, SwapFree 这几个字段,显示的内容和 free 命令是一样的。

cat /proc/meminfo
less /proc/meminfo
more/proc/meminfo
egrep --color 'Mem|Cache|Swap' /proc/meminfo

vmstat

通过 vmstat-s 选项来显示内存使用

vmstat -s
 16293800 K total memory
  9125168 K used memory
 10143256 K active memory
  2252796 K inactive memory
  1001072 K free memory
   746520 K buffer memory
  5421040 K swap cache
 16636924 K total swap
  3174096 K used swap
 13462828 K free swap
 33736485 non-nice user cpu ticks
    57413 nice user cpu ticks
 10985936 system cpu ticks
465486969 idle cpu ticks
  6378633 IO-wait cpu ticks
        0 IRQ cpu ticks
   114100 softirq cpu ticks
        0 stolen cpu ticks
 41745039 pages paged in
125109404 pages paged out
  1098642 pages swapped in
  2128871 pages swapped out
2199460184 interrupts
3279263642 CPU context switches
1514898192 boot time
   3676272 forks

前面几行就能看到内存使用。

vmstat 命令能够显示进程,内存,paging,block IO,traps,disks,和 cpu 的活动及使用状态。

top

top 命令通常被用来检查每一个进程的内存和 CPU 使用,但其实也可以用他来查看系统整个的内存使用,最上方的数据就能看到。

同样,可视化程度更高的 htop 也能够快速的查看到使用的内存

dmidecode

通过一下命令也可以查看,这个命令只能够查看到物理内存条的大小,但是可以提供给我们一个信息就是系统的总内存,是由两个 8G 内存组成,而不是由 8 个 2G 内存条组成。

sudo dmidecode -t 17

选项 -t--type 缩写

sudo dmidecode -t 17
# dmidecode 3.0
Getting SMBIOS data from sysfs.
SMBIOS 2.8 present.

Handle 0x001A, DMI type 17, 40 bytes
Memory Device
        Array Handle: 0x0019
        Error Information Handle: Not Provided
        Total Width: 64 bits
        Data Width: 64 bits
        Size: 8192 MB
        Form Factor: DIMM
        Set: None
        Locator: DIMM1
        Bank Locator: Not Specified
        Type: DDR4
        Type Detail: Synchronous
        Speed: 2400 MHz
        Manufacturer: 802C0000802C
        Serial Number: 12303315
        Asset Tag: 0F161300
        Part Number: 8ATF1G64AZ-2G3B1
        Rank: 1
        Configured Clock Speed: 2133 MHz
        Minimum Voltage: Unknown
        Maximum Voltage: Unknown
        Configured Voltage: 1.2 V

Handle 0x001B, DMI type 17, 40 bytes
Memory Device
        Array Handle: 0x0019
        Error Information Handle: Not Provided
        Total Width: 64 bits
        Data Width: 64 bits
        Size: 8192 MB
        Form Factor: DIMM
        Set: None
        Locator: DIMM2
        Bank Locator: Not Specified
        Type: DDR4
        Type Detail: Synchronous
        Speed: 2400 MHz
        Manufacturer: 802C0000802C
        Serial Number: 1230330E
        Asset Tag: 0F161300
        Part Number: 8ATF1G64AZ-2G3B1
        Rank: 1
        Configured Clock Speed: 2133 MHz
        Minimum Voltage: Unknown
        Maximum Voltage: Unknown
        Configured Voltage: 1.2 V

这个输出信息中可以看到电脑使用的两条 8G 的内存,类型 DDR4,速度是 2400 MHz.

下面这个命令也能够查看电脑上安装的内存条

sudo lshw -short -C memory
H/W path        Device     Class          Description
=====================================================
/0/0                       memory         64KiB BIOS
/0/18/15                   memory         128KiB L1 cache
/0/18/16                   memory         1MiB L2 cache
/0/18/17                   memory         8MiB L3 cache
/0/14                      memory         128KiB L1 cache
/0/19                      memory         16GiB System Memory
/0/19/0                    memory         8GiB DIMM Synchronous 2400 MHz (0.4 ns)
/0/19/1                    memory         8GiB DIMM Synchronous 2400 MHz (0.4 ns)
/0/19/2                    memory         [empty]
/0/19/3                    memory         [empty]
/0/100/1f.2                memory         Memory controller

GUI

同样可以使用 GUI 来查看,Gnome System Monitor 提供一个简单的界面显示系统进程,内存和文件系统。可以使用以下命令在终端开启

gnome-system-monitor

reference


2018-01-10 linux , memory , ram , free , command

Gson 使用

Gson 是 Google 发布的一个用于序列化和反序列化 json 的工具库,可以非常轻松的实现 json 到 java Object 的转变,也同样非常简单的可以将一个 Java 实例序列化为 json。

基本使用

Gson 最基本的使用方法,无非就是 toJson()fromJson() 两个函数,对于简单类,可以使用如下方式:

String json = gson.toJson(target); // serializes target to Json MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2

如果类中含有数组,会需要用到 toJson(Object, Type)fromJson(String, Type) 这两个方法:

Type listType = new TypeToken<List<String>>() {}.getType();
List<String> target = new LinkedList<String>();
target.add("blah");

Gson gson = new Gson();
String json = gson.toJson(target, listType);
List<String> target2 = gson.fromJson(json, listType);

属性重命名

Gson 默认情况下会使用 POJO 一致的属性名去解析和生成 json 字串,但是如果想要解析和生成的时候重命名字段,可以使用 @SerializedName 来重命名。

比如 json 中的字段叫做 email_address,而在 Java 类中,可以改为 emailAddress。这样,gson 在生成时会自动将 emailAddress 属性,改为 email_address

@SerializedName("email_address")
public String emailAddress;

如果 emailAddress 在 json 中还有其他的方式,也可以使用:

@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;

重新格式化序列化和反序列化的内容

在将 json 转化为 Java Object 的时候,可以自定义 Deserializer ,格式化其中的某一些字段,比如下面内容,将 dateOfBirth 修改了格式。

public class ActorGsonDeserializer implements JsonDeserializer<ActorGson> {
    private SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a");

    @Override
    public ActorGson deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext)
            throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();

        JsonElement jsonImdbId = jsonObject.get("imdbId");
        JsonElement jsonDateOfBirth = jsonObject.get("dateOfBirth");
        JsonArray jsonFilmography = jsonObject.getAsJsonArray("filmography");

        ArrayList<String> filmList = new ArrayList<>();
        if (jsonFilmography != null) {
            for (int i = 0; i < jsonFilmography.size(); i++) {
                filmList.add(jsonFilmography.get(i).getAsString());
            }
        }

        ActorGson actorGson = null;
        try {
            actorGson = new ActorGson(jsonImdbId.getAsString(), sdf.parse(jsonDateOfBirth.getAsString()), filmList);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return actorGson;
    }
}



Gson gson = new GsonBuilder()
        .registerTypeAdapter(ActorGson.class, new ActorGsonDeserializer())
        .create();

忽略某些字段

使用 transient 关键字

在 java 序列化是,一旦变量被 transient 修饰,变量将不再是持久化的一部分,变量内容在序列化后无法获得访问。同样如果在使用 Gson 序列化 json 的时候,添加关键字 transient 同样,Gson 也会忽略该字段:

private transient int id;

需要注意的是,如果一个 field 是 static 静态变量,gson 也会排除。Gson 在创建的时候可以使用 excludeFieldsWithModifiers 来指定排除的 field:

Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
    .create();

使用 @Expose 注解保留关心的字段

最早不知道 transient 关键字的时候,看文档中只写了 @Expose 注解,但其实效果是一样的。使用 @Expose 注解来保留关心的字段,其他不需要的字段可以不注解,同样能够达到效果。

private int id; // 忽略id
@Expose private String name;    //保留name

如果使用 @Expose 注解,那么则需要使用 GsonBuilder()

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

自定义 ExclusionStrategy 规则

如果有更加复杂的排除规则,比如某一批Field,或者指定的 Class 不需要 serialize ,可以使用 ExclusionStrategy 来自定规则。

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.jutils.gson.entity.Car;
import com.jutils.gson.entity.PersonStrategy;

/**
 * ExclusionStrategy
 *
 * A strategy (or policy) definition that is used to decide whether or not a field or top-level class should be serialized or deserialized as part of the JSON output/input.
 */
public class ExcluStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        // ignore id field
        if (f.getDeclaringClass() == PersonStrategy.class && f.getName().equals("id")) {
            return true;
        }
        return false;
    }

    @Override
    public boolean shouldSkipClass(Class<?> aClass) {
        if (aClass == Car.class) {
            return true;
        }
        return false;
    }
}

比如说上面这个,就忽略 PersonStrategy 类中的 Field “id”,还有 Car 类。如果使用这种方式,那么需要在构造 gson 时:

Gson gson = new GsonBuilder().setExclusionStrategies(new ExcluStrategy()).create();

实现自定义注解

如果熟悉了这个 ExclusionStrategy 就可以书写自己的注解。

注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

AnnotationExclusionStrategy 类

public class AnnotationExclusionStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes fieldAttributes) {
        return fieldAttributes.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> aClass) {
        return false;
    }
}

测试方法

@Test
public void annotationTest() {
    PersonAnnotation annotation = new PersonAnnotation(1L, "name", 10);
    Gson gson = new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();
    String s = gson.toJson(annotation);
    System.out.println(s);
}

reference


2018-01-09 java , gson , json , google

小米路由器 3G 开启 SSH 安装 MT 工具箱

下面是小米路由器折腾记录,包括开启 SSH,然后安装 MT 工具箱,主要是为了其中的两个插件,一个是去广告,一个是 SS 代理,不过附带竟然发现了 frp 插件,开心啊。下面就是具体的记录。

小米路由器刷入开发版

下载 开发版,在后台点击上传安装开发版的 bin,然后等待重启,完成开发版安装。

小米路由器开始 SSH

小米帐号绑定小米路由器,设置路由器可正常上网,并使用手机版小米 wifi 绑定路由器,然后在绑定小米账号的前提下,进入 https://d.miwifi.com/rom/ssh 这个网站,然后找到 SSH 登录的 root 密码,之后会用到。

工具包使用方法:小米路由器需升级到开发版 0.5.28 及以上,小米路由器 mini 需升级到开发版 0.3.84 及以上,小米路由器 3 即将支持。注意:稳定版不支持。 请将下载的工具包 bin 文件复制到 U 盘(FAT/FAT32 格式)的根目录下,保证文件名为 miwifi_ssh.bin; 断开小米路由器的电源,将 U 盘插入 USB 接口; 按住 reset 按钮之后重新接入电源,指示灯变为黄色闪烁状态即可松开 reset 键; 等待 3-5 秒后安装完成之后,小米路由器会自动重启,之后您就可以尽情折腾啦 :)

如果 Chrome 浏览器出现错误提示:”This site can’t be reached. d.miwifi.com refused to connect. ERR_CONNECTION_REFUSED”,需要手动将http替换为https

刷入 MT 工具箱

MT 工具箱是目前第三方插件里面最为方便易用的插件集合

KMS 服务器,VSFTP 服务器,VPN 服务器,远程管理,ARIA2,Koolproxy 广告过滤,阿呆喵广告过滤,Shadowsocks,webshell, frp 服务

Misstar Tools 2.0 工具箱安装,经过上面的几个步骤,开启 SSH 之后,使用 ssh root@192.168.31.1 来连接路由器,使用之前获取的 SSH root 密码登录,进去之后 passwd 修改 root 密码,以方便下一次使用,然后直接执行如下代码,就能安装 MT 工具箱。

wget http://www.misstar.com/tools/appstore/install.sh -O /tmp/install.sh && chmod a+x /tmp/install.sh && /tmp/install.sh

卸载

wget http://www.misstar.com/tools/uninstall.sh -O /tmp/uninstall.sh && chmod +x /tmp/uninstall.sh && /tmp/uninstall.sh

安装 Shadowsocks 科学上网插件的方法

在开启 SSH,并且安装 Misstar Tools 工具箱的前提下,有两种方法可以安装 Shadowsocks 插件,第一种就是使用备份的文件,传入路由器之后运行安装,第二种直接在安装页面修改页面内容,推荐使用第二种方法。

方法一:使用文件安装

  1. 将压缩包中两个文件传到路由器,放在同一目录,名字不要改。
  2. 执行:chmod +x ./install_ss & ./install_ss add
  3. 刷新路由器后台,科学上网插件已经出现了。
  4. 卸载的时候执行:chmod +x ./install_ss & ./install_ss del

from: http://bbs.xiaomi.cn/t-13765387

方法二:修改页面 ID

使用 Chrome 打开 MT 插件管理页面,使用开发者工具,定位页面中任意一个 安装 按钮,然后找到代码中的 id="ftp" 字样,修改为 id="ss" ,然后点安装,成功后会回到 MT 工具箱首页,配置使用即可。

刷其他固件

按照上面官方的步骤操作完路由器就已经获取 root 权限了,再使用 ssh 工具连接路由即可,建议在进行下一步操作之前备份原版分区文件

root@XiaoQiang:~# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 07f80000 00020000 "ALL"
mtd1: 00080000 00020000 "Bootloader"
mtd2: 00040000 00020000 "Config"
mtd3: 00040000 00020000 "Bdata"
mtd4: 00040000 00020000 "Factory"
mtd5: 00040000 00020000 "crash"
mtd6: 00040000 00020000 "crash_syslog"
mtd7: 00040000 00020000 "reserved0"
mtd8: 00400000 00020000 "kernel0"
mtd9: 00400000 00020000 "kernel1"
mtd10: 02000000 00020000 "rootfs0"
mtd11: 02000000 00020000 "rootfs1"
mtd12: 03580000 00020000 "overlay"
mtd13: 012a6000 0001f000 "ubi_rootfs"
mtd14: 030ec000 0001f000 "data"

查看 U 盘挂载的位置命令:

df -h

U 盘一般是 /extdisks/ 开头,后面的可能不一样,我的是: /extdisks/sda4/

备份小米路由器 3G 原版分区到文件,每行是一条命令,分别执行,最后一条可能会报错,可不用理会,最有用的是 mtd0-mtd4

dd if=/dev/mtd0 of=/extdisks/sda4/rom/ALL.bin
dd if=/dev/mtd1 of=/extdisks/sda4/rom/Bootloader.bin
dd if=/dev/mtd2 of=/extdisks/sda4/rom/Config.bin
dd if=/dev/mtd3 of=/extdisks/sda4/rom/Bdata.bin
dd if=/dev/mtd4 of=/extdisks/sda4/rom/Factory.bin
dd if=/dev/mtd5 of=/extdisks/sda4/rom/crash.bin
dd if=/dev/mtd6 of=/extdisks/sda4/rom/crash_syslog.bin
dd if=/dev/mtd7 of=/extdisks/sda4/rom/reserved0.bin
dd if=/dev/mtd8 of=/extdisks/sda4/rom/kernel0.bin
dd if=/dev/mtd9 of=/extdisks/sda4/rom/kernel1.bin
dd if=/dev/mtd10 of=/extdisks/sda4/rom/rootfs0.bin
dd if=/dev/mtd11 of=/extdisks/sda4/rom/rootfs1.bin
dd if=/dev/mtd12 of=/extdisks/sda4/rom/overlay.bin
dd if=/dev/mtd13 of=/extdisks/sda4/rom/ubi_rootfs.bin
dd if=/dev/mtd14 of=/extdisks/sda4/rom/data.bin

首先,下载 Breed 刷入不死 breed,

下载地址:https://breed.hackpascal.net/ (搜索 breed-mt7621-xiaomi-r3g.bin)

用命令下载,在电脑终端中运行

wget https://breed.hackpascal.net/breed-mt7621-xiaomi-r3g.bin

将文件传入路由器

scp breed-mt7621-xiaomi-r3g.bin root@192.168.31.1:/tmp/

进入路由器

ssh root@192.168.31.1
cd /tmp
mtd -r write breed-mt7621-xiaomi-r3g.bin Bootloader

机器会重新启动,指示灯变蓝,确保电脑设置为自动获取 IP 地址,使用网线连接。刷入成功后,断掉电源,用东西顶住复位键不松开,然后再接上电源等待 10 秒左右放开复位键,浏览器输入 http://192.168.1.1 即可进行 Breed Web 恢复界面:

如果要刷入其他固件,打开 Breed Web 恢复控制台,点击左侧“固件更新”,钩选“固件”,选择固件,再点“上传”

更多内容可查看: http://www.right.com.cn/forum/thread-161906-1-1.html

小米路由器的目录结构

小米路由器基本上沿用了 Linux 的目录结构,但是也有一些区别,在用了一段时间之后发现某些目录被写满了导致一些第三方服务无法开启,也是很恼人了。这里及列一下这些目录的作用,以便于清理。

/       根目录
bin    二进制可执行命令
boot   bootloader 启动相关
data   用户数据文件
dev    设备文件,驱动等等
etc    配置文件
extdisks        外置硬盘挂载点
lib     共享库
mnt     临时挂载点
opt     可选程序安装点
proc    系统内存映射虚拟目录,可用来获取系统信息
root    系统管理员主目录
sbin    系统管理命令
sys
userdisk    路由硬盘(一般为内置)
usr         存放应用程序和文件
tmp        临时存放点
www         浏览器网页存放区

reference


2018-01-07 router , xiaomi

斐讯 k2p 刷机

以前也整理过一篇 TP LINK MR12U 刷 Openwrt ,路由器刷机和手机刷机异曲同工。不过刷机需谨慎,稍有错误就有可能导致硬件损坏。前些时候入手了一个斐讯的 K2P,

大致分为几步,第一步开启 telnet,刷入 uboot,然后通过 uboot 刷入新固件。

因为新版本固件 V22.7.8.2版本及之后 需要使用 特殊工具 才能开启 telnet ,所以先要使用这个工具来开启 telnet。然后再使用 a大 的脚本安装 breed。

Opboot及Breed进入方法:

如果你当前是官改或其他第三方固件,请在opboot或breed输入:

  1. 计算机设置为自动获取IP,计算机网线连接K2P的任一LAN口
  2. K2P断电,按住K2P复位键,K2P开电,按住10秒后放开
  3. 访问http://192.168.1.1,可刷入K2P的任何版本
  4. 重新启动后建议K2P恢复一次出厂设置;

官改版本

地址: http://www.right.com.cn/forum/thread-221578-1-1.html

官改版本比较稳定,占用内存小,同时也带去广告,SS/SSR,内网穿透等等功能

潘多拉固件

地址: http://www.right.com.cn/forum/thread-216468-1-1.html

潘多拉版本,同时也支持 SS/SSR,去广告,定时重启等等功能

梅林固件

地址: http://www.right.com.cn/forum/thread-255053-1-1.html

  • 支持adbyby及koolproxy广告屏蔽
  • 支持最新版本ss及S-S R客户端
  • 支持webshell、kms、ngrok内网穿透、wol网络唤醒
  • 支持ssh、用户脚本、硬盘休眠、指示灯控制等

Openwrt 固件

地址: http://www.right.com.cn/forum/thread-240730-1-1.html

K2P基于openwrt最新稳定版chaos_calmer的固件

Padavan

荒野无灯 http://www.right.com.cn/forum/thread-218704-1-1.html 这个固件和原版固件的性能对比可以参考这一篇 http://www.acwifi.net/3173.html

下载地址:http://files.80x86.io/router/rom/K2P/

k2p 全套其他的固件,可以参考这个帖子 http://www.right.com.cn/forum/thread-254947-1-1.html

最后总结下,其实大部分的教程都能在恩山找到,在斐讯的论坛里面,找精华贴,能过滤很多杂乱的信息。并且通过 Google 关键词搜索也能够很快的找到答案。


2018-01-06 k2p , 路由器 , router , openwrt , linux , phicomm

CollectionUtils

Apache commons 包中 CollectionUtils 常用方法介绍。

org.apache.commons.collections.CollectionUtils;

将所有元素添加到一个集合

addAll(Collection<C> collection, C[] elements) 将后面的元素添加到集合中,或者是添加一个非空元素 addIgnoreNull(Collection<T> collection, T object)

Collate (整理)两个列表

将两个有序的列表合二为一,使用 collate() 方法

List<Customer> sortedList = CollectionUtils.collate(list1, list2);

将一个列表中一种类型的对象转变为另一组对象

使用 collect() 方法

Collection<Address> addressCol = CollectionUtils.collect(list1, 
  new Transformer<Customer, Address>() {
    public Address transform(Customer customer) {
        return customer.getAddress();
    }
});

判断是否包含

containsAll(Collection<?> coll1, Collection<?> coll2) 判断是否 coll2 中所有元素都在 coll1 中

containsAny(Collection<?> coll1, Collection<?> coll2) 判断 coll2 中有没有至少一个元素在 coll1 中

CollectionUtils.isSubCollection(list3, list1) 可以使用该函数来判断两个集合是否是包含关系。

过滤元素

使用 CollectionUtils.filter 可以用来过滤 Collection 中不满足条件的元素。这个函数接收两个参数,第一个参数为列表,第二个参数为 Predicate 用来设置条件。

boolean isModified = CollectionUtils.filter(linkedList1, 
  new Predicate<Customer>() {
    public boolean evaluate(Customer customer) {
        return Arrays.asList("Daniel","Kyle").contains(customer.getName());
    }
});

filter() 函数是当 Predicate 返回 false 时移除元素;filterInverse() 函数是当 Predicate 返回 true 时移除元素。

如果想要获取过滤的结果,可以使用selectselectRejected 这两个函数。

判断集合是否为空

CollectionUtils.isEmpty(null): true
CollectionUtils.isEmpty(new ArrayList()): true
CollectionUtils.isEmpty({a,b}): false

判断集合是否不为空

CollectionUtils.isNotEmpty(null): false
CollectionUtils.isNotEmpty(new ArrayList()): false
CollectionUtils.isNotEmpty({a,b}): true

2个集合间的操作

集合a: {1,2,3,3,4,5} 集合b: {3,4,4,5,6,7}

CollectionUtils.union(a, b)(并集): {1,2,3,3,4,4,5,6,7}
CollectionUtils.intersection(a, b)(交集): {3,4,5}
CollectionUtils.disjunction(a, b)(交集的补集): {1,2,3,4,6,7}
CollectionUtils.disjunction(b, a)(交集的补集): {1,2,3,4,6,7}
CollectionUtils.subtract(a, b)(A与B的差): {1,2,3}
CollectionUtils.subtract(b, a)(B与A的差): {4,6,7}

更多的内容可以 Google: https://www.google.com/search?q=CollectionUtils

示例代码可以到 https://github.com/einverne/jutils/tree/master/src/test 来查看。


2018-01-03

2017 年读书笔记

又到一年的年末,2017 年对于我来说是变化最大的一年,这一年里完成了身份的转变,自此步入社会。这一年年初的时候定下目标年度 100 本书,看来又是无法完成了,现在看豆瓣的标注,也仅仅只有 30 本左右,距离目标甚是遥远。这里也不想再找借口,2017 年中,很大一部分时间共享给了王者荣耀和荒野行动,这两个手游占据了原本打散的碎片化时间。今年也一度差点荒废了 Kindle,Kinde10000 的消失,曾一度让 Kindle 在床头吃灰。

2017 年的读书记录如果再按照以前的分类划分的话,也没太多的变化,唯一增加的分类可能是增加了金融相关的一些书籍。那下面就总结一下好了。

小说

排在首位的永远是小说,在去年读过了大部分的东野圭吾的小说之后,今年似乎找不到主题,除了零星捡漏又看了几部东野圭吾的小说之外,其他作者的很少涉猎。不过今年读过的东野圭吾的小说中也还是有让我记忆比较深刻的几本:

  • 首先是这本《恋爱的贡多拉》,起初看到这本书的名字的时候,甚至一度怀疑这个作者到底是不是东野圭吾,仔细的查看封面和前言之后确认,这确实是东野圭吾的作品。这本书的文风与之前相比截然不同,这么轻松诙谐的文字,也只有东野圭吾这样的鬼才才能写出来吧。看这本书似乎就是看一个剧,东野圭吾通过滑雪场的缆车把书中几对情侣的各种“恩怨情仇”很诙谐的讲述出来,这样的作品直接拿去改编成剧本都丝毫不会有任何违和。不过这也正是这本书的毛病所在,太狗血的爱情桥段,太烂俗的现代都市爱情故事。不过我更欣赏东野圭吾在书中表述出来的不同人物形象和性格,都知道东野圭吾的推理小说中都是一些阴冷黑暗的角色,而这本不是,甚至这一本可以当成恋爱小说看,里面的人物形象都跃然纸上,不同主人公处理事务各不相同,也最终导致他们的结局不同,总之非常有趣。
  • 《东野圭吾的最后致意》这是一本随笔录,我记得以前也看过一部《我晃荡的青春》随笔录,东野圭吾从工程师,毅然辞职当起职业作家一路并不是一帆风顺,在经历了这么多的曲折之后,才有了今天的东野圭吾,从这本随笔录中也能看到当时努力写作的东野圭吾,幽默的文风一如从前。
  • 再就是《风雪追击》这一本不是那么出彩,滑雪场相关
  • 《悖论 13》 这算是科幻小说吗?我翻开豆瓣的评论,当时看完我写了,在这本小说中,我看到了好莱坞大片的元素,看到了《2012》,看到了《末日崩塌》,看到了《后天》这样的灾难大片的大场景,或许如果有一天有人把这个小说改编成电影,我也还是会去看一看的。其实这个故事很简答,出现了一个平行宇宙,里面的地球出现了各种灾难,一帮人自救互救的故事。
  • 《魔球》很早的作品了,平平
  • 《沉睡的人鱼之家》书中提出了一个值得思考的假设“如果一个人有着正常人的所有生命特征但长睡不醒那算是死亡吗?如果把一把刀插到植物人身上我算杀人吗?”
  • 《白马山庄杀人事件》或者叫 鹅妈妈旅舍杀人事件,关于这本书的出版,其实在《东野圭吾的最后致意》一书中也有提到,现在出版的名字是编辑给起的,原本的名字并不是白马山庄,看完其实也知道整本书和白马山庄并没有大多的关系。
  • 《疾风回旋曲》看完这本小说之后,也顺便把改编的电影也看了,东野圭吾是那么的喜欢滑雪嘛~最近太多关于滑雪场的故事了。
  • 《布谷鸟的蛋是谁的》 年中的时候看的,又是一本滑雪有关的,当然书中更多的讲得是关于孩子,血缘等等
  • 《拉普拉斯的魔女》说实话我不知道怎么归纳这一本,科幻?悬疑?就像当时在豆瓣写下的短评一样,我以为是讲述一个类似《达芬奇密码》这样保护秘密的故事,但其实故事更加复杂曲折。
  • 《平行世界·爱情故事》”带着科幻元素的爱情故事,及时记忆都不在了,爱情也依然不变。”现在看这段短评怎感觉和 《星际穿越》中及时时光穿梭,亲情不变这么像呢。
  • 《黎明之街》也是老早的作品了,tag 婚外情

专业书

今年看的专业书虽然数量有所增长,但是还是没达到目标。

  • 《REST API Design Rulebook》
  • 《Mastering Regular Expressions 3rd : Regular Expressions》
  • 《Mastering Nginx》
  • 《微服务设计》
  • 《Spring 实战(第 4 版)》

还有好多都零零落落没看完。

其他

  • 支付战争 : 互联网金融创世纪,一个产品到达一定瓶颈的时候,需要借助很多的营销策略让用户增长跟上一个层级,这本书就是 PayPal 当时早期员工写的如何帮助 PayPal 获取用户的回忆录
  • 增长黑客 : 创业公司的用户与收入增长秘籍,tag 营销、获取用户
  • 《告白与告别》
  • 《冷浪漫》
  • 《细说汉字》
  • 《汉字构形学讲座》
  • 《基金投资入门与实战技巧》
  • 《一本书读懂黄金白银投资理财》
  • 《你今天真好看》
  • 《重新定义公司 : 谷歌是如何运营的》
  • 《孩子你慢慢来》

2017-12-31 book , reading , collection

电子书

最近文章

  • 使用 Huginn 搭建自己的 IFTTT IFTTT, Zapier
  • notion 使用记录 很早就有人推荐 Notion,但是注册了用了一下,本以为就是一个在线的 Google Docs,可昨天在豆瓣看到一篇文章介绍比 Trello 更加智能的代替品,然后一看就是 Notion,于是就再来研究下 Notion。然后发现原来 Notion 可以是 Google Calendar, 可以是 Trello,可以是 Google Docs,可以是 todo list,可以是 Google Excel。甚至可以导入 word,markdown,html,csv。
  • Google Cloud Platform 使用向导 Google Cloud 提供 300 刀的初始优惠,而最近我的 Linode 节点越来越不稳定,时常撞墙,所以不得不再别人强烈推荐下注册了一下 GCP。这里就记录一下遇到的问题,其他具体的细节网上已经够多,就不再赘述。
  • html 转 pdf 命令行工具 wkhtmltopdf 最近因为用 HTML 写了一个文档,当想要输出时保存为 PDF,而 Chrome 自带的打印功能,本来就能够快速的保存为 PDF,但是却保留不了页面中的链接,所以找到了这个 wkhtmltopdf.
  • freemarker Java 模板引擎 FreeMarker is a free Java-based template engine, originally focusing on dynamic web page generation with MVC software architecture. However, it is a general purpose template engine, with no dependency on servlets or HTTP or HTML, and is thus often used for generating source code, configuration files or e-mails. by wikiPedia