利用 Jenkins 持续集成 Android 程序

Jenkins 是 Java 编写的开源持续集成(Continuous integration)工具 1。在上一篇推荐网站中提到的 AlternativeTo 中去搜索一下,能够看到很多持续集成的工具,像 GitHub 上经常看到的 Travis CI, 还有 Jenkins 的前身 Hudson。

安装

详情请见官网.

Linux 下:

wget -q -O - https://jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins

直接安装源中的 Package 有几点需要注意:

  1. Jenkins 会以守护进程(daemon)随机启动, 查看 /etc/init.d/jenkins
  2. 创建了 jenkins 用户来运行服务
  3. Log 文件 /var/log/jenkins/jenkins.log
  4. 配置地址 /etc/default/jenkins 比如 JENKINS_HOME 在该文件配置
  5. 默认 Jenkins 使用 8080 端口,启动 http://localhost:8080 开始配置 Jenkins 吧。

更多服务器 Apache , Nginx 的配置请见官网。

全局配置

这里默认已经有 Android 开发环境,也就是 JDK, Android-SDK,Gradle 都已经是完整的。进入 http://localhost:8080 配置,首次今日需要验证身份信息,验证之后创建用户,然后下载插件,进入之后,系统管理 -> 管理插件,需要安装以下插件:

  • Android Lint Plugin
  • Git plugin
  • Gradle plugin

如有其他插件 GitHub , gitlab 啦,可以就使用环境来选择安装。

更新玩插件,进入 系统管理 ->Global Tool Configuration , 然后配置 JDK 目录, Gradle 目录,Git 目录。当然需要知道当前自己机器上的绝对地址:

  • JDK: /usr/lib/jvm/java-7-openjdk-amd64
  • Gradle: /home/einverne/android-studio/gradle/gradle-2.10
  • Git: git

系统管理 -> 系统系统设置:

Environment variables 下添加:

键 : ANDROID_HOME 值:/home/einverne/Android/Sdk

如果这一行不添加,而 Android Studio 的工程没有在 local.properties 中指定 sdk.dir=/home/einverne/Android/Sdk 的话, Jenkins build failed, cannot found Android sdk2.

项目配置

配置好上面的环境,新建项目,名字 +”构建一个自由风格的软件项目”。 然后进入项目,配置

General

项目名字,项目描述, GitHub Project url。 如果想要自定义路径,可以在高级中设置。

源码管理

当然使用的是 Git, 然后是项目地址,然后需要验证身份信息,当然有很多的方式,用户名密码,用户名密钥,等等,根据自己的适用情况添加即可。可以选择分支 build.

构建触发器

这里可以选择何时触发构建, 有很多的方式,可以是有变动自动构建,也可以是定时构建,当然也有很多的触发方式。

构建环境

这边基本上在全局环境配置的时候就已经满足,细化一下 build 环境。

构建

这里需要选择 Invoke Gradle script, 然后在配置中选择刚刚在全局配置中配置的 Gradle 版本。

在 Tasks 下输入 clean build .

