Dell TB15 拓展坞 Diy 风扇散热(多图)

Dell TB15 拓展坞用了一周多, 先简单说下自己发现的优缺点:

缺点:

  1. 必须破解才能使用
  2. 散热有问题, 似乎是官方召回的产品, 持续使用时外壳40℃+ 稳定性不明
  3. 有时 USB、Lan口 会没反应, 需要重启Dock

优点:

  1. 可以给 15’ mbp 充电, 58W. 之前用绿*的 HUB 和官方充电器只能到 52W, 时不时会提示不在充电.
  2. DP/mini-DP 支持 4K 60Hz
  3. 千兆 Lan
  4. 便宜. *宝上洋垃圾 + 180w 电源适配器 500+大洋

下面开始 DIY

▲带电测试几分钟, 红框内的芯片触摸温度50℃+

Read More

CrossWalk Chromium Jar 修改指南

CrossWalk 是由 Intel 主导, 基于 Chromium 的 WebView 实现. 旨在为不同平台提供高效并且一致的 Web 体验.
关于项目简介、优劣对比、控件的使用, 建议参考官方文档.
本文主要从Java层 以修改最新 Stable 22.52.561.4 介绍如何下载源码修改Jar

1. 源码下载

官方提供的下载方式与 Chromium 一样,需要使用 gclient 下载. 但是代码仓库较大, 国内网络环境下基本无望.

另外一个方式可以直接从 GitHub 上下载:
https://github.com/crosswalk-project/crosswalk
https://github.com/crosswalk-project/chromium-crosswalk
下载完大概8GB

1
2
3
4
5
6
7
8
9
10
$ cd crosswalk
$ git branch -av
remotes/origin/crosswalk-21 c8e322d Bump version to 21.51.546.7
remotes/origin/crosswalk-22 67b33ab Bump version to 22.52.561.4
remotes/origin/crosswalk-23 8ba6a38 Bump version to 23.53.589.4
# Crosswalk-22 正好是需要的版本
$ git checkout crosswalk-22

#在 DEPS.xwalk 中可以找到对应 Chromium_Crosswalk 仓库对应 Commit
# chromium_crosswalk_rev = 'cbf42ea903acd7510548806a99efd200da37d492'
1
2
3
4
5
$ cd chromium-crosswalk
$ git branch -av
remotes/origin/crosswalk-22/52.0.2743.116 cbf42ea Merge pull request ...
# 找到对应的 Commit
$ git checkout crosswalk-22/52.0.2743.116

2. Java/Jar 修改

官方建议的构建方式需要 Linux/macOS, 如果只是修改Java层的代码则不需要这么麻烦.

  1. 使用 Intellij IDEA 新建 Java 工程, 将 xwalk_core_library_java.jar 添加进依赖.
  2. Src 下添加同包名同类名的类
  3. Build – Build Artifacts – Build

具体配置如下:

不要使用 cnpm 安装 F8App, react-native.js:120 ...require('React'), SyntaxError: Unexpected token ...

不要使用 cnpm install 安装,不然运行时会报错。
开始被这个帖子误导浪费半天时间……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ react-native run-android
/f8app/node_modules/.npminstall/react-native/0.23.1/react-native/Libraries/react-native/react-native.js:120
...require('React'),
^^^

SyntaxError: Unexpected token ...
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:414:25)
at Module._extensions..js (module.js:442:10)
at Object.require.extensions.(anonymous function) [as .js] (/f8app/node_modules/.npminstall/babel-register/6.7.2/babel-register/lib/node.js:134:7)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:311:12)
at Module.require (module.js:366:17)
at require (module.js:385:17)
at /f8app/node_modules/.npminstall/babel-core/6.7.7/babel-core/lib/transformation/file/options/option-manager.js:368:22
at Array.map (native)

Android 播放加密视频的一个思路

基本思路

Android 播放加密视频的基本思路就是:本地启动一个HTTP服务作为代理,解密之后给播放器播放
这样的好处就是可以自定义加密方案,更重要的是 加密/解密模块 可以和视频播放模块解耦

处理HTTP头

这里使用的 NanoHTTPD , 一个纯Java实现的微型WEB服务器,代码开源。
代码实现起来很简单,只要继承然后重写两个方法就可以。
稍微复杂的是怎样处理从视频播放器过来的请求。

通过 Charles 代理就能发现简单的规律。
VideoView播放在线视频的时候,发出的GET请求。第一次是一个普通的请求,务器返回200;之后的则全是分段请求,服务器返回206。

第一次请求 和 结果


第二次请求 和 结果

两次Request的区别是 后面的有Range字段
两次Response的区别是:

  1. 第一次 返回HTTP200 第二次 返回HTTP206
  2. 第一次 Content-Length是文件的整体大小; 第二次Content-Lenght是剩余的大小
  3. 第二次 返回Content-Range

有了区别之后就简单了,剩下要做的就是: 根据Req的不同,返回不同的 HTTP头 和数据

