什么是Web-Worker?
WebWorker类似于浏览器中的多线程操作。之前的JS中,无论你是使用setTimeout setIntever 还是 使用了XMLHttpRequest,都是在一个线程里面,前两个使用消息队列,XMLHttpRequest则是浏览器会帮你进行闲时进行,归根结底,都是在一个线程里面跑。如果用户想进行一些阻塞操作,很可能会产生卡住页面的情况。甚至于我们想实现一个类似于Android的专用 公用Service,那该怎么办?
H5新标准提出了WebWorker的概念,各个浏览器都各自实现了,先来看一下WebWorker能做什么。
WebWorker特点,在后台线程执行JS的能力,与页面通过send message这种方式通信。
WebWorker有两种,专用worker(dedicatedworker)与公用worker(sharedworker)。
疑问:worker与主线程如何同步,worker与主线程同时操作了一个DOM元素,会不会产生脏数据?所以,worker的能力被加以限制,不能访问DOM元素。
worker在chrome中是如何实现的?
chrome浏览器是多进程架构,分为Browser进程以及Render进程,每打开一个页面,浏览器都会为其分配一个Render进程,webkit以及js都运行在这个进程内。如果要起一个DedicatedWorker,Chrome会在Render进程中起一个线程。如果要起一个SharedWorker就稍微复杂一点,必须起一个专门的进程,并且,相同的SharedWorker不管你创建多少次,都只存在一个。
Android中的Chrome有一个限制,限定9个进程。
1.如果用户创建太多SharedWorker,可能第二个标签页都打不开?
2.SharedWorker的优先级如何定义?如果使用SharedWorker的页面都在后台,其优先级如何?
目前Android上的SharedWorker还处于讨论阶段,未实现。
下面看一下worker的基本用法,DedicatedWorker:
worker.js
1 2 3 4 5 |
this.addEventListener('message', function(e) { var data = e.data; console.log("worker: " + data); this.postMessage(data + 1); }); |
worker中需要一个this.onmessage接收消息
postMessage发送消息
参数在 event.data中
main.js
1 2 3 4 5 6 7 8 9 10 |
if (window.Worker) { var worker = new Worker("./worker.js"); worker.onmessage = function(e) { document.getElementById("worker").innerHTML = e.data; }; document.addEventListener('keydown', function(evt) { worker.postMessage(evt.keyCode); }); } |
使用Worker这个API来创建DedicatedWorker。
同样通过worker实例的onmessage和postMessage通信。
结束Worker:
在worker中,可以通过close()来kill掉自己
main中则调用worker.terminate()
如果worker运行中出现错误,在main中使用worker.onerror可以接收到错误消息
SharedWorker:
https://github.com/mdn/simple-shared-worker
SharedWorker跟DedicatedWorker有两个不同的地方:
1.通信不再直接通过worker,而是worker的port类
2.worker中需要实现onconnect,且其参数中有一个port列表,但目前只使用到了第一个
sharedworker.js
1 2 3 4 5 6 7 8 9 |
onconnect = function(e) { var port = e.ports[0]; port.onmessage = function(e) { var workerResult = 'Result: ' + (e.data[0] * e.data[1]); port.postMessage(workerResult); } } |
main.js
1 2 3 4 5 6 7 8 9 10 |
if (!!window.SharedWorker) { var myWorker = new SharedWorker("worker.js"); myWorker.port.postMessage([squareNumber.value,squareNumber.value]); myWorker.port.onmessage = function(e) { result2.textContent = e.data; console.log('Message received from worker'); } } |
当然 还可以写另一个页面,同样可以使用跟main.js类似的方法跟sharedworker通信。
ServiceWorker:
ServiceWorker是WebWorker的一种,它更加复杂,所以也更加强大。
首先,ServiceWorker有独立的生命周期:
1.Register:主线程调用API注册ServiceWorker
2.Installing:浏览器启动安装过程,加载和缓存一些静态资源
有可能失败进入Error状态
3.Activated:激活阶段,此阶段可以升级ServiceWorker
4.激活后,ServiceWorker会接管页面,如果页面是刚刚注册,本次不会被接管,下次加载页面才会接管。
5.ServiceWorker接管页面后,如果有fetch和message事件,会处于onfetch和onmessage,其他情况可能被终止。
ServiceWorker的特性:
1.它是一个worker,同样不能操作dom元素,同样可以通过postMessage与调用线程通信
2.ServiceWorker增加了网络处理,onfetch
3.ServiceWorker不被使用的时候,它会自己终止,再次使用会被重新激活,不要依靠它的内存来保存信息,请用webStorage或者indexDB。
4.ServiceWorker大量使用Promise,就是封装的一个callback标准
5.ServiceWorker权限很大,即所有网络请求都经过它,可以劫持连接,伪造和过滤响应,所以只能在https网页上注册ServiceWorker(是只能么?)。
6.ServiceWorker只作用于同域的fetch。onfetch有一个缓存的例子,我们通过缓存类缓存一些request和response,下次request,直接去缓存找response,处理失败了再去网络实时请求。注:response的类型需要是basic,即同域请求。
8.ServiceWorker的自动更新。
当网页激活时,浏览器会检查ServiceWorker是否有更新(有一个字节不同就会认为有更新),浏览器后台下载。
下载完开始运行,进入install状态,之后进入waitting状态。因为此时旧的ServiceWorker仍然在运行。
当页面被杀掉,旧去新来。
当然,如果你之前缓存了request,更新后需要清理一下。
现有问题:
如果在Install时失败了,页面无法感知。
ServiceWorker主要作用是在onfetch里面缓存/处理request。
https://github.com/GoogleChrome/samples
中有大量的onfetch与cache结合使用做离线应用的例子,在此不多赘述。
来自
http://blog.csdn.net/yl02520/article/details/14446763
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
Android下NDK开发的动态库(.so,shared library)增加版本号信息
在Android下面开发,免不了要涉及到C/C++层的开发,这就会涉及到崩溃异常的处理问题。
随着程序的不断升级,更新,会出现多个版本的动态库同时在线上共存的问题,一旦出现崩溃日志,往往不能方便的知道到底是哪个版本出现的崩溃。
传统的Linux,可以通过编译时候指定版本号来处理,最后生成如“libc.so.6”这种形式的文件名,我们可以根据文件名来获得相应的版本信息。遗憾的是,这种命名方式,在Android上面是不支持的。
目前的需求主要有三点:
-
不依赖文件名,查询到版本号
版本号写在文件名中,是一个比较方便的方式,但是带来的问题却是,靠不住!文件名可以随意更改,往往传来传去,文件名中的信息就改得面目全非了。
-
logcat的崩溃日志中能得到版本号
系统自带的logcat会在进程崩溃的时候,打印出崩溃栈,但是信息非常的精简,只有当前的线程回退栈帧信息(backtrace)以及几个关键寄存器信息,往往不能准确提供有足够的信息。而如果想在客户的设备上面,拿到完整的core-dump信息,只能是呵呵一下了!
-
C/C++层的版本号变动,不要改动JAVA层的代码
一方面,如果我们把版本号写在动态库的名字里面,这样会造成每次动态库的升级,JAVA的调用代码都需要变动,小团队还好,大团队,完全不可想象。另一方面,如果是系统框架层的开发,更加悲催,一个文件名的改动,影响到一片,当然,软链接也是个不错的选择,但是这样,常用的APK开发又要折腾一番,免不了一堆的抱怨。
针对上面的需求,我们逐个来提供解决方案:
-
不依赖文件名,查询到版本号
在任意的C/C++文件中增加如下代码
1 |
const static __attribute__((unused,section(".SO_VERSION"))) char Version[] = VERSION; |
在Android.mk文件中增加
1 |
LOCAL_CFLAGS += -DVERSION='"1.0.3"' |
编译生成的".so"文件使用如下命令即可查询版本号信息:
1 2 3 4 |
$readelf --string-dump=".SO_VERSION" libSo.so String dump of section '.SO_VERSION': [ 0] 1.0.3 |
解释:
上面的代码的目的,是要求GCC在一个名为".SO_VERSION"的段内,记录我们的版本号信息。“unused”属性用于告诉GCC,不要在编译的时候警告这个变量没有被任何代码引用过。
注意事项:
在定义变量"Version"的时候,不要使用"volatile"来修饰。这个关键字影响到了最后生成的段的"PROGBITS"标记位置,这个标记表明了最后加载到内存中的数据是否可修改(我们当然希望这个位置不可修改,如果可写,可能由于越界导致版本号的不准确)。
没有使用"volatile"修饰
1 2 |
$ readelf -S libSo.so | grep .SO_VERSION [12] .SO_VERSION PROGBITS 0003f520 03f520 000008 00 A 0 0 8 |
使用"volatile"修饰
1 2 |
$ readelf -S libSo.so | grep .SO_VERSION [12] .SO_VERSION PROGBITS 0003f520 03f520 000008 00 WA 0 0 8 |
注意两者的不同,一个属性是"A",一个属性是"WA"。
-
logcat的崩溃日志中能得到版本号,并且C/C++层的版本号变动,不要改动JAVA层的代码
目前对于这个问题的解决方法,是设置代理So。具体操作方式如下(假定我们生成的".so"项目LOCAL_MODULE:=So):
1.修改原来项目中的"JNI_OnLoad","JNI_OnUnLoad"函数,重新命名为 "So_JNI_OnLoad","So_JNI_OnUnLoad" 其他代码不变,并且在"So-jni.h"中对这两个函数进行声明。
2.所有的JNI方法都通过 "JavaVM->RegisterNatives" 方法进行动态注册。
3.建立SoStub-jni.cpp,代码如下:
1 2 3 4 5 6 7 8 9 10 11 |
#include "So-jni.h" extern "C" JNIEXPORT int JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { return So_JNI_OnLoad(vm,reserved); } extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) { return So_JNI_OnUnload(vm,reserved); } |
4.原工程的"LOCAL_MODULE"修改为"LOCAL_MODULE:=So_1_0_3"(版本号根据实际情况调整即可)
5.修改Android.mk,增加如下内容
1 2 3 4 5 6 7 |
include $(CLEAR_VARS) LOCAL_MODULE := SoStub LOCAL_SRC_FILES := SoStub-jni.cpp LOCAL_SHARED_LIBRARIES += So_1_0_3 include $(BUILD_SHARED_LIBRARY) |
6.修改JAVA层的调用代码
1 2 3 |
static { System.loadLibrary("So"); } |
为:
1 2 3 |
static { System.loadLibrary("SoStub"); } |
解释:
由于logcat打印的崩溃栈,信息极少,因此我们只能采取这种折中的办法,这样设置之后,崩溃栈中会打印出"libSo_1_0_3.so"这样的字样,我们就可以知道版本号了。
JS处理相对路径
遇到引擎不能处理相对路径的问题,写了一个简单的相对路径转绝对路径的函数,留下备用:
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 |
function rel2Abs(path) { var _path = path; var locationStr = location.href.toString(); console.log(locationStr); var result = ''; var relative; while (relative = _path.substring(0, _path.indexOf("\/"))) { console.log(relative); if (relative === '.') { // 遇到. path去掉最前面的./ // 遇到.当前路径去掉/后面内容 _path = _path.substring(_path.indexOf('\/') + 1, _path.length); locationStr = locationStr.substring(0, locationStr.lastIndexOf('\/') + 1); } else if (relative === '..') { // 遇到.. path去掉最前面的../ // 遇到.. 当前路径去掉/xxx,然后再去掉/后面的内容 _path = _path.substring(_path.indexOf('\/') + 1, _path.length); locationStr = locationStr.substring(0, locationStr.lastIndexOf('\/')); locationStr = locationStr.substring(0, locationStr.lastIndexOf('\/') + 1); } else { break; } } var result = locationStr + _path; path = result; return result; }<code> |
原理很简单,只支持/,且遇到错误不能自动处理
Android Studio(JAVA) 编译警告:使用了未经检查或不安全的操作
在编译Android Studio(JAVA)程序时发生了如下的警告:
使用了未经检查或不安全的操作
要了解详细信息,请使用 "-Xlint:unchecked" 重新编译。
- 如何设置Android Stuido开启 "-Xlint:unchecked"
修改build.gradle
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 |
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { applicationId "com.a.b.c" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { minifyEnabled false } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.0.1' compile 'com.android.support:design:23.0.1' } |
增加
1 2 3 |
tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:unchecked" } |
修改后的如下:
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 |
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { applicationId "com.a.b.c" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { minifyEnabled false } } tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:unchecked" } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.0.1' compile 'com.android.support:design:23.0.1' } |
- 警告信息的处理例子
包含-Xlint:unchecked警告的代码
1 2 3 4 5 6 |
final LinkedHashMap<WeakReference<Object>,WeakReference<Object>> aWeakArray = new LinkedHashMap<>(); for (Iterator iter = aWeakArray.entrySet().iterator(); iter.hasNext(); ) { LinkedHashMap.Entry element = (LinkedHashMap.Entry)iter.next(); WeakReference<Object> aWeakObj = (WeakReference<Object>)element.getKey(); WeakReference<Object> aWeakTag = (WeakReference<Object>)element.getValue(); } |
消除警告后的代码如下:
1 2 3 4 5 6 |
final LinkedHashMap<WeakReference<Object>,WeakReference<Object>> aWeakArray = new LinkedHashMap<>(); for (Iterator<LinkedHashMap.Entry<WeakReference<Object>,WeakReference<Object>> > iter = aWeakArray.entrySet().iterator(); iter.hasNext(); ) { LinkedHashMap.Entry<WeakReference<Object>,WeakReference<Object>> element = iter.next(); WeakReference<Object> aWeakObj = element.getKey(); WeakReference<Object> aWeakTag = element.getValue(); } |
同样的方法适用于"-Xlint:deprecation"。
svnrdump dump 实现 SVN 库的远程导出
svnrdump dump 命令必须 在 SVN 1.7版本以上提供的用于远程dump SVN库的命令.
以导出libyuv的svn库为例:
1 |
svnrdump dump http://libyuv.googlecode.com/svn/ > libyuv.dump |
可以在这里下载已经DUMP完成之后的版本。目前的版本号是1444。
恢复DUMP文件到Subversion工程,请参考 Could not open the requested SVN filesystem
Debian和OpenMediaVault在命令行下设置更新源
很多时候我们比较纠结的问题是,“该把哪个Debian镜像发布站点加入source.list文件?”。
Ubuntu的图形界面中有一个测试工具,命令行下面的Debian也有一个现成的程序:netselect。
安装netselect
1 |
$ sudo apt-get install netselect |
不带参数运行它时会显示它的帮助信息。运行它时加上以空格分隔的镜像主机列表,它会返回一个分值和列表中的一个主机名。这个分值通过评估ping time和hopsnumber(一个网络请求报文到达目标主机所经过的转发主机的个数)得出,它与镜像站点预计下载速度成反比(数值越小越好)。返回的主机名是主机列表中得分最低的那个(查看列表中所以主机的得分情况可使用-vv选项)。看出下的例子:
1 2 3 |
$ netselect http://mirrors.aliyun.com/debian/ http://mirrors.163.com/debian/ 5 http://mirrors.163.com/debian/ |
它表示,在netselect后列出的所有主机中,http://mirrors.163.com/debian/是下载速度最快的主机,其得分为5。
注意,最近163的服务器不知道发生了何种故障,导致各种更新失败,尽管测试的结果是163更快,但是我们建议还是使用阿里云的服务器。
把netselect找到的连接速度最快的镜像站点手工加入/etc/apt/sources.list文件.
最新版本的netselect软件包包含了netselect-apt脚本,它使上述操作自动完成。只需将发布目录树做为参数(默认为stable)输入,sources.list文件就会生成速度最快的main和non-US镜像站点列表,并保存在当前目录下。
1 2 3 4 5 6 7 8 9 |
$ sudo apt-get install netselect-apt $ netselect-apt stable $ sudo mv /etc/apt/sources.list /etc/apt/sources.list.old $ sudo mv sources.list /etc/apt/sources.list $ sudo apt-get update |
对于OpenMediaVault用户还是手工修改配置文件好了,主要是上面的工具生成的比较简略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# # deb cdrom:[Debian GNU/Linux 7.0.0 _Wheezy_ - Official Snapshot amd64 LIVE/INSTALL Binary 20150108-14:12]/ wheezy contrib main non-free #deb cdrom:[Debian GNU/Linux 7.0.0 _Wheezy_ - Official Snapshot amd64 LIVE/INSTALL Binary 20150108-14:12]/ wheezy contrib main non-free deb http://mirrors.aliyun.com/debian/ wheezy main deb-src http://mirrors.aliyun.com/debian/ wheezy main deb http://security.debian.org/ wheezy/updates main contrib non-free deb-src http://security.debian.org/ wheezy/updates main contrib non-free # wheezy-updates, previously known as 'volatile' deb http://mirrors.aliyun.com/debian/ wheezy-updates main contrib non-free deb-src http://mirrors.aliyun.com/debian/ wheezy-updates main contrib non-free |
JS常用方法封装
做项目是遇到一个封装的不错的Util类,写一下,长期备用:
CMD标准
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
define(function(require, exports, module) { "use strict"; function Util() {} (function() { Util.isWindow = function(obj) { return obj !== null && obj === obj.window; }; Util.isFunction = function(obj) { return typeof obj === 'function'; }; Util.isObject = function(obj) { return typeof obj === 'object'; }; Util.isArray = function(obj) { return obj instanceof Array; }; Util.isPlainObject = function(obj) { return Util.isObject(obj) && !Util.isWindow(obj); }; Util.isString = function(obj) { return typeof obj === 'string'; }; // 将其他的object统统搞到一个object中去 // 这个函数不如直接写两个参数看着舒服 target objects,就不用各种slice shift了 Util.extend = function(target) { var deep; var args = [].slice.call(arguments, 1); if (typeof target === 'boolean') { deep = target; target = args.shift(); } args.forEach(function(arg) { extend(target, arg, deep); }); return target; // 这个函数就是把source里面的一层一层往下展开给target,object 和 array会一直展开 function extend(target, source, deep) { for (var key in source) { if (deep && (Util.isPlainObject(source[key]) || Array.isArray(source[key]))) { if (Util.isPlainObject(source[key]) && !Util.isPlainObject(source[key])) { target[key] = {}; } if (Array.isArray(source[key]) && !Array.isArray(target[key])) { target[key] = []; } extend(target[key], source[key], deep); } else { target[key] = source[key]; } } } }; Util.ajaxGet = function(url, data, callback) { // 简单学习一下XMLHttpRequest,一个JS对象,提供了封装的获得url上资源数据的方法,支持xml http ftp file // 步骤简单: open(method, url, sync), send(), 如果是异步的话 就调用onreadystatechange方法 var xhr = new XMLHttpRequest(); // 设置超时时间为30s xhr.timeout = 30000; xhr.onreadystatechange = function() { console.log("xhr.status is " + xhr.readyState); if (xhr.readyState === 4) { var status = 0; try { status = xhr.status; } catch(e) { // eat and ignore the exception, since access the status of XHR might cause a exception after timeout occurs; return; } if (status === 200) { var result = null; try { result = JSON.parse(xhr.responseText.replace(/\n|\r|\t|\b|\f/g, '')); } catch(e) { callback(null); return; } callback(result); } else { callback(null); } xhr.onreadystatechange = null; xhr = null; } }; xhr.withCredentials = true; xhr.open("GET", url + "?" + data, true); xhr.send(null); return xhr; }; Util.ajaxPost = function(url, data, callback) { var xhr = new XMLHttpRequest(); // 原来navigator除了online,还有个connection // 这个connection,而且这个connection很刁,何以拿到type,还有change事件 // 关键是一大堆浏览器都TM的不支持 var connection = navigator.connection; switch(connection.type) { case connection.WIFI : case connection.ETHERNET: xhr.timeout = 3000; break; case connection.CELL_3G: case connection.CELL_4G: xhr.timeout = 5000; break; case connection.CELL_2G: xhr.timeout = 30000; break; default : xhr.timeout = 30000; } // POST比GET复杂,首先不同网络timeout不一样,其次有很多事件需要监听 xhr.addEventListener("error", function(e) { if (e && e.loaded === 60) { // https error if (e.total === 9) { callback("CERTIFICATE TIME ERROR"); } else { callback("CERTIFICATE ERROR"); } } else { callback("NETWORK_ERROR"); } }); xhr.addEventListener("abort", function() { console.log("request abort!"); }); xhr.addEventListener("timeout", function() { callback("TIMEOUT"); }); xhr.addEventListener("readystatechange", function() { if (xhr.readyState === 4) { if (xhr.status === 200) { var result; try { result = JSON.parse(xhr.responseText.replace(/\n|\r|\t|\b|\f/g, '')); } catch(e) { callback("DATA_INVALID"); return; } callback(result); } else if (xhr.status !== 0) { callback("HTTP_ERROR"); } } }); // withCredentials = true 将使得请求会带上cookie xhr.withCredentials = true; xhr.open("POST", url, true); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.send(data); return xhr; }; Util.abort = function(xhr) { xhr.abort; }; Util.getGeoLocation = function(callback) { var timeout = 30000; if (navigator.connection && navigator.connection.type) { switch (navigator.connection.type) { // wifi case 2: timeout = 8000; break; // 2G case 3: timeout = 30000; break; // 3g: case 4: timeout = 20000; break; default: timeout = 30000; break; } } // 这里就是浏览器获取地理位置的用法 navigator.geolocation.getCurrentPosition( function(position) { callback.call(this, position); }.bind(this), function(error) { callback.call(this, null); }.bind(this), { enableHighAccuracy: true, maximumAge: 180000, timeout: timeout }); }; // 实时定位, 返回一个ID,用来取消监听 Util.startWatchGeoLocation = function(callback) { return navigator.geolocation.watchPosition( function(position) { callback.call(this, position); }.bind(this), function() { callback.call(this, null); }.bind(this), { maximumAge: 600000, timeout: 20000 } ); }; Util.stopWatchGeoLocation = function(geoId) { navigator.geolocation.clearWatch(geoId); }; // 主要用来 ajax request中把object转为字符串 Util.paramToStr = function(params) { var data = ''; var isFirstChar = true; for (var key in params) { if (params.hasOwnProperty(key)) { if (isFirstChar === true) { isFirstChar = false; data += key + "=" + params[key]; } else { data += "&" + key + "=" + params[key]; } } } return data; }; // 预加载一个Image Util.preloadImage = function(images) { if (images && images.length) { for(var i = 0; i < images.length; i++) { var imgContainer = new Image(); imgContainer.src = images[i]; imgContainer = null; } } }; // 就是把 rgb(,,)换成了rgba(,,,) Util.addOpacity = function(color, opacity) { var newColor = color.replace(/rgb/g, "rgba").replace(/\)/g, "," + opacity + ")"); return newColor; }; Util.isOffline = function() { return !navigator.onLine; }; Util.getStorageValue = function(key) { var item = window.localStorage.getItem(key); if (!item) { return; } item = JSON.parse(item); var data = item.data; var type = item.type; var value = null; switch(type) { case "Boolean": value = Boolean(data); break; case "String": value = String(data); break; case "Number": value = Number(data); break; case "JSON": value = JSON.parse(data); break; } return value; }; Util.setStorageValue = function(key, value) { var type = null; var data = value; if (typeof value === "boolean") { type = "Boolean"; } else if (typeof value === "string") { type = "String"; } else if (typeof value === "number") { type = "Number"; } else if (typeof value === "object") { type = "JSON"; data = JSON.stringify(value); } window.localStorage.setItem(key, JSON.stringify({data: data, type: type})); } }).(); module.exports = Util; }); |
Ubuntu 12.04 + Apache2.2.22搭建SPDY服务器
SPDY是Google开发的基于传输控制协议(TCP)的应用层协议,该协议规定在一个SPDY连接内可以有无限个并行请求,服务器可以主动向客户端发起通信向客户端推送数据,通过请求优化、预加载、压缩HTTP 来达到加速的目的。
对用记而言,SPDY是基于SSL加密,它可以让网络访问更安全,用户隐私更加得到保护。对站长而言,SPDY在降低连接数目的同时,还使服务器上每个客户端占用的资源减少,从释放出更多内存和CPU ,让网站的浏览速度提升不少。
SPDY协议已经被Chrome、Firefox、Opera、IE 11以上支持,用户在访问使用SPDY协议加载的网站几乎感觉不到与普通的Https页面访问有何不同,而SPDY带来的页面加载速度提升和服务器性能优化确是有十分重要意义的。
具体的操作步骤如下:
- 启用Apache2的HTTPS支持
参考 UBUNTU 12.04 下 APACHE 2.2.22 开启 HTTPS
- 下载Apache2的mod_spdy模块
官网:https://developers.google.com/speed/spdy/mod_spdy/
由于众所周知的原因,本站提供下载(2015-11-6版本)
mod_spdy 64-bit .deb (Debian/Ubuntu)
mod_spdy 32-bit .deb (Debian/Ubuntu)
- 安装mod_spdy模块(本站是64位系统)
1 2 |
$sudo dpkg -i mod-spdy-*.deb $sudo apt-get -f install |
- 重启Apache2
1 |
$sudo service apache2 restart |
- 验证
目前验证貌似没有起作用啊!目前最新的chrome已经没办法进行验证了,主要是由于HTTP/2已经发布,Google放弃了spdy,转而支持HTTP/2,还是静待新版本的HTTP/2吧。
- 卸载
1 2 |
$sudo dpkg -r mod-spdy-beta $sudo dpkg -P mod-spdy-beta |
- 参考链接
Linux Mint + Apache2.2搭建SSL/HTTPS/SPDY服务器
https://developers.google.com/speed/spdy/mod_spdy/
Ubuntu 12.04安装PHP5的php-curl扩展
在使用WP-Statistics统计插件的时候,提示GeoIP需要 php-curl扩展。搜索了一下,安装方式如下:
1 |
$ sudo apt-get install php5-curl |
安装完成后,需要重启Apache2
1 |
$ sudo service apache2 restart |