构建后操作

  1. 选择 Publish Android Lint results, 以便输出 Lint 结果,设置中输入 **/lint-results*.xml.
  2. 选择 Archive the artifacts 来存档 apk 文件,设置中输入 **/*.apk。 默认输出文件在 Workspace 下 app/build/outputs/apk/

遇到问题

Android Lint

Jenkins 默认启用 Lint 检查,所以需要在 build.gradleandroid 下添加

lintOptions {
    abortOnError false;
}

图文教程,Windows 下教程参考下面文章。

reference


2016-05-01 android , jenkins , gradle , ci

推荐网站之代替品:alternativeTo

总说“授人以鱼不如授人以渔”,寻找不同平台上的代替品,每次都会点进这个网站,或许是上一段时间内跨不同操作系统太频繁,所以导致寻求Mac下 Windows 某个软件的代替品的需求升高,现在渐渐的培养起自己只使用多平台客户端都存在的应用使用,而有些是在没有Linux下的应用,也尽量的能够找到功能相似的代替。所以 AlternativeTo 这个网站给我带来了很多的便利。

官网地址:http://alternativeto.net/

应用实例:

My Tracks

My Tracks 原本是Google推出Android上记录GPS轨迹的应用,在很早开始就一直使用,无奈Google宣布停止这款应用,而自身推出的 Fit 功能上也赶不上 My Tracks,想到这里真心感觉有点讽刺,几年后推出的产品在功能上竟然赶不上几年以前推出的应用。Google Photos 与 Picasa 也是。说偏了,回到 My Tracks, 直接访问 http://alternativeto.net/software/my-tracks/ 就能够找到很多的应用,上面有些用户也会给出功能的对比,有所少人喜欢某个应用,在几乎所有的情况下,排名第一的几乎就是最好用的了。

KMPlayer

有的时候多逛一逛这个网站能够有不少收获,就比如早之前我一直用的 KMPlayer , 而自某一个版本开始 KMPlayer 开始变得臃肿,收纳很多无用的功能之后,就可以到 KMPlayer 的 alternativeTo 来寻求代替品,最后会发现 PotPlayer 也非常好用,然后我一直使用至今。当然在 Linux 下就又可以跑到 PotPlayer 下面,然后找到 MPlayer 和 SMPlayer ,都非常不错,当然 VLC Media Player 也很棒。

当然还有很多很多的例子,都需要慢慢的发现,我曾经在上面找过 IDE 的代替品,找过编辑器的代替品,找过 SQLite Manager 的代替品,等等,他最好最方便的功能就是,当你在一个平台已经养成了一个使用习惯,突然间更换了一个平台,然后对这个环境很陌生,而在这个网站的指导下就能够迅速的找到代替品。快速的适应新的环境。而这样引出来的另一个问题就是,需要熟悉不同的工具。所以就回归到我之前所说,我几乎现在只考虑使用三个平台 Windows, Mac, Linux 下都存在产品的应用了。虽然现在像 Evernote,网易云音乐同样没有Linux客户端,但这已经是非常少的特例了。使用同样的应用,同样的环境才能提高效率,并且不同去适应不同的工具的差异。


2016-04-29 推荐网站 , website

每天学习一个命令:ps 显示当前运行进程

Linux 中的 ps 命令是 Process Status 的缩写。ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信息,就可以使用top命令。

简介

要对进程进行监测和控制,首先必须要了解当前进程的情况,也就是需要查看当前进程,而 ps 命令就是最基本同时也是非常强大的进程查看命令。使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等。总之大部分信息都是可以通过执行该命令得到的。

ps - report a snapshot of the current processes.

ps 提供的是执行时刻进程的状态,它提供的结果不是动态连续的;如果想对进程进行连续监控,应该用 top/htop 工具。

Linux 上进程有5种状态:

  1. 运行(正在运行或在运行队列中等待)
  2. 中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号)
  3. 不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有中断发生)
  4. 僵死(进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放)
  5. 停止(进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行)

ps工具标识进程的5种状态码:

D 不可中断 uninterruptible sleep (usually IO)
R 运行 runnable (on run queue)
S 中断 sleeping
T 停止 traced or stopped
Z 僵死 a defunct (”zombie”) process

语法

ps --help all

Usage:
 ps [options]

Basic options:
 -A, -e               all processes
 -a                   all with tty, except session leaders
  a                   all with tty, including other users
 -d                   all except session leaders
 -N, --deselect       negate selection
  r                   only running processes
  T                   all processes on this terminal
  x                   processes without controlling ttys

Selection by list:
 -C <command>         command name
 -G, --Group <gid>    real group id or name
 -g, --group <group>  session or effective group name
 -p, --pid <pid>      process id
     --ppid <pid>     select by parent process id
 -s, --sid <session>  session id
 -t, t, --tty <tty>   terminal
 -u, U, --user <uid>  effective user id or name
 -U, --User <uid>     real user id or name

  selection <arguments> take either:
    comma-separated list e.g. '-u root,nobody' or
    blank-separated list e.g. '-p 123 4567'

Output formats:
 -F                   extra full
 -f                   full-format, including command lines 
  f, --forest         ascii art process tree 显示进程间关系
 -H                   show process hierarchy 显示树形结构
 -j                   jobs format
  j                   BSD job control format
 -l                   long format
  l                   BSD long format
 -M, Z                add security data (for SE Linux)
 -O <format>          preloaded with default columns
  O <format>          as -O, with BSD personality
 -o, o, --format <format>
                      user defined format
  s                   signal format
  u                   user-oriented format
  v                   virtual memory format
  X                   register format
 -y                   do not show flags, show rrs vs. addr (used with -l)
     --context        display security context (for SE Linux)
     --headers        repeat header lines, one per page
     --no-headers     do not print header at all
     --cols, --columns, --width <num>
                      set screen width
     --rows, --lines <num>
                      set screen height

Show threads:
  H                   as if they where processes
 -L                   possibly with LWP and NLWP columns
 -m, m                after processes
 -T                   possibly with SPID column

Miscellaneous options:
 -c                   show scheduling class with -l option
  c                   show true command name 显示进程真实名称
  e                   show the environment after command
  k,    --sort        specify sort order as: [+|-]key[,[+|-]key[,...]]
  L                   list format specifiers
  n                   display numeric uid and wchan
  S,    --cumulative  include some dead child process data
 -y                   do not show flags, show rss (only with -l)
 -V, V, --version     display version information and exit
 -w, w                unlimited output width

        --help <simple|list|output|threads|misc|all>
                      display help and exit

For more details see ps(1).

基本使用

打印所有用户的运行进程

ps aux
  • a 显示所有用户的进程
  • u 显示进程的所有者
  • x 显示非终端启用的进程

输出:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0  10656   668 ?        Ss   Apr12   1:14 init [2]
root         2  0.0  0.0      0     0 ?        S    Apr12   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        S    Apr12   4:28 [ksoftirqd/0]
root         5  0.0  0.0      0     0 ?        S    Apr12   0:00 [kworker/u:0]

说明:

  • USER:该 process 属于那个使用者账号的
  • PID :该 process 的号码
  • %CPU:该 process 使用掉的 CPU 资源百分比
  • %MEM:该 process 所占用的物理内存百分比
  • VSZ :该 process 使用掉的虚拟内存量 (Kbytes)
  • RSS :该 process 占用的固定的内存量 (Kbytes)
  • TTY :该 process 是在那个终端机上面运作,若与终端机无关,则显示 ?,另外, tty1-tty6 是本机上面的登入者程序,若为 pts/0 等等的,则表示为由网络连接进主机的程序。
  • STAT:该程序目前的状态,主要的状态有
  • R :该程序目前正在运作,或者是可被运作
  • S :该程序目前正在睡眠当中 (可说是 idle 状态),但可被某些讯号 (signal) 唤醒。
  • T :该程序目前正在侦测或者是停止了
  • Z :该程序应该已经终止,但是其父程序却无法正常的终止他,造成 zombie (疆尸) 程序的状态
  • START:该 process 被触发启动的时间
  • TIME :该 process 实际使用 CPU 运作的时间
  • COMMAND:该程序的实际指令

显示所有进程信息

ps -A

输出:

  PID TTY          TIME CMD
    1 ?        00:01:20 systemd
    2 ?        00:00:00 kthreadd
    3 ?        00:00:13 ksoftirqd/0
    5 ?        00:00:00 kworker/0:0H
    7 ?        00:17:02 rcu_sched
    8 ?        00:00:00 rcu_bh
    9 ?        00:00:03 migration/0
   10 ?        00:00:02 watchdog/0

显示指定用户所有进程

ps a -u root

显示所有进程信息,连同命令行

 ps -ef
 ps aux

ps 与grep 常用组合用法,查找特定进程

ps -ef | grep ssh

输出:

ps -ef | grep ssh
root     18928 22845  0 21:10 ?        00:00:00 sshd: root@pts/4
root     19852 20288  0 21:25 pts/2    00:00:00 grep ssh
root     22845     1  0 Jun18 ?        00:00:32 /usr/sbin/sshd

将目前属于您自己这次登入的 PID 与相关信息列示出来

ps -l

输出:

F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 S     0 17398 17394  0  75   0 - 16543 wait   pts/0    00:00:00 bash
4 R     0 17469 17398  0  77   0 - 15877 -      pts/0    00:00:00 ps

各相关信息的意义:

  • F 代表这个程序的旗标 (flag), 4 代表使用者为 super user
  • S 代表这个程序的状态 (STAT),关于各 STAT 的意义将在内文介绍
  • UID 程序被该 UID 所拥有
  • PID 就是这个程序的 ID !
  • PPID 则是其上级父程序的ID
  • C CPU 使用的资源百分比
  • PRI 这个是 Priority (优先执行序) 的缩写,详细后面介绍
  • NI 这个是 Nice 值,在下一小节我们会持续介绍
  • ADDR 这个是 kernel function,指出该程序在内存的那个部分。如果是个 running的程序,一般就是 “-“
  • SZ 使用掉的内存大小
  • WCHAN 目前这个程序是否正在运作当中,若为 - 表示正在运作
  • TTY 登入者的终端机位置
  • TIME 使用掉的 CPU 时间。
  • CMD 所下达的指令为何

在预设的情况下, ps 仅会列出与目前所在的 bash shell 有关的 PID 而已,所以,当使用 ps -l 的时候,只有三个 PID。

列出类似程序树的程序显示

ps -axjf

输出:

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    0     2     0     0 ?           -1 S        0   0:00 [kthreadd]
    2     3     0     0 ?           -1 S        0   4:28  \_ [ksoftirqd/0]
    2     5     0     0 ?           -1 S        0   0:00  \_ [kworker/u:0]
    2     6     0     0 ?           -1 S        0   0:00  \_ [migration/0]
    1 20287 20287 20287 ?           -1 Ss       0   0:01 SCREEN -S lnmp
20287 20288 20288 20288 pts/2    19855 Ss       0   0:00  \_ /bin/bash
20288 19855 19855 20288 pts/2    19855 R+       0   0:00      \_ ps -axjf

找出与 cron 与 syslog 这两个服务有关的 PID 号码

ps aux | grep '(cron|syslog)'

输出:

root      2682  0.0  0.0  83384  2000 ?        Sl   Nov02   0:00 /sbin/rsyslogd -i /var/run/syslogd.pid -c 5
root      2735  0.0  0.0  74812  1140 ?        Ss   Nov02   0:00 crond
root     17475  0.0  0.0  61180   832 pts/0    S+   16:27   0:00 egrep (cron|syslog)

输出指定的字段

ps -o pid,ppid,pgrp,session,tpgid,comm

  PID  PPID  PGRP  SESS TPGID COMMAND
17398 17394 17398 17398 17478 bash
17478 17398 17478 17398 17478 ps

外延

ps aux | less
ps aux > /tmp/ps.txt

2016-04-25 ps , command , linux , process

should I git ignore .project file under eclipse

Recently, I have worked on a C++ project create by eclipse using CDT. And I generate my .gitignore file at gitignore.io. However when I shared my project to Git, and want to import my project on other computer. I find I cannot import my Exist project into Eclipe workspace.

After I did search, I notice that my gitignore file just ignore .project file which has an annotation says that it’s Eclipse Core. I found that each time Eclipse want to import an exist project. Eclipse will try to find this file. The eclipse documentation states the porpuse of .project file as follow:

The purpose of this file is to make the project self-describing, so that a project that is zipped up or released to a server can be correctly recreated in another workspace.

and

If a new project is created at a location that contains an existing project description file, the contents of that description file will be honoured as the project description. One exception is that the project name in the file will be ignored if it does not match the name of the project being created. If the description file on disk is invalid, the project creation will fail.

So I decide to not ignore .project in git version control. And remove the .project ignore in .gitignore file. After that it is easy for me to import project into Eclipse.

I followed @lanoxx’s idea to keep .project file under git version control. So after you cloned your repository on other place, you can simply use Import -> Existing Project from Workspace. Eclipse will take care the .project file and recreate other project related config files for you, like .cproject under C++ project, and .classpath or .settings/ under java environment.

If you do not share the .project file, then it is not possible to import the project with Eclipse. You will need to create a new project with the project wizard first, and then you can choose import “General->File System”, this will copy all the files into your workspace. This is probably not what you want, because it means that you cannot clone the git repository into the workspace, you must clone it somewhere else and then import it from there. Therefore you should always share the .project file.

About all, I talk about the .project file. Following I have done a little search for the .cproject. This file contain all the settings provided for the particular selected Toolchain. For example, if the project needs to be created with gcc, then this .cproject file will contain all the compiler, linker options used by gcc. This file is also important to reimport the project. All your settings in project properties will remain if you choose to keep this file under track.

reference


2016-04-23 eclipse , C++ , CDT , git , gitignore

每天学习一个命令:pssh 一条命令在多个主机上执行

OpenSSH 毫无疑问是世界上使用最广泛允许用户通过终端安全连接远程主机的工具了,但是 OpenSSH 存在一个比较大的问题就是不能在多台主机中执行相同的命令,OpenSSH 并不是设计做此用途的。所以 Parallel SSH 或者简称 PSSH 就出现了。PSSH 是一个 python 编写的用来在多台主机中并行执行相同命令的工具。

PSSH 工具集包含如下命令:

  • pssh
  • pscp
  • prsync
  • pnuke - 并行在多台机器中杀死进程
  • pslurp - 从多台主机中拷贝文件到中心主机

installation

sudo apt install python-pip
sudo pip install pssh

使用

首先创建 pssh host 文件

192.168.0.10:22
192.168.0.11:22

比如执行 echo

pssh -h pssh-hosts-file -l root -A echo "TEST"

比如查看硬盘使用情况

pssh -h pssh-hosts-file -l root -A -i "df -hT"

同理

pssh -h pssh-hosts-file -l root -A -i "uptime"

总结

PSSH 工具适合用来在管理员需要在多台主机上执行重复命令时。

reference


2016-04-20 pssh , ssh , openssh

btsync 体验

最近在安装 Resilio Sync 的时候发现,竟然被屏蔽了,官网被屏蔽了竟然连 key 都下载不了,可以使用之前写过的 proxychains 代理 来使用代理下载这个 key。

更新

惊闻 btsync 已经改名字,现在叫 Resilio Sync ,官网地址也改为: https://www.resilio.com/

可以从 这里 获取不同平台客户端。

Linux 安装 Package 可以参考 https://help.getsync.com/hc/en-us/articles/206178924 这里

创建文件 /etc/apt/sources.list.d/resilio-sync.list, 添加如下内容:

deb http://linux-packages.resilio.com/resilio-sync/deb resilio-sync non-free

添加公钥:

wget -qO - https://linux-packages.resilio.com/resilio-sync/key.asc | sudo apt-key add -

安装:

sudo apt-get update
sudo apt-get install resilio-sync

配置文件存储地址 https://help.getsync.com/hc/en-us/articles/206664690

Linux 下配置文件地址: /var/lib/resilio-sync

啊,安装好了之后,添加这个 key,当个示例啦 BB63I5PBPBFDELAPXI6NTF47IPNZQAAJZ ,一周一本好书。

如果想要手动开启或者关闭 Resilio Sync 可以使用如下命令:

sudo service resilio-sync stop
sudo service resilio-sync start

原文

全称 BitTorrent Sync , 我习惯了叫他 btsync 了。想要了解他的前世今生直接去看维基百科就好了。一句话概括,他就是一个同步工具,类Dropbox,但是利用 P2P等等 bt 种子的技术。当然私人使用当成网盘工具也好,当成分享工具也好,看个人使用了。不过黑客提醒,虽然是去中心化的,但是安全性依然存在问题,最好不要传输私人信息。

官网地址: https://www.getsync.com/

全平台

现在使用任何一个工具或者服务,我首要考虑的问题离不开跨平台了,最好是Windows , Linux, Mac 下全部都有,不然在平台间来回切换不同的服务和工具时间成本,学习成本太高了。也正是因为这个原因我放弃了 Google Drive 而转用 Dropbox,作为主力同步工具。当然 btsync 在全平台都有客户端,甚至连一些 NAS,路由器设备都有。

安装

安装非常简单,去官网下载,下一步下一步,OK。当然 Linux 下,如果不想使用 下一步下一步安装法,也可以使用命令从 PPA 里拖。

PPA

sudo add-apt-repository ppa:tuxpoldo/btsync
sudo apt-get update

For normal desktop use, you only need to install btsync-user:

sudo apt-get install btsync-user

Alternatively, if you’re setting up your BTSync server, install btsync:

sudo apt-get install btsync

btsync client

在官网根据自己的机器选择合适的 client 下载并解压。并运行:

./btsync

即可。

默认的Web GUI地址是 : http://127.0.0.1:8888

更加详细的安装指南可以参考这篇

VPS上架设

类似 Linux 下安装,官网下载并解压 btsync 文件。

tar -zxvf BitTorrent-Sync_x64.tar.gz

然后执行:

./btsync --dump-sample-config > btsync.conf

创建配置文件,然后修改 btsync.conf 配置文件中的:

"listen" : "0.0.0.0:8888" 

还有 loginpassword , 端口默认是8888,可修改成其他没有冲突的。loginpassword 是登陆用户名和密码。其他配置看注释修改即可。参考官网 config 文章.

然后保存配置文件,启动:

./btsync --config btsync.conf

在浏览器中就能够在 http://ip:port/ 访问 Web GUI。

然后在本地获取同步 key ,和 VPS 上同步即可。

技巧

移动同步后的文件夹

如果你已经同步了一个文件夹,比如在 ~/books,现在想要将该同步的目录移动到 ~/btsync/books 目录下。 就像Dropbox 同步已经存在的文件夹一样,如果单纯的再重新下载一边太麻烦了。所以幸好 btsync 和 dropbox 都有这样的性质,同步的内容都有文件记录,将文件重新加入索引,等索引完之后就可以继续和其他的文件同步了。

  1. 拷贝该文件夹的”共享秘钥”
  2. 从 btsync 中移除该文件夹
  3. 在本地硬盘移动文件夹到新的位置
  4. 重新在 btsync 用之前的”共享秘钥“,添加该文件夹

VPS 上启用 https

默认 btsync 的 web gui 是没有启用加密的,如果想要使用 https://ip:port/gui 来访问,则需要使用 config 配置,并在config 配置中设置 force_https, ssl_certificate,ssl_private_key ,然后重启 btsync 。

如果觉得这样让 btsync 直接获取证书不安全,这里 还有另外一种配置,利用 nginx 的代理。

分享密钥的网站

推荐几个常用 KEY

  • 神key BCWHZRSLANR64CGPTXRE54ENNSIUE5SMO
  • 最新电影 BA6RXJ7YOAOOFV42V6HD56XH4QVIBL2P6
  • 经济学人 BYRRPM52YK6Z6TETDQITFXBV647XLCNIO
  • Kindle 中文伴侣精品 BOC3NIGPF2DOKETOF2FAHXJXE2HF24QWC
  • 每周更新图书 BB63I5PBPBFDELAPXI6NTF47IPNZQAAJZ

reference


2016-04-18 btsync , 产品体验

小行星视图App review

最近迷上了360度全景,搞了得图800,和理光 theta m15的全景相机玩。然后就对周边的全景处理App都体验了一下。最早接触到 Sphere Photo 也要归功于 Google 的原生 Camera, 然而因为不知道的原因,在 Android kitkat 之后的版本中这个小行星视图就消失不见了。而我还是依然很怀念这样的视图。下面就是一些 Play Store 中存在的制作小行星视图的App。

Android Apps

Tiny Planet FX Pro

完整却不够完美 市场上唯一一款收费的小行星app,但是给我的感觉却是做的不够精致和完美,今天竟然处理出不完整的人脸。处理图像甚至比不上另一款免费的app。

Little Planet

除了开始的广告一切还好 开始的广告可能是让很多人打低分的原因,但是这款app,确实达到了我的需求,并且实现的很好。开发者可以尝试提供pro版本来去除广告或者内购去广告。

Tiny Planet - Globe Photo

广告多功能简单 太多影响操作的广告,横幅广告侵占了操作预览区域。功能比不上其他app。

Spherify

功能简单广告影响使用 功能几乎没有,处理时间太长。广告占据太多篇幅。

最后推荐 Little Planet , 虽然在App开始出现了全屏广告,但是效果及功能和收费的 Tiny Planet FX Pro 相差不大。

temple

android

cosoc

How to make sphere photo using photoshop

大多数时候我没有时间用 Google Camera 照完整个全景,这个时候如果可以后期合成当然是最好的,于是就有了这样一篇文章 ,只要有一张全景照片,利用 Photoshop 同样可以实现 Sphere Photo。 同样也可以参考这篇文章

最后推荐一个社区 https://plus.google.com/u/0/communities/115970110085205516914 ,社群的简介里面有很多教程,并且分享的 Post,质量都很高。


2016-04-16 Android , SpherePhoto , Android app

Python 标准库网关接口 wsgiref

The Web Server Gateway Interface(WSGI) 是网络服务器软件和网络应用程序之前的标准接口。通过共同的网关标准接口可以让同一个应用支持不同的服务器。只有网络服务器或者编程框架的作者需要熟悉 WSGI 设计,如果使用现存的框架则不太需要关心更底层的网关接口。

wsgiref 是 Python 的 WSGI 标准实现,可以用来帮助实现网络服务器和应用框架。他提供了一系列工具用来操作 WSGI 环境变量和响应头,也提供了基础的类来实现 WSGI 服务器,提供了简单的 HTTP 服务来给 WSGI 应用提供服务器,提供了一个符合 WSGI 标准(PEP 3333) 的验证工具来验证 WSGI 服务器和应用。

文档

几个部分

wsgiref.util

wsgiref.util 这个模块提供了一系列工具用来操作 WSGI environments。

wsgiref.headers

该模块提供了一个简单的类 Headers 用来操纵 WSGI 相应头。

wsgiref.simple_server

该模块基于 http.server 实现了一个简单的 HTTP 服务器,这个服务器能够给 WSGI 应用提供服务。每一个服务器实例在特定的 host 和 port 上提供一个 WSGI 应用服务。如果要在听一个 host 和 port 上给不同应用程序提供服务,那么你需要创建一个 WSGI 应用,并且传入 PATH_INFO 来选择哪一个应用程序来来被每一次请求调用。

wsgiref.validate

验证模块。

wsgiref.handlers

该模块提供了实现 WSGI 服务器和网关的基础类。这些基础类能够处理和 WSGI 应用的大部分通信。

Example

简单的例子

from wsgiref.simple_server import make_server

# Every WSGI application must have an application object - a callable
# object that accepts two arguments. For that purpose, we're going to
# use a function (note that you're not limited to a function, you can
# use a class for example). The first argument passed to the function
# is a dictionary containing CGI-style environment variables and the
# second variable is the callable object (see PEP 333).
def hello_world_app(environ, start_response):
    status = '200 OK'  # HTTP Status
    headers = [('Content-type', 'text/plain; charset=utf-8')]  # HTTP Headers
    start_response(status, headers)

    # The returned object is going to be printed
    return [b"Hello World"]

httpd = make_server('', 8000, hello_world_app)
print("Serving on port 8000...")

# Serve until process is killed
httpd.serve_forever()

reference


2016-04-06 wsgi , python , wsgiref

MyBatis 自动生成的 Java client 方法区别

MyBatis 自动生成的 Java client generator 会产生如下的方法;

  • countByExample
  • deleteByPrimaryKey
  • deleteByExample
  • insert
  • insertSelective
  • selectByPrimaryKey
  • selectByExample
  • selectByExampleWithBLOBs
  • updateByPrimaryKey (with an override to specify whether or not to update BLOB columns)
  • updateByPrimaryKeySelective (will only update non-null fields in the parameter class)
  • updateByExample (with an override to specify whether or not to update BLOB columns)
  • updateByExampleSelective (will only update non-null fields in the parameter class)

前面一些方法看名字都能知道其用法,但是有些还是有些模棱两可。比如 withBLOBs 和 没有 BLOB 方法的区别。

selectByExample 和 selectByExampleWithBLOBs 区别

如需检索的字段中包含大字段类型时,必须用 selectByExampleWithBLOBs,不检索大字段时,用 selectByExample 就足够了。update 同样如此。

MyBatis Generator

MyBatis GeneratorXML 配置文件在大多数情况下由 XML 配置提供。文件会配置 MyBatis Generator :

  • 如何连接数据库
  • 生成什么 Object,以及如何生成
  • 哪一些数据表需要被生成 Object

下面是基础的模板

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
  <classPathEntry location="/Program Files/IBM/SQLLIB/java/db2java.zip" />

  <context id="DB2Tables" targetRuntime="MyBatis3">
    <jdbcConnection driverClass="COM.ibm.db2.jdbc.app.DB2Driver"
        connectionURL="jdbc:db2:TEST"
        userId="db2admin"
        password="db2admin">
    </jdbcConnection>

    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

    <javaModelGenerator targetPackage="test.model" targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <sqlMapGenerator targetPackage="test.xml"  targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

    <javaClientGenerator type="XMLMAPPER" targetPackage="test.dao"  targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

    <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
      <property name="useActualColumnNames" value="true"/>
      <generatedKey column="ID" sqlStatement="DB2" identity="true" />
      <columnOverride column="DATE_FIELD" property="startDate" />
      <ignoreColumn column="FRED" />
      <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
    </table>

  </context>
</generatorConfiguration>

context 元素

<context> 元素用来定义生成 Object 的环境,子元素用来定义数据库连接方式,生成对象的类型,和需要生成的 table. 多个 <context> 标签可以配置在 <generatorConfiguration> 下,允许配置多个数据库连接。

jdbcConnection

顾名思义,连接数据库配置

plugin 元素

<plugin> 下定义一些插件,这些插件用来扩展或者修改 MyBatis Generator 生成的代码。plugin 是 context 的子元素。

generatorConfiguration

generatorConfiguration 配置,文档地址这里,每个子元素文档都存在。

javaTypeResolver

这个标签用来配置 MySQL 数据类型到 Java 类型转换过程的精度,比如使用 forceBigDecimals 那么就是默认尝试使用 java.math.BigDecial 来处理 Decimal 和 Numberic 字段。


2016-04-05 java , mybatis , mysql

Python 容易混淆的知识点

星号解压列表元组

简单的解压列表和元组就省略,如果在解压时想要忽略一个元素,之前我们知道可以使用 _ 来忽略

first, _ = ("Ein", "Verne")

这是第二个元素不关心,也就不取了,但是如果要忽略一批元素呢

>>> record = ('ACME', 50, 3.14, (06,04,1989))
>>> name, *_, (*_, year) = record

这时就可以批量忽略中间的 50, 3.14 还有括号中的月份日期了。

Python 中的 slice

之前在看 slice 相关的内容的时候只简单的看了类似 list[:5] 这样的切片操作,几天突然朋友问道下面三元的切片操作,竟然一时没有反应过来,去看了一下文档,原来 slice 可以支持三个参数 obj[start:stop:step],那么综合之前学习的内容就很好解释了。

先来复习一下,假设 l = list(range(10)),那么

l[1:3]              # 结果 [1,2]  左边包括,右边不包括
l[:5]               # [0,1,2,3,4]  不包括 index 为 5 的值
l[5:]               # [5,6,7,8,9]
l[-5:]              # 取后 5 个 [5,6,7,8,9]
l[:-4]              # 除了后四位,输出 [0,1,2,3,4,5]

所以只有 l[start:end] 两个参数的很好理解,只需要直到切片操作是负数表示的序列中的 index 就行。

如果加上 step,那就是 l[start:end:step],而对于正数 step 也很好理解:

l[1:5:2]            # 从第 1 位到第 5 位,不包括 5 位,每次前进 2 个,结果 [1,3]
l[:5:1]             # 从第 0 个取到第 5 个,不包括第 5 位,每次前进 1,输出 [0,1,2,3,4]
l[5::1]             # 从 index 5 取值到最后,每次进一位,输出 [5,6,7,8,9]

再来看下 step 为负数的情况

l[::-1]             # 逆序输出所有的元素
l[-1:-5:-1]         # 第 -1 位到第 -5 位,不包括第 -5 位,每次往前一位,所以为 [9,8,7,6]
l[-1::-1]           # 从最后一位开始,每次往前一位,直到最前面 输出 [9,8,7,6,5,4,3,2,1,0]
l[5::-1]            # 可以怎么理解,从序号 5 开始,每次往前一位,直到最后,所以输出 [5,4,3,2,1,0]

如果 start 没有被省略,那么找到 start,然后往前查找直到 stop 就行,而如果 start 被省略,则需要从最末尾往前:

l[:5:-1]            # 因为省略了开头,所以需要看 step 正负,这里为 -1,所以从最后一位往前直到 index 为 5,所以输出 [9,8,7,6]

这样就比较清除的解决这些问题了,如果 step 换成 2 ,也是一样的模式。

打开文件的模式

python 文件处理时会遇到 open("filename", "mode") 这个函数后面的参数模式:

  • r 只读模式打开文件,文件指针在文件开头
  • rb 以二进制读,文件指针在文件开头
  • r+ 读写模式 (cannot truncate a file),文件指针在文件开头
  • rb+ 以二进制文件读或者写,文件指针在文件开头
  • w 以写模式打开文件,只写入,任何同名文件会被覆盖,如果文件不存在会创建新文件写入
  • w+ 读写模式 (can truncate a file)
  • wb 以二进制模式写,同名文件覆盖,不存在创建新文件
  • wb+ 以二进制模式读写,同名文件覆盖,不存在创建
  • a 附加模式,在文件末增加,文件指针在文件末尾,如果文件不存在会创建新的文件写
  • ab 以二进制形式附加,文件指针在末尾
  • a+ 附加,和读 打开文件,指针在文件末尾
  • ab+ 以二进制打开文件读或者附加,如果文件存在,文件指针指向文件末尾
  • x python 3 中新模式,如果文件存在会创建失败

所以可以总结一些规律

  • b 模式是以二进制形式打开
  • + 如果放在 r 后,是读写,放在 w 后也是 读写,所以有 + 模式表示读和写

String Bytes and Unicode in Python

例子

# Python 2

>>> print type("Hello World!")
<type 'str'>
# this is a byte string

>>>print type(u"Hello World!")
<type 'unicode'>
# this is a Unicode string

Python 3

# Python 3

>>> print(type("Hello World!"))
<class 'str'>
# this is a Unicode string

>>> print(type(b"Hello World!"))
<class 'bytes'>
# this is a byte string

总而言之,Python 2 中字面 str 以 bytes 存储,前缀 u’hello’ 表示 unicode 对象,以 unicode 存储。

而 Python 3,字面 str 以 Unicode 存储,前缀 b’hello’ 表示获取 bytes 对象,或者 str.encode() 来获取 bytes 对象。

Python 3.x 中有三种 string 对象类型,一种是文本类型,另外两种是二进制数据

  • str 表示 Unicode 文本 (both 8bit and wider)
  • bytes 表示二进制内容
  • bytearray,一种可变的 bytes 类型

encoding vs decoding

类型转换

  • encoding 是将 string 根据 encoding name 转变为 raw bytes
  • decoding 是将 raw string 根据 encoding name 转变为 string

将 string 转换为 raw bytes

  • str.encode() 或者 str(B, encoding)

将 raw bytes 转变为 str

  • bytes(S, encoding) 或者 bytes.decode()

Unicode 编码

在处理 Unicode 编码文本,Python 支持

  • “\xNN” hex byte value escapes
  • “\uNNNN” and “\UNNNNNNNN” Unicode escapes,在 unicode escapes 中,前一种使用 4 个十六进制数字编码 2-byte(16-bit) 字符串,后一种使用 8 个十六进制数字编码 4-byte(32-bit) 文本

ASCII 编码

在处理 ASCII 编码时

>>> ord('X')
88
>>> chr(88)
'X'

>>> S = 'XYZ'
>>> S.encode('ascii')               # Values 0..127 in 1 byte (7 bits) each
>>> S.encode('latin-1')             # Values 0..255 in 1 byte (8 bits) each
S.encode('utf-8')                   # Values 0..127 in 1 byte, 128..2047 in 2, others 3 or 4

非 ASCII 编码

在处理非 ASCII 编码时,可能会用到之前提到的 Unicode 编码

>>> chr(0xc4)                       # 0xC4, 0xE8: characters outside ASCII's range
>>> S = '\xc4\xe8'                  # Single byte 8-bit hex escapes
>>> S = '\u00c4\u00e8'              # 16-bit Unicode escapes
>>> len(S)                          # 2 characters long (not number of bytes!)

编解码非 ASCII 编码

如果我们使用 encode 来将一个非 ASCII 字符串使用 ASCII 编码转变为 raw bytes ,会抛出错误。

>>> S = '\u00c4\u00e8'
>>> S.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1:
ordinal not in range(128)

>>> S.encode('latin-1')             # One byte per character
b'\xc4\xe8'

>>> S.encode('utf-8')               # Two bytes per character
b'\xc3\x84\xc3\xa8'

>>> len(S.encode('latin-1')))
2
>>> len(S.encode('utf-8'))
4

多重继承

在 Python 中如果一个类继承自多个类,这种行为被称为多重继承 multiple inheritance,虽然多重继承非常有用,但是需要注意一些问题,否则会出现不可预见的问题。

在使用多重继承时,如果一个方法从多个超类中继承,先继承的类中的方法会重写后继承类中的方法。(超类顺序为定义 class 时的顺序)。

property

@property 是一个将用户计算的东西伪装成为一个属性。

from math import pi

class Circle():
    def __init__(self,r):
        self.r = r

    @property
    def area(self):
        return pi*self.r**2

c = Circle(2)
print(c.area)

在这里 area() 虽然被定义为一个方法,但是类的实例可以使用点来访问 area ,假装是类的一个属性。

静态方法和类成员方法

Python 2.4 中,引入了装饰器(decorators) 的语法,能够对任何可调用的对象进行包装,既能够用于方法也能够用于函数。使用 @ 操作符,在方法或函数上将装饰器列出,指定一个或者多个装饰器。多个装饰器在应用时的顺序与指定顺序相反。

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')

classmethod 必须有一个指向 class object 的 reference 作为第一参数,而 staticmethod 则不需要。classmethod 通常被用来作为构造函数重载。

Class Method

C++ 有重载的功能,但是 Python 缺乏重载的机制,所以就有了 classmethod,可以想象成另外一个构造函数

@classmethod
def from_string(cls, date_as_string):
    day, month, year = map(int, date_as_string.split('-'))
    date1 = cls(day, month, year)
    return date1

date2 = Date.from_string('11-09-2012')

cls 是一个持有 class self 的对象,但是不是 class 的一个实例。如果继承了 Date 类,所有的子类都会拥有 from_string 方法。

Static method

对于这个例子,is_date_valid 方法不需要关心类内部的其他变量和方法,但是这个 valid 方法和 Date 相关,可以定义为静态方法。和其他语言中的静态方法可以一起理解。

getattr setattr

拦截 intercept 对象的所有特性访问是可能的,然后有一些魔法方法。比如 __getattr___setattr__

  • __getattribute__(self.name) 当特性 name 被访问时自动被调用,只能在新式类中使用
  • __getattr__(self.name) 当特性 name 被访问且对象没有相应的特性时被自动调用
  • __setattr__(self.name.value) 当试图给特性 name 赋值时被自动调用
  • __delattr__(self.name) 当试图删除特性 name 时被自动调用

这里是一些区别

  • __getattribute__ 无条件被调用,任何对象的属性访问时都会隐式调用,比如 t.__dict__ 其实是执行了 t.__getattribute__("__dict__") ,所以如果我们在重载__getattribute__中又调用__dict__的话,会无限递归,用 object 来避免,即 object.getattribute(self, name)
  • __getattr__ 只有 __getattribute__ 找不到的时候才会调用 __getattr__

举例

class Attr(object):

    def __init__(self, name):
        self.name = name

    def __getattribute__(self, item):
        print("__getattribute__ " + item)
        return object.__getattribute__(self, item)

    def __getattr__(self, item):
        print("__getattr__ " + item)

    def __setattr__(self, key, value):
        print("__setattr__ key %s, value %s" % (key, value))
        object.__setattr__(self, key, value)


if __name__ == '__main__':
    attr = Attr('wendy')
    print("main " + attr.name)
    attr.name = 'nancy'

在这个例子中,输出结果

__setattr__ key name, value wendy
__getattribute__ name
main wendy
__setattr__ key name, value nancy

迭代器

在 Python 中,很多对象都是可以通过 for 语句来直接遍历的,例如 list、string、dict 等等,这些对象都可以被称为可迭代对象。迭代器对象要求支持迭代器协议的对象,在 Python 中,支持迭代器协议就是实现对象的__iter__()next() 方法。其中__iter__() 方法返回迭代器对象本身;next() 方法返回容器的下一个元素,在结尾时引发 StopIteration 异常。

class PowTwo:
    """Class to implement an iterator
    of powers of two"""

    def __init__(self, max=0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n <= self.max:
            result = 2 ** self.n
            self.n += 1
            return result
        else:
            raise StopIteration


p = PowTwo(5)
for i in p:
    print(i)

生成器

生成器通过生成器函数产生,生成器函数可以通过常规的 def 语句来定义,但是不用 return 返回,而是用 yield 一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议。

在理解生成器之前,首先要先理解上面提到的迭代器,使用迭代器能够非常轻松的遍历列表等等中的内容,虽然迭代器很好用,但是迭代器会将内容全都放到内存中,所以一旦遇到特别庞大的列表,可能就会遇到问题。

所以有了生成器,生成器是一种特殊的迭代器,只能够遍历一次。

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)

这里 mygenerator 需要使用 ()

然后 yield 关键字,就像 return ,但是表示方法会返回一个生成器。

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

返回 yield 的方法在方法被调用的时候并不会执行,只有在使用 for 来遍历该生成器时,才会执行。执行的顺序是,方法每 yield 一次就返回一次,等待 for 中执行完毕,继续执行生成器方法中的下一次 yield,然后返回 for 中,然后反复执行直到生成器没有 yield 任何内容。

模块

告诉编译器去哪里找,一种方法就是编辑 sys.path 另外一种方法就是使用 PYTHONPATH 环境变量

Python 3.6.1 (default, Apr 23 2017, 12:32:16)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.path)
['', '/home/einverne/.pyenv/versions/3.6.1/lib/python36.zip', '/home/einverne/.pyenv/versions/3.6.1/lib/python3.6', '/home/einverne/.pyenv/versions/3.6.1/lib/python3.6/lib-dynload', '/home/einverne/.pyenv/versions/3.6.1/lib/python3.6/site-packages']

对于不同的操作系统 PYTHONPATH 配置可能有些差异,在类 Unix 系统下

export PYTHONPATH=$PYTHONPATH:~/pypath:~/py1

为组织模块,可以分组为包 package, 为了让 Python 将其作为包对待,必须包含一个 __init__.py 的文件。

假如构造了如下的目录结构

├── douban
│   ├── album.py
│   ├── celebrity.py
│   ├── __init__.py

定义了 douban 目录,那么该文件夹下的 __init__.py 就是 douban 包(模块)的代码,其他两个 album.pycelebrity.py 分别是 album 和 celebrity 模块。

如果单纯的引入

import douban

那么 __init__ 模块的内容是可用的,但是其他两个模块是不可用的。

import douban.album

那么这个时候 album 模块也是可用的。但这时候只能通过全名 douban.album. 来使用。

from douban import celebrity

这时候 celebrity 模块可用,可以通过短名来使用,比如 celebrity.xxx

模块中定义 __all__ = [] 向外导出可用方法。在别人应用该模块时,打印 __all__ 内容就能够清晰看到模块对外提供的方法。

标准库

sys 模块能够轻松访问 Python 解释器联系的变量和函数

函数或变量 描述
argv 命令行参数,包括脚本名字
exit([arg]) 退出,可选参数给定返回值或错误
modules 映射模块名字到载入模块的字典
path 查找模块所在目录的目录名列表
platform 平台
stdin 标准输入流
stdout 标准输出流
stderr 标准错误流

os 模块提供了访问操作系统服务的功能。

os.path子模块包含了检查构造删除目录和文件的函数。

time 模块能够实现,获取当前时间,操作时间和日期,从字符串读取时间以及格式化时间为字符串等等功能。

函数 描述
asctime([tuple]) 将时间元组转换为字符串
localtime([secs]) 将秒转换为日期元组,本地时间
mktime(tuple) 将时间元组转换为本地时间
sleep(secs) 休眠
strptime(string[, format]) 将字符串解析为时间元组
time() 当前时间,秒

random 模块包括返回随机数的函数,可以用于模拟或者任何产生随机数的程序。

f-Strings

f-Strings 在 Python 3.6 以后引入的新特性,新语法,用来输出格式化的文本 (PEP 498)

>>> name = "Eric"
>>> age = 74
>>> f"Hello, {name}. You are {age}."
'Hello, Eric. You are 74.'

Python 以前的格式化输出,总或多或少有些毛病

>>> "Hello, %s. You are %s." % (name, age)
>>> "Hello, {}. You are {}.".format(name, age)

关于输出字符串各种方式的优缺点、性能比较可以参考这篇

reference


2016-04-04 python , class , inheritance

电子书

最近文章