继承InputStream

Demo中用到的”加密”很简单: 直接在文件头部加入一些16进制的文字,一般的视频播放就没法识别了。
当然还可以有其他拓展的加密方法,比如数据乱序存储(在文件头部存储读取索引)、数据按块大小进行加密等

Demo中直接继承InputStream, 然后重写几个必要的方法,通过InputStream.Skip()方法跳过无用的文件头即可。

Demo

源码见:Github Repo

RxJava 批量计算网络连接速度

1.计算连接耗时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static long calcConnCost(String url) {
long start = new Date().getTime();
long end = start;
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestProperty("Range", "bytes=0-0");
conn.setIfModifiedSince(1);
start = new Date().getTime();
if (conn.getResponseCode() == 206 || conn.getResponseCode() == 200) {
end = new Date().getTime();
}
conn.disconnect();
conn = null;
} catch (IOException e) {
e.printStackTrace();
}
return end - start;
}

2.异步计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        Observable.just("http://baidu.com")
.map(new Func1<String, Long>() {
@Override
public Long call(String url) {
return calcConnCost(url);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long cost) {
Log.d("time", cost + "");
}
});
//输出 time:90

3.请求十次,求平均值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        MathObservable.averageLong(
Observable.just("baidu.com")
.repeat(10)
.map(new Func1<String, Long>() {
@Override
public Long call(String url) {
return calcConnCost(url);
}
})
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long averageCost) {
Log.e("Average", averageCost + "");
}
});
//输出Average:90

4.一组Url求平均值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        List<String> urls = new ArrayList<>();
MathObservable.averageLong(
Observable.from(urls)
.map(new Func1<String, Long>() {
@Override
public Long call(String url) {
return calcConnCost(url);
}
})
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long averageCost) {
Log.e("Average", averageCost + "");
}
});
//输出 Average:90

5.多组Url分别求平均值

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
        Map<String, Set<String>> hostUrl = new HashMap<>();
Observable.from(hostUrl.entrySet())
.flatMap(new Func1<Map.Entry<String, Set<String>>, Observable<Long>>() {
@Override
public Observable<Long> call(Map.Entry<String, Set<String>> stringSetEntry) {
return MathObservable.averageLong(
Observable.from(stringSetEntry.getValue())
.map(new Func1<String, Long>() {
@Override
public Long call(String url) {
return calcConnCost(url);
}
})
);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Long>() {
@Override
public void call(Long averageCost) {
Log.e("Average", averageCost + "");
}
});
//输出 Average:70
//输出 Average:60
//输出 Average:50
//...

6.多组Url分别求平均值并保留host

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
        Map<String, Set<String>> hostUrl = new HashMap<>();
Observable.from(hostUrl.entrySet())
.flatMap(new Func1<Map.Entry<String, Set<String>>, Observable<Map.Entry<String, Long>>>() {
@Override
public Observable<Map.Entry<String, Long>> call(Map.Entry<String, Set<String>> stringSetEntry) {
return Observable.zip(
Observable.just(stringSetEntry.getKey()),
MathObservable.averageLong(
Observable.from(stringSetEntry.getValue()).map(new Func1<String, Long>() {
@Override
public Long call(String url) {
return NetSpeedUtil.calcConnCost(url);
}
})),
new Func2<String, Long, Map.Entry<String, Long>>() {
@Override
public Map.Entry<String, Long> call(String host, Long cost) {
return new AbstractMap.SimpleEntry<>(host, cost);
}
}
);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Map.Entry<String, Long>>() {
@Override
public void call(Map.Entry<String, Long> hostCostEntry) {
Log.e(hostCostEntry.getKey(), hostCostEntry.getValue() + "");
}
});
//输出 baidu.com:90
//输出 yahoo.com:110
//输出 google.cn:190
//...

7.拆分之后可读性更好一些…

ArcMap Python 脚本编写入门索引

本文主要是Python脚本批量处理ArcMap数据的入门资料索引.

如果是简单的批量处理数据, 可以直接使用ArcMap内置的Python窗口处理.而对于稍复杂或经常重复的操作就可以考虑用插件来完成.

ArcMap中的Python窗口:

###ArcMap中的Python插件
ArcMap中的python插件有两种形式, 一种是Toolbox里直接运行的py脚本, 添加时需要设置字段. 比如下图里的 Add Geometry Attributes 就属于此类.

另一种是作为esriaddin插件形式的,可以添加菜单、工具条;单文件形式存在,方便安装和传播,不过似乎不方便调试。

###开始吧,皮卡丘

  1. 首先python要有入门水平, 推荐廖雪峰的 Python教程, 看一两遍差不多入门.
  2. Programming ArcGIS 10.1 with Python Cookbook, 这本书不错, 难度一般,例数据比较全.
  3. 什么是 Python 加载项?
  4. gis.stackexchange.com 基本上ArcPy相关的问题都可以查到.