coding( ⊙ o ⊙ )
2014-11-25T02:39:54+00:00
飞飞的菜园子
haogefeifei@qq.com
Git入门 - 2.Git基础
2014-06-17T00:00:00+00:00
/git/2014/06/17/git-basics
<p>文章源自:https://github.com/jnavila/progit</p>
<h1 id="git-">Git 基础</h1>
<p>读完本章你就能上手使用 Git 了。本章将介绍几个最基本的,也是最常用的 Git 命令,以后绝大多数时间里用到的也就是这几个命令。读完本章,你就能初始化一个新的代码仓库,做一些适当配置;开始或停止跟踪某些文件;暂存或提交某些更新。我们还会展示如何让 Git 忽略某些文件,或是名称符合特定模式的文件;如何既快且容易地撤消犯下的小错误;如何浏览项目的更新历史,查看某两次更新之间的差异;以及如何从远程仓库拉数据下来或者推数据上去。</p>
<h2 id="git--1">取得项目的 Git 仓库</h2>
<p>有两种取得 Git 项目仓库的方法。第一种是在现存的目录下,通过导入所有文件来创建新的 Git 仓库。第二种是从已有的 Git 仓库克隆出一个新的镜像仓库来。</p>
<h3 id="section">在工作目录中初始化新仓库</h3>
<p>要对现有的某个项目开始用 Git 管理,只需到此项目所在的目录,执行:</p>
<p>$ git init</p>
<p>初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目录,但我们还没有开始跟踪管理项目中的任何一个文件。(在第九章我们会详细说明刚才创建的 <code>.git</code> 目录中究竟有哪些文件,以及都起些什么作用。)</p>
<p>如果当前目录下有几个文件想要纳入版本控制,需要先用 <code>git add</code> 命令告诉 Git 开始对这些文件进行跟踪,然后提交:</p>
<p>$ git add *.c
$ git add README
$ git commit -m ‘initial project version’</p>
<p>稍后我们再逐一解释每条命令的意思。不过现在,你已经得到了一个实际维护着若干文件的 Git 仓库。</p>
<h3 id="section-1">从现有仓库克隆</h3>
<p>如果想对某个开源项目出一份力,可以先把该项目的 Git 仓库复制一份出来,这就需要用到 <code>git clone</code> 命令。如果你熟悉其他的 VCS 比如 Subversion,你可能已经注意到这里使用的是 <code>clone</code> 而不是 <code>checkout</code>。这是个非常重要的差别,Git 收取的是项目历史的所有数据(每一个文件的每一个版本),服务器上有的数据克隆之后本地也都有了。实际上,即便服务器的磁盘发生故障,用任何一个克隆出来的客户端都可以重建服务器上的仓库,回到当初克隆时的状态(虽然可能会丢失某些服务器端的挂钩设置,但所有版本的数据仍旧还在,有关细节请参考第四章)。</p>
<p>克隆仓库的命令格式为 <code>git clone [url]</code>。比如,要克隆 Ruby 语言的 Git 代码仓库 Grit,可以用下面的命令:</p>
<p>$ git clone git://github.com/schacon/grit.git</p>
<p>这会在当前目录下创建一个名为<code>grit</code>的目录,其中包含一个 <code>.git</code> 的目录,用于保存下载下来的所有版本记录,然后从中取出最新版本的文件拷贝。如果进入这个新建的 <code>grit</code> 目录,你会看到项目中的所有文件已经在里边了,准备好后续的开发和使用。如果希望在克隆的时候,自己定义要新建的项目目录名称,可以在上面的命令末尾指定新的名字:</p>
<p>$ git clone git://github.com/schacon/grit.git mygrit</p>
<p>唯一的差别就是,现在新建的目录成了 <code>mygrit</code>,其他的都和上边的一样。</p>
<p>Git 支持许多数据传输协议。之前的例子使用的是 <code>git://</code> 协议,不过你也可以用 <code>http(s)://</code> 或者 <code>user@server:/path.git</code> 表示的 SSH 传输协议。我们会在第四章详细介绍所有这些协议在服务器端该如何配置使用,以及各种方式之间的利弊。</p>
<h2 id="section-2">记录每次更新到仓库</h2>
<p>现在我们手上已经有了一个真实项目的 Git 仓库,并从这个仓库中取出了所有文件的工作拷贝。接下来,对这些文件作些修改,在完成了一个阶段的目标之后,提交本次更新到仓库。</p>
<p>请记住,工作目录下面的所有文件都不外乎这两种状态:已跟踪或未跟踪。已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新,已修改或者已放入暂存区。而所有其他文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存区域。初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改。</p>
<p>在编辑过某些文件之后,Git 将这些文件标为已修改。我们逐步把这些修改过的文件放到暂存区域,直到最后一次性提交所有这些暂存起来的文件,如此重复。所以使用 Git 时的文件状态变化周期如图 2-1 所示。</p>
<p>Insert 18333fig0201.png
图 2-1. 文件的状态变化周期</p>
<h3 id="section-3">检查当前文件状态</h3>
<p>要确定哪些文件当前处于什么状态,可以用 <code>git status</code> 命令。如果在克隆仓库之后立即执行此命令,会看到类似这样的输出:</p>
<p>$ git status
On branch master
nothing to commit, working directory clean</p>
<p>这说明你现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过。此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪的新文件,否则 Git 会在这里列出来。最后,该命令还显示了当前所在的分支是 <code>master</code>,这是默认的分支名称,实际是可以修改的,现在先不用考虑。下一章我们就会详细讨论分支和引用。</p>
<p>现在让我们用 vim 创建一个新文件 README,保存退出后运行 <code>git status</code> 会看到该文件出现在未跟踪文件列表中:</p>
<p>$ vim README
$ git status
On branch master
Untracked files:
(use “git add <file>..." to include in what will be committed)</file></p>
<pre><code> README
</code></pre>
<p>nothing added to commit but untracked files present (use “git add” to track)</p>
<p>在状态报告中可以看到新建的<code>README</code>文件出现在“Untracked files”下面。未跟踪的文件意味着Git在之前的快照(提交)中没有这些文件;Git 不会自动将之纳入跟踪范围,除非你明明白白地告诉它“我需要跟踪该文件”,因而不用担心把临时文件什么的也归入版本管理。不过现在的例子中,我们确实想要跟踪管理 README 这个文件。</p>
<h3 id="section-4">跟踪新文件</h3>
<p>使用命令 <code>git add</code> 开始跟踪一个新文件。所以,要跟踪 README 文件,运行:</p>
<p>$ git add README</p>
<p>此时再运行 <code>git status</code> 命令,会看到 README 文件已被跟踪,并处于暂存状态:</p>
<p>$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> new file: README
</code></pre>
<p>只要在 “Changes to be committed” 这行下面的,就说明是已暂存状态。如果此时提交,那么该文件此时此刻的版本将被留存在历史记录中。你可能会想起之前我们使用 <code>git init</code> 后就运行了 <code>git add</code> 命令,开始跟踪当前目录下的文件。在 <code>git add</code> 后面可以指明要跟踪的文件或目录路径。如果是目录的话,就说明要递归跟踪该目录下的所有文件。(译注:其实 <code>git add</code> 的潜台词就是把目标文件快照放入暂存区域,也就是 add file into staged area,同时未曾跟踪过的文件标记为需要跟踪。这样就好理解后续 add 操作的实际意义了。)</p>
<h3 id="section-5">暂存已修改文件</h3>
<p>现在我们修改下之前已跟踪过的文件 <code>benchmarks.rb</code>,然后再次运行 <code>status</code> 命令,会看到这样的状态报告:</p>
<p>$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> new file: README
</code></pre>
<p>Changes not staged for commit:
(use “git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)</file></file></p>
<pre><code> modified: benchmarks.rb
</code></pre>
<p>文件 <code>benchmarks.rb</code> 出现在 “Changes not staged for commit” 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。要暂存这次更新,需要运行 <code>git add</code> 命令(这是个多功能命令,根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等)。现在让我们运行 <code>git add</code> 将 benchmarks.rb 放到暂存区,然后再看看 <code>git status</code> 的输出:</p>
<p>$ git add benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> new file: README
modified: benchmarks.rb
</code></pre>
<p>现在两个文件都已暂存,下次提交时就会一并记录到仓库。假设此时,你想要在 <code>benchmarks.rb</code> 里再加条注释,重新编辑存盘后,准备好提交。不过且慢,再运行 <code>git status</code> 看看:</p>
<p>$ vim benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> new file: README
modified: benchmarks.rb
</code></pre>
<p>Changes not staged for commit:
(use “git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)</file></file></p>
<pre><code> modified: benchmarks.rb
</code></pre>
<p>怎么回事? <code>benchmarks.rb</code> 文件出现了两次!一次算未暂存,一次算已暂存,这怎么可能呢?好吧,实际上 Git 只不过暂存了你运行 <code>git add</code> 命令时的版本,如果现在提交,那么提交的是添加注释前的版本,而非当前工作目录中的版本。所以,运行了 <code>git add</code> 之后又作了修订的文件,需要重新运行 <code>git add</code> 把最新版本重新暂存起来:</p>
<p>$ git add benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> new file: README
modified: benchmarks.rb
</code></pre>
<h3 id="section-6">忽略某些文件</h3>
<p>一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。我们可以创建一个名为 <code>.gitignore</code> 的文件,列出要忽略的文件模式。来看一个实际的例子:</p>
<p>$ cat .gitignore
*.[oa]
*~</p>
<p>第一行告诉 Git 忽略所有以 <code>.o</code> 或 <code>.a</code> 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的,我们用不着跟踪它们的版本。第二行告诉 Git 忽略所有以波浪符(<code>~</code>)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。此外,你可能还需要忽略 <code>log</code>,<code>tmp</code> 或者 <code>pid</code> 目录,以及自动生成的文档等等。要养成一开始就设置好 <code>.gitignore</code> 文件的习惯,以免将来误提交这类无用的文件。</p>
<p>文件 <code>.gitignore</code> 的格式规范如下:</p>
<ul>
<li>所有空行或者以注释符号 <code>#</code> 开头的行都会被 Git 忽略。</li>
<li>可以使用标准的 glob 模式匹配。</li>
<li>匹配模式最后跟反斜杠(<code>/</code>)说明要忽略的是目录。</li>
<li>要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(<code>!</code>)取反。</li>
</ul>
<p>所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(<code>*</code>)匹配零个或多个任意字符;<code>[abc]</code> 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(<code>?</code>)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 <code>[0-9]</code> 表示匹配所有 0 到 9 的数字)。</p>
<p>我们再看一个 <code>.gitignore</code> 文件的例子:</p>
<p># 此为注释 – 将被 Git 忽略
# 忽略所有 .a 结尾的文件
<em>.a
# 但 lib.a 除外
!lib.a
# 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
/TODO
# 忽略 build/ 目录下的所有文件
build/
# 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
doc/</em>.txt
# ignore all .txt files in the doc/ directory
doc/<em>*/</em>.txt</p>
<p>A <code>**/</code> pattern is available in Git since version 1.8.2.</p>
<h3 id="section-7">查看已暂存和未暂存的更新</h3>
<p>实际上 <code>git status</code> 的显示比较简单,仅仅是列出了修改过的文件,如果要查看具体修改了什么地方,可以用 <code>git diff</code> 命令。稍后我们会详细介绍 <code>git diff</code>,不过现在,它已经能回答我们的两个问题了:当前做的哪些更新还没有暂存?有哪些更新已经暂存起来准备好了下次提交? <code>git diff</code> 会使用文件补丁的格式显示具体添加和删除的行。</p>
<p>假如再次修改 <code>README</code> 文件后暂存,然后编辑 <code>benchmarks.rb</code> 文件后先别暂存,运行 <code>status</code> 命令将会看到:</p>
<p>$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> new file: README
</code></pre>
<p>Changes not staged for commit:
(use “git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)</file></file></p>
<pre><code> modified: benchmarks.rb
</code></pre>
<p>要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 <code>git diff</code>:</p>
<p>$ git diff
diff –git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..da65585 100644
— a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
@commit.parents[0].parents[0].parents[0]
end</p>
<ul>
<li>run_code(x, ‘commits 1’) do</li>
<li>git.commits.size</li>
<li>end
+
run_code(x, ‘commits 2’) do
log = git.commits(‘master’, 15)
log.size</li>
</ul>
<p>此命令比较的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容。</p>
<p>若要看已经暂存起来的文件和上次提交时的快照之间的差异,可以用 <code>git diff --cached</code> 命令。(Git 1.6.1 及更高版本还允许使用 <code>git diff --staged</code>,效果是相同的,但更好记些。)来看看实际的效果:</p>
<p>$ git diff –cached
diff –git a/README b/README
new file mode 100644
index 0000000..03902a1
— /dev/null
+++ b/README2
@@ -0,0 +1,5 @@
+grit
+ by Tom Preston-Werner, Chris Wanstrath
+ http://github.com/mojombo/grit
+
+Grit is a Ruby library for extracting information from a Git repository</p>
<p>请注意,单单 <code>git diff</code> 不过是显示还没有暂存起来的改动,而不是这次工作和上次提交之间的差异。所以有时候你一下子暂存了所有更新过的文件后,运行 <code>git diff</code> 后却什么也没有,就是这个原因。</p>
<p>像之前说的,暂存 benchmarks.rb 后再编辑,运行 <code>git status</code> 会看到暂存前后的两个版本:</p>
<p>$ git add benchmarks.rb
$ echo ‘# test line’ » benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> modified: benchmarks.rb
</code></pre>
<p>Changes not staged for commit:
(use “git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)</file></file></p>
<pre><code> modified: benchmarks.rb
</code></pre>
<p>现在运行 <code>git diff</code> 看暂存前后的变化:</p>
<p>$ git diff
diff –git a/benchmarks.rb b/benchmarks.rb
index e445e28..86b2f7c 100644
— a/benchmarks.rb
+++ b/benchmarks.rb
@@ -127,3 +127,4 @@ end
main()</p>
<p>##pp Grit::GitRuby.cache_client.stats
+# test line</p>
<p>然后用 <code>git diff --cached</code> 查看已经暂存起来的变化:</p>
<p>$ git diff –cached
diff –git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..e445e28 100644
— a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
@commit.parents[0].parents[0].parents[0]
end</p>
<ul>
<li>run_code(x, ‘commits 1’) do</li>
<li>git.commits.size</li>
<li>end
+
run_code(x, ‘commits 2’) do
log = git.commits(‘master’, 15)
log.size</li>
</ul>
<h3 id="section-8">提交更新</h3>
<p>现在的暂存区域已经准备妥当可以提交了。在此之前,请一定要确认还有什么修改过的或新建的文件还没有 <code>git add</code> 过,否则提交的时候不会记录这些还没暂存起来的变化。所以,每次准备提交前,先用 <code>git status</code> 看下,是不是都已暂存起来了,然后再运行提交命令 <code>git commit</code>:</p>
<p>$ git commit</p>
<p>这种方式会启动文本编辑器以便输入本次提交的说明。(默认会启用 shell 的环境变量 <code>$EDITOR</code> 所指定的软件,一般都是 vim 或 emacs。当然也可以按照第一章介绍的方式,使用 <code>git config --global core.editor</code> 命令设定你喜欢的编辑软件。)</p>
<p>编辑器会显示类似下面的文本信息(本例选用 Vim 的屏显方式展示):</p>
<p># Please enter the commit message for your changes. Lines starting
# with ‘#’ will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# new file: README
# modified: benchmarks.rb
#
~
~
~
“.git/COMMIT_EDITMSG” 10L, 283C</p>
<p>可以看到,默认的提交消息包含最后一次运行 <code>git status</code> 的输出,放在注释行里,另外开头还有一空行,供你输入提交说明。你完全可以去掉这些注释行,不过留着也没关系,多少能帮你回想起这次更新的内容有哪些。(如果觉得这还不够,可以用 <code>-v</code> 选项将修改差异的每一行都包含到注释中来。)退出编辑器时,Git 会丢掉注释行,将说明内容和本次更新提交到仓库。</p>
<p>另外也可以用 -m 参数后跟提交说明的方式,在一行命令中提交更新:</p>
<p>$ git commit -m “Story 182: Fix benchmarks for speed”
[master 463dc4f] Story 182: Fix benchmarks for speed
2 files changed, 3 insertions(+)
create mode 100644 README</p>
<p>好,现在你已经创建了第一个提交!可以看到,提交后它会告诉你,当前是在哪个分支(master)提交的,本次提交的完整 SHA-1 校验和是什么(<code>463dc4f</code>),以及在本次提交中,有多少文件修订过,多少行添改和删改过。</p>
<p>记住,提交时记录的是放在暂存区域的快照,任何还未暂存的仍然保持已修改状态,可以在下次提交时纳入版本管理。每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。</p>
<h3 id="section-9">跳过使用暂存区域</h3>
<p>尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给 <code>git commit</code> 加上 <code>-a</code> 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 <code>git add</code> 步骤:</p>
<p>$ git status
On branch master
Changes not staged for commit:
(use “git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)</file></file></p>
<pre><code> modified: benchmarks.rb
</code></pre>
<p>no changes added to commit (use “git add” and/or “git commit -a”)
$ git commit -a -m ‘added new benchmarks’
[master 83e38c7] added new benchmarks
1 files changed, 5 insertions(+)</p>
<p>看到了吗?提交之前不再需要 <code>git add</code> 文件 benchmarks.rb 了。</p>
<h3 id="section-10">移除文件</h3>
<p>要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。可以用 <code>git rm</code> 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。</p>
<p>如果只是简单地从工作目录中手工删除文件,运行 <code>git status</code> 时就会在 “Changes not staged for commit” 部分(也就是_未暂存_清单)看到:</p>
<p>$ rm grit.gemspec
$ git status
On branch master
Changes not staged for commit:
(use “git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)</file></file></p>
<pre><code> deleted: grit.gemspec
</code></pre>
<p>no changes added to commit (use “git add” and/or “git commit -a”)</p>
<p>然后再运行 <code>git rm</code> 记录此次移除文件的操作:</p>
<p>$ git rm grit.gemspec
rm ‘grit.gemspec’
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> deleted: grit.gemspec
</code></pre>
<p>最后提交的时候,该文件就不再纳入版本管理了。如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 <code>-f</code>(译注:即 force 的首字母),以防误删除文件后丢失修改的内容。</p>
<p>另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。换句话说,仅是从跟踪清单中删除。比如一些大型日志文件或者一堆 <code>.a</code> 编译文件,不小心纳入仓库后,要移除跟踪但不删除文件,以便稍后在 <code>.gitignore</code> 文件中补上,用 <code>--cached</code> 选项即可:</p>
<p>$ git rm –cached readme.txt</p>
<p>后面可以列出文件或者目录的名字,也可以使用 glob 模式。比方说:</p>
<p>$ git rm log/*.log</p>
<p>注意到星号 <code>*</code> 之前的反斜杠 <code>\</code>,因为 Git 有它自己的文件模式扩展匹配方式,所以我们不用 shell 来帮忙展开(译注:实际上不加反斜杠也可以运行,只不过按照 shell 扩展的话,仅仅删除指定目录下的文件而不会递归匹配。上面的例子本来就指定了目录,所以效果等同,但下面的例子就会用递归方式匹配,所以必须加反斜杠。)。此命令删除所有 <code>log/</code> 目录下扩展名为 <code>.log</code> 的文件。类似的比如:</p>
<p>$ git rm *~</p>
<p>会递归删除当前目录及其子目录中所有 <code>~</code> 结尾的文件。</p>
<h3 id="section-11">移动文件</h3>
<p>不像其他的 VCS 系统,Git 并不跟踪文件移动操作。如果在 Git 中重命名了某个文件,仓库中存储的元数据并不会体现出这是一次改名操作。不过 Git 非常聪明,它会推断出究竟发生了什么,至于具体是如何做到的,我们稍后再谈。</p>
<p>既然如此,当你看到 Git 的 <code>mv</code> 命令时一定会困惑不已。要在 Git 中对文件改名,可以这么做:</p>
<p>$ git mv file_from file_to</p>
<p>它会恰如预期般正常工作。实际上,即便此时查看状态信息,也会明白无误地看到关于重命名操作的说明:</p>
<p>$ git mv README.txt README
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> renamed: README.txt -> README
</code></pre>
<p>其实,运行 <code>git mv</code> 就相当于运行了下面三条命令:</p>
<p>$ mv README.txt README
$ git rm README.txt
$ git add README</p>
<p>如此分开操作,Git 也会意识到这是一次改名,所以不管何种方式都一样。当然,直接用 <code>git mv</code> 轻便得多,不过有时候用其他工具批处理改名的话,要记得在提交前删除老的文件名,再添加新的文件名。</p>
<h2 id="section-12">查看提交历史</h2>
<p>在提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,可以使用 <code>git log</code> 命令查看。</p>
<p>接下来的例子会用我专门用于演示的 simplegit 项目,运行下面的命令获取该项目源代码:</p>
<p>git clone git://github.com/schacon/simplegit-progit.git</p>
<p>然后在此项目中运行 <code>git log</code>,应该会看到下面的输出:</p>
<p>$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Mon Mar 17 21:52:11 2008 -0700</p>
<pre><code> changed the version number
</code></pre>
<p>commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Sat Mar 15 16:40:33 2008 -0700</p>
<pre><code> removed unnecessary test code
</code></pre>
<p>commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Sat Mar 15 10:31:28 2008 -0700</p>
<pre><code> first commit
</code></pre>
<p>默认不用任何参数的话,<code>git log</code> 会按提交时间列出所有的更新,最近的更新排在最上面。看到了吗,每次更新都有一个 SHA-1 校验和、作者的名字和电子邮件地址、提交时间,最后缩进一个段落显示提交说明。</p>
<p><code>git log</code> 有许多选项可以帮助你搜寻感兴趣的提交,接下来我们介绍些最常用的。</p>
<p>我们常用 <code>-p</code> 选项展开显示每次提交的内容差异,用 <code>-2</code> 则仅显示最近的两次更新:</p>
<p>$ git log -p -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Mon Mar 17 21:52:11 2008 -0700</p>
<pre><code> changed the version number
</code></pre>
<p>diff –git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
— a/Rakefile
+++ b/Rakefile
@@ -5,5 +5,5 @@ require ‘rake/gempackagetask’
spec = Gem::Specification.new do |s|
s.name = “simplegit”
- s.version = “0.1.0”
+ s.version = “0.1.1”
s.author = “Scott Chacon”
s.email = “schacon@gee-mail.com</p>
<p>commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Sat Mar 15 16:40:33 2008 -0700</p>
<pre><code> removed unnecessary test code
</code></pre>
<p>diff –git a/lib/simplegit.rb b/lib/simplegit.rb
index a0a60ae..47c6340 100644
— a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ class SimpleGit
end</p>
<p>end
-
-if $0 == <strong>FILE</strong>
- git = SimpleGit.new
- puts git.show
-end
\ No newline at end of file</p>
<p>该选项除了显示基本信息之外,还在附带了每次 commit 的变化。当进行代码审查,或者快速浏览某个搭档提交的 commit 的变化的时候,这个参数就非常有用了。</p>
<p>某些时候,单词层面的对比,比行层面的对比,更加容易观察。Git 提供了 <code>--word-diff</code> 选项。我们可以将其添加到 <code>git log -p</code> 命令的后面,从而获取单词层面上的对比。在程序代码中进行单词层面的对比常常是没什么用的。不过当你需要在书籍、论文这种很大的文本文件上进行对比的时候,这个功能就显出用武之地了。下面是一个简单的例子:</p>
<p>$ git log -U1 –word-diff
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Mon Mar 17 21:52:11 2008 -0700</p>
<pre><code> changed the version number
</code></pre>
<p>diff –git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
— a/Rakefile
+++ b/Rakefile
@@ -7,3 +7,3 @@ spec = Gem::Specification.new do |s|
s.name = “simplegit”
s.version = [-“0.1.0”-]{+”0.1.1”+}
s.author = “Scott Chacon”</p>
<p>如你所见,这里并没有平常看到的添加行或者删除行的信息。这里的对比显示在行间。新增加的单词被 <code>{+ +}</code> 括起来,被删除的单词被 <code>[- -]</code> 括起来。在进行单词层面的对比的时候,你可能希望上下文( context )行数从默认的 3 行,减为 1 行,那么可以使用 <code>-U1</code> 选项。上面的例子中,我们就使用了这个选项。 </p>
<p>另外,<code>git log</code> 还提供了许多摘要选项可以用,比如 <code>--stat</code>,仅显示简要的增改行数统计:</p>
<p>$ git log –stat
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Mon Mar 17 21:52:11 2008 -0700</p>
<pre><code> changed the version number
</code></pre>
<p>Rakefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)</p>
<p>commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Sat Mar 15 16:40:33 2008 -0700</p>
<pre><code> removed unnecessary test code
</code></pre>
<p>lib/simplegit.rb | 5 —–
1 file changed, 5 deletions(-)</p>
<p>commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Sat Mar 15 10:31:28 2008 -0700</p>
<pre><code> first commit
</code></pre>
<p>README | 6 ++++++
Rakefile | 23 +++++++++++++++++++++++
lib/simplegit.rb | 25 +++++++++++++++++++++++++
3 files changed, 54 insertions(+)</p>
<p>每个提交都列出了修改过的文件,以及其中添加和移除的行数,并在最后列出所有增减行数小计。
还有个常用的 <code>--pretty</code> 选项,可以指定使用完全不同于默认格式的方式展示提交历史。比如用 <code>oneline</code> 将每个提交放在一行显示,这在提交数很大时非常有用。另外还有 <code>short</code>,<code>full</code> 和 <code>fuller</code> 可以用,展示的信息或多或少有些不同,请自己动手实践一下看看效果如何。</p>
<p>$ git log –pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test code
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit</p>
<p>但最有意思的是 <code>format</code>,可以定制要显示的记录格式,这样的输出便于后期编程提取分析,像这样:</p>
<p>$ git log –pretty=format:”%h - %an, %ar : %s”
ca82a6d - Scott Chacon, 11 months ago : changed the version number
085bb3b - Scott Chacon, 11 months ago : removed unnecessary test code
a11bef0 - Scott Chacon, 11 months ago : first commit</p>
<p>表 2-1 列出了常用的格式占位符写法及其代表的意义。</p>
<!-- Attention to translators: this is a table declaration.
The lines must be formatted as follows
<TAB><First column text><TAB><Second column text>
-->
<p>选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 -date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明</p>
<p>你一定奇怪_作者(author)_和_提交者(committer)_之间究竟有何差别,其实作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人。所以,当你为某个项目发布补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者。我们会在第五章再详细介绍两者之间的细微差别。</p>
<p>用 oneline 或 format 时结合 <code>--graph</code> 选项,可以看到开头多出一些 ASCII 字符串表示的简单图形,形象地展示了每个提交所在的分支及其分化衍合情况。在我们之前提到的 Grit 项目仓库中可以看到:</p>
<p>$ git log –pretty=format:”%h %s” –graph
* 2d3acf9 ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch ‘master’ of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
* 11d191e Merge branch ‘defunkt’ into local</p>
<p>以上只是简单介绍了一些 <code>git log</code> 命令支持的选项。表 2-2 还列出了一些其他常用的选项及其释义。</p>
<!-- Attention to translators: this is a table declaration.
The lines must be formatted as follows
<TAB><First column text><TAB><Second column text>
-->
<p>选项 说明
-p 按补丁格式显示每个更新之间的差异。
–word-diff 按 word diff 格式显示差异。
–stat 显示每次更新的文件修改统计信息。
–shortstat 只显示 –stat 中最后的行数修改添加移除统计。
–name-only 仅在提交信息后显示已修改的文件清单。
–name-status 显示新增、修改、删除的文件清单。
–abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
–relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。
–graph 显示 ASCII 图形表示的分支合并历史。
–pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。
–oneline <code>--pretty=oneline --abbrev-commit</code> 的简化用法。</p>
<h3 id="section-13">限制输出长度</h3>
<p>除了定制输出格式的选项之外,<code>git log</code> 还有许多非常实用的限制输出长度的选项,也就是只输出部分提交信息。之前我们已经看到过 <code>-2</code> 了,它只显示最近的两条提交,实际上,这是 <code>-<n></code> 选项的写法,其中的 <code>n</code> 可以是任何自然数,表示仅显示最近的若干条提交。不过实践中我们是不太用这个选项的,Git 在输出所有提交时会自动调用分页程序(less),要看更早的更新只需翻到下页即可。</p>
<p>另外还有按照时间作限制的选项,比如 <code>--since</code> 和 <code>--until</code>。下面的命令列出所有最近两周内的提交:</p>
<p>$ git log –since=2.weeks</p>
<p>你可以给出各种时间格式,比如说具体的某一天(“2008-01-15”),或者是多久以前(“2 years 1 day 3 minutes ago”)。</p>
<p>还可以给出若干搜索条件,列出符合的提交。用 <code>--author</code> 选项显示指定作者的提交,用 <code>--grep</code> 选项搜索提交说明中的关键字。(请注意,如果要得到同时满足这两个选项搜索条件的提交,就必须用 <code>--all-match</code> 选项。否则,满足任意一个条件的提交都会被匹配出来)</p>
<p>另一个真正实用的<code>git log</code>选项是路径(path),如果只关心某些文件或者目录的历史提交,可以在 <code>git log</code> 选项的最后指定它们的路径。因为是放在最后位置上的选项,所以用两个短划线(<code>--</code>)隔开之前的选项和后面限定的路径名。</p>
<p>表 2-3 还列出了其他常用的类似选项。</p>
<!-- Attention to translators: this is a table declaration.
The lines must be formatted as follows
<TAB><First column text><TAB><Second column text>
-->
<p>选项 说明
-(n) 仅显示最近的 n 条提交
–since, –after 仅显示指定时间之后的提交。
–until, –before 仅显示指定时间之前的提交。
–author 仅显示指定作者相关的提交。
–committer 仅显示指定提交者相关的提交。</p>
<p>来看一个实际的例子,如果要查看 Git 仓库中,2008 年 10 月期间,Junio Hamano 提交的但未合并的测试脚本(位于项目的 t/ 目录下的文件),可以用下面的查询命令:</p>
<p>$ git log –pretty=”%h - %s” –author=gitster –since=”2008-10-01” \
–before=”2008-11-01” –no-merges – t/
5610e3b - Fix testcase failure when extended attribute
acd3b9e - Enhance hold_lock_file_for_{update,append}()
f563754 - demonstrate breakage of detached checkout wi
d1a43f2 - reset –hard/read-tree –reset -u: remove un
51a94af - Fix “checkout –track -b newbranch” on detac
b0ad11e - pull: allow “git pull origin $something:$cur</p>
<p>Git 项目有 20,000 多条提交,但我们给出搜索选项后,仅列出了其中满足条件的 6 条。</p>
<h3 id="section-14">使用图形化工具查阅提交历史</h3>
<p>有时候图形化工具更容易展示历史提交的变化,随 Git 一同发布的 gitk 就是这样一种工具。它是用 Tcl/Tk 写成的,基本上相当于 <code>git log</code> 命令的可视化版本,凡是 <code>git log</code> 可以用的选项也都能用在 gitk 上。在项目工作目录中输入 gitk 命令后,就会启动图 2-2 所示的界面。</p>
<p>Insert 18333fig0202.png
图 2-2. gitk 的图形界面</p>
<p>上半个窗口显示的是历次提交的分支祖先图谱,下半个窗口显示当前点选的提交对应的具体差异。</p>
<h2 id="section-15">撤消操作</h2>
<p>任何时候,你都有可能需要撤消刚才所做的某些操作。接下来,我们会介绍一些基本的撤消操作相关的命令。请注意,有些撤销操作是不可逆的,所以请务必谨慎小心,一旦失误,就有可能丢失部分工作成果。</p>
<h3 id="section-16">修改最后一次提交</h3>
<p>有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操作,可以使用 <code>--amend</code> 选项重新提交:</p>
<p>$ git commit –amend</p>
<p>此命令将使用当前的暂存区域快照提交。如果刚才提交完没有作任何改动,直接运行此命令的话,相当于有机会重新编辑提交说明,但将要提交的文件快照和之前的一样。</p>
<p>启动文本编辑器后,会看到上次提交时的说明,编辑它确认没问题后保存退出,就会使用新的提交说明覆盖刚才失误的提交。</p>
<p>如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 <code>--amend</code> 提交:</p>
<p>$ git commit -m ‘initial commit’
$ git add forgotten_file
$ git commit –amend</p>
<p>上面的三条命令最终只是产生一个提交,第二个提交命令修正了第一个的提交内容。</p>
<h3 id="section-17">取消已经暂存的文件</h3>
<p>接下来的两个小节将演示如何取消暂存区域中的文件,以及如何取消工作目录中已修改的文件。不用担心,查看文件状态的时候就提示了该如何撤消,所以不需要死记硬背。来看下面的例子,有两个修改过的文件,我们想要分开提交,但不小心用 <code>git add .</code> 全加到了暂存区域。该如何撤消暂存其中的一个文件呢?其实,<code>git status</code> 的命令输出已经告诉了我们该怎么做:</p>
<p>$ git add .
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> modified: README.txt
modified: benchmarks.rb
</code></pre>
<p>就在 “Changes to be committed” 下面,括号中有提示,可以使用 <code>git reset HEAD <file>...</code> 的方式取消暂存。好吧,我们来试试取消暂存 benchmarks.rb 文件:</p>
<p>$ git reset HEAD benchmarks.rb
Unstaged changes after reset:
M benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> modified: README.txt
</code></pre>
<p>Changes not staged for commit:
(use “git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)</file></file></p>
<pre><code> modified: benchmarks.rb
</code></pre>
<p>这条命令看起来有些古怪,先别管,能用就行。现在 benchmarks.rb 文件又回到了之前已修改未暂存的状态。</p>
<h3 id="section-18">取消对文件的修改</h3>
<p>如果觉得刚才对 benchmarks.rb 的修改完全没有必要,该如何取消修改,回到之前的状态(也就是修改之前的版本)呢?<code>git status</code> 同样提示了具体的撤消方法,接着上面的例子,现在未暂存区域看起来像这样:</p>
<p>Changes not staged for commit:
(use “git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)</file></file></p>
<pre><code> modified: benchmarks.rb
</code></pre>
<p>在第二个括号中,我们看到了抛弃文件修改的命令(至少在 Git 1.6.1 以及更高版本中会这样提示,如果你还在用老版本,我们强烈建议你升级,以获取最佳的用户体验),让我们试试看:</p>
<p>$ git checkout – benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use “git reset HEAD <file>..." to unstage)</file></p>
<pre><code> modified: README.txt
</code></pre>
<p>可以看到,该文件已经恢复到修改前的版本。你可能已经意识到了,这条命令有些危险,所有对文件的修改都没有了,因为我们刚刚把之前版本的文件复制过来重写了此文件。所以在用这条命令前,请务必确定真的不再需要保留刚才的修改。如果只是想回退版本,同时保留刚才的修改以便将来继续工作,可以用下章介绍的 stashing 和分支来处理,应该会更好些。</p>
<p>记住,任何已经提交到 Git 的都可以被恢复。即便在已经删除的分支中的提交,或者用 <code>--amend</code> 重新改写的提交,都可以被恢复(关于数据恢复的内容见第九章)。所以,你可能失去的数据,仅限于没有提交过的,对 Git 来说它们就像从未存在过一样。</p>
<h2 id="section-19">远程仓库的使用</h2>
<p>要参与任何一个 Git 项目的协作,必须要了解该如何管理远程仓库。远程仓库是指托管在网络上的项目仓库,可能会有好多个,其中有些你只能读,另外有些可以写。同他人协作开发某个项目时,需要管理这些远程仓库,以便推送或拉取数据,分享各自的工作进展。
管理远程仓库的工作,包括添加远程库,移除废弃的远程库,管理各式远程库分支,定义是否跟踪这些分支,等等。本节我们将详细讨论远程库的管理和使用。</p>
<h3 id="section-20">查看当前的远程库</h3>
<p>要查看当前配置有哪些远程仓库,可以用 <code>git remote</code> 命令,它会列出每个远程库的简短名字。在克隆完某个项目后,至少可以看到一个名为 origin 的远程库,Git 默认使用这个名字来标识你所克隆的原始仓库:</p>
<p>$ git clone git://github.com/schacon/ticgit.git
Cloning into ‘ticgit’…
remote: Reusing existing pack: 1857, done.
remote: Total 1857 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1857/1857), 374.35 KiB | 193.00 KiB/s, done.
Resolving deltas: 100% (772/772), done.
Checking connectivity… done.
$ cd ticgit
$ git remote
origin</p>
<p>也可以加上 <code>-v</code> 选项(译注:此为 <code>--verbose</code> 的简写,取首字母),显示对应的克隆地址:</p>
<p>$ git remote -v
origin git://github.com/schacon/ticgit.git (fetch)
origin git://github.com/schacon/ticgit.git (push)</p>
<p>如果有多个远程仓库,此命令将全部列出。比如在我的 Grit 项目中,可以看到:</p>
<p>$ cd grit
$ git remote -v
bakkdoor git://github.com/bakkdoor/grit.git
cho45 git://github.com/cho45/grit.git
defunkt git://github.com/defunkt/grit.git
koke git://github.com/koke/grit.git
origin git@github.com:mojombo/grit.git</p>
<p>这样一来,我就可以非常轻松地从这些用户的仓库中,拉取他们的提交到本地。请注意,上面列出的地址只有 origin 用的是 SSH URL 链接,所以也只有这个仓库我能推送数据上去(我们会在第四章解释原因)。</p>
<h3 id="section-21">添加远程仓库</h3>
<p>要添加一个新的远程仓库,可以指定一个简单的名字,以便将来引用,运行 <code>git remote add [shortname] [url]</code>:</p>
<p>$ git remote
origin
$ git remote add pb git://github.com/paulboone/ticgit.git
$ git remote -v
origin git://github.com/schacon/ticgit.git
pb git://github.com/paulboone/ticgit.git</p>
<p>现在可以用字符串 <code>pb</code> 指代对应的仓库地址了。比如说,要抓取所有 Paul 有的,但本地仓库没有的信息,可以运行 <code>git fetch pb</code>:</p>
<p>$ git fetch pb
remote: Counting objects: 58, done.
remote: Compressing objects: 100% (41/41), done.
remote: Total 44 (delta 24), reused 1 (delta 0)
Unpacking objects: 100% (44/44), done.
From git://github.com/paulboone/ticgit
* [new branch] master -> pb/master
* [new branch] ticgit -> pb/ticgit</p>
<p>现在,Paul 的主干分支(master)已经完全可以在本地访问了,对应的名字是 <code>pb/master</code>,你可以将它合并到自己的某个分支,或者切换到这个分支,看看有些什么有趣的更新。</p>
<h3 id="section-22">从远程仓库抓取数据</h3>
<p>正如之前所看到的,可以用下面的命令从远程仓库抓取数据到本地:</p>
<p>$ git fetch [remote-name]</p>
<p>此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。运行完成后,你就可以在本地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分支,一探究竟。(我们会在第三章详细讨论关于分支的概念和操作。)</p>
<p>如果是克隆了一个仓库,此命令会自动将远程仓库归于 origin 名下。所以,<code>git fetch origin</code> 会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新(或是上次 fetch 以来别人提交的更新)。有一点很重要,需要记住,fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合并。</p>
<p>如果设置了某个分支用于跟踪某个远端仓库的分支(参见下节及第三章的内容),可以使用 <code>git pull</code> 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支。在日常工作中我们经常这么用,既快且好。实际上,默认情况下 <code>git clone</code> 命令本质上就是自动创建了本地的 master 分支用于跟踪远程仓库中的 master 分支(假设远程仓库确实有 master 分支)。所以一般我们运行 <code>git pull</code>,目的都是要从原始克隆的远端仓库中抓取数据后,合并到工作目录中的当前分支。</p>
<h3 id="section-23">推送数据到远程仓库</h3>
<p>项目进行到一个阶段,要同别人分享目前的成果,可以将本地仓库中的数据推送到远程仓库。实现这个任务的命令很简单: <code>git push [remote-name] [branch-name]</code>。如果要把本地的 master 分支推送到 <code>origin</code> 服务器上(再次说明下,克隆操作会自动使用默认的 master 和 origin 名字),可以运行下面的命令:</p>
<p>$ git push origin master</p>
<p>只有在所克隆的服务器上有写权限,或者同一时刻没有其他人在推数据,这条命令才会如期完成任务。如果在你推数据前,已经有其他人推送了若干更新,那你的推送操作就会被驳回。你必须先把他们的更新抓取到本地,合并到自己的项目中,然后才可以再次推送。有关推送数据到远程仓库的详细内容见第三章。</p>
<h3 id="section-24">查看远程仓库信息</h3>
<p>我们可以通过命令 <code>git remote show [remote-name]</code> 查看某个远程仓库的详细信息,比如要看所克隆的 <code>origin</code> 仓库,可以运行:</p>
<p>$ git remote show origin
* remote origin
URL: git://github.com/schacon/ticgit.git
Remote branch merged with ‘git pull’ while on branch master
master
Tracked remote branches
master
ticgit</p>
<p>除了对应的克隆地址外,它还给出了许多额外的信息。它友善地告诉你如果是在 master 分支,就可以用 <code>git pull</code> 命令抓取数据合并到本地。另外还列出了所有处于跟踪状态中的远端分支。</p>
<p>上面的例子非常简单,而随着使用 Git 的深入,<code>git remote show</code> 给出的信息可能会像这样:</p>
<p>$ git remote show origin
* remote origin
URL: git@github.com:defunkt/github.git
Remote branch merged with ‘git pull’ while on branch issues
issues
Remote branch merged with ‘git pull’ while on branch master
master
New remote branches (next fetch will store in remotes/origin)
caching
Stale tracking branches (use ‘git remote prune’)
libwalker
walker2
Tracked remote branches
acl
apiv2
dashboard2
issues
master
postgres
Local branch pushed with ‘git push’
master:master</p>
<p>它告诉我们,运行 <code>git push</code> 时缺省推送的分支是什么(译注:最后两行)。它还显示了有哪些远端分支还没有同步到本地(译注:第六行的 <code>caching</code> 分支),哪些已同步到本地的远端分支在远端服务器上已被删除(译注:<code>Stale tracking branches</code> 下面的两个分支),以及运行 <code>git pull</code> 时将自动合并哪些分支(译注:前四行中列出的 <code>issues</code> 和 <code>master</code> 分支)。</p>
<h3 id="section-25">远程仓库的删除和重命名</h3>
<p>在新版 Git 中可以用 <code>git remote rename</code> 命令修改某个远程仓库在本地的简称,比如想把 <code>pb</code> 改成 <code>paul</code>,可以这么运行:</p>
<p>$ git remote rename pb paul
$ git remote
origin
paul</p>
<p>注意,对远程仓库的重命名,也会使对应的分支名称发生变化,原来的 <code>pb/master</code> 分支现在成了 <code>paul/master</code>。</p>
<p>碰到远端仓库服务器迁移,或者原来的克隆镜像不再使用,又或者某个参与者不再贡献代码,那么需要移除对应的远端仓库,可以运行 <code>git remote rm</code> 命令:</p>
<p>$ git remote rm paul
$ git remote
origin</p>
<h2 id="section-26">打标签</h2>
<p>同大多数 VCS 一样,Git 也可以对某一时间点上的版本打上标签。人们在发布某个软件版本(比如 v1.0 等等)的时候,经常这么做。本节我们一起来学习如何列出所有可用的标签,如何新建标签,以及各种不同类型标签之间的差别。</p>
<h3 id="section-27">列显已有的标签</h3>
<p>列出现有标签的命令非常简单,直接运行 <code>git tag</code> 即可:</p>
<p>$ git tag
v0.1
v1.3</p>
<p>显示的标签按字母顺序排列,所以标签的先后并不表示重要程度的轻重。</p>
<p>我们可以用特定的搜索模式列出符合条件的标签。在 Git 自身项目仓库中,有着超过 240 个标签,如果你只对 1.4.2 系列的版本感兴趣,可以运行下面的命令:</p>
<p>$ git tag -l ‘v1.4.2.*’
v1.4.2.1
v1.4.2.2
v1.4.2.3
v1.4.2.4</p>
<h3 id="section-28">新建标签</h3>
<p>Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。</p>
<h3 id="section-29">含附注的标签</h3>
<p>创建一个含附注类型的标签非常简单,用 <code>-a</code> (译注:取 <code>annotated</code> 的首字母)指定标签名字即可:</p>
<p>$ git tag -a v1.4 -m ‘my version 1.4’
$ git tag
v0.1
v1.3
v1.4</p>
<p>而 <code>-m</code> 选项则指定了对应的标签说明,Git 会将此说明一同保存在标签对象中。如果没有给出该选项,Git 会启动文本编辑软件供你输入标签说明。</p>
<p>可以使用 <code>git show</code> 命令查看相应标签的版本信息,并连同显示打标签时的提交对象。</p>
<p>$ git show v1.4
tag v1.4
Tagger: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Mon Feb 9 14:45:11 2009 -0800</p>
<p>my version 1.4</p>
<p>commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7… a6b4c97…
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Sun Feb 8 19:02:46 2009 -0800</p>
<pre><code> Merge branch 'experiment'
</code></pre>
<p>我们可以看到在提交对象信息上面,列出了此标签的提交者和提交时间,以及相应的标签说明。</p>
<h3 id="section-30">签署标签</h3>
<p>如果你有自己的私钥,还可以用 GPG 来签署标签,只需要把之前的 <code>-a</code> 改为 <code>-s</code> (译注: 取 <code>signed</code> 的首字母)即可:</p>
<p>$ git tag -s v1.5 -m ‘my signed 1.5 tag’
You need a passphrase to unlock the secret key for
user: “Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>”
1024-bit DSA key, ID F721C45A, created 2009-02-09</p>
<p>现在再运行 <code>git show</code> 会看到对应的 GPG 签名也附在其内:</p>
<p>$ git show v1.5
tag v1.5
Tagger: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Mon Feb 9 15:22:20 2009 -0800</p>
<p>my signed 1.5 tag
—–BEGIN PGP SIGNATURE—–
Version: GnuPG v1.4.8 (Darwin)</p>
<p>iEYEABECAAYFAkmQurIACgkQON3DxfchxFr5cACeIMN+ZxLKggJQf0QYiQBwgySN
Ki0An2JeAVUCAiJ7Ox6ZEtK+NvZAj82/
=WryJ
—–END PGP SIGNATURE—–
commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7… a6b4c97…
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Sun Feb 8 19:02:46 2009 -0800</p>
<pre><code> Merge branch 'experiment'
</code></pre>
<p>稍后我们再学习如何验证已经签署的标签。</p>
<h3 id="section-31">轻量级标签</h3>
<p>轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。要创建这样的标签,一个 <code>-a</code>,<code>-s</code> 或 <code>-m</code> 选项都不用,直接给出标签名字即可:</p>
<p>$ git tag v1.4-lw
$ git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5</p>
<p>现在运行 <code>git show</code> 查看此标签信息,就只有相应的提交对象摘要:</p>
<p>$ git show v1.4-lw
commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7… a6b4c97…
Author: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Sun Feb 8 19:02:46 2009 -0800</p>
<pre><code> Merge branch 'experiment'
</code></pre>
<h3 id="section-32">验证标签</h3>
<p>可以使用 <code>git tag -v [tag-name]</code> (译注:取 <code>verify</code> 的首字母)的方式验证已经签署的标签。此命令会调用 GPG 来验证签名,所以你需要有签署者的公钥,存放在 keyring 中,才能验证:</p>
<p>$ git tag -v v1.4.2.1
object 883653babd8ee7ea23e6a5c392bb739348b1eb61
type commit
tag v1.4.2.1
tagger Junio C Hamano <a href="mailto:junkio@cox.net">junkio@cox.net</a> 1158138501 -0700</p>
<p>GIT 1.4.2.1</p>
<p>Minor fixes since 1.4.2, including git-mv and git-http with alternates.
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Good signature from “Junio C Hamano <a href="mailto:junkio@cox.net">junkio@cox.net</a>”
gpg: aka “[jpeg image of size 1513]”
Primary key fingerprint: 3565 2A26 2040 E066 C9A7 4A7D C0C6 D9A4 F311 9B9A</p>
<p>若是没有签署者的公钥,会报告类似下面这样的错误:</p>
<p>gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Can’t check signature: public key not found
error: could not verify the tag ‘v1.4.2.1’</p>
<h3 id="section-33">后期加注标签</h3>
<p>你甚至可以在后期对早先的某次提交加注标签。比如在下面展示的提交历史中:</p>
<p>$ git log –pretty=oneline
15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch ‘experiment’
a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support
0d52aaab4479697da7686c15f77a3d64d9165190 one more thing
6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch ‘experiment’
0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function
4682c3261057305bdd616e23b64b0857d832627b added a todo file
166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile
964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo
8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme</p>
<p>我们忘了在提交 “updated rakefile” 后为此项目打上版本号 v1.2,没关系,现在也能做。只要在打标签的时候跟上对应提交对象的校验和(或前几位字符)即可:</p>
<p>$ git tag -a v1.2 9fceb02</p>
<p>可以看到我们已经补上了标签:</p>
<p>$ git tag
v0.1
v1.2
v1.3
v1.4
v1.4-lw
v1.5</p>
<p>$ git show v1.2
tag v1.2
Tagger: Scott Chacon <a href="mailto:schacon@gee-mail.com">schacon@gee-mail.com</a>
Date: Mon Feb 9 15:32:16 2009 -0800</p>
<p>version 1.2
commit 9fceb02d0ae598e95dc970b74767f19372d61af8
Author: Magnus Chacon <a href="mailto:mchacon@gee-mail.com">mchacon@gee-mail.com</a>
Date: Sun Apr 27 20:43:35 2008 -0700</p>
<pre><code> updated rakefile ...
</code></pre>
<h3 id="section-34">分享标签</h3>
<p>默认情况下,<code>git push</code> 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。其命令格式如同推送分支,运行 <code>git push origin [tagname]</code> 即可: </p>
<p>$ git push origin v1.5
Counting objects: 50, done.
Compressing objects: 100% (38/38), done.
Writing objects: 100% (44/44), 4.56 KiB, done.
Total 44 (delta 18), reused 8 (delta 1)
To git@github.com:schacon/simplegit.git
* [new tag] v1.5 -> v1.5</p>
<p>如果要一次推送所有本地新增的标签上去,可以使用 <code>--tags</code> 选项:</p>
<p>$ git push origin –tags
Counting objects: 50, done.
Compressing objects: 100% (38/38), done.
Writing objects: 100% (44/44), 4.56 KiB, done.
Total 44 (delta 18), reused 8 (delta 1)
To git@github.com:schacon/simplegit.git
* [new tag] v0.1 -> v0.1
* [new tag] v1.2 -> v1.2
* [new tag] v1.4 -> v1.4
* [new tag] v1.4-lw -> v1.4-lw
* [new tag] v1.5 -> v1.5</p>
<p>现在,其他人克隆共享仓库或拉取数据同步后,也会看到这些标签。</p>
<h2 id="section-35">技巧和窍门</h2>
<p>在结束本章之前,我还想和大家分享一些 Git 使用的技巧和窍门。很多使用 Git 的开发者可能根本就没用过这些技巧,我们也不是说在读过本书后非得用这些技巧不可,但至少应该有所了解吧。说实话,有了这些小窍门,我们的工作可以变得更简单,更轻松,更高效。</p>
<h3 id="section-36">自动补全</h3>
<p>如果你用的是 Bash shell,可以试试看 Git 提供的自动补全脚本。下载 Git 的源代码,进入 <code>contrib/completion</code> 目录,会看到一个 <code>git-completion.bash</code> 文件。将此文件复制到你自己的用户主目录中(译注:按照下面的示例,还应改名加上点:<code>cp git-completion.bash ~/.git-completion.bash</code>),并把下面一行内容添加到你的 <code>.bashrc</code> 文件中:</p>
<p>source ~/.git-completion.bash</p>
<p>也可以为系统上所有用户都设置默认使用此脚本。Mac 上将此脚本复制到 <code>/opt/local/etc/bash_completion.d</code> 目录中,Linux 上则复制到 <code>/etc/bash_completion.d/</code> 目录中。这两处目录中的脚本,都会在 Bash 启动时自动加载。</p>
<p>如果在 Windows 上安装了 msysGit,默认使用的 Git Bash 就已经配好了这个自动补全脚本,可以直接使用。</p>
<p>在输入 Git 命令的时候可以敲两次跳格键(Tab),就会看到列出所有匹配的可用命令建议:</p>
<p>$ git co<tab><tab>
commit config</tab></tab></p>
<p>此例中,键入 git co 然后连按两次 Tab 键,会看到两个相关的建议(命令) commit 和 config。继而输入 <code>m<tab></code> 会自动完成 <code>git commit</code> 命令的输入。</p>
<p>命令的选项也可以用这种方式自动完成,其实这种情况更实用些。比如运行 <code>git log</code> 的时候忘了相关选项的名字,可以输入开头的几个字母,然后敲 Tab 键看看有哪些匹配的:</p>
<p>$ git log –s<tab>
--shortstat --since= --src-prefix= --stat --summary</tab></p>
<p>这个技巧不错吧,可以节省很多输入和查阅文档的时间。</p>
<h3 id="git--2">Git 命令别名</h3>
<p>Git 并不会推断你输入的几个字符将会是哪条命令,不过如果想偷懒,少敲几个命令的字符,可以用 <code>git config</code> 为命令设置别名。来看看下面的例子:</p>
<p>$ git config –global alias.co checkout
$ git config –global alias.br branch
$ git config –global alias.ci commit
$ git config –global alias.st status</p>
<p>现在,如果要输入 <code>git commit</code> 只需键入 <code>git ci</code> 即可。而随着 Git 使用的深入,会有很多经常要用到的命令,遇到这种情况,不妨建个别名提高效率。</p>
<p>使用这种技术还可以创造出新的命令,比方说取消暂存文件时的输入比较繁琐,可以自己设置一下:</p>
<p>$ git config –global alias.unstage ‘reset HEAD –’</p>
<p>这样一来,下面的两条命令完全等同:</p>
<p>$ git unstage fileA
$ git reset HEAD fileA</p>
<p>显然,使用别名的方式看起来更清楚。另外,我们还经常设置 <code>last</code> 命令:</p>
<p>$ git config –global alias.last ‘log -1 HEAD’</p>
<p>然后要看最后一次的提交信息,就变得简单多了:</p>
<p>$ git last
commit 66938dae3329c7aebe598c2246a8e6af90d04646
Author: Josh Goebel <a href="mailto:dreamer3@example.com">dreamer3@example.com</a>
Date: Tue Aug 26 19:48:51 2008 +0800</p>
<pre><code> test for current head
Signed-off-by: Scott Chacon <schacon@example.com>
</code></pre>
<p>可以看出,实际上 Git 只是简单地在命令中替换了你设置的别名。不过有时候我们希望运行某个外部命令,而非 Git 的子命令,这个好办,只需要在命令前加上 <code>!</code> 就行。如果你自己写了些处理 Git 仓库信息的脚本的话,就可以用这种技术包装起来。作为演示,我们可以设置用 <code>git visual</code> 启动 <code>gitk</code>:</p>
<p>$ git config –global alias.visual ‘!gitk’</p>
<h2 id="section-37">小结</h2>
<p>到目前为止,你已经学会了最基本的 Git 本地操作:创建和克隆仓库,做出修改,暂存并提交这些修改,以及查看所有历史修改记录。接下来,我们将学习 Git 的必杀技特性:分支模型。</p>
Git入门 - 1.Git介绍
2014-06-16T00:00:00+00:00
/git/2014/06/16/git-introduction
<p>文章源自:https://github.com/jnavila/progit</p>
<h1 id="section">起步</h1>
<p>本章介绍开始使用 Git 前的相关知识。我们会先了解一些版本控制工具的历史背景,然后试着让 Git 在你的系统上跑起来,直到最后配置好,可以正常开始开发工作。读完本章,你就会明白为什么 Git 会如此流行,为什么你应该立即开始使用它。</p>
<h2 id="section-1">关于版本控制</h2>
<p>什么是版本控制?我为什么要关心它呢?版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。在本书所展示的例子中,我们仅对保存着软件源代码的文本文件作版本控制管理,但实际上,你可以对任何类型的文件进行版本控制。</p>
<p>如果你是位图形或网页设计师,可能会需要保存某一幅图片或页面布局文件的所有修订版本(这或许是你非常渴望拥有的功能)。采用版本控制系统(VCS)是个明智的选择。有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态。你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。但额外增加的工作量却微乎其微。</p>
<h3 id="section-2">本地版本控制系统</h3>
<p>许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。这么做唯一的好处就是简单。不过坏处也不少:有时候会混淆所在的工作目录,一旦弄错文件丢了数据就没法撤销恢复。</p>
<p>为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异(见图 1-1)。</p>
<p>Insert 18333fig0101.png
图 1-1. 本地版本控制系统</p>
<p>其中最流行的一种叫做 rcs,现今许多计算机系统上都还看得到它的踪影。甚至在流行的 Mac OS X 系统上安装了开发者工具包之后,也可以使用 rcs 命令。它的工作原理基本上就是保存并管理文件补丁(patch)。文件补丁是一种特定格式的文本文件,记录着对应文件修订前后的内容变化。所以,根据每次修订后的补丁,rcs 可以通过不断打补丁,计算出各个版本的文件内容。</p>
<h3 id="section-3">集中化的版本控制系统</h3>
<p>接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统( Centralized Version Control Systems,简称 CVCS )应运而生。这类系统,诸如 CVS,Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这已成为版本控制系统的标准做法(见图 1-2)。</p>
<p>Insert 18333fig0102.png
图 1-2. 集中化的版本控制系统</p>
<p>这种做法带来了许多好处,特别是相较于老式的本地 VCS 来说。现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。</p>
<p>事分两面,有好有坏。这么做最显而易见的缺点是中央服务器的单点故障。如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。要是中央服务器的磁盘发生故障,碰巧没做备份,或者备份不够及时,就会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,而被客户端偶然提取出来的保存在本地的某些快照数据就成了恢复数据的希望。但这样的话依然是个问题,你不能保证所有的数据都已经有人事先完整提取出来过。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。</p>
<h3 id="section-4">分布式版本控制系统</h3>
<p>于是分布式版本控制系统( Distributed Version Control System,简称 DVCS )面世了。在这类系统中,像 Git,Mercurial,Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份(见图 1-3)。</p>
<p>Insert 18333fig0103.png
图 1-3. 分布式版本控制系统</p>
<p>更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。</p>
<h2 id="git-">Git 简史</h2>
<p>同生活中的许多伟大事件一样,Git 诞生于一个极富纷争大举创新的年代。Linux 内核开源项目有着为数众广的参与者。绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。到 2002 年,整个项目组开始启用分布式版本控制系统 BitKeeper 来管理和维护代码。</p>
<p>到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了免费使用 BitKeeper 的权力。这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds )不得不吸取教训,只有开发一套属于自己的版本控制系统才不至于重蹈覆辙。他们对新的系统制订了若干目标:</p>
<ul>
<li>速度</li>
<li>简单的设计</li>
<li>对非线性开发模式的强力支持(允许上千个并行开发的分支)</li>
<li>完全分布式</li>
<li>有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)</li>
</ul>
<p>自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。它的速度飞快,极其适合管理大项目,它还有着令人难以置信的非线性分支管理系统(见第三章),可以应付各种复杂的项目开发需求。</p>
<h2 id="git--1">Git 基础</h2>
<p>那么,简单地说,Git 究竟是怎样的一个系统呢?请注意,接下来的内容非常重要,若是理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余。在开始学习 Git 的时候,请不要尝试把各种概念和其他版本控制系统(诸如 Subversion 和 Perforce 等)相比拟,否则容易混淆每个操作的实际意义。Git 在保存和处理各种信息的时候,虽然操作起来的命令形式非常相近,但它与其他版本控制系统的做法颇为不同。理解这些差异将有助于你准确地使用 Git 提供的各种工具。</p>
<h3 id="section-5">直接记录快照,而非差异比较</h3>
<p>Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容,请看图 1-4。</p>
<p>Insert 18333fig0104.png
图 1-4. 其他系统在每个版本中记录着各个文件的具体差异</p>
<p>Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一链接。Git 的工作方式就像图 1-5 所示。</p>
<p>Insert 18333fig0105.png
图 1-5. Git 保存每次更新时的文件快照</p>
<p>这是 Git 同其他系统的重要区别。它完全颠覆了传统版本控制的套路,并对各个环节的实现方式作了新的设计。Git 更像是个小型的文件系统,但它同时还提供了许多以此为基础的超强工具,而不只是一个简单的 VCS。稍后在第三章讨论 Git 分支管理的时候,我们会再看看这样的设计究竟会带来哪些好处。</p>
<h3 id="section-6">近乎所有操作都是本地执行</h3>
<p>在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网。但如果用 CVCS 的话,差不多所有操作都需要连接网络。因为 Git 在本地磁盘上就保存着所有当前项目的历史更新,所以处理起来速度飞快。</p>
<p>举个例子,如果要浏览项目的历史更新摘要,Git 不用跑到外面的服务器上去取数据回来,而直接从本地数据库读取后展示给你看。所以任何时候你都可以马上翻阅,无需等待。如果想要看当前版本的文件和一个月前的版本之间有何差异,Git 会取出一个月前的快照和当前文件作一次差异运算,而不用请求远程服务器来做这件事,或是把老版本的文件拉到本地来作比较。</p>
<p>用 CVCS 的话,没有网络或者断开 VPN 你就无法做任何事情。但用 Git 的话,就算你在飞机或者火车上,都可以非常愉快地频繁提交更新,等到了有网络的时候再上传到远程仓库。同样,在回家的路上,不用连接 VPN 你也可以继续工作。换作其他版本控制系统,这么做几乎不可能,抑或非常麻烦。比如 Perforce,如果不连到服务器,几乎什么都做不了(译注:默认无法发出命令 <code>p4 edit file</code> 开始编辑文件,因为 Perforce 需要联网通知系统声明该文件正在被谁修订。但实际上手工修改文件权限可以绕过这个限制,只是完成后还是无法提交更新。);如果是 Subversion 或 CVS,虽然可以编辑文件,但无法提交更新,因为数据库在网络上。看上去好像这些都不是什么大问题,但实际体验过之后,你就会惊喜地发现,这其实是会带来很大不同的。</p>
<h3 id="section-7">时刻保持数据完整性</h3>
<p>在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。换句话说,不可能在你修改了文件或目录之后,Git 一无所知。这项特性作为 Git 的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。</p>
<p>Git 使用 SHA-1 算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个 SHA-1 哈希值,作为指纹字符串。该字串由 40 个十六进制字符(0-9 及 a-f)组成,看起来就像是:</p>
<p>24b9da6552252987aa493b52f8696cd6d3b00373</p>
<p>Git 的工作完全依赖于这类指纹字串,所以你会经常看到这样的哈希值。实际上,所有保存在 Git 数据库中的东西都是用此哈希值来作索引的,而不是靠文件名。 </p>
<h3 id="section-8">多数操作仅添加数据</h3>
<p>常用的 Git 操作大多仅仅是把数据添加到数据库。因为任何一种不可逆的操作,比如删除数据,都会使回退或重现历史版本变得困难重重。在别的 VCS 中,若还未提交更新,就有可能丢失或者混淆一些修改的内容,但在 Git 里,一旦提交快照之后就完全不用担心丢失数据,特别是养成定期推送到其他仓库的习惯的话。</p>
<p>这种高可靠性令我们的开发工作安心不少,尽管去做各种试验性的尝试好了,再怎样也不会弄丢数据。至于 Git 内部究竟是如何保存和恢复数据的,我们会在第九章讨论 Git 内部原理时再作详述。</p>
<h3 id="section-9">文件的三种状态</h3>
<p>好,现在请注意,接下来要讲的概念非常重要。对于任何一个文件,在 Git 内都只有三种状态:已提交(committed),已修改(modified)和已暂存(staged)。已提交表示该文件已经被安全地保存在本地数据库中了;已修改表示修改了某个文件,但还没有提交保存;已暂存表示把已修改的文件放在下次提交时要保存的清单中。</p>
<p>由此我们看到 Git 管理项目时,文件流转的三个工作区域:Git 的工作目录,暂存区域,以及本地仓库。</p>
<p>Insert 18333fig0106.png
图 1-6. 工作目录,暂存区域,以及本地仓库</p>
<p>每个项目都有一个 Git 目录(译注:如果 <code>git clone</code> 出来的话,就是其中 <code>.git</code> 的目录;如果 <code>git clone --bare</code> 的话,新建的目录本身就是 Git 目录。),它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。</p>
<p>从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从 Git 目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑。</p>
<p>所谓的暂存区域只不过是个简单的文件,一般都放在 Git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。</p>
<p>基本的 Git 工作流程如下:</p>
<ol>
<li>在工作目录中修改某些文件。</li>
<li>对修改后的文件进行快照,然后保存到暂存区域。</li>
<li>提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。</li>
</ol>
<p>所以,我们可以从文件所处的位置来判断状态:如果是 Git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。到第二章的时候,我们会进一步了解其中细节,并学会如何根据文件状态实施后续操作,以及怎样跳过暂存直接提交。</p>
<h2 id="git">安装 Git</h2>
<p>是时候动手尝试下 Git 了,不过得先安装好它。有许多种安装方式,主要分为两种,一种是通过编译源代码来安装;另一种是使用为特定平台预编译好的安装包。</p>
<h3 id="section-10">从源代码安装</h3>
<p>若是条件允许,从源代码安装有很多好处,至少可以安装最新的版本。Git 的每个版本都在不断尝试改进用户体验,所以能通过源代码自己编译安装最新版本就再好不过了。有些 Linux 版本自带的安装包更新起来并不及时,所以除非你在用最新的 distro 或者 backports,那么从源代码安装其实该算是最佳选择。</p>
<p>Git 的工作需要调用 curl,zlib,openssl,expat,libiconv 等库的代码,所以需要先安装这些依赖工具。在有 yum 的系统上(比如 Fedora)或者有 apt-get 的系统上(比如 Debian 体系),可以用下面的命令安装:</p>
<p>$ yum install curl-devel expat-devel gettext-devel \
openssl-devel zlib-devel</p>
<p>$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
libz-dev libssl-dev</p>
<p>之后,从下面的 Git 官方站点下载最新版本源代码:</p>
<p>http://git-scm.com/download</p>
<p>然后编译并安装:</p>
<p>$ tar -zxf git-1.7.2.2.tar.gz
$ cd git-1.7.2.2
$ make prefix=/usr/local all
$ sudo make prefix=/usr/local install</p>
<p>现在已经可以用 <code>git</code> 命令了,用 <code>git</code> 把 Git 项目仓库克隆到本地,以便日后随时更新:</p>
<p>$ git clone git://git.kernel.org/pub/scm/git/git.git</p>
<h3 id="linux-">在 Linux 上安装</h3>
<p>如果要在 Linux 上安装预编译好的 Git 二进制安装包,可以直接用系统提供的包管理工具。在 Fedora 上用 yum 安装:</p>
<p>$ yum install git-core</p>
<p>在 Ubuntu 这类 Debian 体系的系统上,可以用 apt-get 安装:</p>
<p>$ apt-get install git</p>
<h3 id="mac-">在 Mac 上安装</h3>
<p>在 Mac 上安装 Git 有两种方式。最容易的当属使用图形化的 Git 安装工具,界面如图 1-7,下载地址在:</p>
<p>http://sourceforge.net/projects/git-osx-installer/</p>
<p>Insert 18333fig0107.png
图 1-7. Git OS X 安装工具</p>
<p>另一种是通过 MacPorts (<code>http://www.macports.org</code>) 安装。如果已经装好了 MacPorts,用下面的命令安装 Git:</p>
<p>$ sudo port install git-core +svn +doc +bash_completion +gitweb</p>
<p>这种方式就不需要再自己安装依赖库了,Macports 会帮你搞定这些麻烦事。一般上面列出的安装选项已经够用,要是你想用 Git 连接 Subversion 的代码仓库,还可以加上 +svn 选项,具体将在第八章作介绍。(译注:还有一种是使用 homebrew(<code>https://github.com/mxcl/homebrew</code>):<code>brew install git</code>。)</p>
<h3 id="windows-">在 Windows 上安装</h3>
<p>在 Windows 上安装 Git 同样轻松,有个叫做 msysGit 的项目提供了安装包,可以到 GitHub 的页面上下载 exe 安装文件并运行:</p>
<p>http://msysgit.github.com/</p>
<p>完成安装之后,就可以使用命令行的 <code>git</code> 工具(已经自带了 ssh 客户端)了,另外还有一个图形界面的 Git 项目管理工具。</p>
<p>给 Windows 用户的敬告:你应该在 msysGit 提供的 Unix 风格的 shell 来运行 Git。在 Unix 风格的 shell 中,可以使用本书中提及的复杂多行的命令。对于那些需要在 Windows 命令行中使用 Git 的用户,必须注意:在参数中间有空格的时候,必须使用双引号将参数括起来(在 Linux 中是单引号);另外,如果扬抑符(^)作为参数的结尾,并且作为这一行的最后一个字符,则这个参数也需要用双引号括起来。因为扬抑符在 Windows 命令行中表示续行(译注:即下一行为这一行命令的继续)。</p>
<h2 id="git--2">初次运行 Git 前的配置</h2>
<p>一般在新的系统上,我们都需要先配置下自己的 Git 工作环境。配置工作只需一次,以后升级时还会沿用现在的配置。当然,如果需要,你随时可以用相同的命令修改已有的配置。</p>
<p>Git 提供了一个叫做 <code>git config</code> 的工具(译注:实际是 <code>git-config</code> 命令,只不过可以通过 <code>git</code> 加一个名字来呼叫此命令。),专门用来配置或读取相应的工作环境变量。而正是由这些环境变量,决定了 Git 在各个环节的具体工作方式和行为。这些变量可以存放在以下三个不同的地方:</p>
<ul>
<li><code>/etc/gitconfig</code> 文件:系统中对所有用户都普遍适用的配置。若使用 <code>git config</code> 时用 ` –system` 选项,读写的就是这个文件。</li>
<li><code>~/.gitconfig</code> 文件:用户目录下的配置文件只适用于该用户。若使用 <code>git config</code> 时用 ` –global` 选项,读写的就是这个文件。</li>
<li>当前项目的 Git 目录中的配置文件(也就是工作目录中的 <code>.git/config</code> 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 <code>.git/config</code> 里的配置会覆盖 <code>/etc/gitconfig</code> 中的同名变量。</li>
</ul>
<p>在 Windows 系统上,Git 会找寻用户主目录下的 <code>.gitconfig</code> 文件。主目录即 <code>$HOME</code> 变量指定的目录,一般都是 <code>C:\Documents and Settings\$USER</code>。此外,Git 还会尝试找寻 <code>/etc/gitconfig</code> 文件,只不过看当初 Git 装在什么目录,就以此作为根目录来定位。</p>
<h3 id="section-11">用户信息</h3>
<p>第一个要配置的是你个人的用户名称和电子邮件地址。这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录:</p>
<p>$ git config –global user.name “John Doe”
$ git config –global user.email johndoe@example.com</p>
<p>如果用了 <code>--global</code> 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 <code>--global</code> 选项重新配置即可,新的设定保存在当前项目的 <code>.git/config</code> 文件里。</p>
<h3 id="section-12">文本编辑器</h3>
<p>接下来要设置的是默认使用的文本编辑器。Git 需要你输入一些额外消息的时候,会自动调用一个外部文本编辑器给你用。默认会使用操作系统指定的默认编辑器,一般可能会是 Vi 或者 Vim。如果你有其他偏好,比如 Emacs 的话,可以重新设置:</p>
<p>$ git config –global core.editor emacs</p>
<h3 id="section-13">差异分析工具</h3>
<p>还有一个比较常用的是,在解决合并冲突时使用哪种差异分析工具。比如要改用 vimdiff 的话:</p>
<p>$ git config –global merge.tool vimdiff</p>
<p>Git 可以理解 kdiff3,tkdiff,meld,xxdiff,emerge,vimdiff,gvimdiff,ecmerge,和 opendiff 等合并工具的输出信息。当然,你也可以指定使用自己开发的工具,具体怎么做可以参阅第七章。</p>
<h3 id="section-14">查看配置信息</h3>
<p>要检查已有的配置信息,可以使用 <code>git config --list</code> 命令:</p>
<p>$ git config –list
user.name=Scott Chacon
user.email=schacon@gmail.com
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
…</p>
<p>有时候会看到重复的变量名,那就说明它们来自不同的配置文件(比如 <code>/etc/gitconfig</code> 和 <code>~/.gitconfig</code>),不过最终 Git 实际采用的是最后一个。</p>
<p>也可以直接查阅某个环境变量的设定,只要把特定的名字跟在后面即可,像这样:</p>
<p>$ git config user.name
Scott Chacon</p>
<h2 id="section-15">获取帮助</h2>
<p>想了解 Git 的各式工具该怎么用,可以阅读它们的使用帮助,方法有三:</p>
<p>$ git help <verb>
$ git <verb> --help
$ man git-<verb></verb></verb></verb></p>
<p>比如,要学习 config 命令可以怎么用,运行:</p>
<p>$ git help config</p>
<p>我们随时都可以浏览这些帮助信息而无需连网。
不过,要是你觉得还不够,可以到 Freenode IRC 服务器(irc.freenode.net)上的 <code>#git</code> 或 <code>#github</code> 频道寻求他人帮助。这两个频道上总有着上百号人,大多都有着丰富的 git 知识,并且乐于助人。</p>
<h2 id="section-16">小结</h2>
<p>至此,你该对 Git 有了点基本认识,包括它和以前你使用的 CVCS 之间的差别。现在,在你的系统上应该已经装好了 Git,设置了自己的名字和电邮。接下来让我们继续学习 Git 的基础知识。</p>
Java设计模式 - 常用单例
2014-06-04T00:00:00+00:00
/java/2014/06/04/java-singleton-pattern
<p>单例是最常用到的一个设计模式,java的单例实现方式还是挺多的,总结下遇到的一些单例的实现</p>
<h2 id="section">一. 最简单的单例</h2>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Singleton</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="n">instance</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="kd">private</span> <span class="nf">Singleton</span><span class="o">()</span> <span class="o">{</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">instance</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">Singleton</span><span class="o">();</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">instance</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div>
<p>这种有并发问题,为了避免并发问题需要在判断的时候加锁。于是有了下面的实现。</p>
<h2 id="section-1">二. 加同步的单例</h2>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Singleton</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="n">instance</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="kd">private</span> <span class="nf">Singleton</span><span class="o">()</span> <span class="o">{</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">synchronized</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">instance</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">Singleton</span><span class="o">();</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">instance</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div>
<p>这种方式在每次获取对象的时候都要先获取锁,如果多次获取对象的话,会造成一些性能开销。考虑到只有在第一次创建对象时才存在并发问题,于是便有了双重判断的实现方式。</p>
<h2 id="section-2">三. 双重判断的同步的单例</h2>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Singleton</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">volatile</span> <span class="n">Singleton</span> <span class="n">instance</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="kd">private</span> <span class="nf">Singleton</span><span class="o">()</span> <span class="o">{</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">instance</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="kd">synchronized</span><span class="o">(</span><span class="n">Singleton</span><span class="o">.</span><span class="na">class</span><span class="o">){</span>
<span class="k">if</span><span class="o">(</span><span class="n">instance</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
<span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">Singleton</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">instance</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div>
<p>java的同步有很多陷阱,考虑到JMM的指令重排问题,instance被声明为volatile,这能够避免一部分问题,但是仍然会有一些并发的陷阱,比如CPU内部流水线也会做些指令的重排,这可不是受JVM指令控制的。如果我们创建一个非常大的单例对象,有可能会出现这种小概率事件。于是,干脆将同步问题扼杀在摇篮里。</p>
<h2 id="section-3">四. 典型饥饿模式的单例</h2>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Singleton</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">Singleton</span><span class="o">();</span>
<span class="kd">private</span> <span class="nf">Singleton</span><span class="o">()</span> <span class="o">{</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">instance</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div>
<p>JVM在对类做初始化时,会做同步处理,以保证一个类只被初始化一次。一般,饥饿模式的单例已经能满足大多数的需求,如果你非常渴望延迟加载,而又不想自己去做同步的话。可以试试第5种方式。</p>
<h2 id="section-4">五. 非同步延迟单例</h2>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Singleton</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">SingletonHolder</span><span class="o">{</span>
<span class="kd">static</span> <span class="n">Singleton</span> <span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">Singleton</span><span class="o">();</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="nf">Singleton</span><span class="o">()</span> <span class="o">{</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">SingletonHolder</span><span class="o">.</span><span class="na">instance</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div>
<p>这种方式利用JVM初始化的机制来达到延迟加载的目的,个人比较喜欢这种单例实现。不过具体实现的时候还是可能会有各种问题。比如构造类的时候可能出现异常,或者在初始化的时候出现死锁等问题。 最近看到有人用枚举来实现单例,也挺巧妙的。</p>
<h2 id="section-5">六. 枚举单例</h2>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">enum</span> <span class="n">Singleton</span> <span class="o">{</span>
<span class="n">INSTANCE</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">Singleton</span> <span class="nf">getInstance</span><span class="o">(){</span>
<span class="k">return</span> <span class="n">INSTANCE</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></div>
<p>觉得这种方式比较容易引起歧义,没太想好它的应用场景。不过保证单例是没啥问题。</p>
如何编写更棒的代码:11个核心要点
2014-05-27T00:00:00+00:00
/others/2014/05/27/11-tips-to-coding-better
<p>作为一个合格的程序员,有太多的理由促使你去编写干净利落且可读性强的代码。最重要的是因为你编写的代码,将来会有很多人一次次地阅读。当你有一天回过头来看自己的代码时,你就会明白编写优雅的代码是多么的重要。另外,如果别人来阅读你编写的代码,你是否想知道别人看到那些烂代码无比抓狂的感受。因此,花多一点的时间去编写优雅的代码,将来说不定会给你节省更多的时间。</p>
<p>那么,如何编写更棒的代码,下面是11条基本规则:</p>
<ul>
<li>
<p>1、保持方法简短扼要</p>
</li>
<li>
<p>2、永远永远不要将同一个变量用于不同的目的</p>
</li>
<li>
<p>3、尽可能让变量和方法的名称能够描述要实现的功能</p>
</li>
<li>
<p>4、尽可能将变量定义在最靠近它们的地方</p>
</li>
<li>
<p>5、不要出现让人费解的数字</p>
</li>
<li>
<p>6、要像对待朋友一样对待你擅长的语言</p>
</li>
<li>
<p>7、不要逆常规而行</p>
</li>
<li>
<p>8、千万小心过早的优化代码</p>
</li>
<li>
<p>9、要常常重构经过测试的代码</p>
</li>
<li>
<p>10、不要沉溺于过度的设计技巧</p>
</li>
<li>
<p>11、随时随地学习新的知识</p>
</li>
</ul>
<p>下面我们来对每一点详细展开介绍。</p>
<h2 id="section">1、保持方法简短扼要</h2>
<p>尽管很多人都遵循这条规则,但是它依然很重要。总的来说,编写的方法最好能在首屏完全显示。试想,如果你需要滚动页面才能看到整一个方法,那是一件多么分散注意力的事情。一个方法最好能保持在5 – 20行之间,当然,你也要视具体情况而定,并不是一概而论的。对于getter和setter方法,通常只需一行代码,所以它们看起来更像是类成员的存取访问器。</p>
<h2 id="section-1">2、远永远不要将同一个变量用于不同的目的</h2>
<p>一个变量应该只能被用于一个目的,我们可以通过使用常量(C++中用const标识,Java中用final标识),帮助编译器优化代码编译,也可以向程序标识“这个变量是不能被改变的”,这样我们编写的代码就有更好的可读性。</p>
<h2 id="section-2">3、尽可能让变量和方法的名称能够描述要实现的功能</h2>
<p>一段通俗易懂的程序代码,应该是任何人只要看了代码,就能明白程序是用来干嘛的。所以我建议大家尽量少用缩写,除非是程序界公认的简写习惯,像下面的简写习惯:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">src</span> <span class="o">-</span> <span class="n">source</span>
<span class="n">pos</span> <span class="o">-</span> <span class="n">position</span>
<span class="n">prev</span> <span class="o">-</span> <span class="n">previous</span></code></pre></div>
<p>如果你觉得描述性的简写方式没有价值,你可以比较一下n, ns, nsisd和numTeamMembers, seatCount, numSeatsInStadium。</p>
<h2 id="section-3">4、尽可能将变量定义在最靠近它们的地方</h2>
<p>当你在盖房子的时候,总不希望把锤子放在别人家的院子里吧,相反,你会把盖房的工具放得尽可能近,定义变量也是同样的道理。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">int</span> <span class="n">foo</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="n">int</span> <span class="n">bar</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
<span class="sr">//</span> <span class="n">bunch</span> <span class="n">of</span> <span class="n">code</span> <span class="n">that</span> <span class="n">uses</span> <span class="s2">"bar"</span>
<span class="sr">//</span> <span class="n">but</span> <span class="n">doesn</span><span class="err">'</span><span class="n">t</span> <span class="n">care</span> <span class="n">about</span> <span class="s2">"foo"</span>
<span class="sr">//</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="n">baz</span><span class="p">(</span><span class="n">foo</span><span class="p">);</span></code></pre></div>
<p>我们可以这样重构代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">int</span> <span class="n">bar</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
<span class="sr">//</span> <span class="n">bunch</span> <span class="n">of</span> <span class="n">code</span> <span class="n">that</span> <span class="n">use</span> <span class="s2">"bar"</span>
<span class="sr">//</span> <span class="n">but</span> <span class="n">doesn</span><span class="err">'</span><span class="n">t</span> <span class="n">care</span> <span class="n">about</span> <span class="s2">"foo"</span>
<span class="sr">//</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="n">int</span> <span class="n">foo</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="n">baz</span><span class="p">(</span><span class="n">foo</span><span class="p">);</span></code></pre></div>
<p>当你把变量的声明跟使用它的地方相隔太远的时候(甚至是超过一屏),那的确会给你带来很大的麻烦。你会经常滚动页面去寻找这个变量,导致你很难在大脑中保持代码之间的连贯性。</p>
<h2 id="section-4">5、不要出现让人费解的数字</h2>
<p>任何时候,你要比较一些常量时,都要将它们定义成constant类型。团队之间调试代码时最让人头疼是出现下面的代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">il</span> <span class="o"><</span> <span class="mi">4384</span></code></pre></div>
<p>把它替换成下面的代码该多好:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">inputLength</span> <span class="o"><</span> <span class="no">MAX_INPUT_LENGTH</span></code></pre></div>
<h2 id="section-5">6、要像对待朋友一样对待你擅长的语言</h2>
<p>学习一种新的编程语言是一件很有趣的事情,从中你可以用很酷的方式学到新东西。还有就是让一个对某种语言很专业的人去学另外一种语言,很多时候会让人心有余而力不足。举个例子,你让一个Java大牛去学Ruby,他应该会用Ruby的方式去解决问题,而不是继续沿用Java的解决问题的思想。</p>
<p>当你需要循环输出5遍”Hello World“时,Java代码应该会是这样:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">5</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">"Hello world!"</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>但是用Ruby,你也许会这样写:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="n">.</span><span class="mi">5</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"Hello world!"</span>
<span class="k">end</span></code></pre></div>
<p>这些看上去都很不错,但是最完美的方式可能是下面这样:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="mi">5</span><span class="o">.</span><span class="n">times</span> <span class="p">{</span> <span class="nb">puts</span> <span class="s2">"Hello world!"</span> <span class="p">}</span></code></pre></div>
<h2 id="section-6">7、不要逆常规而行</h2>
<p>每一种编程语言都有自己的约束习惯,总的来说,大家对Java的编程习惯可能会了解得比较多,我们一起来看看其中的一些习惯:</p>
<ul>
<li>方法名以小写字母开头,后面紧跟的是大写字母开头的单词,比如veryLongVariableName。</li>
<li>类名一般都是大写字母开头的单词组合。</li>
<li>常量的命名都是大写字母的单词,之间用下划线隔开,比如MY_CONSTANT</li>
<li>左大括号应该跟if在同一行</li>
</ul>
<p>只有在迫不得已的时候才能打破这种规则,千万不要因为不喜欢这种做法而违背已经约定好的编码习俗。如果你身为团队一员,想改变一些编码规则的话,那也可以,不过当你把自己的代码分享给没有你这种习惯的队友的时候,棘手的问题会迎面而来。</p>
<h2 id="section-7">8、千万小心过早的优化代码</h2>
<p>过早的优化是所有问题的根源,至少电视上是这么说的…你的首要任务是编写容易理解的代码,而不要求你能很快写出来。除非你的程序运行很慢,否则谈优化都是为时太早。如果你想优化你的程序,那么得先找出程序的问题,这就是我们需要profilers这个工具的原因。</p>
<p>在没有找到问题源头就去优化代码,这样做你所要付出的代价就是破坏了程序的结构,至少会丧失程序的可读性。如果你发现程序运行缓慢了,也不要盲目地重构代码,要先找到导致运行慢的根本原因。</p>
<p>千万不要傻乎乎地去解决根本不存在的问题。</p>
<h2 id="section-8">9、要常常重构经过测试的代码</h2>
<p>世上没有绝对完美的事情。尽管你认为自己的代码已经写得非常完美了,过一段时间也要经常去看看它,也许那时你会对自己大骂:”怎么会那么傻!”</p>
<p>有一种提高代码质量的方法,那就是经常重构通过测试的代码。所谓通过测试,我指的是程序要能正常工作,你可以通过自动化测试或者手动测试来确保这一点。</p>
<p>首先你要确保程序能够正常运行,第一次我们并不需要写出多么完美的程序,能用就行,接下来我们可以慢慢重构,让它逐渐变得完美。这种开发方式很有TDD的味道,关键在于你需要熟悉重构的每一个环节。如果你熟练使用一些高级的IDE,像IntelliJ IDEA,那你的重构工作将会简单很多。</p>
<p>重构完以后,也许你会碰到很多这样那样的问题,甚至会破坏正常的程序,这就是我们要利用自动化测试的原因了。当你重构完以后,跑一遍单元测试就能避免这些令人头疼的问题了。</p>
<h2 id="section-9">10、不要沉溺于过度的设计技巧</h2>
<p>当我第一次接触到设计模式这一概念时,我觉得自己找到了“圣杯”。这些精妙的设计思想可以让你工作更加顺利,也可以让你的设计浅显易懂,因为你可以简单的说“我使用了观察者模式”,而不同大费周章的解释一通。然而问题来了,由于有些问题看起来太自然太简单了,你会把那些设计模式的思想应用到任何地方,为什么不把这个类设计成单例模式(singleton)?干嘛不去创建一些工厂类呢?</p>
<p>于是用80行代码就能完成的脚本,结果你用了10个类,15个接口和一堆泛型和注释,这其中的97%代码并没有做实质上的事情。设计模式虽然非常有用,可以帮助你简化设计,但是这并不是说你可以到处使用它们。你可以使用设计模式,但是不能将它滥用了。</p>
<h2 id="section-10">11、随时随地学习新的知识</h2>
<p>编程就是一项随时学习新事物的工作,当你学到了新的类库或者编程语言时,你会迫不及待地丢掉老的代码,进而去重写它们。然而有很多理由说明你不该这么做。</p>
<p>将一个新的类库或者框架应用到现有的项目中就会出现类似的问题。比如说你正在为一个Web项目写Javascript,但是中间你发现了jQuery,这时候你会迫不及待想把jQuery应用进去,而丢掉原来的Javascript代码,即便你根本没用jQuery写过任何项目。</p>
<p>最好的方式是你先用jQuery学着写一些简单的例子,把你项目中要用到的技术都学会。比如说你想要用AJAX?就先在项目之外写一些关于AJAX的简单例子,等到完全掌握了,就可以将老代码从项目中移除。</p>
<p>如果你热衷于编程,我强烈推荐你阅读Steve McConnell编写的《Code Complete》,它将永远改变你的编程思维。</p>
<p>VIA: <a href="http://www.html5tricks.com/11-tips-to-coding-better.html">11 tips to coding better</a></p>
Android Gson
2014-05-22T00:00:00+00:00
/android/2014/05/22/android-gson
<p>目前的客户端大都有和服务端进行交互,而数据的格式基本就是json了,于是在Android开发中就经常用到json解析,方便的是Google已经为我们提供了一个很棒的json解析库–gson,那么今天就来总结分享下gson的各种用法。</p>
<p>gson的官方下载地址:<a href="https://code.google.com/p/google-gson/">google-gson</a></p>
<h2 id="section">单个对象</h2>
<p>首先我们来看一个最简单的用法,假设json的数据格式是这样的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="p">{</span>
<span class="s2">"id"</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
<span class="s2">"body"</span><span class="p">:</span> <span class="s2">"It is my post"</span><span class="p">,</span>
<span class="s2">"number"</span><span class="p">:</span> <span class="mi">0</span><span class="o">.</span><span class="mi">13</span><span class="p">,</span>
<span class="s2">"created_at"</span><span class="p">:</span> <span class="s2">"2014-05-22 19:12:38"</span>
<span class="p">}</span></code></pre></div>
<p>那么我们只需要定义对应的一个类:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">int</span> <span class="nb">id</span><span class="p">;</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">body</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">float</span> <span class="n">number</span><span class="p">;</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">created_at</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>使用起来只需如下几行代码就行了:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">JSON_DATA</span> <span class="o">=</span> <span class="s2">"..."</span><span class="p">;</span>
<span class="no">Foo</span> <span class="n">foo</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Gson</span><span class="p">()</span><span class="o">.</span><span class="n">fromJson</span><span class="p">(</span><span class="no">JSON</span><span class="p">,</span> <span class="no">Foo</span><span class="o">.</span><span class="n">class</span><span class="p">);</span></code></pre></div>
<p>这里是最简单的用法,created_at直接定义了String类型,如果你想要Date类型的也可以,就变成下面的例子:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">int</span> <span class="nb">id</span><span class="p">;</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">body</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">float</span> <span class="n">number</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">Date</span> <span class="n">created_at</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">JSON_DATA</span> <span class="o">=</span> <span class="s2">"..."</span><span class="p">;</span>
<span class="no">GsonBuilder</span> <span class="n">gsonBuilder</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">GsonBuilder</span><span class="p">();</span>
<span class="n">gsonBuilder</span><span class="o">.</span><span class="n">setDateFormat</span><span class="p">(</span><span class="s2">"yyyy-MM-dd HH:mm:ss"</span><span class="p">);</span>
<span class="no">Gson</span> <span class="n">gson</span> <span class="o">=</span> <span class="n">gsonBuilder</span><span class="o">.</span><span class="n">create</span><span class="p">();</span>
<span class="no">Foo</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">gson</span><span class="o">.</span><span class="n">fromJson</span><span class="p">(</span><span class="no">JSON</span><span class="p">,</span> <span class="no">Foo</span><span class="o">.</span><span class="n">class</span><span class="p">);</span></code></pre></div>
<p>有人说created_at不是java风格,java编程规范是驼峰结构,那么ok,Gson很人性化的也提供注解的方式,只需要把Foo对象改成这样就ok了:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">int</span> <span class="nb">id</span><span class="p">;</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">body</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">float</span> <span class="n">number</span><span class="p">;</span>
<span class="vi">@SerializedName</span><span class="p">(</span><span class="s2">"created_at"</span><span class="p">)</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">createdAt</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>然后用法不变,是不是很方便。</p>
<h2 id="section-1">对象的嵌套</h2>
<p>假设要返回如下数据:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="p">{</span>
<span class="s2">"id"</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
<span class="s2">"body"</span><span class="p">:</span> <span class="s2">"It is my post"</span><span class="p">,</span>
<span class="s2">"number"</span><span class="p">:</span> <span class="mi">0</span><span class="o">.</span><span class="mi">13</span><span class="p">,</span>
<span class="s2">"created_at"</span><span class="p">:</span> <span class="s2">"2014-05-22 19:12:38"</span>
<span class="s2">"foo2"</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">"id"</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="s2">"haha"</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>那么对象的定义是这样的</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">int</span> <span class="nb">id</span><span class="p">;</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">body</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">float</span> <span class="n">number</span><span class="p">;</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">created_at</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">ChildFoo</span> <span class="n">foo2</span><span class="p">;</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">ChildFoo</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">int</span> <span class="nb">id</span><span class="p">;</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="nb">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-2">对象数组</h2>
<p>假如返回的是json数组,如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">[</span><span class="p">{</span>
<span class="s2">"id"</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
<span class="s2">"body"</span><span class="p">:</span> <span class="s2">"It is my post1"</span><span class="p">,</span>
<span class="s2">"number"</span><span class="p">:</span> <span class="mi">0</span><span class="o">.</span><span class="mi">13</span><span class="p">,</span>
<span class="s2">"created_at"</span><span class="p">:</span> <span class="s2">"2014-05-20 19:12:38"</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="s2">"id"</span><span class="p">:</span> <span class="mi">101</span><span class="p">,</span>
<span class="s2">"body"</span><span class="p">:</span> <span class="s2">"It is my post2"</span><span class="p">,</span>
<span class="s2">"number"</span><span class="p">:</span> <span class="mi">0</span><span class="o">.</span><span class="mi">14</span><span class="p">,</span>
<span class="s2">"created_at"</span><span class="p">:</span> <span class="s2">"2014-05-22 19:12:38"</span>
<span class="p">}</span><span class="o">]</span></code></pre></div>
<p>这种解析有两种方法:</p>
<ul>
<li>1、解析成数组</li>
</ul>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">JSON_DATA</span> <span class="o">=</span> <span class="s2">"..."</span><span class="p">;</span>
<span class="no">Foo</span><span class="o">[]</span> <span class="n">foos</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Gson</span><span class="p">()</span><span class="o">.</span><span class="n">fromJson</span><span class="p">(</span><span class="no">JSON_DATA</span><span class="p">,</span> <span class="no">Foo</span><span class="o">[].</span><span class="n">class</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">这时候想转成</span><span class="no">List</span><span class="err">的话调用如下方法</span>
<span class="sr">//</span> <span class="no">List</span><span class="o"><</span><span class="no">Foo</span><span class="o">></span> <span class="n">foosList</span> <span class="o">=</span> <span class="no">Arrays</span><span class="o">.</span><span class="n">asList</span><span class="p">(</span><span class="n">foos</span><span class="p">);</span></code></pre></div>
<ul>
<li>2、解析成List</li>
</ul>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">JSON_DATA</span> <span class="o">=</span> <span class="s2">"..."</span><span class="p">;</span>
<span class="no">Type</span> <span class="n">listType</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">TypeToken</span><span class="o"><</span><span class="no">ArrayList</span><span class="o"><</span><span class="no">Foo</span><span class="o">>></span><span class="p">(){}</span><span class="o">.</span><span class="n">getType</span><span class="p">();</span>
<span class="no">ArrayList</span><span class="o"><</span><span class="no">Foo</span><span class="o">></span> <span class="n">foos</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Gson</span><span class="p">()</span><span class="o">.</span><span class="n">fromJson</span><span class="p">(</span><span class="no">JSON_DATA</span><span class="p">,</span> <span class="n">listType</span><span class="p">);</span></code></pre></div>
<h2 id="section-3">总结</h2>
<p>上面基本就总结到开发中常用到的集中类型,用法很简单方便,主要需要json数据抽象成对应的数据模型就ok了。不过阿里也有一套自己的开源json解析库–FastJson,据说性能更佳,但是实际应用中感觉Gson的解析已经相当快了,而且更习惯用Google官方的东西,所以对<a href="https://github.com/alibaba/fastjson">FastJson</a>没有怎么研究,以后有时间使用体验一下。</p>
Android 屏幕适配
2014-05-16T00:00:00+00:00
/android/2014/05/16/android-screen-adaptation
<p>众所周知,Android机型尺寸各种各样,于是屏幕适配就成了Android开发中很重要的一环。Android屏幕适配可能一些开发者都会遇到这样的问题,今天就来分享下屏幕适配,你会发现其实Android屏幕适配也可以很简单。</p>
<h2 id="section">基本概念</h2>
<p>Android屏幕适配必须要理解的一些概念,这部分可能比较枯燥,但是俗话说的好“工欲善其事,必先利器”,翻译过来就是“有什么样的枪,决定你打什么样的鸟”,一旦这些概念你理解掌握了,屏幕适配你自然而然就觉得简单多了。</p>
<ul>
<li>px</li>
</ul>
<p>是英文单词pixel的缩写,意为像素,屏幕上的点。我们通常所说的分辨率如480X800就是指的像素。</p>
<p>在设计领域中,像素是用来计算数码影像的最小单位。计算机中显示的图像并非连续的线条组成,而是由许多肉眼看不见的小点组成。如果把影像放大数倍,会发现这些连续色调其实是由许多色彩相近的小点所组成,这些小点就是构成影像的最小单位“像素”。由于是最小的独立显示单位,px均为整数,不会出现0.5px的情况。如:</p>
<p>看这个色彩鲜艳的LED灯(原图大小)</p>
<p><img src="/image/pixel_origin.png" /></p>
<p>你能想象这才是他的本来面目吗?(放大之后)</p>
<p><img src="/image/pixel_scale.jpeg" /></p>
<ul>
<li>in</li>
</ul>
<p>表示英寸,是屏幕的物理尺寸。每英寸等于2.54厘米。例如我们经常说的手机屏幕大小有,5(英)寸、4(英)寸就是指这个单位。这些尺寸是屏幕的对角线长度。如果手机的屏幕是4英寸,表示手机的屏幕(可视区域)对角线长度是4 X 2.54 = 10.16厘米。</p>
<ul>
<li>dpi</li>
</ul>
<p>dpi是Dots Per Inch的缩写, 每英寸点数,即每英寸包含像素个数。比如320X480分辨率的手机,宽2英寸,高3英寸, 每英寸包含的像素点的数量为320/2=160dpi(横向)或480/3=160dpi(纵向),160就是这部手机的dpi,横向和纵向的这个值都是相同的,原因是大部分手机屏幕使用正方形的像素点。</p>
<ul>
<li>density</li>
</ul>
<p>屏幕密度,density和dpi的关系为 density = dpi/160</p>
<ul>
<li>dp</li>
</ul>
<p>也即dip,设备独立像素,device independent pixels的缩写,Android特有的单位,在屏幕密度dpi = 160屏幕上,1dp = 1px。</p>
<ul>
<li>sp</li>
</ul>
<p>和dp很类似,一般用来设置字体大小,和dp的区别是它可以根据用户的字体大小偏好来缩放。</p>
<h2 id="android-drawable">Android Drawable</h2>
<p>我们新建一个Android项目后应该可以看到很多drawable文件夹,分别对应不同的dpi</p>
<ul>
<li>
<p>drawable-ldpi (dpi=120, density=0.75)</p>
</li>
<li>
<p>drawable-mdpi (dpi=160, density=1)</p>
</li>
<li>
<p>drawable-hdpi (dpi=240, density=1.5)</p>
</li>
<li>
<p>drawable-xhdpi (dpi=320, density=2)</p>
</li>
<li>
<p>drawable-xxhdpi (dpi=480, density=3)</p>
</li>
</ul>
<p>市面上的一些Android教程大多都是教的是为每种dpi都出一套图片资源,这个固然是一种解决办法,但同时也是一种非常笨的方法,为美工或者设计增加了不少的工作量不说,同时也会让你的apk包变的很大。那么有没有什么好的方法既能保证屏幕适配,又可以最小占用设计资源,同时最好又只使用一套dpi的图片资源呢?下面就来讲解下项目中总结出来的这个方法。</p>
<p>首先必须清楚一个自动渲染的概念,Android SDK会自动屏幕尺寸选择对应的资源文件进行渲染,如SDK检测到你手机dpi是160的话会优先到drawable-mdpi文件夹下找对应的图片资源,注意只是优先,假设你手机dpi是160,但是你只在xhpdi文件夹下有对应的图片资源文件,程序一样可以正常运行。所以理论上来说只需要提供一种规格的图片资源就ok了,如果只提供ldpi规格的图片,对于大分辨率的手机如果把图片放大就会不清晰,所以需要提供一套你需要支持的最大dpi的图片,这样即使用户的手机分辨率很小,这样图片缩小依然很清晰。</p>
<h2 id="xhdpi">xhdpi成为首选</h2>
<p>上面说了只需要提供一套大的dpi的图片就ok了,现在市面手机分辨率最大可达到1080X1920的分辨率,如Nexus5,dpi属于xxhdpi,但是毕竟还没普及,目前市面上最普遍的高端机的分辨率还多集中在720X1080范围,也就是多集中在xhdpi,所以目前来看xhpdi规格的图片成为了首选。当然随着技术规格的提高以后发展,以后可能市场上xxdpi的手机会越来越普遍,但这是后话。</p>
<h2 id="section-1">设计资源紧张怎么办?</h2>
<p>在现在的App开发中,基本都会有iOS和Android版本,有些公司为了保持App不同版本的体验交互一致,还有些公司的设计资源可能比较紧张,这些情况下iOS和Android版本基本是一个设计师主导,而大多数情况下设计师可能更会以iPhone手机为基础进行设计,包括后期的切图之类的。这个时候身为Android开发人员你是否还要求设计师单独为Android端切一套图片资源呢?这会让你们的设计师崩溃的,下面就来告诉一个项目中总结的更棒的方法。</p>
<p>相信设计师们一般都会用最新的iPhone5(5s和5的尺寸以及分辨率都一样)来做原型设计,而iPhone5的屏幕分辨率为640X1164, 屏幕尺寸为4英寸,根据勾股定理(a^2 + b^2 = c^2)640^2+1164^2=1764496, 然后再对其开根号可求出屏幕对角线的分辨率为:1328,除以4可得出iphone5的dpi:1328/4≈332
可以看出iPhone5的屏幕的dpi约等于320, 刚好属于xhdpi,所以你可以很自豪的像你们的设计师说不用专门为Android端切图,直接把iPhone的那一套切好的图片资源放入drawable-xhdpi文件夹里就ok了。</p>
<h2 id="wrapcontent-vs-dp">wrap_content VS dp</h2>
<p>wrap_content和dp都是在Android开发中应该经常用到的,然后它们冥冥中是有关系的。</p>
<p>假设你看了这篇文章后都是统一有xhdpi的资源,那么你用wrap_content完全没有问题,Android会自动为其他规格的dpi屏幕适配,比如你在xhdpi放了一张120X120px大小的图片,那么在在hdpi屏幕上显示的就只有120/1.5=80px大小,但是如果你不小心同样把这张图片也放入了mdpi了,这个时候用wrap_content显示就会有问题,具体看下面的例子:</p>
<p>例如假设你只在drawable_xhdpi文件夹下放了test图片,xhdpi的设备会去xhdpi文件夹下找到test图片并直接显示,而mdpi的设备优先会去mdpi文件夹里查找test图片,但是没找到,最后在xhdpi文件夹下找到,然后会自动根据density计算并缩放显示出来,实际显示出来的大小是120/2=60px, 所以整体的显示比例才会看起来比较正常</p>
<ul>
<li>mdpi</li>
</ul>
<p><img src="/image/mdpi_test.png" /></p>
<ul>
<li>xhdpi</li>
</ul>
<p><img src="/image/xhdpi_test.png" /></p>
<p>但是如果你在mdpi文件夹里也放入了同样的图片,那么mdpi的设备会直接去mdpi文件夹里寻找到test图片,并直接显示,而这时候显示不会缩放,实际显示大小就是120X120,在mdpi的屏幕上看起来就会比较大,如图:</p>
<p><img src="/image/mdpi_test2.png" /></p>
<p>通过上面整个过程,大家应该理解了Android加载资源的整个过程, wrap_content同样可以用dp来代替,就拿上面这个例子,在xhdpi文件夹内放入了一张120X120像素的test图片,宽高直接除density就得出dp的数值,即这种情况下以下代码是等同的.</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">ImageView</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">src</span><span class="o">=</span><span class="s2">"@drawable/test"</span> <span class="sr">/></span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">ImageView</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"60dp"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"60dp"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">src</span><span class="o">=</span><span class="s2">"@drawable/test"</span> <span class="sr">/></span></code></pre></div>
<h2 id="section-2">总结</h2>
<p>相信通过以上的讲解,对Android UI中的一些基本概念有个很好的理解,实际开发工作中也有一些高效的方法可以参考,应该可以应对大部分的屏幕适配工作。但是项目中仍然有一些比较特殊的适配需求满足不了,以后会针对一些特殊的需求进行示例讲解。</p>
认识一下Sketch
2014-05-15T00:00:00+00:00
/design/2014/05/15/learn-sketch
<p>Jean-Marc Denis(原文作者,前Sparrow设计师,现在在Google工作)在参加WWDC 2013时认识了Sketch的开发团队,所以在第一时间尝试了Sketch,当时由于一些功能的缺失,所以还是折回去使用Photoshop了。过了一段时间,他发现设计师社区开始疯狂着迷于这款新的设计工具,所以决定再试一试,想看看迭代了一段时间的Sketch现在能完成多少原先他需要用Photoshop来做的工作。现在,他80%的设计工作都是由Sketch来完成的,所以写了这篇文章来帮助大家一起了解Sketch。</p>
<h2 id="photoshop">Photoshop并不是一款合适的界面设计工具</h2>
<p>为什么我们会期待一个新的设计工具?因为当我们更多的关注效率和关注协作,就越发现Photoshop已经不足以满足我们的期待了。下面看看具体的理由:</p>
<ul>
<li>并非为设计师打造</li>
</ul>
<p>Photoshop是为图像处理开发的,诸如路径和矢量工具都是后期才加上去的,所以如果你关心一下Photoshop的更新路径,用户界面设计师完全不是Adobe做这条产品时关心的用户群。所以,如果抛去那些复杂的功能,一个为用户界面设计师打造的设计工具完全可以做得更简单,更高效。</p>
<ul>
<li>不适合移动时代</li>
</ul>
<p>相信我们都会觉得为不同的设备输出不同分辨率的素材而痛苦不已吧。或许,你会使用一些第三方插件或是启用一些模板来处理这些事,但依然是很浪费时间的。</p>
<ul>
<li>引擎太过陈旧</li>
</ul>
<p>Photoshop的引擎是上个世纪为图像处理而打造的,非常的耗费资源,即使是启动一下都要等很久,更别说一次移动多个分组了。话说回来,由于要兼顾Mac和Windows平台,能做到这样已经不容易了。</p>
<ul>
<li>越来越跟不上时代</li>
</ul>
<p>大公司的执行效率实在太低。不知道大伙是不是和我一样,非常痛苦地等了好几个月才等到Photoshop对Retina屏幕的支持。诸如自动对齐的等功能我已经不做期待了。</p>
<h2 id="sketch">为什么Sketch确实值得尝试</h2>
<ul>
<li>自动保存和多版本控制</li>
</ul>
<p>有没有梦想过拜托机械的手动保存?Sketch可以做到。Sketch在工作的时候会不断地自动保存成果,并且允许你回复到此前的任一版本。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*-4cYKm2z-PiOVFnk.png" /></p>
<ul>
<li>矢量作图并且像素级的完美</li>
</ul>
<p>矢量图意味着可以任意扩展。你没有必要不断地去调整素材的尺寸,Sketch会自动就帮你维持一个像素级完美的作品。如果有的时候你必须用像素点来作图,比如说是画图标或者是插画设计,Sketch 也提供了从矢量切换到像素视图的功能。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/1600/0*55Ko51isBD3VVcSL.png" /></p>
<ul>
<li>智能标尺</li>
</ul>
<p>你喜欢xScope吗?还是正在用选框工具来进行测量?又或者是难用的网格?Sketch的智能标尺可以帮你轻松地把设计元素的对齐方式、内外间距都调整完美。就我个人而言,这个功能非常节省时间而且避免了复杂的人工计算,是我觉得最有用的功能之一。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*DOaquv046FxhhUz6.gif" /></p>
<ul>
<li>随时随地编辑元素</li>
</ul>
<p>在Sketch里我们可以随时随地修改一个元素的圆角、高度和宽度。只需要简单地修改数值就可以轻松调整元素,对于刚从Photoshop切换过来的设计师而言非常好用。甚至会让你产生强烈的依赖。</p>
<ul>
<li>图形拼接</li>
</ul>
<p>在Sketch 里你可以很轻松地把多个图形合并,构成一个新的图形。并且Sketch在提供了多种图形合并模式的同时,还允许你随时修改已经合并图形的子图形。这项功能使得你可以很轻松地管理和创造更复杂的图形。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*4piteVCDqPNzWS3V.jpeg" /></p>
<ul>
<li>每个图层都可以有多种混合效果</li>
</ul>
<p>在Photoshop里想要让一个图层有多种混合效果是很困难的。但在Sketch里,你可以很容易给一个图层加上多种混合效果,看看下面的示例就知道了。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*UWRr3uQgedzzHtS6.png" /></p>
<ul>
<li>自动选择最接近的像素边界</li>
</ul>
<p>自动选择最接近的像素边界可以让一个图形或者是图层自动修正边距到一个像素级完善的位置。比如可以把一个宽度是5.3px的图形自动修正到5px,这样就不会有模糊的像素点和粗糙的图形了。我自己给这项功能设了一个快捷键(Command+Opitons+x),来确保我做出的每一个图形都像素级完美。这比人工一个一个按照网格修改图形高效得多。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*me1mYlEw5C1JKAKY.jpeg" /></p>
<ul>
<li>样式链接</li>
</ul>
<p>有的时候你会做一些充斥文字的设计,样式链接可以帮助你设定一个固定的样式来快速地应用在新的文字上。如果你修改了其中任何一处的样式,那么所有链接该样式的文字都会自动更新到新的样式。并且,这项功能也可以应用在图形上。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/774/0*bTuYN8VIHPDImDXh.gif" /></p>
<ul>
<li>导出素材</li>
</ul>
<p>用Photoshop的时候,最痛苦的事莫过于切割元素,然后到处素材。Sketch则可以帮助你快速地导出各种分辨率和格式(pdf, eps, svg, png, jpg, tiff)的素材,通常导出只需要一个点击,并且不用借助任何第三方应用程序。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/914/0*Qjf-7dLH9R7H-yOk.png" /></p>
<ul>
<li>分布式布局</li>
</ul>
<p>作富文本设计的时候,布局会变得非常重要,这项功能可以帮助你快速地试验不同的方案。</p>
<ul>
<li>网格</li>
</ul>
<p>其实可以把网格功能理解成按照纵横两个维度自动地结构化布局内容。这个对于排版布局而言,非常方便。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*7TrwZwsBKN-hGLxk.jpeg" /></p>
<ul>
<li>出色的文字渲染</li>
</ul>
<p>Sketch的文字渲染引擎非常出色,可以用来做很出色的文字布局和字体设计。因此,你可以放心地放弃Photoshop的抗锯齿功能。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*2wahLljvKwgbT_Ws.png" /></p>
<ul>
<li>CSS样式导出</li>
</ul>
<p>如果你在做一些网页设计,Sketch可以帮助你快速地把你的成果到处成CSS样式。</p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*qgh086TCiTWrVOl9.png" /></p>
<p><img src="https://d262ilb51hltx0.cloudfront.net/max/800/0*NxQ0xvJylnXQbGVm.png" /></p>
<ul>
<li>旋转副本</li>
</ul>
<p>这是一项小功能,但是确实可以节约你很多时间和精力。</p>
<ul>
<li>Sketch Mirror</li>
</ul>
<p>Sketch Mirror是一个Universal App,可以方便你在iPhone或是iPad上查看自己的成果。(作者撰写这个文章的时候Sketch还没有推出官方的镜像工具,所以这一段是译者补充的)。</p>
<h2 id="section">快速响应是杀手锏</h2>
<p>Sketch是一个小团队,发展非常快速。简单扫一眼Sketch的升级日志,你会发现每一个更新都会带来新的功能。
我非常建议你去使用Beta版本,你会见证Sketch快速的升级。
Sketch团队非常善于聆听用户的诉求,因而软件不断地变得更好。你可以去tenderapp上向他们提出新功能的要求。</p>
<h2 id="section-1">相关链接</h2>
<ul>
<li>
<p><a href="http://www.bohemiancoding.com/download/sketch.zip">下载试用版</a></p>
</li>
<li>
<p><a href="http://www.bohemiancoding.com/sketch/buy">Mac App Store下载地址</a></p>
</li>
<li>
<p><a href="http://blog.mengto.com/topic/sketch/">Ment To 的博文</a></p>
</li>
<li>
<p><a href="http://sketchtips.tumblr.com/">Sketch 技巧分享博客</a></p>
</li>
<li>
<p><a href="http://www.sketchappsources.com/">Sketch 资源下载</a></p>
</li>
<li>
<p><a href="http://www.douban.com/group/sketchapp/">Sketch 豆瓣中文小组</a></p>
</li>
<li>
<p><a href="http://community.sketchcn.com/">Sketch 中文社区</a></p>
</li>
</ul>
<p>原文来自<a href="http://jianshu.io/p/7e8905b18620">http://jianshu.io/p/7e8905b18620</a></p>
Android AdapterView setEmptyView
2014-05-11T00:00:00+00:00
/android/2014/05/11/adapterview-setemptyview
<p>当我们使用ListView或GridView的时候,当列表为空的时候,我们往往需要一个Loading或者一段提示文字又或者一个特殊的View来提示用户操作,这个时候就用到了setEmptyView()方法。</p>
<p>setEmptyView()其实是AdapterView的方法,而我们开发中常用到的ListView, GridView, ExpandableListView等都是继承于AdapterView的,所以可以直接调用这个方法,下面来看看具体的如何使用:</p>
<p>以默认显示一个提示文字的TextView为例,一般有以下两种用法:</p>
<h2 id="empty-viewlistview">1. Empty View和ListView在同一个布局文件里</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">FrameLayout</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"match_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"match_parent"</span><span class="o">></span>
<span class="o"><</span><span class="no">ListView</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"match_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"match_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/list_view"</span> <span class="sr">/></span>
<span class="sr"> <TextView </span>
<span class="sr"> android:id="@+id/</span><span class="n">tv_empty</span><span class="s2">"</span>
<span class="s2"> android:layout_width="</span><span class="n">wrap_content</span><span class="s2">"</span>
<span class="s2"> android:layout_height="</span><span class="n">wrap_content</span><span class="s2">"</span>
<span class="s2"> android:layout_gravity="</span><span class="n">center</span><span class="s2">"</span>
<span class="s2"> android:text="</span><span class="no">Loading</span> <span class="n">data</span><span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="s2">" /></span>
<span class="s2"></FrameLayout></span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ListView</span> <span class="n">listView</span> <span class="o">=</span> <span class="p">(</span><span class="no">ListView</span><span class="p">)</span><span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">list_view</span><span class="p">);</span>
<span class="n">listView</span><span class="o">.</span><span class="n">setEmptyView</span><span class="p">(</span><span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">iv_empty</span><span class="p">));</span></code></pre></div>
<h2 id="empty-viewview">2. Empty View在单独的布局文件里,这种一般适用于比较复杂的View或者打算在多个地方复用</h2>
<p>setEmptyView()这个方法是有限制的,这个View必须在当前的View hierarchy的节点上,所以当我们写在外面单独的布局文件里时,需要把View添加到当前的View hierarchy的节点上。所以就需要下面的用法:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">View</span> <span class="n">emptyView</span> <span class="o">=</span> <span class="no">View</span><span class="o">.</span><span class="n">inflate</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">empty_view</span><span class="p">,</span> <span class="n">null</span><span class="p">);</span>
<span class="p">((</span><span class="no">ViewGroup</span><span class="p">)</span><span class="n">list</span><span class="o">.</span><span class="n">getParent</span><span class="p">())</span><span class="o">.</span><span class="n">addView</span><span class="p">(</span><span class="n">emptyView</span><span class="p">);</span>
<span class="no">ListView</span> <span class="n">listView</span> <span class="o">=</span> <span class="p">(</span><span class="no">ListView</span><span class="p">)</span><span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">list_view</span><span class="p">);</span>
<span class="n">listView</span><span class="o">.</span><span class="n">setEmptyView</span><span class="p">(</span><span class="n">emptyView</span><span class="p">);</span></code></pre></div>
Android WebView上传文件
2014-04-25T00:00:00+00:00
/android/2014/04/25/android-webview-file-upload
<p>在这次食物上传功能的开发中,其中WebView中上传图片需要调用系统的文件系统,默认WebView是不支持文件上传的,需要自己手动配置一些东西,具体代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">BrowserActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">WebView</span> <span class="n">mWebView</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">ValueCallback</span><span class="o"><</span><span class="no">Uri</span><span class="o">></span> <span class="n">mUploadMessage</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">final</span> <span class="n">static</span> <span class="n">int</span> <span class="no">FILECHOOSER_RESULTCODE</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">outState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">outState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">activity_browser</span><span class="p">);</span>
<span class="n">mWebView</span> <span class="o">=</span> <span class="p">(</span><span class="no">WebView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">webview</span><span class="p">);</span>
<span class="n">mWebView</span><span class="o">.</span><span class="n">getSettings</span><span class="p">()</span><span class="o">.</span><span class="n">setJavaScriptEnabled</span><span class="p">(</span><span class="kp">true</span><span class="p">);</span>
<span class="n">mWebView</span><span class="o">.</span><span class="n">setWebChromeClient</span><span class="p">(</span><span class="kp">new</span> <span class="no">MyWebClient</span><span class="p">());</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">protected</span> <span class="n">void</span> <span class="n">onActivityResult</span><span class="p">(</span><span class="n">int</span> <span class="n">requestCode</span><span class="p">,</span> <span class="n">int</span> <span class="n">resultCode</span><span class="p">,</span> <span class="no">Intent</span> <span class="n">intent</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">requestCode</span> <span class="o">==</span> <span class="no">FILECHOOSER_RESULTCODE</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">null</span> <span class="o">==</span> <span class="n">mUploadMessage</span><span class="p">)</span>
<span class="k">return</span><span class="p">;</span>
<span class="no">Uri</span> <span class="n">result</span> <span class="o">=</span> <span class="n">intent</span> <span class="o">==</span> <span class="n">null</span> <span class="o">||</span> <span class="n">resultCode</span> <span class="o">!=</span> <span class="no">RESULT_OK</span> <span class="p">?</span> <span class="n">null</span> <span class="p">:</span> <span class="n">intent</span><span class="o">.</span><span class="n">getData</span><span class="p">();</span>
<span class="n">mUploadMessage</span><span class="o">.</span><span class="n">onReceiveValue</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
<span class="n">mUploadMessage</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">MyWebClient</span> <span class="n">extends</span> <span class="no">WebChromeClient</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">For</span> <span class="no">Android</span> <span class="mi">3</span><span class="o">.</span><span class="mi">0</span><span class="o">-</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">openFileChooser</span><span class="p">(</span><span class="no">ValueCallback</span><span class="o"><</span><span class="no">Uri</span><span class="o">></span> <span class="n">uploadMsg</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mUploadMessage</span> <span class="o">=</span> <span class="n">uploadMsg</span><span class="p">;</span>
<span class="no">Intent</span> <span class="n">i</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">ACTION_GET_CONTENT</span><span class="p">);</span>
<span class="n">i</span><span class="o">.</span><span class="n">addCategory</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">CATEGORY_OPENABLE</span><span class="p">);</span>
<span class="n">i</span><span class="o">.</span><span class="n">setType</span><span class="p">(</span><span class="s2">"image/*"</span><span class="p">);</span>
<span class="n">startActivityForResult</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">createChooser</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="s2">"File Chooser"</span><span class="p">),</span> <span class="no">FILECHOOSER_RESULTCODE</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="no">For</span> <span class="no">Android</span> <span class="mi">3</span><span class="o">.</span><span class="mi">0</span><span class="o">+</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">openFileChooser</span><span class="p">(</span><span class="no">ValueCallback</span><span class="o"><</span><span class="no">Uri</span><span class="o">></span> <span class="n">uploadMsg</span><span class="p">,</span> <span class="nb">String</span> <span class="n">acceptType</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mUploadMessage</span> <span class="o">=</span> <span class="n">uploadMsg</span><span class="p">;</span>
<span class="no">Intent</span> <span class="n">i</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">ACTION_GET_CONTENT</span><span class="p">);</span>
<span class="n">i</span><span class="o">.</span><span class="n">addCategory</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">CATEGORY_OPENABLE</span><span class="p">);</span>
<span class="n">i</span><span class="o">.</span><span class="n">setType</span><span class="p">(</span><span class="s2">"*/*"</span><span class="p">);</span>
<span class="n">startActivityForResult</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">createChooser</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="s2">"File Browser"</span><span class="p">),</span> <span class="no">FILECHOOSER_RESULTCODE</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="no">For</span> <span class="no">Android</span> <span class="mi">4</span><span class="o">.</span><span class="mi">1</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">openFileChooser</span><span class="p">(</span><span class="no">ValueCallback</span><span class="o"><</span><span class="no">Uri</span><span class="o">></span> <span class="n">uploadMsg</span><span class="p">,</span> <span class="nb">String</span> <span class="n">acceptType</span><span class="p">,</span> <span class="nb">String</span> <span class="n">capture</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mUploadMessage</span> <span class="o">=</span> <span class="n">uploadMsg</span><span class="p">;</span>
<span class="no">Intent</span> <span class="n">i</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">ACTION_GET_CONTENT</span><span class="p">);</span>
<span class="n">i</span><span class="o">.</span><span class="n">addCategory</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">CATEGORY_OPENABLE</span><span class="p">);</span>
<span class="n">i</span><span class="o">.</span><span class="n">setType</span><span class="p">(</span><span class="s2">"image/*"</span><span class="p">);</span>
<span class="n">startActivityForResult</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">createChooser</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="s2">"File Chooser"</span><span class="p">),</span> <span class="no">FILECHOOSER_RESULTCODE</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>主要就是自定义了WebChromeClient,然而测试时发现在4.4以上的Android版本依然不可以,搜了下openFileChooser方法在4.4以后不是public的,所以以后不建议这种直接在WebView上传文件的做法。</p>
<p>stackoverflow参考链接:<a href="http://stackoverflow.com/questions/19882331/html-file-input-in-android-webview-android-4-4-kitkat">HTML file input in android webview (android 4.4, kitkat)</a></p>
Android 布局优化
2014-04-10T00:00:00+00:00
/android/2014/04/10/android-optimize-layout
<p>在开发过程中我们经常说性能优化,但性能优化是一个比较宽泛的概念。在Android开发中性能优化可能包括:Java代码优化, 算法优化, SQLite优化, 布局优化等。那么这篇博客就来总结并分享下Android开发中的布局优化。</p>
<h2 id="section">布局原则</h2>
<p>在Android UI布局过程中,通过遵守一些惯用、有效的布局原则,我们可以制作出高效且复用性高的UI,概括来说包括如下几点:</p>
<ul>
<li>
<p>尽量多使用RelativeLayout和LinearLayout, 不要使用绝对布局AbsoluteLayout,在布局层次一样的情况下, 建议使用LinearLayout代替RelativeLayout, 因为LinearLayout性能要稍高一点,但往往RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局。</p>
</li>
<li>
<p>将可复用的组件抽取出来并通过include标签使用;</p>
</li>
<li>
<p>使用ViewStub标签来加载一些不常用的布局;</p>
</li>
<li>
<p>使用merge标签减少布局的嵌套层次;</p>
</li>
</ul>
<h2 id="relativelayout-vs-linearlayout">RelativeLayout VS LinearLayout</h2>
<p>第一条原则说了布局层次一样的情况下LinearLayout比RelativeLayout要好, 但往往RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局。假如需要实现如下布局:</p>
<p><img src="/image/layout_demo.png" /></p>
<p>用LinearLayout来实现xml代码如下:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><LinearLayout</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">android:layout_width=</span><span class="s">"fill_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"?android:attr/listPreferredItemHeight"</span>
<span class="na">android:padding=</span><span class="s">"6dip"</span><span class="nt">></span>
<span class="nt"><ImageView</span>
<span class="na">android:id=</span><span class="s">"@+id/icon"</span>
<span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_height=</span><span class="s">"fill_parent"</span>
<span class="na">android:layout_marginRight=</span><span class="s">"6dip"</span>
<span class="na">android:src=</span><span class="s">"@drawable/icon"</span> <span class="nt">/></span>
<span class="nt"><LinearLayout</span>
<span class="na">android:orientation=</span><span class="s">"vertical"</span>
<span class="na">android:layout_width=</span><span class="s">"0dip"</span>
<span class="na">android:layout_weight=</span><span class="s">"1"</span>
<span class="na">android:layout_height=</span><span class="s">"fill_parent"</span><span class="nt">></span>
<span class="nt"><TextView</span>
<span class="na">android:layout_width=</span><span class="s">"fill_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"0dip"</span>
<span class="na">android:layout_weight=</span><span class="s">"1"</span>
<span class="na">android:gravity=</span><span class="s">"center_vertical"</span>
<span class="na">android:text=</span><span class="s">"My Application"</span> <span class="nt">/></span>
<span class="nt"><TextView</span>
<span class="na">android:layout_width=</span><span class="s">"fill_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"0dip"</span>
<span class="na">android:layout_weight=</span><span class="s">"1"</span>
<span class="na">android:singleLine=</span><span class="s">"true"</span>
<span class="na">android:ellipsize=</span><span class="s">"marquee"</span>
<span class="na">android:text=</span><span class="s">"Simple application that shows how to use RelativeLayout"</span> <span class="nt">/></span>
<span class="nt"></LinearLayout></span>
<span class="nt"></LinearLayout></span></code></pre></div>
<p>而用RelativeLayout实现代码如下:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><RelativeLayout</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">android:layout_width=</span><span class="s">"fill_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"?android:attr/listPreferredItemHeight"</span>
<span class="na">android:padding=</span><span class="s">"6dip"</span><span class="nt">></span>
<span class="nt"><ImageView</span>
<span class="na">android:id=</span><span class="s">"@+id/icon"</span>
<span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_height=</span><span class="s">"fill_parent"</span>
<span class="na">android:layout_alignParentTop=</span><span class="s">"true"</span>
<span class="na">android:layout_alignParentBottom=</span><span class="s">"true"</span>
<span class="na">android:layout_marginRight=</span><span class="s">"6dip"</span>
<span class="na">android:src=</span><span class="s">"@drawable/icon"</span> <span class="nt">/></span>
<span class="nt"><TextView</span>
<span class="na">android:id=</span><span class="s">"@+id/secondLine"</span>
<span class="na">android:layout_width=</span><span class="s">"fill_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"26dip"</span>
<span class="na">android:layout_toRightOf=</span><span class="s">"@id/icon"</span>
<span class="na">android:layout_alignParentBottom=</span><span class="s">"true"</span>
<span class="na">android:layout_alignParentRight=</span><span class="s">"true"</span>
<span class="na">android:singleLine=</span><span class="s">"true"</span>
<span class="na">android:ellipsize=</span><span class="s">"marquee"</span>
<span class="na">android:text=</span><span class="s">"Simple application that shows how to use RelativeLayout"</span> <span class="nt">/></span>
<span class="nt"><TextView</span>
<span class="na">android:layout_width=</span><span class="s">"fill_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_toRightOf=</span><span class="s">"@id/icon"</span>
<span class="na">android:layout_alignParentRight=</span><span class="s">"true"</span>
<span class="na">android:layout_alignParentTop=</span><span class="s">"true"</span>
<span class="na">android:layout_above=</span><span class="s">"@id/secondLine"</span>
<span class="na">android:layout_alignWithParentIfMissing=</span><span class="s">"true"</span>
<span class="na">android:gravity=</span><span class="s">"center_vertical"</span>
<span class="na">android:text=</span><span class="s">"My Application"</span> <span class="nt">/></span>
<span class="nt"></RelativeLayout></span></code></pre></div>
<p>可以看到用RelativeLayout实现,布局层次明显少了,所以大多数时候优先推荐使用RelativeLayout。</p>
<h2 id="section-1">查看布局层次</h2>
<p>如何查看布局层次呢?有两种办法:一是通过手机的开发者选项,4.0及以上Android版本可通过设置->开发者选项->显示布局边界打开页面布局显示,看看是否有不必要的节点和嵌套。第二种就是利用SDK自带的UI性能检测工具HierarchyViewer。
进入sdk目录下的tools文件夹下,找到HierarchyViewer并运行(此时保持你的模拟器或真机正在运行需要进行分析的App),双击我们正在显示的这个App所代表的进程。接下来便会进入hierarchyviewer的界面,我们可以在这里很清晰看到正在运行的UI的布局层次结构以及它们之间的关系。大概的显示如下图:</p>
<p><img src="/image/HierarchyViewer.png" /></p>
<p>通过布局图我们可以看到根节点DecorView下包含一个LinearLayout, 这个LinearLayout就是包含Activity布局和状态栏的整个屏幕显示的布局父节点,这个LinearLayout有两个子节点, 一个是FrameLayout, FrameLayout就是Activity布局中默认的父布局节点, 这个节点下面就包含了我们自己写的xml布局, 还有一个子节点就是ViewStub,关于这个节点我们在后面会详细介绍。</p>
<h2 id="include-">< include />的使用</h2>
<p>在实际开发中,我们经常会遇到一些共用的UI组件,比如带返回按钮的导航栏,如果为每一个xml文件都设置这部分布局,一是重复的工作量大,二是如果有变更,那么每一个xml文件都得修改。还好,Android为我们提供了include标签,顾名思义,通过它,我们可以将这些共用的组件抽取出来单独放到一个xml文件中,然后使用include标签导入共用布局,这样,前面提到的两个问题都解决了。下面以在一个布局main.xml中用include引入另一个布局header.xml为例。</p>
<p>header.xml文件</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="cp"><?xml version="1.0" encoding="utf-8"?></span>
<span class="nt"><RelativeLayout</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span> <span class="nt">></span>
<span class="nt"><Button</span>
<span class="na">android:id=</span><span class="s">"@+id/button"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"@dimen/dp_40"</span>
<span class="na">android:layout_above=</span><span class="s">"@+id/text"</span><span class="nt">/></span>
<span class="nt"><TextView</span>
<span class="na">android:id=</span><span class="s">"@+id/text"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"@dimen/dp_40"</span>
<span class="na">android:layout_alignParentBottom=</span><span class="s">"true"</span>
<span class="na">android:text=</span><span class="s">"@string/app_name"</span> <span class="nt">/></span>
<span class="nt"></RelativeLayout></span></code></pre></div>
<p>然后我们在需要引入footer的布局xml中通过include导入这个共用布局。</p>
<p>main.xml文件</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><FrameLayout</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span><span class="nt">></span>
<span class="nt"><TextView</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
<span class="na">android:text=</span><span class="s">"hello world"</span> <span class="nt">/></span>
<span class="nt"><RelativeLayout</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_gravity=</span><span class="s">"center"</span> <span class="nt">></span>
<span class="nt"><include</span> <span class="na">layout=</span><span class="s">"@layout/header"</span> <span class="nt">/></span>
<span class="nt"></RelativeLayout></span>
<span class="nt"></FrameLayout></span></code></pre></div>
<p>通过这种方式,我们既能提高UI的制作和复用效率,也能保证制作的UI布局更加规整和易维护。</p>
<h2 id="merge-">< merge />的使用</h2>
<p>merge标签的作用是合并UI布局,使用该标签能降低UI布局的嵌套层次。merge标签可用于两种典型情况:</p>
<ul>
<li>
<p>布局根结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity内容布局的parent view就是个FrameLayout,所以可以用merge消除只剩一个,这一点可以从上图中看到。</p>
</li>
<li>
<p>某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中。</p>
</li>
</ul>
<p>以第一种情况为例,main.xml布局就可以优化如下:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><merge</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span><span class="nt">></span>
<span class="nt"><FrameLayout</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span><span class="nt">></span>
<span class="nt"><TextView</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
<span class="na">android:text=</span><span class="s">"hello world"</span> <span class="nt">/></span>
<span class="nt"><RelativeLayout</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_gravity=</span><span class="s">"center"</span> <span class="nt">></span>
<span class="nt"><include</span> <span class="na">layout=</span><span class="s">"@layout/header"</span> <span class="nt">/></span>
<span class="nt"></RelativeLayout></span>
<span class="nt"></FrameLayout></span>
<span class="nt"></merge></span></code></pre></div>
<p>以第二种情况为例,header.xml布局可以优化如下:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="cp"><?xml version="1.0" encoding="utf-8"?></span>
<span class="nt"><merge</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span> <span class="nt">></span>
<span class="nt"><Button</span>
<span class="na">android:id=</span><span class="s">"@+id/button"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"@dimen/dp_40"</span>
<span class="na">android:layout_above=</span><span class="s">"@+id/text"</span><span class="nt">/></span>
<span class="nt"><TextView</span>
<span class="na">android:id=</span><span class="s">"@+id/text"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"@dimen/dp_40"</span>
<span class="na">android:layout_alignParentBottom=</span><span class="s">"true"</span>
<span class="na">android:text=</span><span class="s">"@string/app_name"</span> <span class="nt">/></span>
<span class="nt"></merge></span></code></pre></div>
<p>这样就不会有多余的FrameLayout和RelativeLayout节点了。</p>
<h2 id="viewstub">ViewStub标签</h2>
<p>viewstub标签同include标签一样可以用来引入一个外部布局,不同的是,viewstub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存。
viewstub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等。</p>
<p>我们新建一个xml文件用来显示一个网络错误时提示信息error.xml:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><RelativeLayout</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">xmlns:tools=</span><span class="s">"http://schemas.android.com/tools"</span>
<span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_height=</span><span class="s">"wrap_content"</span> <span class="nt">></span>
<span class="nt"><TextView</span>
<span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_centerInParent=</span><span class="s">"true"</span>
<span class="na">android:background=</span><span class="s">"@android:color/white"</span>
<span class="na">android:padding=</span><span class="s">"10dip"</span>
<span class="na">android:text=</span><span class="s">"Message"</span>
<span class="na">android:textColor=</span><span class="s">"@android:color/black"</span> <span class="nt">/></span>
<span class="nt"></RelativeLayout></span></code></pre></div>
<p>然后在main.xml里面加入ViewStub的标签引入上面的布局:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><merge</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">xmlns:tools=</span><span class="s">"http://schemas.android.com/tools"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:background=</span><span class="s">"@android:color/darker_gray"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span> <span class="nt">></span>
...
<span class="nt"><ViewStub</span>
<span class="na">android:id=</span><span class="s">"@+id/error_layout"</span>
<span class="na">android:layout_width=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_gravity=</span><span class="s">"center"</span>
<span class="na">android:layout=</span><span class="s">"@layout/error"</span> <span class="nt">/></span>
<span class="nt"></merge></span></code></pre></div>
<p>在java中通过(ViewStub)findViewById(id)找到ViewStub,通过stub.inflate()展开ViewStub,然后得到子View,如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">private</span> <span class="no">View</span> <span class="n">errorView</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">showError</span><span class="p">()</span> <span class="p">{</span>
<span class="sr">//</span> <span class="ow">not</span> <span class="n">repeated</span> <span class="n">infalte</span>
<span class="k">if</span> <span class="p">(</span><span class="n">errorView</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">errorView</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">VISIBLE</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="no">ViewStub</span> <span class="n">stub</span> <span class="o">=</span> <span class="p">(</span><span class="no">ViewStub</span><span class="p">)</span><span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">error_layout</span><span class="p">);</span>
<span class="n">errorView</span> <span class="o">=</span> <span class="n">stub</span><span class="o">.</span><span class="n">inflate</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">showContent</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">errorView</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">errorView</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">GONE</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>在上面showError()中展开了ViewStub,同时我们对errorView进行了保存,这样下次不用继续inflate。</p>
<h2 id="section-2">总结</h2>
<p>这篇Blog没有详细介绍HierarchyViewer工具的使用,相信如果对布局原则比较熟练之后,对工具的依赖大大减少,开发效率也会大大的提升。除这些布局原则之外,还需要大家对Android各个组件的属性很熟悉,比如如果要做这么一个布局, 一个图片和一个文本的布局,新手们往往会用一个Layout嵌套ImageView和TextView来做, 但是当我们知道TextView有drawableLeft, drawableRight等属性时,那么实现这样的一个布局是非常快速高效的。总之,且学且实践!</p>
Android FlipLayout
2014-04-04T00:00:00+00:00
/android/2014/04/04/android-fliplayout
<p>这周新功能有一个类似web版Google+翻转的特效,Android自带的动画效果全是基于平面的,像实现这种3D效果必须要自定义,于是自己写了个demo。效果如下:</p>
<p><img src="/image/FlipLayout.gif" /></p>
<p>主要思路其实也蛮简单的,主要是自定义一个Animation,然后在applyTransformation方法里通过矩阵变换让其按照y轴旋转,只是在旋转到中间画面的切换细节稍微处理下。</p>
<h2 id="section">使用</h2>
<p>用法非常简单,可以直接在xml中使用,类似下面:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="cp"><?xml version="1.0" encoding="utf-8"?></span>
<span class="nt"><com.storm.fliplayout.lib.FlipLayout</span> <span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">android:id=</span><span class="s">"@+id/flipLayout"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span> <span class="nt">></span>
<span class="nt"><TextView</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"200dp"</span>
<span class="na">android:layout_gravity=</span><span class="s">"center"</span>
<span class="na">android:background=</span><span class="s">"#FFCCCCCC"</span>
<span class="na">android:gravity=</span><span class="s">"center"</span>
<span class="na">android:text=</span><span class="s">"@string/front"</span>
<span class="na">android:textAppearance=</span><span class="s">"@android:style/TextAppearance.Large"</span> <span class="nt">/></span>
<span class="nt"><TextView</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"200dp"</span>
<span class="na">android:layout_centerInParent=</span><span class="s">"true"</span>
<span class="na">android:layout_gravity=</span><span class="s">"center"</span>
<span class="na">android:background=</span><span class="s">"#FF999999"</span>
<span class="na">android:gravity=</span><span class="s">"center"</span>
<span class="na">android:text=</span><span class="s">"@string/back"</span>
<span class="na">android:textAppearance=</span><span class="s">"@android:style/TextAppearance.Large"</span>
<span class="na">android:visibility=</span><span class="s">"gone"</span> <span class="nt">/></span>
<span class="nt"></com.storm.fliplayout.lib.FlipLayout></span></code></pre></div>
<p>当然使用中不仅限于TextView,你同样可以放很负责的布局进去,但是要注意保证FlipLayout只有两个child。</p>
<p>github地址:<a href="https://github.com/stormzhang/FlipLayout">FlipLayout</a></p>
Android SwipeRefreshLayout
2014-03-29T00:00:00+00:00
/android/2014/03/29/android-swiperefreshlayout
<p>今天在Google+上看到了SwipeRefreshLayout这个名词,遂搜索了下,发现竟然是刚刚google更新sdk新增加的一个widget,于是赶紧抢先体验学习下。</p>
<h2 id="swiperefreshlayout">SwipeRefreshLayout</h2>
<p>SwipeRefreshLayout字面意思就是下拉刷新的布局,继承自ViewGroup,在support v4兼容包下,但必须把你的support library的版本升级到19.1。 提到下拉刷新大家一定对ActionBarPullToRefresh比较熟悉,而如今google推出了更官方的下拉刷新组件,这无疑是对开发者来说比较好的消息。利用这个组件可以很方便的实现Google Now的刷新效果,见下图:</p>
<p><img src="/image/SwipeRefreshLayout.gif" /></p>
<h2 id="section">主要方法</h2>
<ul>
<li>
<p>setOnRefreshListener(OnRefreshListener): 为布局添加一个Listener</p>
</li>
<li>
<p>setRefreshing(boolean): 显示或隐藏刷新进度条</p>
</li>
<li>
<p>isRefreshing(): 检查是否处于刷新状态</p>
</li>
<li>
<p>setColorScheme(): 设置进度条的颜色主题,最多能设置四种</p>
</li>
</ul>
<h2 id="xml">xml布局文件</h2>
<p>布局文件很简单,只需要在最外层加上SwipeRefreshLayout,然后他的child是可滚动的view即可,如ScrollView或者ListView。如:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><android.support.v4.widget.SwipeRefreshLayout</span>
<span class="na">xmlns:android=</span><span class="s">"http://schemas.android.com/apk/res/android"</span>
<span class="na">android:id=</span><span class="s">"@+id/swipe_container"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span><span class="nt">></span>
<span class="nt"><ScrollView</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"match_parent"</span><span class="nt">></span>
<span class="nt"><TextView</span>
<span class="na">android:text=</span><span class="s">"@string/hello_world"</span>
<span class="na">android:layout_width=</span><span class="s">"match_parent"</span>
<span class="na">android:layout_height=</span><span class="s">"wrap_content"</span>
<span class="na">android:layout_marginTop=</span><span class="s">"16dp"</span>
<span class="na">android:gravity=</span><span class="s">"center"</span><span class="nt">/></span>
<span class="nt"></ScrollView></span>
<span class="nt"></android.support.v4.widget.SwipeRefreshLayout></span></code></pre></div>
<h2 id="activity">Activity代码</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">protected</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">activity_main</span><span class="p">);</span>
<span class="n">swipeLayout</span> <span class="o">=</span> <span class="p">(</span><span class="no">SwipeRefreshLayout</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">swipe_container</span><span class="p">);</span>
<span class="n">swipeLayout</span><span class="o">.</span><span class="n">setOnRefreshListener</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="n">swipeLayout</span><span class="o">.</span><span class="n">setColorScheme</span><span class="p">(</span><span class="n">android</span><span class="o">.</span><span class="n">R</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">holo_blue_bright</span><span class="p">,</span>
<span class="n">android</span><span class="o">.</span><span class="n">R</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">holo_green_light</span><span class="p">,</span>
<span class="n">android</span><span class="o">.</span><span class="n">R</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">holo_orange_light</span><span class="p">,</span>
<span class="n">android</span><span class="o">.</span><span class="n">R</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">holo_red_light</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onRefresh</span><span class="p">()</span> <span class="p">{</span>
<span class="kp">new</span> <span class="no">Handler</span><span class="p">()</span><span class="o">.</span><span class="n">postDelayed</span><span class="p">(</span><span class="kp">new</span> <span class="no">Runnable</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span> <span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="n">swipeLayout</span><span class="o">.</span><span class="n">setRefreshing</span><span class="p">(</span><span class="kp">false</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">},</span> <span class="mi">5000</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>上面的代码很简单,只需要给SwipeRefreshLayout添加一个listener,值得说明的是setColorScheme方法是设置刷新进度条的颜色,最多只能设置4种循环显示,默认第一个是随用户手势加载的颜色进度条。</p>
<h2 id="section-1">源码</h2>
<p>写了的小demo在github上,地址在:<a href="https://github.com/stormzhang/SwipeRefreshLayoutDemo">SwipeRefreshLayoutDemo</a></p>
<h2 id="section-2">总结</h2>
<p>google在不断完善自己的sdk,推出越来越多的组件,其目的是让开发更简单,设计上更统一,这可能是google未来的方向,不管怎样,这对开发者来说无疑是非常好的消息。</p>
Android WebView播放视频问题
2014-03-23T00:00:00+00:00
/android/2014/03/23/android-webview-play-video
<p>此次的方案用到WebView,而且其中会有视频嵌套,在默认的WebView中直接播放视频会有问题,而且不同的SDK版本情况还不一样,网上搜索了下解决方案,在此记录下.</p>
<pre><code>webView.getSettings.setPluginState(PluginState.ON);
webView.setWebChromeClient(new WebChromeClient());
</code></pre>
<p>然后在webView的Activity配置里面加上:</p>
<pre><code>android:hardwareAccelerated="true"
</code></pre>
<p>以上可以正常播放视频了,但是webview的页面都finish了居然还能听到视频播放的声音,于是又查了下发现webview的
onResume方法可以继续播放,onPause可以暂停播放,
但是这两个方法都是在Added in API level 11添加的,所以需要用反射来完成。</p>
<p>停止播放:在页面的onPause方法中使用:</p>
<pre><code>webView.getClass().getMethod("onPause").invoke(webView,(Object[])null);
</code></pre>
<p>继续播放:在页面的onResume方法中使用:</p>
<pre><code>webView.getClass().getMethod("onResume").invoke(webView,(Object[])null);
</code></pre>
<p>这样就可以控制视频的暂停和继续播放了。</p>
Best Practices for Using Alpha
2014-03-16T00:00:00+00:00
/android/2014/03/16/best-practices-for-using-alpha
<p>Alpha是图形界面开发中常用的特效,通常我们会使用以下代码来实现Alpha特效:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">view</span><span class="o">.</span><span class="n">setAlpha</span><span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="n">f</span><span class="p">);</span>
<span class="no">View</span><span class="o">.</span><span class="n">ALPHA</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="n">f</span><span class="p">);</span>
<span class="no">ObjectAnimator</span><span class="o">.</span><span class="n">ofFloat</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="s2">"alpha"</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="n">f</span><span class="p">)</span><span class="o">.</span><span class="n">start</span><span class="p">();</span>
<span class="n">view</span><span class="o">.</span><span class="n">animate</span><span class="p">()</span><span class="o">.</span><span class="n">alpha</span><span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="n">f</span><span class="p">)</span><span class="o">.</span><span class="n">start</span><span class="p">();</span>
<span class="n">view</span><span class="o">.</span><span class="n">setAnimation</span><span class="p">(</span><span class="kp">new</span> <span class="no">AlphaAnimation</span><span class="p">(</span><span class="mi">1</span><span class="o">.</span><span class="mi">0</span><span class="n">f</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="n">f</span><span class="p">));</span></code></pre></div>
<p>其效果都等同于:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">canvas</span><span class="o">.</span><span class="n">saveLayer</span><span class="p">(</span><span class="n">l</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="mi">127</span><span class="p">,</span> <span class="no">Canvas</span><span class="o">.</span><span class="n">CLIP_TO_LAYER_SAVE_FLAG</span><span class="p">);</span></code></pre></div>
<p>所以常见的alpha特效是通过将图像绘制到offscreen buffer中然后显示出来,这样的操作是非常消耗资源的,甚至可能导致性能问题,在开发过程中我们可以通过其他方式避免创建offsreen buffer。</p>
<h2 id="textview">TextView</h2>
<p>对于TextView我们通常需要文字透明效果,而不是View本身透明,所以,直接设置带有alpha值的TextColor是比较高效的方式。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="no">Not</span> <span class="n">this</span>
<span class="n">textView</span><span class="o">.</span><span class="n">setAlpha</span><span class="p">(</span><span class="n">alpha</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">以下方式可以避免创建</span> <span class="n">offscreen</span> <span class="n">buffer</span>
<span class="n">int</span> <span class="n">newTextColor</span> <span class="o">=</span> <span class="p">(</span><span class="n">int</span><span class="p">)</span> <span class="p">(</span><span class="mh">0xFF</span> <span class="o">*</span> <span class="n">alpha</span><span class="p">)</span> <span class="o"><<</span> <span class="mi">24</span> <span class="o">|</span> <span class="n">baseTextColor</span> <span class="o">&</span> <span class="mh">0xFFFFFF</span><span class="p">;</span>
<span class="n">textView</span><span class="o">.</span><span class="n">setTextColor</span><span class="p">(</span><span class="n">newTextColor</span><span class="p">);</span></code></pre></div>
<h2 id="imageview">ImageView</h2>
<p>同样的对于只具有src image的ImageView,直接调用setImageAlpha()方法更为合理。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="no">Not</span> <span class="n">this</span><span class="p">,</span> <span class="n">setAlpha</span><span class="err">方法由</span><span class="no">View</span><span class="err">继承而来,性能不佳</span>
<span class="n">imageView</span><span class="o">.</span><span class="n">setAlpha</span><span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">5</span><span class="n">f</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">使用以下方式时,</span><span class="no">ImageView</span><span class="err">会在绘制图片时单独为图片指定</span><span class="no">Alpha</span>
<span class="sr">//</span> <span class="err">可以避免创建</span> <span class="n">offScreenBuffer</span>
<span class="n">imageView</span><span class="o">.</span><span class="n">setImageAlpha</span><span class="p">((</span><span class="n">int</span><span class="p">)</span> <span class="n">alpha</span> <span class="o">*</span> <span class="mi">255</span><span class="p">);</span></code></pre></div>
<h2 id="customview">CustomView</h2>
<p>类似的,自定义控件时,应该直接去设置paint的alpha。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="no">Not</span> <span class="n">this</span>
<span class="n">customView</span><span class="o">.</span><span class="n">setAlpha</span><span class="p">(</span><span class="n">alpha</span><span class="p">);</span>
<span class="sr">//</span> <span class="no">But</span> <span class="n">this</span>
<span class="n">paint</span><span class="o">.</span><span class="n">setAlpha</span><span class="p">((</span><span class="n">int</span><span class="p">)</span> <span class="n">alpha</span> <span class="o">*</span> <span class="mi">255</span><span class="p">);</span>
<span class="n">canvas</span><span class="o">.</span><span class="n">draw</span><span class="o">*</span><span class="p">(</span><span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="n">,</span> <span class="n">paint</span><span class="p">);</span></code></pre></div>
<p>同时Android提供了hasOverlappingRendering()接口,通过重写该接口可以告知系统当前View是否存在内容重叠的情况,帮助系统优化绘制流程,原理是这样的:对于有重叠内容的View,系统简单粗暴的使用 offscreen buffer来协助处理。当告知系统该View无重叠内容时,系统会分别使用合适的alpha值绘制每一层。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">/**</span>
<span class="sr"> * Returns whether this View has content which overlaps. This function, intended to be</span>
<span class="sr"> * overridden by specific View types, is an optimization when alpha is set on a view. If</span>
<span class="sr"> * rendering overlaps in a view with alpha < 1, that view is drawn to an offscreen buffer</span>
<span class="sr"> * and then composited it into place, which can be expensive. If the view has no overlapping</span>
<span class="sr"> * rendering, the view can draw each primitive with the appropriate alpha value directly.</span>
<span class="sr"> * An example of overlapping rendering is a TextView with a background image, such as a</span>
<span class="sr"> * Button. An example of non-overlapping rendering is a TextView with no background, or</span>
<span class="sr"> * an ImageView with only the foreground image. The default implementation returns true;</span>
<span class="sr"> * subclasses should override if they have cases which can be optimized.</span>
<span class="sr"> *</span>
<span class="sr"> * @return true if the content in this view might overlap, false otherwise.</span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">hasOverlappingRendering</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>最后引用Chet Haase的一句话作为总结</p>
<blockquote>
<p>“You know what your view is doing, so do the right thing for your situation.”</p>
</blockquote>
<p>Via <a href="http://imid.me/blog/2014/01/17/best-practices-for-using-alpha/">Android Tips: Best Practices for Using Alpha</a></p>
我理想的工作环境
2014-03-06T00:00:00+00:00
/other/2014/03/06/expected-develop-environment
<p>做开发也有些时日了,貌似每个工程师心中都有个理想的工作环境,那么就来说说我心中理想的工作环境吧。首先声明个人是做Android开发。</p>
<h2 id="section">硬件环境</h2>
<ul>
<li>RMBP + Dell 23寸显示器</li>
</ul>
<p>双屏貌似是程序员必备,个人认为Dell的23寸刚好不错。至于RMBP则是工程师们的最爱,用过Mac感觉不会再用别的电脑了。类Unix的OS X系统完全兼容大多数开发软件,强大的硬件配置让你不用担心性能问题,反正自从用了Mac,很少碰到Windows上的卡顿问题了。</p>
<ul>
<li>Nexus 5</li>
</ul>
<p>做Android开发怎能没有一个像样的手机,要论目前最好的Android手机是什么,那毫无疑问应该是Google的5儿子。从硬件配置到使用体验都没得说,用了N5我想说再也不想用其他手机了。要实在说缺点的话就是耗电量大了点,但这应该是智能手机的通病了把。</p>
<h2 id="section-1">软件环境</h2>
<ul>
<li>Android Studio + Gradle</li>
</ul>
<p>Android Studio绝对是Android开发的未来,各种功能都很棒,可能唯一的不足是现在还不太成熟,而且从Eclipse迁移过来各种不习惯,但你要坚信这是未来,所以早点学习上手总不是坏处。至于用Gradle来自动编译、管理依赖非常简单方便,这篇博客则介绍了如何使用Gradle。<a href="http://stormzhang.github.io/android/2014/02/28/android-gradle/">Android Gradle</a></p>
<ul>
<li>Git</li>
</ul>
<p>目前用Git来做版本控制应该都没得说吧,但在分支的管理上更习惯用GitFlow,这里有一篇博客介绍了GitFLow—<a href="http://stormzhang.github.io/git/2014/01/29/git-flow/">使用Git Flow管理开发流程</a>。</p>
<ul>
<li>MarkDown</li>
</ul>
<p>个人推荐程序员写文档必须用MarkDown格式,语法简单方便,兼容Html,如果用Word不是觉得太低级了么!</p>
<ul>
<li>Sublime Text</li>
</ul>
<p>除了IDE之外这个算是最常用的编辑器了,很棒!当然你可能是vim粉,那就略过此条吧!</p>
<ul>
<li>iTerm</li>
</ul>
<p>个人觉得应该是Mac下最好用的命令行工具了吧!</p>
<ul>
<li>OmniGraffle</li>
</ul>
<p>Mac上比较好用的画图工具,可以画出非常棒的流程图,类图。</p>
<ul>
<li>Remote</li>
</ul>
<p>还有什么比支持远程工作更兴奋的事么?</p>
<p>当然可能还有一些很棒且常用的软件如Chrome, Source Tree, Dash, PS, Axure等等,自己理想的开发环境就差不多这样了,最后如果你是土豪,那怎能不给自己配备一个高大上的人体工学椅呢?</p>
Android Gradle
2014-02-28T00:00:00+00:00
/android/2014/02/28/android-gradle
<p>Google I/O 2013发布了新的开发工具Android Studio和新的构建系统<a href="http://tools.android.com/tech-docs/new-build-system">Gradle</a>, Android Studio自不必说,这是Android IDE的未来。这篇文章就来学习下Gradle。</p>
<h2 id="gradle">什么是Gradle?</h2>
<p>Gradle 是以 Groovy 语言为基础,面向Java应用为主,基于DSL语法的自动化构建工具。说到Java的自动化构建工具,大家一定对Ant和Maven都不会陌生,对,Gradle就是这样一种类似的工具,不过它比Ant和Maven强大的多,而且使用起来更加方便简单并且兼容Maven。</p>
<p>下面列举了一些使用Gradle构建Android项目的好处:</p>
<ul>
<li>
<p>在IDE环境和命令行下使用同一个构建系统</p>
</li>
<li>
<p>改进的依赖关系管理</p>
</li>
<li>
<p>更容易地集成到自动化构建系统</p>
</li>
</ul>
<h2 id="gradle-1">安装Gradle</h2>
<p>如果你是用Android Studio,第一次运行的时候需要自动安装Gradle,这个过程很漫长,而且有可能需要翻墙才下载的了,所以很多用户以致于认为死机了,所以最好的办法是手动来安装吧。</p>
<p>由于自己是mac电脑,所以以下仅以OSX系统为例,Windows系统的请自行网上搜索安装过程。</p>
<p>首先去<a href="http://www.gradle.org/downloads">Gradle</a>官网下载最新版本,目前最新版本是1.11,如下图直接下载并解压gradle-1.11-all.zip放到一个目录下(当然你可以选择直接下载bin),之后建立软连接:</p>
<p><img src="/image/gradle_download.png" /></p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">cd</span> <span class="sr">/usr/</span><span class="n">bin</span>
<span class="n">ln</span> <span class="o">-</span><span class="n">s</span> <span class="sr">/Users/s</span><span class="n">torm</span><span class="o">/</span><span class="n">gradle</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">gradle</span> <span class="n">gradle</span>
<span class="c1">#-> /Users/storm/gradle/bin/gradle是我放gradle的目录</span></code></pre></div>
<p>之后在命令行下输入gradle -v,如果显示了版本的信息则安装成功了。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">gradle</span> <span class="o">-</span><span class="n">v</span>
<span class="o">------------------------------------------------------------</span>
<span class="no">Gradle</span> <span class="mi">1</span><span class="o">.</span><span class="mi">11</span>
<span class="o">------------------------------------------------------------</span>
<span class="no">Build</span> <span class="ss">time</span><span class="p">:</span> <span class="mi">2014</span><span class="o">-</span><span class="mo">02</span><span class="o">-</span><span class="mi">11</span> <span class="mi">11</span><span class="p">:</span><span class="mi">34</span><span class="p">:</span><span class="mi">39</span> <span class="no">UTC</span>
<span class="no">Build</span> <span class="ss">number</span><span class="p">:</span> <span class="n">none</span>
<span class="ss">Revision</span><span class="p">:</span> <span class="n">a831fa866d46cbee94e61a09af15f9dd95987421</span>
<span class="ss">Groovy</span><span class="p">:</span> <span class="mi">1</span><span class="o">.</span><span class="mi">8</span><span class="o">.</span><span class="mi">6</span>
<span class="ss">Ant</span><span class="p">:</span> <span class="no">Apache</span> <span class="no">Ant</span><span class="p">(</span><span class="no">TM</span><span class="p">)</span> <span class="n">version</span> <span class="mi">1</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">2</span> <span class="n">compiled</span> <span class="n">on</span> <span class="no">July</span> <span class="mi">8</span> <span class="mi">2013</span>
<span class="ss">Ivy</span><span class="p">:</span> <span class="mi">2</span><span class="o">.</span><span class="mi">2</span><span class="o">.</span><span class="mi">0</span>
<span class="ss">JVM</span><span class="p">:</span> <span class="mi">1</span><span class="o">.</span><span class="mi">6</span><span class="o">.</span><span class="mo">0_65</span> <span class="p">(</span><span class="no">Apple</span> <span class="no">Inc</span><span class="o">.</span> <span class="mi">20</span><span class="o">.</span><span class="mi">65</span><span class="o">-</span><span class="n">b04</span><span class="o">-</span><span class="mi">462</span><span class="p">)</span>
<span class="ss">OS</span><span class="p">:</span> <span class="no">Mac</span> <span class="no">OS</span> <span class="n">X</span> <span class="mi">10</span><span class="o">.</span><span class="mi">9</span><span class="o">.</span><span class="mi">1</span> <span class="n">x86_64</span></code></pre></div>
<h2 id="gradle-">Gradle 基本概念</h2>
<p>如果你用Android Studio新建一个项目的时候,默认生成一大堆关于gradle的东西,其中最重要的是一个build.gradle的文件,内容如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">buildscript</span> <span class="p">{</span>
<span class="n">repositories</span> <span class="p">{</span>
<span class="n">mavenCentral</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">dependencies</span> <span class="p">{</span>
<span class="n">classpath</span> <span class="s1">'com.android.tools.build:gradle:0.8.+'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">apply</span> <span class="ss">plugin</span><span class="p">:</span> <span class="s1">'android'</span>
<span class="n">android</span> <span class="p">{</span>
<span class="n">compileSdkVersion</span> <span class="mi">19</span>
<span class="n">buildToolsVersion</span> <span class="s2">"19.0.0"</span>
<span class="n">defaultConfig</span> <span class="p">{</span>
<span class="n">minSdkVersion</span> <span class="mi">14</span>
<span class="n">targetSdkVersion</span> <span class="mi">19</span>
<span class="n">versionCode</span> <span class="mi">1</span>
<span class="n">versionName</span> <span class="s2">"1.0"</span>
<span class="p">}</span>
<span class="n">buildTypes</span> <span class="p">{</span>
<span class="n">release</span> <span class="p">{</span>
<span class="n">runProguard</span> <span class="kp">false</span>
<span class="n">proguardFiles</span> <span class="n">getDefaultProguardFile</span><span class="p">(</span><span class="s1">'proguard-android.txt'</span><span class="p">),</span> <span class="s1">'proguard-rules.txt'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">dependencies</span> <span class="p">{</span>
<span class="n">compile</span> <span class="s1">'com.android.support:support-v4:19.0.+'</span>
<span class="p">}</span></code></pre></div>
<p>buildscript节点的内容完全不用动,大概意思就是支持maven,声明Gradle的版本,刚开始很纳闷0.8是什么意思,Gradle的版本不是1.11么,我搜了好久都不知道这个数字和版本有什么关系,后来在<a href="http://tools.android.com/tech-docs/new-build-system">http://tools.android.com/tech-docs/new-build-system</a>上有一项release note,才大概知道是如何对应起来的。</p>
<p><img src="/image/gradle_release_note.png" /></p>
<p>apply plugin节点声明构建的项目类型,这里当然是android了</p>
<p>android节点设置编译android项目的参数,接下来,我们的构建android项目的所有配置都在这里完成。相信上述都能看懂,就简单的字面意思。</p>
<h2 id="gradle-android">构建一个Gradle Android项目</h2>
<p>上述算是一个最基本的Android Gradle配置文件了,但是如果项目中引用了一些第三方的jar包,则需要添加些东西,比如项目依赖了一个sunpport_v4的jar包,则完整的build.gradle文件如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">buildscript</span> <span class="p">{</span>
<span class="n">repositories</span> <span class="p">{</span>
<span class="n">mavenCentral</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">dependencies</span> <span class="p">{</span>
<span class="n">classpath</span> <span class="s1">'com.android.tools.build:gradle:0.8.+'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">apply</span> <span class="ss">plugin</span><span class="p">:</span> <span class="s1">'android'</span>
<span class="n">android</span> <span class="p">{</span>
<span class="n">compileSdkVersion</span> <span class="mi">19</span>
<span class="n">buildToolsVersion</span> <span class="s2">"19.0.0"</span>
<span class="n">defaultConfig</span> <span class="p">{</span>
<span class="n">minSdkVersion</span> <span class="mi">14</span>
<span class="n">targetSdkVersion</span> <span class="mi">19</span>
<span class="n">versionCode</span> <span class="mi">1</span>
<span class="n">versionName</span> <span class="s2">"1.0"</span>
<span class="p">}</span>
<span class="n">buildTypes</span> <span class="p">{</span>
<span class="n">release</span> <span class="p">{</span>
<span class="n">runProguard</span> <span class="kp">false</span>
<span class="n">proguardFiles</span> <span class="n">getDefaultProguardFile</span><span class="p">(</span><span class="s1">'proguard-android.txt'</span><span class="p">),</span> <span class="s1">'proguard-rules.txt'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">dependencies</span> <span class="p">{</span>
<span class="sr">//</span><span class="err">单文件依赖</span>
<span class="n">compile</span> <span class="n">files</span><span class="p">(</span><span class="s1">'libs/android-support-v4.jar")</span>
<span class="s1"> //某个文件夹下面全部依赖</span>
<span class="s1"> compile fileTree(dir: '</span><span class="n">libs</span><span class="s1">', include: '</span><span class="o">*.</span><span class="n">jar</span><span class="err">'</span><span class="p">)</span>
<span class="p">}</span></code></pre></div>
<p>接着在命令行cd 到项目目录下</p>
<pre><code>gradle clean
</code></pre>
<p>如果是第一次使用gradle构建,则会下载相关依赖包并且对环境进行初始化,如果出错了,一般可能是下载超时,试多几次即可,最后你会看到如下提示:BUILD SUCCESSFUL
完成以上的步骤,就可以正式使用gralde 构建你的android项目了。</p>
<p>接着执行</p>
<pre><code>gradle build
</code></pre>
<p>就完成了android 项目的构建了。如果,你是照着以上步骤走的话,你将会在项目目录里面看到一个build 的目录,里面就是用gradle 构建android项目的全部东西了。最终打包的apk 就在build/apk 目录下了。然后,你会发现,两个apk 一个是 [项目名]-debug-unaligned [项目名]-release-unsigned,看名字就猜到一个是调试模式没有进行优化的apk(可直接安装),一个是release模式但没有签名的apk(不可直接安装)。</p>
<h2 id="section">打签名包</h2>
<p>默认输出 release apk 是没有签名的,那么我们需要签名的很简单,只需要在android{}里面补充加上如下代码即可。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">signingConfigs</span> <span class="p">{</span>
<span class="n">myConfig</span> <span class="p">{</span>
<span class="n">storeFile</span> <span class="n">file</span><span class="p">(</span><span class="s2">"storm.keystore"</span><span class="p">)</span>
<span class="n">storePassword</span> <span class="s2">"storm"</span>
<span class="n">keyAlias</span> <span class="s2">"storm"</span>
<span class="n">keyPassword</span> <span class="s2">"storm"</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">buildTypes</span><span class="p">{</span>
<span class="n">release</span> <span class="p">{</span>
<span class="n">signingConfig</span> <span class="n">signingConfigs</span><span class="o">.</span><span class="n">myConfig</span>
<span class="n">runProguard</span> <span class="kp">true</span>
<span class="n">proguardFiles</span> <span class="n">getDefaultProguardFile</span><span class="p">(</span><span class="s1">'proguard-android.txt'</span><span class="p">),</span> <span class="s1">'proguard-rules.txt'</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>然后,运行</p>
<pre><code>gradle clean
gradle build
</code></pre>
<p>接着在build/apk目录下就生成了我们需要的签名的apk。</p>
<h2 id="section-1">更多</h2>
<p>buildscript{}</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Configures</span> <span class="n">the</span> <span class="n">build</span> <span class="n">script</span> <span class="n">classpath</span> <span class="k">for</span> <span class="n">this</span> <span class="n">project</span><span class="o">.</span> <span class="err">设置脚本的运行环境</span></code></pre></div>
<p>repositories{}</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Returns</span> <span class="n">a</span> <span class="n">handler</span> <span class="n">to</span> <span class="n">create</span> <span class="n">repositories</span> <span class="n">which</span> <span class="n">are</span> <span class="n">used</span> <span class="k">for</span> <span class="n">retrieving</span> <span class="n">dependencies</span> <span class="ow">and</span> <span class="n">uploading</span> <span class="n">artifacts</span> <span class="n">produced</span> <span class="n">by</span> <span class="n">the</span> <span class="n">project</span><span class="o">.</span>
<span class="err">大意就是支持</span><span class="n">java</span> <span class="err">依赖库管理(</span><span class="n">maven</span><span class="o">/</span><span class="n">ivy</span><span class="err">)</span><span class="p">,</span><span class="err">用于项目的依赖</span></code></pre></div>
<p>dependencies{}</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">The</span> <span class="n">dependency</span> <span class="n">handler</span> <span class="n">of</span> <span class="n">this</span> <span class="n">project</span><span class="o">.</span> <span class="no">The</span> <span class="n">returned</span> <span class="n">dependency</span> <span class="n">handler</span> <span class="n">instance</span> <span class="n">can</span> <span class="n">be</span> <span class="n">used</span> <span class="k">for</span> <span class="n">adding</span> <span class="kp">new</span> <span class="n">dependencies</span><span class="o">.</span> <span class="no">For</span> <span class="n">accessing</span> <span class="n">already</span> <span class="n">declared</span> <span class="n">dependencies</span><span class="p">,</span> <span class="n">the</span> <span class="n">configurations</span> <span class="n">can</span> <span class="n">be</span> <span class="n">used</span><span class="o">.</span>
<span class="err">依赖包的定义。支持</span><span class="n">maven</span><span class="o">/</span><span class="n">ivy</span><span class="err">,远程,本地库,也支持单文件,如果前面定义了</span><span class="n">repositories</span><span class="p">{}</span><span class="n">maven</span> <span class="err">库,使用</span><span class="n">maven</span><span class="err">的依赖的时候只需要按照用类似于</span><span class="n">com</span><span class="o">.</span><span class="n">android</span><span class="o">.</span><span class="n">tools</span><span class="o">.</span><span class="n">build</span><span class="ss">:gradle</span><span class="p">:</span><span class="mi">0</span><span class="o">.</span><span class="mi">4</span><span class="err">,</span><span class="n">gradle</span> <span class="err">就会自动的往远程库下载相应的依赖。</span></code></pre></div>
<p>apply plugin:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="err">声明构建的项目类型,这里当然是</span><span class="n">android</span><span class="err">了。。。</span></code></pre></div>
<p>android{}</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="err">设置编译</span><span class="n">android</span><span class="err">项目的参数,接下来,我们的构建</span><span class="n">android</span><span class="err">项目的所有配置都在这里完成。</span></code></pre></div>
Android InstanceState
2014-02-21T00:00:00+00:00
/android/2014/02/21/android-instancestate
<p>Android开发中我们常用Activity,对Activity的生命周期也是了如指掌,然而我们往往会忽略两个方法,onSaveInstanceState()和onRestoreInstanceState(), 在开发过程中很少用到,但在有时候掌握其用法会帮我们起到比较好的效果。</p>
<h2 id="section">基本作用</h2>
<p>Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。</p>
<p>在activity被杀掉之前调用保存每个实例的状态,以保证该状态可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle) (传入的Bundle参数是由onSaveInstanceState封装好的)中恢复。这个方法在一个activity被杀死前调用,当该activity在将来某个时刻回来时可以恢复其先前状态。</p>
<p>例如,如果activity B启用后位于activity A的前端,在某个时刻activity A因为系统回收资源的问题要被杀掉,A通过onSaveInstanceState将有机会保存其用户界面状态,使得将来用户返回到activity A时能通过onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢复界面的状态。</p>
<h2 id="onsaveinstancestate-">onSaveInstanceState() 什么时候调用</h2>
<p>从这句话可以知道,当某个activity变得”容易”被系统销毁时,该activity的onSaveInstanceState()就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。</p>
<p>注意上面的双引号,何为”容易”?意思就是说该activity还没有被销毁,而仅仅是一种可能性。这种可能性有哪些?通过重写一个activity的所有生命周期的onXXX方法,包括onSaveInstanceState()和onRestoreInstanceState() 方法,我们可以清楚地知道当某个activity(假定为activity A)显示在当前task的最上层时,其onSaveInstanceState()方法会在什么时候被执行,有这么几种情况:</p>
<ul>
<li>当用户按下HOME键时</li>
</ul>
<p>这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,因此系统会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则</p>
<ul>
<li>
<p>长按HOME键,选择运行其他的程序时</p>
</li>
<li>
<p>按下电源按键(关闭屏幕显示)时</p>
</li>
<li>
<p>从activity A中启动一个新的activity时</p>
</li>
<li>
<p>屏幕方向切换时,例如从竖屏切换到横屏时</p>
</li>
</ul>
<p>在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState()一定会被执行,且也一定会执行onRestoreInstanceState()。</p>
<p>总而言之,onSaveInstanceState()的调用遵循一个重要原则,即当系统存在“未经你许可”时销毁了我们的activity的可能时,则onSaveInstanceState()会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。如果调用,调用将发生在onPause()或onStop()方法之前。(虽然测试时发现多数在onPause()前)</p>
<h2 id="onrestoreinstancestate">onRestoreInstanceState()什么时候调用</h2>
<p>onRestoreInstanceState()被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行 此也说明上二者,大多数情况下不成对被使用。</p>
<p>onRestoreInstanceState()在onStart() 和 onPostCreate(Bundle)之间调用。</p>
<h2 id="onsaveinstancestate">onSaveInstanceState()方法的默认实现</h2>
<p>如果我们没有覆写onSaveInstanceState()方法, 此方法的默认实现会自动保存activity中的某些状态数据, 比如activity中各种UI控件的状态.。android应用框架中定义的几乎所有UI控件都恰当的实现了onSaveInstanceState()方法,因此当activity被摧毁和重建时, 这些UI控件会自动保存和恢复状态数据. 比如EditText控件会自动保存和恢复输入的数据,而CheckBox控件会自动保存和恢复选中状态.开发者只需要为这些控件指定一个唯一的ID(通过设置android:id属性即可), 剩余的事情就可以自动完成了.如果没有为控件指定ID, 则这个控件就不会进行自动的数据保存和恢复操作。</p>
<p>由上所述, 如果我们需要覆写onSaveInstanceState()方法, 一般会在第一行代码中调用该方法的默认实现:super.onSaveInstanceState(outState)。</p>
<h2 id="onsaveinstancestate-1">是否需要重写onSaveInstanceState()方法</h2>
<p>既然该方法的默认实现可以自动的保存UI控件的状态数据, 那什么时候需要覆写该方法呢? </p>
<p>如果需要保存额外的数据时, 就需要覆写onSaveInstanceState()方法。大家需要注意的是:onSaveInstanceState()方法只适合保存瞬态数据, 比如UI控件的状态, 成员变量的值等,而不应该用来保存持久化数据,持久化数据应该当用户离开当前的 activity时,在 onPause() 中保存(比如将数据保存到数据库或文件中)。说到这里,还要说一点的就是在onPause()中不适合用来保存比较费时的数据,所以这点要理解。</p>
<p>由于onSaveInstanceState()方法方法不一定会被调用, 因此不适合在该方法中保存持久化数据, 例如向数据库中插入记录等. 保存持久化数据的操作应该放在onPause()中。若是永久性值,则在onPause()中保存;若大量,则另开线程吧,别阻塞UI线程。</p>
<h2 id="section-1">示例</h2>
<p>我一般不习惯调用onRestoreInstanceState方法,而比较习惯在onCreate方法直接处理。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MainActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="p">{</span>
<span class="kp">private</span> <span class="nb">String</span> <span class="n">message</span> <span class="o">=</span> <span class="s2">""</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">EditText</span> <span class="n">text</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">Button</span> <span class="n">button</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="sr">/** Called when the activity is first created. */</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">main</span><span class="p">);</span>
<span class="n">text</span> <span class="o">=</span> <span class="p">(</span><span class="no">EditText</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">editText1</span><span class="p">);</span>
<span class="n">button</span> <span class="o">=</span> <span class="p">(</span><span class="no">Button</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">btnSave</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">savedInstanceState</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span>
<span class="n">message</span> <span class="o">=</span> <span class="n">savedInstanceState</span><span class="o">.</span><span class="n">getString</span><span class="p">(</span><span class="s2">"message"</span><span class="p">);</span>
<span class="n">button</span><span class="o">.</span><span class="n">setOnClickListener</span><span class="p">(</span><span class="kp">new</span> <span class="no">OnClickListener</span><span class="p">(){</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onClick</span><span class="p">(</span><span class="no">View</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">getApplicationContext</span><span class="p">(),</span> <span class="s2">"保存"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_SHORT</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onResume</span><span class="p">(){</span>
<span class="k">super</span><span class="o">.</span><span class="n">onResume</span><span class="p">();</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">getApplicationContext</span><span class="p">(),</span> <span class="n">message</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_LONG</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onSaveInstanceState</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">){</span>
<span class="k">super</span><span class="o">.</span><span class="n">onSaveInstanceState</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">savedInstanceState</span><span class="o">.</span><span class="n">putString</span><span class="p">(</span><span class="s2">"message"</span><span class="p">,</span> <span class="n">text</span><span class="o">.</span><span class="n">getText</span><span class="p">()</span><span class="o">.</span><span class="n">toString</span><span class="p">());</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>以上示例直接在onCreate方法中判断savedInstanceState是否为空,和直接在onRestoreInstanceState方法中处理作用一样。</p>
git submodule的使用
2014-02-13T00:00:00+00:00
/git/2014/02/13/git-submodule
<p>开发过程中经常用到的Git操作在这篇博客<a href="http://stormzhang.github.io/git/2014/01/27/git-common-command/">Git常用命令备忘</a>有介绍,但是没有涉及到submodule的命令,这也是比较常用的命令,这篇博客就介绍下git submodule的用法.</p>
<p>开发过程中,经常会有一些通用的部分希望抽取出来做成一个公共库来提供给别的工程来使用,而公共代码库的版本管理是个麻烦的事情。而且一旦更新了就要同步到多个引用的系统中,这个时候使用git submodule,然后执行: git submodule update就全部搞定了。</p>
<p>下面就以Android开发为例,讲述下submodule的具体用法。</p>
<p>假设一个Android Demo的目录是这样的:app, extras。其中app是程序的主要目录,extras目录是引用的一些library, 比如程序中引用了volley的library.</p>
<h2 id="section">添加</h2>
<p>为当前工程添加submodule,命令如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">submodule</span> <span class="n">add</span> <span class="err">仓库地址</span> <span class="err">路径</span>
<span class="err">即</span>
<span class="n">git</span> <span class="n">submodule</span> <span class="n">add</span> <span class="ss">https</span><span class="p">:</span><span class="sr">//</span><span class="n">android</span><span class="o">.</span><span class="n">googlesource</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">platform</span><span class="o">/</span><span class="n">frameworks</span><span class="o">/</span><span class="n">volley</span> <span class="n">extras</span></code></pre></div>
<p>命令执行完成,会在当前工程根路径下生成一个名为“.gitmodules”的文件,其中记录了子模块的信息。添加完成以后,再将子模块所在的文件夹添加到工程中即可。</p>
<h2 id="section-1">更新</h2>
<p>如果过了一段时间volley库有更新,这时候我们的app也需要更新,命令如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">submodule</span> <span class="n">update</span></code></pre></div>
<h2 id="section-2">删除</h2>
<p>ubmodule的删除稍微麻烦点:首先,要在“.gitmodules”文件中删除相应配置信息。然后,执行“git rm –cached ”命令将子模块所在的文件从git中删除。</p>
<h2 id="submodule">下载的工程带有submodule</h2>
<p>当使用git clone下来的工程中带有submodule时,初始的时候,submodule的内容并不会自动下载下来的,此时,只需执行如下命令:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">submodule</span> <span class="n">update</span> <span class="o">--</span><span class="n">init</span> <span class="o">--</span><span class="n">recursive</span></code></pre></div>
<p>即可将子模块内容下载下来后工程才不会缺少相应的文件。</p>
Android Volley
2014-02-11T00:00:00+00:00
/android/2014/02/11/android-volley
<h2 id="section">背景</h2>
<p><a href="https://android.googlesource.com/platform/frameworks/volley">Volley</a>是Google I/O 2013推出的网络通信库,在volley推出之前我们一般会选择比较成熟的第三方网络通信库,如:</p>
<ul>
<li>
<p><a href="http://loopj.com/android-async-http/">android-async-http</a></p>
</li>
<li>
<p><a href="http://square.github.io/retrofit/">retrofit</a></p>
</li>
<li>
<p><a href="http://square.github.io/okhttp/">okhttp</a></p>
</li>
</ul>
<p>他们各有优劣,之前个人则比较喜欢用android-async-http, 如今Google推出了官方的针对Android平台上的网络通信库,能使网络通信更快,更简单,更健壮,Volley在提供了高性能网络通讯功能的同时,对网络图片加载也提供了良好的支持,完全可以满足简单REST客户端的需求, 我们没有理由不跟上时代的潮流。</p>
<h2 id="volley">使用Volley</h2>
<p>下载Volley源码并build jar包。</p>
<pre><code>$ git clone https://android.googlesource.com/platform/frameworks/volley
$ cd volley
$ android update project -p
$ ant jar
</code></pre>
<p>然后把生成的jar包引用到我们的项目中,extras目录下则包含了目前最新的volley源码。</p>
<h2 id="section-1">用法</h2>
<p>在github上创建了一个学习Volley的项目,讲的再多不如代码来的直接,具体见项目这里:<a href="https://github.com/stormzhang/AndroidVolley">AndroidVolley</a></p>
Java String Format
2014-02-09T00:00:00+00:00
/java/2014/02/09/java-string-format
<p>在Android开发中,经常会用到和后端Api进行交互,而目前基本上都是REST风格的Api,会经常遇到拼接Url的情况,例如下面一个api:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">GET</span> <span class="sr">/api/</span><span class="n">v1</span><span class="o">/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="o">.</span><span class="n">json?page</span><span class="o">=</span><span class="mi">2</span><span class="o">&</span><span class="n">token</span><span class="o">=</span><span class="n">asdfghjkl</span></code></pre></div>
<p>这里url里的id以及参数page和token都是动态的,这时候最好的做法就是用String.format()方法.</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">final</span> <span class="nb">String</span> <span class="no">POST</span> <span class="o">=</span> <span class="s2">"/api/v1/posts/%1$d.json?page=%2$d&token=%3$s"</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="nb">String</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="no">POST</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"asdfghjkl"</span><span class="p">));</span>
<span class="c1">#=> /api/v1/posts/12.json?page=1&token=asdfghjkl</span></code></pre></div>
<p>很方便吧,String.format是在JDK1.5中新增的静态方法,功能强。它主要功能是格式化数据,大致分为这些类(常规类型、字符类型、数值类型、日期类型)。它的语法如下:
常规类型、字符类型和数值类型的格式说明符的语法:%[参数索引位置$][转换标识符][最小官渡][.保留精度位数]转换方式</p>
<p>如上述1$, 2$代表参数索引位置,而d, s则是转换标示符,关于更多的转换标示符见下面:</p>
<ul>
<li>
<p>%s 字符串类型 如”mingrisoft”</p>
</li>
<li>
<p>%c 字符类型 ‘m’</p>
</li>
<li>
<p>%b 布尔类型 true</p>
</li>
<li>
<p>%d 整数类型(十进制) 99</p>
</li>
<li>
<p>%x 整数类型(十六进制) FF</p>
</li>
<li>
<p>%o 整数类型(八进制) 77</p>
</li>
<li>
<p>%f 浮点类型 99.99</p>
</li>
<li>
<p>%a 十六进制浮点类型 FF.35AE</p>
</li>
<li>
<p>%e 指数类型 9.38e+5</p>
</li>
<li>
<p>%g 通用浮点类型(f和e类型中较短的)</p>
</li>
<li>
<p>%% 百分比类型 %</p>
</li>
<li>
<p>%n 换行符</p>
</li>
<li>
<p>%tx 日期与时间类型(x代表不同的日期与时间转换符)</p>
</li>
</ul>
<p>这里只介绍一些简单的用法,String.format尤其对日期类型格式化的时候更加强大,更多用法详见JDK api.</p>
使用Git Flow管理开发流程
2014-01-29T00:00:00+00:00
/git/2014/01/29/git-flow
<p>我们都知道, 在 git 的分支功能相对 svn 确实方便许多,而且也非常推荐使用分支来做开发. 我的做法是每个项目都有2个分支, master 和 develop. master 分支是主分支, 保证程序有一个 稳定版本, develop 则是开发用的分支, 几乎所有的功能开发, bug 修复都在这个分支上, 完成后 再合并回 master.</p>
<p>但是情况并不是这么简单. 有时当我们正在开发一个功能, 但程序突然出现 bug 需要及时去修复的时候, 这时要切回 master 分支, 并基于它创建一个 hotfix 分支. 有时我们在开发一个功能时, 需要停下来去开发另一个功能. 而且所有这些问题都出现 的时候, 发布也会成为比较棘手问题.</p>
<p>也就是说, git branch 功能很强大,但是没有一套模型告诉我们应该怎样在开发的时候善用 这些分支。于是有人就整理出了一套比较好的方案 <a href="http://nvie.com/posts/a-successful-git-branching-model/">A successful Git branching model</a>, 今天我们就一起来学习下这套方案.</p>
<p>简单来说, 他将 branch 分成2个主要分支和3个临时的辅助分支: </p>
<p><img src="/image/gitflow.png" /></p>
<h4 id="section">主要分支</h4>
<ul>
<li>
<p>master: 永远处在即将发布(production-ready)状态</p>
</li>
<li>
<p>develop: 最新的开发状态</p>
</li>
</ul>
<h4 id="section-1">辅助分支</h4>
<ul>
<li>
<p>feature: 开发新功能的分支, 基于 develop, 完成后 merge 回 develop</p>
</li>
<li>
<p>release: 准备要发布版本的分支, 用来修复 bug. 基于 develop, 完成后 merge 回 develop 和 master</p>
</li>
<li>
<p>hotfix: 修复 master 上的问题, 等不及 release 版本就必须马上上线. 基于 master, 完成后 merge 回 master 和 develop</p>
</li>
</ul>
<p>作者还提供了 git-flow 命令工具:</p>
<pre><code>$ git flow init
</code></pre>
<p>接着它会问你一系列的问题,蛋定!尽量使用它的默认值就好了:</p>
<pre><code>No branches exist yet. Base branches must be created now.
Branch name for production releases: [master]
Branch name for "next release" development: [develop]
How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []
</code></pre>
<p>完成后当前所在分支就变成 develop. 任何开发都必须从 develop 开始:</p>
<pre><code>git flow feature start some_awesome_feature
</code></pre>
<p>完成功能开发之后:</p>
<pre><code>git flow feature finish some_awesome_feature
</code></pre>
<p>该命令将会把feature/some_awesome_feature合并到develope分支,然后删除功能(feature)分支。</p>
<p>将一个 feature 分支推到远程服务器:</p>
<pre><code>git flow feature publish some_awesome_feature
或者
git push origin feature/some_awesome_feature
</code></pre>
<p>当你的功能点都完成时(需要发布新版本了),就基于develop创建一个发布(release)分支,然后升级版本号并在最后发布日期前把Bug Fix掉吧:</p>
<pre><code>$ git flow release start v0.1.0
</code></pre>
<p>当你在完成(finish)一个发布分支时,它会把你所作的修改合并到master分支,同时合并回develop分支,所以,你不需要担心你的master分支比develop分支更加超前。</p>
<p>最后一件让git-flow显得威武的事情是它处理热修复(即时的BugFix)的能力,你可以像其他分支一样地创建和完成一个热修复分支,区别是它基于master分支,因此你可以在产品出现问题时快速修复,然后通过”finish”命令把修改合并回master和develop分支。</p>
<p>git flow on github: <a href="https://github.com/nvie/gitflow">https://github.com/nvie/gitflow</a></p>
Git常用命令备忘
2014-01-27T00:00:00+00:00
/git/2014/01/27/git-common-command
<h2 id="git">Git配置</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span> <span class="s2">"storm"</span>
<span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span> <span class="s2">"stormzhang.dev@gmail.com"</span>
<span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="n">color</span><span class="o">.</span><span class="n">ui</span> <span class="kp">true</span>
<span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="k">alias</span><span class="o">.</span><span class="n">co</span> <span class="n">checkout</span> <span class="c1"># 别名</span>
<span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="k">alias</span><span class="o">.</span><span class="n">ci</span> <span class="n">commit</span>
<span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="k">alias</span><span class="o">.</span><span class="n">st</span> <span class="n">status</span>
<span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="k">alias</span><span class="o">.</span><span class="n">br</span> <span class="n">branch</span>
<span class="n">git</span> <span class="n">config</span> <span class="o">--</span><span class="n">global</span> <span class="n">core</span><span class="o">.</span><span class="n">editor</span> <span class="s2">"vim"</span> <span class="c1"># 设置Editor使用vim</span></code></pre></div>
<p>用户的git配置文件~/.gitconfig</p>
<h2 id="git-1">Git常用命令</h2>
<h4 id="section">查看、添加、提交、删除、找回,重置修改文件</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">help</span> <span class="o"><</span><span class="n">command</span><span class="o">></span> <span class="c1"># 显示command的help</span>
<span class="n">git</span> <span class="n">show</span> <span class="c1"># 显示某次提交的内容</span>
<span class="n">git</span> <span class="n">show</span> <span class="vg">$id</span>
<span class="n">git</span> <span class="n">co</span> <span class="o">--</span> <span class="o"><</span><span class="n">file</span><span class="o">></span> <span class="c1"># 抛弃工作区修改</span>
<span class="n">git</span> <span class="n">co</span> <span class="o">.</span> <span class="c1"># 抛弃工作区修改</span>
<span class="n">git</span> <span class="n">add</span> <span class="o"><</span><span class="n">file</span><span class="o">></span> <span class="c1"># 将工作文件修改提交到本地暂存区</span>
<span class="n">git</span> <span class="n">add</span> <span class="o">.</span> <span class="c1"># 将所有修改过的工作文件提交暂存区</span>
<span class="n">git</span> <span class="n">rm</span> <span class="o"><</span><span class="n">file</span><span class="o">></span> <span class="c1"># 从版本库中删除文件</span>
<span class="n">git</span> <span class="n">rm</span> <span class="o"><</span><span class="n">file</span><span class="o">></span> <span class="o">--</span><span class="n">cached</span> <span class="c1"># 从版本库中删除文件,但不删除文件</span>
<span class="n">git</span> <span class="n">reset</span> <span class="o"><</span><span class="n">file</span><span class="o">></span> <span class="c1"># 从暂存区恢复到工作文件</span>
<span class="n">git</span> <span class="n">reset</span> <span class="o">--</span> <span class="o">.</span> <span class="c1"># 从暂存区恢复到工作文件</span>
<span class="n">git</span> <span class="n">reset</span> <span class="o">--</span><span class="n">hard</span> <span class="c1"># 恢复最近一次提交过的状态,即放弃上次提交后的所有本次修改</span>
<span class="n">git</span> <span class="n">ci</span> <span class="o"><</span><span class="n">file</span><span class="o">></span>
<span class="n">git</span> <span class="n">ci</span> <span class="o">.</span>
<span class="n">git</span> <span class="n">ci</span> <span class="o">-</span><span class="n">a</span> <span class="c1"># 将git add, git rm和git ci等操作都合并在一起做</span>
<span class="n">git</span> <span class="n">ci</span> <span class="o">-</span><span class="n">am</span> <span class="s2">"some comments"</span>
<span class="n">git</span> <span class="n">ci</span> <span class="o">--</span><span class="n">amend</span> <span class="c1"># 修改最后一次提交记录</span>
<span class="n">git</span> <span class="n">revert</span> <span class="o"><</span><span class="vg">$id</span><span class="o">></span> <span class="c1"># 恢复某次提交的状态,恢复动作本身也创建了一次提交对象</span>
<span class="n">git</span> <span class="n">revert</span> <span class="no">HEAD</span> <span class="c1"># 恢复最后一次提交的状态</span></code></pre></div>
<h4 id="diff">查看文件diff</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">diff</span> <span class="o"><</span><span class="n">file</span><span class="o">></span> <span class="c1"># 比较当前文件和暂存区文件差异</span>
<span class="n">git</span> <span class="n">diff</span>
<span class="n">git</span> <span class="n">diff</span> <span class="o"><</span><span class="vg">$id1</span><span class="o">></span> <span class="o"><</span><span class="vg">$id2</span><span class="o">></span> <span class="c1"># 比较两次提交之间的差异</span>
<span class="n">git</span> <span class="n">diff</span> <span class="o"><</span><span class="n">branch1</span><span class="o">>.</span><span class="n">.</span><span class="o"><</span><span class="n">branch2</span><span class="o">></span> <span class="c1"># 在两个分支之间比较 </span>
<span class="n">git</span> <span class="n">diff</span> <span class="o">--</span><span class="n">staged</span> <span class="c1"># 比较暂存区和版本库差异</span>
<span class="n">git</span> <span class="n">diff</span> <span class="o">--</span><span class="n">cached</span> <span class="c1"># 比较暂存区和版本库差异</span>
<span class="n">git</span> <span class="n">diff</span> <span class="o">--</span><span class="n">stat</span> <span class="c1"># 仅仅比较统计信息</span></code></pre></div>
<h4 id="section-1">查看提交记录</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">log</span>
<span class="n">git</span> <span class="n">log</span> <span class="o"><</span><span class="n">file</span><span class="o">></span> <span class="c1"># 查看该文件每次提交记录</span>
<span class="n">git</span> <span class="n">log</span> <span class="o">-</span><span class="nb">p</span> <span class="o"><</span><span class="n">file</span><span class="o">></span> <span class="c1"># 查看每次详细修改内容的diff</span>
<span class="n">git</span> <span class="n">log</span> <span class="o">-</span><span class="nb">p</span> <span class="o">-</span><span class="mi">2</span> <span class="c1"># 查看最近两次详细</span></code></pre></div>
<h4 id="tig">tig</h4>
<p>Mac上可以使用tig代替diff和log,brew install tig</p>
<h2 id="git-">Git 本地分支管理</h2>
<h4 id="section-2">查看、切换、创建和删除分支</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">br</span> <span class="o">-</span><span class="n">r</span> <span class="c1"># 查看远程分支</span>
<span class="n">git</span> <span class="n">br</span> <span class="o"><</span><span class="n">new_branch</span><span class="o">></span> <span class="c1"># 创建新的分支</span>
<span class="n">git</span> <span class="n">br</span> <span class="o">-</span><span class="n">v</span> <span class="c1"># 查看各个分支最后提交信息</span>
<span class="n">git</span> <span class="n">br</span> <span class="o">--</span><span class="n">merged</span> <span class="c1"># 查看已经被合并到当前分支的分支</span>
<span class="n">git</span> <span class="n">br</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">merged</span> <span class="c1"># 查看尚未被合并到当前分支的分支</span>
<span class="n">git</span> <span class="n">co</span> <span class="o"><</span><span class="n">branch</span><span class="o">></span> <span class="c1"># 切换到某个分支</span>
<span class="n">git</span> <span class="n">co</span> <span class="o">-</span><span class="n">b</span> <span class="o"><</span><span class="n">new_branch</span><span class="o">></span> <span class="c1"># 创建新的分支,并且切换过去</span>
<span class="n">git</span> <span class="n">co</span> <span class="o">-</span><span class="n">b</span> <span class="o"><</span><span class="n">new_branch</span><span class="o">></span> <span class="o"><</span><span class="n">branch</span><span class="o">></span> <span class="c1"># 基于branch创建新的new_branch</span>
<span class="n">git</span> <span class="n">co</span> <span class="vg">$id</span> <span class="c1"># 把某次历史提交记录checkout出来,但无分支信息,切换到其他分支会自动删除</span>
<span class="n">git</span> <span class="n">co</span> <span class="vg">$id</span> <span class="o">-</span><span class="n">b</span> <span class="o"><</span><span class="n">new_branch</span><span class="o">></span> <span class="c1"># 把某次历史提交记录checkout出来,创建成一个分支</span>
<span class="n">git</span> <span class="n">br</span> <span class="o">-</span><span class="n">d</span> <span class="o"><</span><span class="n">branch</span><span class="o">></span> <span class="c1"># 删除某个分支</span>
<span class="n">git</span> <span class="n">br</span> <span class="o">-</span><span class="n">D</span> <span class="o"><</span><span class="n">branch</span><span class="o">></span> <span class="c1"># 强制删除某个分支 (未被合并的分支被删除的时候需要强制)</span></code></pre></div>
<h4 id="rebase">分支合并和rebase</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">merge</span> <span class="o"><</span><span class="n">branch</span><span class="o">></span> <span class="c1"># 将branch分支合并到当前分支</span>
<span class="n">git</span> <span class="n">merge</span> <span class="n">origin</span><span class="o">/</span><span class="n">master</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">ff</span> <span class="c1"># 不要Fast-Foward合并,这样可以生成merge提交</span>
<span class="n">git</span> <span class="n">rebase</span> <span class="n">master</span> <span class="o"><</span><span class="n">branch</span><span class="o">></span> <span class="c1"># 将master rebase到branch,相当于:</span>
<span class="n">git</span> <span class="n">co</span> <span class="o"><</span><span class="n">branch</span><span class="o">></span> <span class="o">&&</span> <span class="n">git</span> <span class="n">rebase</span> <span class="n">master</span> <span class="o">&&</span> <span class="n">git</span> <span class="n">co</span> <span class="n">master</span> <span class="o">&&</span> <span class="n">git</span> <span class="n">merge</span> <span class="o"><</span><span class="n">branch</span><span class="o">></span></code></pre></div>
<h4 id="git-2">Git补丁管理(方便在多台机器上开发同步时用)</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">diff</span> <span class="o">></span> <span class="o">.</span><span class="n">.</span><span class="o">/</span><span class="n">sync</span><span class="o">.</span><span class="n">patch</span> <span class="c1"># 生成补丁</span>
<span class="n">git</span> <span class="n">apply</span> <span class="o">.</span><span class="n">.</span><span class="o">/</span><span class="n">sync</span><span class="o">.</span><span class="n">patch</span> <span class="c1"># 打补丁</span>
<span class="n">git</span> <span class="n">apply</span> <span class="o">--</span><span class="n">check</span> <span class="o">.</span><span class="n">.</span><span class="o">/</span><span class="n">sync</span><span class="o">.</span><span class="n">patch</span> <span class="c1"># 测试补丁能否成功</span></code></pre></div>
<h4 id="git-3">Git暂存管理</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">stash</span> <span class="c1"># 暂存</span>
<span class="n">git</span> <span class="n">stash</span> <span class="n">list</span> <span class="c1"># 列所有stash</span>
<span class="n">git</span> <span class="n">stash</span> <span class="n">apply</span> <span class="c1"># 恢复暂存的内容</span>
<span class="n">git</span> <span class="n">stash</span> <span class="n">drop</span> <span class="c1"># 删除暂存区</span>
<span class="n">git</span> <span class="n">stash</span> <span class="n">clear</span></code></pre></div>
<h4 id="git-4">Git远程分支管理</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">pull</span> <span class="c1"># 抓取远程仓库所有分支更新并合并到本地</span>
<span class="n">git</span> <span class="n">pull</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">ff</span> <span class="c1"># 抓取远程仓库所有分支更新并合并到本地,不要快进合并</span>
<span class="n">git</span> <span class="n">fetch</span> <span class="n">origin</span> <span class="c1"># 抓取远程仓库更新</span>
<span class="n">git</span> <span class="n">merge</span> <span class="n">origin</span><span class="o">/</span><span class="n">master</span> <span class="c1"># 将远程主分支合并到本地当前分支</span>
<span class="n">git</span> <span class="n">co</span> <span class="o">--</span><span class="n">track</span> <span class="n">origin</span><span class="o">/</span><span class="n">branch</span> <span class="c1"># 跟踪某个远程分支创建相应的本地分支</span>
<span class="n">git</span> <span class="n">co</span> <span class="o">-</span><span class="n">b</span> <span class="o"><</span><span class="n">local_branch</span><span class="o">></span> <span class="n">origin</span><span class="o">/<</span><span class="n">remote_branch</span><span class="o">></span> <span class="c1"># 基于远程分支创建本地分支,功能同上</span>
<span class="n">git</span> <span class="n">push</span> <span class="c1"># push所有分支</span>
<span class="n">git</span> <span class="n">push</span> <span class="n">origin</span> <span class="n">master</span> <span class="c1"># 将本地主分支推到远程主分支</span>
<span class="n">git</span> <span class="n">push</span> <span class="o">-</span><span class="n">u</span> <span class="n">origin</span> <span class="n">master</span> <span class="c1"># 将本地主分支推到远程(如无远程主分支则创建,用于初始化远程仓库)</span>
<span class="n">git</span> <span class="n">push</span> <span class="n">origin</span> <span class="o"><</span><span class="n">local_branch</span><span class="o">></span> <span class="c1"># 创建远程分支, origin是远程仓库名</span>
<span class="n">git</span> <span class="n">push</span> <span class="n">origin</span> <span class="o"><</span><span class="n">local_branch</span><span class="o">></span><span class="p">:</span><span class="o"><</span><span class="n">remote_branch</span><span class="o">></span> <span class="c1"># 创建远程分支</span>
<span class="n">git</span> <span class="n">push</span> <span class="n">origin</span> <span class="p">:</span><span class="o"><</span><span class="n">remote_branch</span><span class="o">></span> <span class="c1">#先删除本地分支(git br -d <branch>),然后再push删除远程分支</span></code></pre></div>
<h4 id="git-5">Git远程仓库管理</h4>
<div class="highlight"><pre><code class="language-sh" data-lang="sh">git remote -v <span class="c"># 查看远程服务器地址和仓库名称</span>
git remote show origin <span class="c"># 查看远程服务器仓库状态</span>
git remote add origin git@github:stormzhang/demo.git <span class="c"># 添加远程仓库地址</span>
git remote <span class="nb">set</span>-url origin git@github.com:stormzhang/demo.git <span class="c"># 设置远程仓库地址(用于修改远程仓库地址</span></code></pre></div>
<h4 id="section-3">创建远程仓库</h4>
<div class="highlight"><pre><code class="language-sh" data-lang="sh">git clone --bare robbin_site robbin_site.git <span class="c"># 用带版本的项目创建纯版本仓库</span>
scp -r my_project.git git@git.csdn.net:~ <span class="c"># 将纯仓库上传到服务器上</span>
mkdir robbin_site.git <span class="o">&&</span> <span class="nb">cd </span>robbin_site.git <span class="o">&&</span> git --bare init <span class="c"># 在服务器创建纯仓库</span>
git remote add origin git@github.com:robbin/robbin_site.git <span class="c"># 设置远程仓库地址</span>
git push -u origin master <span class="c"># 客户端首次提交</span>
git push -u origin develop <span class="c"># 首次将本地develop分支提交到远程develop分支,并且track</span>
git remote <span class="nb">set</span>-head origin master <span class="c"># 设置远程仓库的HEAD指向master分支</span></code></pre></div>
<p>也可以命令设置跟踪远程库和本地库</p>
<div class="highlight"><pre><code class="language-sh" data-lang="sh">git branch --set-upstream master origin/master
git branch --set-upstream develop origin/develop</code></pre></div>
<p>Via <a href="http://robbinfan.com/blog/34/git-common-command">robbinfan</a></p>
Android Animation
2014-01-19T00:00:00+00:00
/android/2014/01/19/android-animation
<p>Android中常用两种动画模式,tween animation和frame animation,即补间动画和帧动画,但在android3.0中又引入了一个新的动画系统:property animation,即属性动画,这三种动画模式在SDK中被称为property animation,view animation,drawable animation。如果想在3.0之前的版本支持property animation的话,那么可使用<a href="https://github.com/JakeWharton/NineOldAndroids">NineOldAndroids</a>,大神JakeWharton的又一开源项目。</p>
<h2 id="view-animationtween-animation">View Animation(Tween Animation)</h2>
<p>View animation只能应用于View对象,而且只支持一部分属性,如支持缩放旋转而不支持背景颜色的改变。</p>
<p>而且对于View animation,它只是改变了View对象绘制的位置,而没有改变View对象本身,比如,你有一个Button,坐标(100,100),Width:200,Height:50,而你有一个动画使其变为Width:100,Height:100,你会发现动画过程中触发按钮点击的区域仍是(100,100)-(300,150)。</p>
<p> View Animation就是一系列View形状的变换,如大小的缩放,透明度的改变,位置的改变,动画的定义既可以用代码定义也可以用XML定义,当然,建议用XML定义。</p>
<p> 可以给一个View同时设置多个动画,比如从透明至不透明的淡入效果,与从小到大的放大效果,这些动画可以同时进行,也可以在一个完成之后开始另一个。</p>
<p>用XML定义的动画放在/res/anim/文件夹内,XML文件的根元素可以为alpha, scale, translate, rotate, interpolator元素或set(表示以上几个动画的集合,set可以嵌套)。默认情况下,所有动画是同时进行的,可以通过startOffset属性设置各个动画的开始偏移(开始时间)来达到动画顺序播放的效果。</p>
<p> 可以通过设置interpolator属性改变动画渐变的方式,如AccelerateInterpolator,开始时慢,然后逐渐加快。默认为AccelerateDecelerateInterpolator。</p>
<p> 定义好动画的XML文件后,可以通过类似下面的代码对指定View应用动画。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ImageView</span> <span class="n">image</span> <span class="o">=</span> <span class="p">(</span><span class="no">ImageView</span><span class="p">)</span><span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">image</span><span class="p">);</span>
<span class="no">Animation</span> <span class="n">animation</span> <span class="o">=</span> <span class="no">AnimationUtils</span><span class="o">.</span><span class="n">loadAnimation</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">R</span><span class="o">.</span><span class="n">anim</span><span class="o">.</span><span class="n">animation</span><span class="p">);</span>
<span class="n">spaceshipImage</span><span class="o">.</span><span class="n">startAnimation</span><span class="p">(</span><span class="n">animation</span><span class="p">);</span></code></pre></div>
<h2 id="timeinterplator">TimeInterplator</h2>
<p>Time interplator定义了属性值变化的方式,如线性均匀改变,开始慢然后逐渐快等。在Property Animation中是TimeInterplator,在View Animation中是Interplator,这两个是一样的,在3.0之前只有Interplator,3.0之后实现代码转移至了TimeInterplator。Interplator继承自TimeInterplator,内部没有任何其他代码。</p>
<ul>
<li>
<p>AccelerateInterpolator 加速,开始时慢中间加速</p>
</li>
<li>
<p>DecelerateInterpolator 减速,开始时快然后减速</p>
</li>
<li>
<p>AccelerateDecelerateInterolator 先加速后减速,开始结束时慢,中间加速</p>
</li>
<li>
<p>AnticipateInterpolator 反向 ,先向相反方向改变一段再加速播放</p>
</li>
<li>
<p>AnticipateOvershootInterpolator 反向加回弹,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值</p>
</li>
<li>
<p>BounceInterpolator 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100</p>
</li>
<li>
<p>CycleIinterpolator 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 * mCycles * Math.PI * input)</p>
</li>
<li>
<p>LinearInterpolator 线性,线性均匀改变</p>
</li>
<li>
<p>OvershottInterpolator 回弹,最后超出目的值然后缓慢改变到目的值</p>
</li>
<li>
<p>TimeInterpolator 一个接口,允许你自定义interpolator,以上几个都是实现了这个接口</p>
</li>
</ul>
<h2 id="drawable-animationframe-animation">Drawable Animation(Frame Animation)</h2>
<p>Drawable Animation(Frame Animation):帧动画,就像GIF图片,通过一系列Drawable依次显示来模拟动画的效果。在XML中的定义方式如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">animation</span><span class="o">-</span><span class="n">list</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">oneshot</span><span class="o">=</span><span class="s2">"false"</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading01"</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"200"</span> <span class="sr">/></span>
<span class="sr"> <item android:drawable="@drawable/</span><span class="n">loading02</span><span class="s2">" android:duration="</span><span class="mi">200</span><span class="s2">" /></span>
<span class="s2"> <item android:drawable="</span><span class="vi">@drawable</span><span class="o">/</span><span class="n">loading03</span><span class="s2">" android:duration="</span><span class="mi">200</span><span class="s2">" /></span>
<span class="s2"></animation-list></span></code></pre></div>
<p>必须以animation-list为根元素,以item表示要轮换显示的图片,duration属性表示各项显示的时间。XML文件要放在/res/drawable/目录下。示例:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ImageView</span> <span class="n">imageView</span> <span class="o">=</span> <span class="p">(</span><span class="no">ImageView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">imageView</span><span class="p">);</span>
<span class="n">imageView</span><span class="o">.</span><span class="n">setBackgroundResource</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">drawable</span><span class="o">.</span><span class="n">drawable_anim</span><span class="p">);</span>
<span class="n">anim</span> <span class="o">=</span> <span class="p">(</span><span class="no">AnimationDrawable</span><span class="p">)</span> <span class="n">imageView</span><span class="o">.</span><span class="n">getBackground</span><span class="p">();</span>
<span class="n">anim</span><span class="o">.</span><span class="n">start</span><span class="p">();</span></code></pre></div>
<h2 id="property-animation">Property Animation</h2>
<p>属性动画,这个是在Android 3.0中才引进的,以前学WPF时里面的动画机制好像就是这个,它更改的是对象的实际属性,在View Animation(Tween Animation)中,其改变的是View的绘制效果,真正的View的属性保持不变,比如无论你在对话中如何缩放Button的大小,Button的有效点击区域还是没有应用动画时的区域,其位置与大小都不变。而在Property Animation中,改变的是对象的实际属性,如Button的缩放,Button的位置与大小属性值都改变了。而且Property Animation不止可以应用于View,还可以应用于任何对象。Property Animation只是表示一个值在一段时间内的改变,当值改变时要做什么事情完全是你自己决定的。</p>
<p>在Property Animation中,可以对动画应用以下属性:</p>
<ul>
<li>
<p>Duration:动画的持续时间</p>
</li>
<li>
<p>TimeInterpolation:属性值的计算方式,如先快后慢</p>
</li>
<li>
<p>TypeEvaluator:根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值</p>
</li>
<li>
<p>Repeat Count and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以此动画一直重复,或播放完时再反向播放</p>
</li>
<li>
<p>Animation sets:动画集合,即可以同时对一个对象应用几个动画,这些动画可以同时播放也可以对不同动画设置不同开始偏移</p>
</li>
<li>
<p>Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为10ms,最终刷新时间还受系统进程调度与硬件的影响</p>
</li>
</ul>
<p>具体使用方式,见<a href="http://nineoldandroids.com/">NineOldAndroids官网</a></p>
ButterKnife--View注入框架
2014-01-12T00:00:00+00:00
/openandroid/android/2014/01/12/android-butterknife
<p>俗话说:“不会偷懒的程序员不是好的程序员!”。作为一名Android开发,是不是经常厌烦了大量的findViewById以及setOnClickListener代码,而<a href="https://github.com/JakeWharton/butterknife">ButterKnife</a>是一个专注于Android系统的View注入框架,让你从此从这些烦人臃肿的代码中解脱出来。先来看一段代码示例说明下ButterKnife是如何简化代码的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">ExampleActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="p">{</span>
<span class="no">TextView</span> <span class="n">title</span><span class="p">;</span>
<span class="no">TextView</span> <span class="n">subtitle</span><span class="p">;</span>
<span class="no">TextView</span> <span class="n">footer</span><span class="p">;</span>
<span class="vi">@Override</span> <span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">simple_activity</span><span class="p">);</span>
<span class="n">title</span> <span class="o">=</span> <span class="p">(</span><span class="no">TextView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">title</span><span class="p">);</span>
<span class="n">subtitle</span> <span class="o">=</span> <span class="p">(</span><span class="no">TextView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">subtitle</span><span class="p">);</span>
<span class="n">footer</span> <span class="o">=</span> <span class="p">(</span><span class="no">TextView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">footer</span><span class="p">);</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Use</span> <span class="n">views</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>而用ButterKnife之后的代码是这样的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">ExampleActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="p">{</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> <span class="no">TextView</span> <span class="n">title</span><span class="p">;</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">subtitle</span><span class="p">)</span> <span class="no">TextView</span> <span class="n">subtitle</span><span class="p">;</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">footer</span><span class="p">)</span> <span class="no">TextView</span> <span class="n">footer</span><span class="p">;</span>
<span class="vi">@Override</span> <span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">simple_activity</span><span class="p">);</span>
<span class="no">ButterKnife</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Use</span> <span class="s2">"injected"</span> <span class="n">views</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>是不是非常简洁易用?下面就来系统的介绍下ButterKnife的用法。</p>
<h2 id="butter-knife-">Butter Knife 的特性</h2>
<ol>
<li>
<p>支持 Activity 中的 View 注入</p>
</li>
<li>
<p>支持 View 中的 View 注入</p>
</li>
<li>
<p>支持 View 事件回调函数注入</p>
</li>
</ol>
<p>目前支持如下事件回调函数:</p>
<ul>
<li>
<p>View: @OnLongClick and @OnFocusChanged.</p>
</li>
<li>
<p>TextView: @OnEditorAction.</p>
</li>
<li>
<p>AdapterView: @OnItemClick and @OnItemLongClick.</p>
</li>
<li>
<p>CompoundButton: @OnCheckedChanged.</p>
</li>
</ul>
<p>下面来看一些注入的示例代码:</p>
<h2 id="activity-">在Activity 中注入</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">ExampleActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="p">{</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> <span class="no">TextView</span> <span class="n">title</span><span class="p">;</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">subtitle</span><span class="p">)</span> <span class="no">TextView</span> <span class="n">subtitle</span><span class="p">;</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">footer</span><span class="p">)</span> <span class="no">TextView</span> <span class="n">footer</span><span class="p">;</span>
<span class="vi">@Override</span> <span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">simple_activity</span><span class="p">);</span>
<span class="no">ButterKnife</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Use</span> <span class="s2">"injected"</span> <span class="n">views</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="fragment-">在 Fragment 中注入</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">FancyFragment</span> <span class="n">extends</span> <span class="no">Fragment</span> <span class="p">{</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">button1</span><span class="p">)</span> <span class="no">Button</span> <span class="n">button1</span><span class="p">;</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">button2</span><span class="p">)</span> <span class="no">Button</span> <span class="n">button2</span><span class="p">;</span>
<span class="vi">@Override</span> <span class="no">View</span> <span class="n">onCreateView</span><span class="p">(</span><span class="no">LayoutInflater</span> <span class="n">inflater</span><span class="p">,</span> <span class="no">ViewGroup</span> <span class="n">container</span><span class="p">,</span> <span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="no">View</span> <span class="n">view</span> <span class="o">=</span> <span class="n">inflater</span><span class="o">.</span><span class="n">inflate</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">fancy_fragment</span><span class="p">,</span> <span class="n">container</span><span class="p">,</span> <span class="kp">false</span><span class="p">);</span>
<span class="no">ButterKnife</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">view</span><span class="p">);</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Use</span> <span class="s2">"injected"</span> <span class="n">views</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="k">return</span> <span class="n">view</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="viewholder-">在 ViewHolder 模式中注入</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyAdapter</span> <span class="n">extends</span> <span class="no">BaseAdapter</span> <span class="p">{</span>
<span class="vi">@Override</span> <span class="kp">public</span> <span class="no">View</span> <span class="n">getView</span><span class="p">(</span><span class="n">int</span> <span class="n">position</span><span class="p">,</span> <span class="no">View</span> <span class="n">view</span><span class="p">,</span> <span class="no">ViewGroup</span> <span class="n">parent</span><span class="p">)</span> <span class="p">{</span>
<span class="no">ViewHolder</span> <span class="n">holder</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">view</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">holder</span> <span class="o">=</span> <span class="p">(</span><span class="no">ViewHolder</span><span class="p">)</span> <span class="n">view</span><span class="o">.</span><span class="n">getTag</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">view</span> <span class="o">=</span> <span class="n">inflater</span><span class="o">.</span><span class="n">inflate</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">whatever</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="kp">false</span><span class="p">);</span>
<span class="n">holder</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">ViewHolder</span><span class="p">(</span><span class="n">view</span><span class="p">);</span>
<span class="n">view</span><span class="o">.</span><span class="n">setTag</span><span class="p">(</span><span class="n">holder</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">holder</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s2">"John Doe"</span><span class="p">);</span>
<span class="sr">//</span> <span class="n">etc</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="k">return</span> <span class="n">convertView</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">static</span> <span class="k">class</span> <span class="nc">ViewHolder</span> <span class="p">{</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">title</span><span class="p">)</span> <span class="no">TextView</span> <span class="nb">name</span><span class="p">;</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">job_title</span><span class="p">)</span> <span class="no">TextView</span> <span class="n">jobTitle</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">ViewHolder</span><span class="p">(</span><span class="no">View</span> <span class="n">view</span><span class="p">)</span> <span class="p">{</span>
<span class="no">ButterKnife</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">view</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section">注入回调函数</h2>
<p>下面是几种注入回调函数的方法示例:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="err">带有</span> <span class="no">Button</span> <span class="err">参数</span>
<span class="vi">@OnClick</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">submit</span><span class="p">)</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">sayHi</span><span class="p">(</span><span class="no">Button</span> <span class="n">button</span><span class="p">)</span> <span class="p">{</span>
<span class="n">button</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s2">"Hello!"</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">不带参数</span>
<span class="vi">@OnClick</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">submit</span><span class="p">)</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">submit</span><span class="p">()</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="n">submit</span> <span class="n">data</span> <span class="n">to</span> <span class="n">server</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">同时注入多个</span> <span class="no">View</span> <span class="err">事件</span>
<span class="vi">@OnClick</span><span class="p">({</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">door1</span><span class="p">,</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">door2</span><span class="p">,</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">door3</span> <span class="p">})</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">pickDoor</span><span class="p">(</span><span class="no">DoorView</span> <span class="n">door</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">door</span><span class="o">.</span><span class="n">hasPrizeBehind</span><span class="p">())</span> <span class="p">{</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"You win!"</span><span class="p">,</span> <span class="no">LENGTH_SHORT</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"Try again"</span><span class="p">,</span> <span class="no">LENGTH_SHORT</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="reset">Reset函数</h2>
<p>如果需要在 界面 销毁的时候,把注入的 View 设置为 Null, 则可以用 reset 函数:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">FancyFragment</span> <span class="n">extends</span> <span class="no">Fragment</span> <span class="p">{</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">button1</span><span class="p">)</span> <span class="no">Button</span> <span class="n">button1</span><span class="p">;</span>
<span class="vi">@InjectView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">button2</span><span class="p">)</span> <span class="no">Button</span> <span class="n">button2</span><span class="p">;</span>
<span class="vi">@Override</span> <span class="no">View</span> <span class="n">onCreateView</span><span class="p">(</span><span class="no">LayoutInflater</span> <span class="n">inflater</span><span class="p">,</span> <span class="no">ViewGroup</span> <span class="n">container</span><span class="p">,</span> <span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="no">View</span> <span class="n">view</span> <span class="o">=</span> <span class="n">inflater</span><span class="o">.</span><span class="n">inflate</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">fancy_fragment</span><span class="p">,</span> <span class="n">container</span><span class="p">,</span> <span class="kp">false</span><span class="p">);</span>
<span class="no">ButterKnife</span><span class="o">.</span><span class="n">inject</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">view</span><span class="p">);</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Use</span> <span class="s2">"injected"</span> <span class="n">views</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="k">return</span> <span class="n">view</span><span class="p">;</span>
<span class="p">}</span>
<span class="vi">@Override</span> <span class="n">void</span> <span class="n">onDestroyView</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onDestroyView</span><span class="p">();</span>
<span class="no">Views</span><span class="o">.</span><span class="n">reset</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>另外 还支持 可选的 View 注入,如果该 View 没有,就没有吧:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">@Optional @InjectView(R.id.might_not_be_there) TextView mightNotBeThere;
@Optional @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {
// TODO ...
}</code></pre></div>
<p>还有两个 findViewById 函数来简化查找 View 的方式,如果上面都满足不了你的需求,你可以用用他们:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">View</span> <span class="n">view</span> <span class="o">=</span> <span class="no">LayoutInflater</span><span class="o">.</span><span class="n">from</span><span class="p">(</span><span class="n">context</span><span class="p">)</span><span class="o">.</span><span class="n">inflate</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">thing</span><span class="p">,</span> <span class="n">null</span><span class="p">);</span>
<span class="no">TextView</span> <span class="n">firstName</span> <span class="o">=</span> <span class="no">Views</span><span class="o">.</span><span class="n">findById</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">first_name</span><span class="p">);</span>
<span class="no">TextView</span> <span class="n">lastName</span> <span class="o">=</span> <span class="no">Views</span><span class="o">.</span><span class="n">findById</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">last_name</span><span class="p">);</span>
<span class="no">ImageView</span> <span class="n">photo</span> <span class="o">=</span> <span class="no">Views</span><span class="o">.</span><span class="n">findById</span><span class="p">(</span><span class="n">view</span><span class="p">,</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">photo</span><span class="p">);</span></code></pre></div>
<p>最后,如果你是用Android Studio来作为IDE的话,那么有一个ButterKnife的插件<a href="https://github.com/inmite/android-butterknife-zelezny">android-butterknife-zelezny</a>, 该插件可以让你手动生成上述注入代码,从此让自己成为一个更懒惰的程序员,上张截图吧。</p>
<p><img src="https://raw2.github.com/inmite/android-butterknife-zelezny/master/img/zelezny_animated.gif" /></p>
Handler引起的内存泄露
2014-01-06T00:00:00+00:00
/android/2014/01/06/android-memory-leak-with-handler
<p>如果你在Activity中定义了一个内部Handler类,如下代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MainActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">Handler</span> <span class="n">mHandler</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Handler</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">handleMessage</span><span class="p">(</span><span class="no">Message</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span><span class="no">TODO</span> <span class="n">handle</span> <span class="n">message</span><span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">activity_main</span><span class="p">);</span>
<span class="n">mHandler</span><span class="o">.</span><span class="n">sendMessageDelayed</span><span class="p">(</span><span class="no">Message</span><span class="o">.</span><span class="n">obtain</span><span class="p">(),</span> <span class="mi">60000</span><span class="p">);</span>
<span class="sr">//</span><span class="n">just</span> <span class="n">finish</span> <span class="n">this</span> <span class="n">activity</span>
<span class="n">finish</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>然后运行Android Lint工具会有一个内存泄露警告:</p>
<pre><code>This Handler class should be static or leaks might occur (com.example.ta.MainActivity.1)
Issue: Ensures that Handler classes do not hold on to a reference to an outer class
Id: HandlerLeak
In Android, Handler classes should be static or leaks might occur. Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class.
</code></pre>
<p>原因是:</p>
<ul>
<li>
<p>当Android应用启动的时候,会先创建一个应用主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。</p>
</li>
<li>
<p>当在主线程中初始化Handler时,该Handler和Looper的消息队列关联。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。</p>
</li>
<li>
<p>在Java中,非静态(匿名)内部类会引用外部类对象。而静态内部类不会引用外部类对象。</p>
</li>
<li>
<p>如果外部类是Activity,则会引起Activity泄露 。</p>
</li>
</ul>
<p>当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。</p>
<p>要修改该问题,只需要按照Lint提示的那样,把Handler类定义为静态即可,然后通过WeakReference 来保持外部的Activity对象。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">private</span> <span class="no">Handler</span> <span class="n">mHandler</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">MyHandler</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="kp">private</span> <span class="n">static</span> <span class="k">class</span> <span class="nc">MyHandler</span> <span class="n">extends</span> <span class="no">Handler</span><span class="p">{</span>
<span class="kp">private</span> <span class="n">final</span> <span class="no">WeakReference</span><span class="o"><</span><span class="no">Activity</span><span class="o">></span> <span class="n">mActivity</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">MyHandler</span><span class="p">(</span><span class="no">Activity</span> <span class="n">activity</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mActivity</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">WeakReference</span><span class="o"><</span><span class="no">Activity</span><span class="o">></span><span class="p">(</span><span class="n">activity</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">handleMessage</span><span class="p">(</span><span class="no">Message</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="n">mActivity</span><span class="o">.</span><span class="n">get</span><span class="p">()</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>所以,当你在Activity中使用内部类的时候,需要时刻考虑您是否可以控制该内部类的生命周期,如果不可以,则最好定义为静态内部类。</p>
<p>参考文章: http://blog.chengyunfeng.com/?p=468</p>
Android Cheatsheet For Designers
2014-01-05T00:00:00+00:00
/design/2014/01/05/android-cheatsheet-for-designers
<p>转载一篇比较好的Android设计文章:<a href="http://petrnohejl.github.io/Android-Cheatsheet-For-Graphic-Designers/#screen-densities-and-icon-dimensions">Android Cheatsheet For Designers</a></p>
Java Enum
2013-12-31T00:00:00+00:00
/java/2013/12/31/java-enum
<h2 id="section">常量</h2>
<p>在JDK1.5 之前,我们定义常量都是: public static final …. 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">enum</span> <span class="no">Color</span> <span class="p">{</span>
<span class="no">RED</span><span class="p">,</span> <span class="no">GREEN</span><span class="p">,</span> <span class="no">YELLOW</span>
<span class="p">}</span></code></pre></div>
<h2 id="switch">switch</h2>
<p>JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">enum</span> <span class="no">Signal</span> <span class="p">{</span>
<span class="no">GREEN</span><span class="p">,</span> <span class="no">YELLOW</span><span class="p">,</span> <span class="no">RED</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">TrafficLight</span> <span class="p">{</span>
<span class="no">Signal</span> <span class="n">color</span> <span class="o">=</span> <span class="no">Signal</span><span class="o">.</span><span class="n">RED</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">change</span><span class="p">()</span> <span class="p">{</span>
<span class="n">switch</span> <span class="p">(</span><span class="n">color</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="ss">RED</span><span class="p">:</span>
<span class="n">color</span> <span class="o">=</span> <span class="no">Signal</span><span class="o">.</span><span class="n">GREEN</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="ss">YELLOW</span><span class="p">:</span>
<span class="n">color</span> <span class="o">=</span> <span class="no">Signal</span><span class="o">.</span><span class="n">RED</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="ss">GREEN</span><span class="p">:</span>
<span class="n">color</span> <span class="o">=</span> <span class="no">Signal</span><span class="o">.</span><span class="n">YELLOW</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-1">向枚举中添加新方法</h2>
<p>如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">enum</span> <span class="no">Color</span> <span class="p">{</span>
<span class="no">RED</span><span class="p">(</span><span class="s2">"红色"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="no">GREEN</span><span class="p">(</span><span class="s2">"绿色"</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="no">BLANK</span><span class="p">(</span><span class="s2">"白色"</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="no">YELLO</span><span class="p">(</span><span class="s2">"黄色"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">成员变量</span>
<span class="kp">private</span> <span class="nb">String</span> <span class="nb">name</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">int</span> <span class="n">index</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">构造方法</span>
<span class="kp">private</span> <span class="no">Color</span><span class="p">(</span><span class="nb">String</span> <span class="nb">name</span><span class="p">,</span> <span class="n">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">;</span>
<span class="n">this</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">index</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">普通方法</span>
<span class="kp">public</span> <span class="n">static</span> <span class="nb">String</span> <span class="n">getName</span><span class="p">(</span><span class="n">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="no">Color</span> <span class="n">c</span> <span class="p">:</span> <span class="no">Color</span><span class="o">.</span><span class="n">values</span><span class="p">())</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">getIndex</span><span class="p">()</span> <span class="o">==</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">c</span><span class="o">.</span><span class="n">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="n">get</span> <span class="n">set</span> <span class="err">方法</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">getName</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">setName</span><span class="p">(</span><span class="nb">String</span> <span class="nb">name</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">int</span> <span class="n">getIndex</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">index</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">setIndex</span><span class="p">(</span><span class="n">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">index</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-2">覆盖枚举的方法</h2>
<p>下面给出一个toString()方法覆盖的例子。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Test</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">enum</span> <span class="no">Color</span> <span class="p">{</span>
<span class="no">RED</span><span class="p">(</span><span class="s2">"红色"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="no">GREEN</span><span class="p">(</span><span class="s2">"绿色"</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="no">BLANK</span><span class="p">(</span><span class="s2">"白色"</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="no">YELLO</span><span class="p">(</span><span class="s2">"黄色"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">成员变量</span>
<span class="kp">private</span> <span class="nb">String</span> <span class="nb">name</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">int</span> <span class="n">index</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">构造方法</span>
<span class="kp">private</span> <span class="no">Color</span><span class="p">(</span><span class="nb">String</span> <span class="nb">name</span><span class="p">,</span> <span class="n">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">;</span>
<span class="n">this</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">index</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">覆盖方法</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">toString</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">this</span><span class="o">.</span><span class="n">index</span> <span class="o">+</span> <span class="s2">"_"</span> <span class="o">+</span> <span class="n">this</span><span class="o">.</span><span class="n">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">main</span><span class="p">(</span><span class="nb">String</span><span class="o">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="no">Color</span><span class="o">.</span><span class="n">RED</span><span class="o">.</span><span class="n">toString</span><span class="p">());</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-3">实现接口</h2>
<p>所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">interface</span> <span class="no">Behaviour</span> <span class="p">{</span>
<span class="n">void</span> <span class="nb">print</span><span class="p">();</span>
<span class="nb">String</span> <span class="n">getInfo</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">enum</span> <span class="no">Color</span> <span class="n">implements</span> <span class="no">Behaviour</span> <span class="p">{</span>
<span class="no">RED</span><span class="p">(</span><span class="s2">"红色"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="no">GREEN</span><span class="p">(</span><span class="s2">"绿色"</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="no">BLANK</span><span class="p">(</span><span class="s2">"白色"</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="no">YELLO</span><span class="p">(</span><span class="s2">"黄色"</span><span class="p">,</span> <span class="mi">4</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">成员变量</span>
<span class="kp">private</span> <span class="nb">String</span> <span class="nb">name</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">int</span> <span class="n">index</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">构造方法</span>
<span class="kp">private</span> <span class="no">Color</span><span class="p">(</span><span class="nb">String</span> <span class="nb">name</span><span class="p">,</span> <span class="n">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">;</span>
<span class="n">this</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">index</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">接口方法</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="n">getInfo</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">this</span><span class="o">.</span><span class="n">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">接口方法</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="nb">print</span><span class="p">()</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">index</span> <span class="o">+</span> <span class="s2">":"</span> <span class="o">+</span> <span class="n">this</span><span class="o">.</span><span class="n">name</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-4">使用接口组织枚举</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">interface</span> <span class="no">Food</span> <span class="p">{</span>
<span class="n">enum</span> <span class="no">Coffee</span> <span class="n">implements</span> <span class="no">Food</span> <span class="p">{</span>
<span class="no">BLACK_COFFEE</span><span class="p">,</span> <span class="no">DECAF_COFFEE</span><span class="p">,</span> <span class="no">LATTE</span><span class="p">,</span> <span class="no">CAPPUCCINO</span>
<span class="p">}</span>
<span class="n">enum</span> <span class="no">Dessert</span> <span class="n">implements</span> <span class="no">Food</span> <span class="p">{</span>
<span class="no">FRUIT</span><span class="p">,</span> <span class="no">CAKE</span><span class="p">,</span> <span class="no">GELATO</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-5">关于枚举集合的使用</h2>
<p>java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而value则可以是任意类型。关于这个两个集合的使用就不在这里赘述,可以参考JDK文档</p>
<h2 id="section-6">方法介绍</h2>
<p>所有枚举类都继承了Enum的方法,下面我们详细介绍这些方法。</p>
<ul>
<li>ordinal()</li>
</ul>
<p>返回枚举值在枚举类种的顺序。这个顺序根据枚举值声明的顺序而定。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Color</span><span class="o">.</span><span class="n">RED</span><span class="o">.</span><span class="n">ordinal</span><span class="p">();</span> <span class="sr">//</span><span class="err">返回结果:</span><span class="mi">0</span>
<span class="no">Color</span><span class="o">.</span><span class="n">BLUE</span><span class="o">.</span><span class="n">ordinal</span><span class="p">();</span> <span class="sr">//</span><span class="err">返回结果:</span><span class="mi">1</span></code></pre></div>
<ul>
<li>compareTo()</li>
</ul>
<p>Enum实现了java.lang.Comparable接口,因此可以比较象与指定对象的顺序。Enum中的compareTo返回的是两个枚举值的顺序之差。当然,前提是两个枚举值必须属于同一个枚举类,否则会抛出ClassCastException()异常。(具体可见源代码)</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Color</span><span class="o">.</span><span class="n">RED</span><span class="o">.</span><span class="n">compareTo</span><span class="p">(</span><span class="no">Color</span><span class="o">.</span><span class="n">BLUE</span><span class="p">);</span> <span class="sr">//</span><span class="err">返回结果</span> <span class="o">-</span><span class="mi">1</span></code></pre></div>
<ul>
<li>values()</li>
</ul>
<p>静态方法,返回一个包含全部枚举值的数组。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Color</span><span class="o">[]</span> <span class="n">colors</span><span class="o">=</span><span class="no">Color</span><span class="o">.</span><span class="n">values</span><span class="p">();</span>
<span class="k">for</span><span class="p">(</span><span class="no">Color</span> <span class="ss">c</span><span class="p">:</span><span class="n">colors</span><span class="p">){</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="n">c</span><span class="o">+</span><span class="s2">","</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span><span class="err">返回结果:</span><span class="no">RED</span><span class="p">,</span><span class="no">BLUE</span><span class="p">,</span><span class="no">BLACK</span> <span class="no">YELLOW</span><span class="p">,</span><span class="no">GREEN</span><span class="p">,</span></code></pre></div>
<ul>
<li>toString()</li>
</ul>
<p>返回枚举常量的名称。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Color</span> <span class="n">c</span><span class="o">=</span><span class="no">Color</span><span class="o">.</span><span class="n">RED</span><span class="p">;</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="n">c</span><span class="p">);</span><span class="o">//</span><span class="err">返回结果</span><span class="p">:</span> <span class="no">RED</span></code></pre></div>
<ul>
<li>valueOf()</li>
</ul>
<p>这个方法和toString方法是相对应的,返回带指定名称的指定枚举类型的枚举常量。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Color</span><span class="o">.</span><span class="n">valueOf</span><span class="p">(</span><span class="s2">"BLUE"</span><span class="p">);</span> <span class="sr">//</span><span class="err">返回结果</span><span class="p">:</span> <span class="no">Color</span><span class="o">.</span><span class="n">BLUE</span></code></pre></div>
<ul>
<li>equals()</li>
</ul>
<p>比较两个枚举类对象的引用。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span><span class="no">JDK</span><span class="err">源代码:</span>
<span class="kp">public</span> <span class="n">final</span> <span class="n">boolean</span> <span class="n">equals</span><span class="p">(</span><span class="no">Object</span> <span class="n">other</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">this</span><span class="o">==</span><span class="n">other</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
Android Debug Tool -- pidcat
2013-12-26T00:00:00+00:00
/devtools/2013/12/26/android-logcat-pidcat
<p>在Android开发的过程中我们会用Logcat来查看应用程序的log信息,而我们一般都是在Eclipse集成好的DDMS上直接使用,调试应用非常方便。但总觉得Logcat并不适合所有场景,Logcat比较适合开发过程中的调试,而且依赖Eclipse也比较重,如果不是开发效率的限制,个人还是更倾向于vim或者Sublime Text这种轻量的编辑器。还好发现了一个命令行环境下的调试工具pidcat。</p>
<p>pidcat其实就是一个python脚本,运行后可以很清晰在命令行下查看应用的log信息,不必依赖eclipse而且可以查看某一单个应用的log信息,运行前请先确保装了python,mac下是自带python的.</p>
<p>项目地址: <a href="https://github.com/stormzhang/pidcat">https://github.com/stormzhang/pidcat</a></p>
<p>运行:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">.</span><span class="n">/pidcat</span><span class="o">.</span><span class="n">py</span> <span class="n">com</span><span class="o">.</span><span class="n">boohee</span><span class="o">.</span><span class="n">one</span></code></pre></div>
<p>下面是我机器上运行的截图</p>
<p><img src="/image/pidcat.png" /></p>
ActiveAndroid--Android轻量级ORM框架
2013-12-20T00:00:00+00:00
/openandroid/android/sqlite/2013/12/20/android-orm-tools-activeandroid
<p>最近在寻找下Android开发中好用的ORM框架,发现了<a href="https://github.com/pardom/ActiveAndroid">ActiveAndroid</a>和<a href="http://ormlite.com/">ORMLite</a>, 相信懂Rails开发的一直都对ActiveRecord情有独钟,使用起来真是太方便了。ActiveAndroid听名字就知道就模仿ActiveRecord的一套框架,于是果断学习下。</p>
<h2 id="section">介绍</h2>
<p>ActiveAndroid算是一个轻量级的ORM框架,简单地通过如save()和delete()等方法来做到增删改查等操作。配置起来也还算简单。</p>
<p>下面是作者的原话:</p>
<pre><code>ActiveAndroid is an active record style ORM (object relational mapper). What does that mean exactly? Well, ActiveAndroid allows you to save and retrieve SQLite database records without ever writing a single SQL statement. Each database record is wrapped neatly into a class with methods like save() and delete().
ActiveAndroid does so much more than this though. Accessing the database is a hassle, to say the least, in Android. ActiveAndroid takes care of all the setup and messy stuff, and all with just a few simple steps of configuration.
</code></pre>
<h2 id="section-1">配置</h2>
<p>在AndroidManifest.xml中我们需要添加这两个</p>
<ul>
<li>
<p>AA_DB_NAME (这个name不能改,但是是可选的,如果不写的话 是默认的”Application.db”这个值)</p>
</li>
<li>
<p>AA_DB_VERSION (optional – defaults to 1)</p>
</li>
</ul>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">manifest</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="n">></span>
<span class="o"><</span><span class="n">application</span> <span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">"com.activeandroid.app.Application"</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="n">></span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="o"><</span><span class="n">meta</span><span class="o">-</span><span class="n">data</span> <span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">"AA_DB_NAME"</span> <span class="ss">android</span><span class="p">:</span><span class="n">value</span><span class="o">=</span><span class="s2">"your.db"</span> <span class="sr">/></span>
<span class="sr"> <meta-data android:name="AA_DB_VERSION" android:value="5" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/application></span>
<span class="sr"></m</span><span class="n">anifest</span><span class="o">></span></code></pre></div>
<p>这个application是必须指定的,但你也可以使用自己的Application,继承自com.activeandroid.app.Application</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyApplication</span> <span class="n">extends</span> <span class="n">com</span><span class="o">.</span><span class="n">activeandroid</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">Application</span> <span class="p">{</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span></code></pre></div>
<p>如果你不想或者不能继承com.activeandroid.app.Application的话,那么就这样</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyApplication</span> <span class="n">extends</span> <span class="no">SomeLibraryApplication</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">();</span>
<span class="no">ActiveAndroid</span><span class="o">.</span><span class="n">initialize</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onTerminate</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onTerminate</span><span class="p">();</span>
<span class="no">ActiveAndroid</span><span class="o">.</span><span class="n">dispose</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>ActiveAndroid.initialize(this);做初始化工作,ActiveAndroid.dispose();做清理工作</p>
<h2 id="section-2">创建数据库模型</h2>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">我们使用@Table(name = "Items")来表示表,使用@Column(name = "Name")来表示列,ActiveAndroid会使用自增长的ID作为主键,然后按照注解描述,将类对应映射为数据库表。</code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vi">@Table</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Items"</span><span class="p">)</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">Item</span> <span class="n">extends</span> <span class="no">Model</span> <span class="p">{</span>
<span class="vi">@Column</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Name"</span><span class="p">)</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="nb">name</span><span class="p">;</span>
<span class="vi">@Column</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Category"</span><span class="p">)</span>
<span class="kp">public</span> <span class="no">Category</span> <span class="n">category</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">Item</span><span class="p">(){</span>
<span class="k">super</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">Item</span><span class="p">(</span><span class="nb">String</span> <span class="nb">name</span><span class="p">,</span> <span class="no">Category</span> <span class="n">category</span><span class="p">){</span>
<span class="k">super</span><span class="p">();</span>
<span class="n">this</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">;</span>
<span class="n">this</span><span class="o">.</span><span class="n">category</span> <span class="o">=</span> <span class="n">category</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-3">依赖关系的数据库表</h2>
<p>假如Item和Category是多对一的关系,那么我们可以这样子创建他们的类</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vi">@Table</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Items"</span><span class="p">)</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">Item</span> <span class="n">extends</span> <span class="no">Model</span> <span class="p">{</span>
<span class="vi">@Column</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Name"</span><span class="p">)</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="nb">name</span><span class="p">;</span>
<span class="vi">@Column</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Category"</span><span class="p">)</span>
<span class="kp">public</span> <span class="no">Category</span> <span class="n">category</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vi">@Table</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Categories"</span><span class="p">)</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">Category</span> <span class="n">extends</span> <span class="no">Model</span> <span class="p">{</span>
<span class="vi">@Column</span><span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="s2">"Name"</span><span class="p">)</span>
<span class="kp">public</span> <span class="nb">String</span> <span class="nb">name</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">List</span><span class="o"><</span><span class="no">Item</span><span class="o">></span> <span class="n">items</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">getMany</span><span class="p">(</span><span class="no">Item</span><span class="o">.</span><span class="n">class</span><span class="p">,</span> <span class="s2">"Category"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-4">保存和更新数据到数据库</h2>
<h3 id="section-5">单条插入</h3>
<p>保存Category对象</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Category</span> <span class="n">restaurants</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Category</span><span class="p">();</span>
<span class="n">restaurants</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s2">"Restaurants"</span><span class="p">;</span>
<span class="n">restaurants</span><span class="o">.</span><span class="n">save</span><span class="p">();</span></code></pre></div>
<p>分配了一个category并且保存到数据库 Item item = new Item(); item.category = restaurants; item.name = “Outback Steakhouse”; item.save();</p>
<h3 id="section-6">批量插入</h3>
<p>如果你要批量插入数据,最好使用事务(transaction)。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ActiveAndroid</span><span class="o">.</span><span class="n">beginTransaction</span><span class="p">();</span>
<span class="n">try</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Item</span> <span class="n">item</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Item</span><span class="p">();</span>
<span class="n">item</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s2">"Example "</span> <span class="o">+</span> <span class="n">i</span><span class="p">;</span>
<span class="n">item</span><span class="o">.</span><span class="n">save</span><span class="p">();</span>
<span class="p">}</span>
<span class="no">ActiveAndroid</span><span class="o">.</span><span class="n">setTransactionSuccessful</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">finally</span> <span class="p">{</span>
<span class="no">ActiveAndroid</span><span class="o">.</span><span class="n">endTransaction</span><span class="p">();</span>
<span class="p">}</span></code></pre></div>
<p>使用事务的话只用了 40ms,不然的话需要4秒。</p>
<h3 id="section-7">删除记录</h3>
<p>我们有三种方式删除一条记录</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Item</span> <span class="n">item</span> <span class="o">=</span> <span class="no">Item</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="no">Item</span><span class="o">.</span><span class="n">class</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">item</span><span class="o">.</span><span class="n">delete</span><span class="p">();</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Item</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="no">Item</span><span class="o">.</span><span class="n">class</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">new</span> <span class="no">Delete</span><span class="p">()</span><span class="o">.</span><span class="n">from</span><span class="p">(</span><span class="no">Item</span><span class="o">.</span><span class="n">class</span><span class="p">)</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="s2">"Id = ?"</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">execute</span><span class="p">();</span></code></pre></div>
<p>很简单吧</p>
<h2 id="section-8">查询数据库</h2>
<p>作者将查询做的非常像SQLite的原生查询语句,几乎涵盖了所有的指令 com.activeandroid.query包下有以下类</p>
<ul>
<li>
<p>Delete</p>
</li>
<li>
<p>From</p>
</li>
<li>
<p>Join</p>
</li>
<li>
<p>Select</p>
</li>
<li>
<p>Set</p>
</li>
<li>
<p>Update</p>
</li>
</ul>
<p>我们举例说明吧</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="no">Item</span> <span class="n">getRandom</span><span class="p">(</span><span class="no">Category</span> <span class="n">category</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kp">new</span> <span class="no">Select</span><span class="p">()</span>
<span class="o">.</span><span class="n">from</span><span class="p">(</span><span class="no">Item</span><span class="o">.</span><span class="n">class</span><span class="p">)</span>
<span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="s2">"Category = ?"</span><span class="p">,</span> <span class="n">category</span><span class="o">.</span><span class="n">getId</span><span class="p">())</span>
<span class="o">.</span><span class="n">orderBy</span><span class="p">(</span><span class="s2">"RANDOM()"</span><span class="p">)</span>
<span class="o">.</span><span class="n">executeSingle</span><span class="p">();</span>
<span class="p">}</span></code></pre></div>
<p>对应的sqlite查询语句就是 select * from Item where Category = ? order by RANDOM(), 当然还支持其他非常多的指令</p>
<ul>
<li>
<p>limit</p>
</li>
<li>
<p>offset</p>
</li>
<li>
<p>as</p>
</li>
<li>
<p>desc/asc</p>
</li>
<li>
<p>inner/outer/cross join</p>
</li>
<li>
<p>group by</p>
</li>
<li>
<p>having 等等</p>
</li>
</ul>
<h2 id="section-9">数据库升级</h2>
<p>如果想给现有的表增加或删除column,这个时候只需要把sql脚本放在/assets/migrations文件夹下,然后需要:</p>
<ol>
<li>
<p>增加数据库版本,即增加配置文件下的application种的AA_DB_VERSION</p>
</li>
<li>
<p>在/assets/migrations文件夹下提供一个NewVersion.sql脚本</p>
</li>
</ol>
<p>ActiveAndroid将会自动执行大于database-version的sql脚本文件</p>
<p>假设需要给Items表增加一个“colour”列,那么需要把AA_DB_VERSION增加到2,并且在/assets/migrations目录下提供一个2.sql的脚本文件,内容如下</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ALTER</span> <span class="no">TABLE</span> <span class="no">Items</span> <span class="no">ADD</span> <span class="no">COLUMN</span> <span class="no">Color</span> <span class="no">INTEGER</span><span class="p">;</span></code></pre></div>
Android ActionBar Compact
2013-12-13T00:00:00+00:00
/android/2013/12/13/android-actionbar-compact
<h2 id="section">介绍</h2>
<p>自Action Bar设计概念在Android 3.0(API 11) 中被Google引入以后,在4.0版本之后更是被Google纳入设计规范中,从Google的各大App中都可以看到这种设计。</p>
<p>但Action Bar虽好,它出现之初Android官方版本的ActionBar 只支持Android 3.0 (API 11)及以后的系统版本。而由于Android众所周知的碎片化问题,当开发者试图在minSdkVersion小于11的系统上使用Action Bar时只好使用大名鼎鼎的JakeWharton发布的<a href="https://github.com/JakeWharton/ActionBarSherlock">ActionBarSherlock</a>。</p>
<p>还好,在Google I/O 2013后,官方版本的兼容 Android 2.1(API 7)及其以后版本的ActionBarCompat终于发布了 (包含在Support Library v7 r18中)。原本使用ActionBarSherlock的一众应用们也开始了升级至ActionBarCompat的工作。这篇博客就来分享下如何使用ActionBarCompat实现Action Bar。</p>
<!-- more -->
<p>首先确认了开发环境中Android SDK已经安装了Support Library r18或以上,目前最新的是19, 接下来,开始实际建立一个ActionBar的开发实例。实现一个含有Action Bar Icon, Title, Action Item 以及Action Overflow的ActionBar Hello World应用。效果如下</p>
<p><img src="http://bcs.duapp.com/mobiletuts//blog/201310//actionbar_sample.png" /></p>
<p>Eclipse+Android ADT环境下:</p>
<h2 id="create-a-blank-android-project">1. Create a blank Android Project</h2>
<p>创建一个空的Android项目,这里具体就不再赘述。</p>
<h2 id="support-v7-appcompat-library">2. 将Support V7 appcompat Library添加至工程</h2>
<p>a. 导入ActionBarCompat工程(这个工程是个Library)</p>
<pre><code>ActionBarCompat的source code位置是:<Android SDK目录>/extras/android/support/v7/appcompat
</code></pre>
<p>这样我们就得到一个名叫android-support-v7-appcompat 的library project</p>
<p>b. 接着在自己新建的project点击右键->选择Properties->选择Android选项</p>
<p><img src="http://bcs.duapp.com/mobiletuts//blog/201310//add_library_eclipse.png" /></p>
<p>点Add, 然后选择 android-support-v7-appcompat</p>
<p><img src="http://bcs.duapp.com/mobiletuts//blog/201310//android-support-v7-appcompat.png" /></p>
<p>点击OK 搞定。</p>
<h2 id="update-style-resources">3. Update Style Resources</h2>
<p>刚才说了ActionBarCompat在使用中会调用一些资源文件,尤其是基于Theme.AppCompat的主题(Theme)用来规范Action Bar的显示。如果使用Action Bar的Activity没有使用基于Theme.AppCompat的主题,程序就不知道该如何配置Action Bar的显示,就会报错导致程序退出。</p>
<p>在AndroidManifest中讲Application的 android:theme属性设置为Theme.AppCompat系列Theme。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">application</span>
<span class="ss">android</span><span class="p">:</span><span class="n">label</span><span class="o">=</span><span class="s2">"@string/app_name"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">icon</span><span class="o">=</span><span class="s2">"@drawable/ic_launcher"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">theme</span><span class="o">=</span><span class="s2">"@style/Theme.AppCompat.Light"</span><span class="o">></span></code></pre></div>
<p>如果你在使用自定义的Theme,则该Theme的parent应设置为Theme.AppCompat系列Theme.</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><!--</span> <span class="no">Application</span> <span class="n">theme</span><span class="o">.</span> <span class="o">--></span>
<span class="o"><</span><span class="n">style</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"AppTheme"</span> <span class="n">parent</span><span class="o">=</span><span class="s2">"@style/Theme.AppCompat.Light"</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"android:windowNoTitle"</span><span class="o">></span><span class="kp">true</span><span class="o"><</span><span class="sr">/item></span>
<span class="sr"> <item name="android:windowBackground">@color/</span><span class="n">global_main_bg</span><span class="o"><</span><span class="sr">/item></span>
<span class="sr"></s</span><span class="n">tyle</span><span class="o">></span></code></pre></div>
<h2 id="extend-actionbaractivity">4. Extend ActionBarActivity</h2>
<p>当要在Activity中使用ActionBar,并要求兼容Android 2.1~3.0之间的系统时,我们不能像往常那样extend Activity,而应extend ActionBarActivity(原因如上所属,Android 3.0以前的系统中Activity API里是没有ActionBar接口的 自然也就无法调用。为了向下兼容,必须使用ActionBarActivity)。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">os</span><span class="o">.</span><span class="n">Bundle</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">support</span><span class="o">.</span><span class="n">v7</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">ActionBar</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">support</span><span class="o">.</span><span class="n">v7</span><span class="o">.</span><span class="n">app</span><span class="o">.</span><span class="n">ActionBarActivity</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">Menu</span><span class="p">;</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">MainActivity</span> <span class="n">extends</span> <span class="no">ActionBarActivity</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">ActionBar</span> <span class="n">actionBar</span><span class="p">;</span>
<span class="vi">@Override</span>
<span class="kp">protected</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">activity_main</span><span class="p">);</span>
<span class="n">actionBar</span><span class="o">=</span><span class="n">getSupportActionBar</span><span class="p">();</span>
<span class="sr">//</span><span class="n">actionBar</span> <span class="n">operation</span>
<span class="n">actionBar</span><span class="o">.</span><span class="n">setTitle</span><span class="p">(</span><span class="s2">"ActionBar"</span><span class="p">);</span>
<span class="sr">//</span><span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="n">.</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onCreateOptionsMenu</span><span class="p">(</span><span class="no">Menu</span> <span class="n">menu</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">Inflate</span> <span class="n">the</span> <span class="n">menu</span><span class="p">;</span> <span class="n">this</span> <span class="n">adds</span> <span class="n">items</span> <span class="n">to</span> <span class="n">the</span> <span class="n">action</span> <span class="n">bar</span> <span class="k">if</span> <span class="n">it</span> <span class="n">is</span> <span class="n">present</span><span class="o">.</span>
<span class="n">getMenuInflater</span><span class="p">()</span><span class="o">.</span><span class="n">inflate</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">menu</span><span class="o">.</span><span class="n">main</span><span class="p">,</span> <span class="n">menu</span><span class="p">);</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="menumainxml-action-baraction-items">5. 修改menu/main.xml (也就是Action Bar中的Action Items)</h2>
<p>你的project中会有一个默认的main.xml,为了向Action Bar中添加几个功能按钮(也就是Action Items),我们需要对menu/main.xml进行些修改:</p>
<ul>
<li>
<p>在root element中添加一个attribute</p>
</li>
<li>
<p>添加新的item项</p>
</li>
</ul>
<p>如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">menu</span>
<span class="ss">xmlns</span><span class="p">:</span><span class="n">app</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res-auto"</span>
<span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/action_settings"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">title</span><span class="o">=</span><span class="s2">"@string/action_settings"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">orderInCategory</span><span class="o">=</span><span class="s2">"100"</span>
<span class="ss">app</span><span class="p">:</span><span class="n">showAsAction</span><span class="o">=</span><span class="s2">"always"</span> <span class="sr">/></span>
<span class="sr"> <item android:id="@+id/</span><span class="n">action_search</span><span class="s2">"</span>
<span class="s2"> android:title="</span><span class="vi">@string</span><span class="o">/</span><span class="n">action_search</span><span class="s2">"</span>
<span class="s2"> android:orderInCategory="</span><span class="mi">1</span><span class="s2">"</span>
<span class="s2"> android:icon="</span><span class="vi">@drawable</span><span class="o">/</span><span class="n">action_search</span><span class="s2">"</span>
<span class="s2"> app:showAsAction="</span><span class="n">always</span><span class="s2">" /></span>
<span class="s2"></menu></span></code></pre></div>
<p>Action Items广泛使用的一些icon,你可以从<a href="http://commondatastorage.googleapis.com/androiddevelopers/design/Android_Design_Icons_20130926.zip">Download the Action Bar Icon Pack</a>下载到。</p>
<p><img src="http://bcs.duapp.com/mobiletuts//blog/201310//action_bar_icons.png" /></p>
<p>最后,如果需要在程序中对ActionBar进行操作,可以通过getSupportActionBar()来实现。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">actionBar</span> <span class="o">=</span> <span class="n">getSupportActionBar</span><span class="p">();</span>
<span class="sr">//</span><span class="n">actionBar</span> <span class="n">operation</span>
<span class="n">actionBar</span><span class="o">.</span><span class="n">setTitle</span><span class="p">(</span><span class="s2">"ActionBar"</span><span class="p">);</span></code></pre></div>
Mac OS X "open Eclipse, you need a Java SE 6 runtime"
2013-12-09T00:00:00+00:00
/android/java/2013/12/09/osx-open-eclipse-you-need-a-java-se-6-runtime-problem
<p>今天把OS X升级到10.9 Mavericks,居然java环境出错了,于是趁这个机会顺便把jdk升级到1.7,下载安装jdk1.7一切搞定之后打开eclipse时竟然弹出提示:</p>
<pre><code>To open “Eclipse,” you need a Java SE 6 runtime. Would you like to install one now?
</code></pre>
<p>经过查找和实验,把解决方案记录分享在此。 </p>
<ol>
<li>修改Java安装目录的Info.plist文件:</li>
</ol>
<p>如,我的系统上是修改:/Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Info.plist 文件,将这部分</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">key</span><span class="o">></span><span class="no">JVMCapabilities</span><span class="o"><</span><span class="sr">/key></span>
<span class="sr"> <array></span>
<span class="sr"><string>CommandLine</s</span><span class="n">tring</span><span class="o">></span>
<span class="o"><</span><span class="sr">/array></span></code></pre></div>
<p>改为如下:(主要是添加了4行东东)</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">key</span><span class="o">></span><span class="no">JVMCapabilities</span><span class="o"><</span><span class="sr">/key></span>
<span class="sr"><array></span>
<span class="sr"> <string>JNI</s</span><span class="n">tring</span><span class="o">></span>
<span class="o"><</span><span class="n">string</span><span class="o">></span><span class="no">BundledApp</span><span class="o"><</span><span class="sr">/string></span>
<span class="sr"> <string>WebStart</s</span><span class="n">tring</span><span class="o">></span>
<span class="o"><</span><span class="n">string</span><span class="o">></span><span class="no">Applets</span><span class="o"><</span><span class="sr">/string></span>
<span class="sr"> <string>CommandLine</s</span><span class="n">tring</span><span class="o">></span>
<span class="o"><</span><span class="sr">/array></span></code></pre></div>
<p>修改后,重启系统,再打开Eclipse这样的软件就会正常启动了。</p>
一个强大的Android模拟器Genymotion
2013-12-04T00:00:00+00:00
/android/2013/12/04/android-genymotion
<p>相信很多Android开发者一定受够了速度慢、体验差效率及其地下的官方模拟器了,自己在平时的开发中几乎是不会用模拟器的,等的时间太久了,但是在一些尺寸适配或是兼容性测试的时候没有足够多的机器进行测试,这个时候就必须得用模拟器来代替了。用的久了真的不堪忍受那龟速般的模拟器,好在今天发现了一款神级模拟器Genymotion,就像发现新大陆般喜爱,下面就来介绍下。</p>
<!-- more -->
<h2 id="genymotion">Genymotion简介</h2>
<p>Genymotion是一套完整的工具,它提供了Android虚拟环境。如果你没有物理机器,又不想忍受官方模拟器的折磨,Genymotion会是你非常不错的选择,
它简直就是开发者、测试人员、推销者甚至是游戏玩家的福音。</p>
<p>Genymotion支持Windows、Linux和Mac OS,容易安装和使用,下面就然我们一起来体验神器给我们带来的快感吧。</p>
<h2 id="genymotion-1">Genymotion特性</h2>
<h4 id="android">最好的Android模拟体验</h4>
<ul>
<li>
<p>支持OpenGL加速,提供最好的3D性能体验</p>
</li>
<li>
<p>可以从Google Play安装应用</p>
</li>
<li>
<p>支持全屏并改善了使用感受</p>
</li>
</ul>
<h4 id="section">全控制</h4>
<ul>
<li>
<p>可同时启动多个模拟器</p>
</li>
<li>
<p>支持传感器管理,如电池状态、GPS、Accelerator加速器</p>
</li>
<li>
<p>支持Shell控制模拟器</p>
</li>
<li>
<p>完全兼容ADB,您可以从主机控制您的模拟器</p>
</li>
</ul>
<h4 id="section-1">管理设备</h4>
<ul>
<li>
<p>易安装</p>
</li>
<li>
<p>兼容Microsoft Windows 32/64 bits, Mac OSX 10.5+ and Linux 32/64 bits</p>
</li>
<li>
<p>可以配置模拟器参数,如屏幕分辨率、内存大小、CPU数量</p>
</li>
<li>
<p>轻松下载、部署最新的Genymotion虚拟设备。</p>
</li>
</ul>
<h4 id="eclipse">从Eclipse启动虚拟设备</h4>
<ul>
<li>使用Genymotion测试您的应用</li>
</ul>
<h2 id="section-2">安装与配置</h2>
<p>安装基本是一路next,虽然Genymotion是免费版的,但是要求注册个账号才可以配置模拟器,配置好启动真是神速啊。运行效果:</p>
<p><img src="http://www.genymotion.cn/images/guide/virtual_device_home.jpg" /></p>
<h2 id="eclipse-1">Eclipse插件安装</h2>
<p>Genymotion还支持Eclipse IDE,这大大方便了我们使用Genymotion来开发应用。安装方式:</p>
<ul>
<li>
<p>启动Eclipse,Help->Install New Software…->Add</p>
</li>
<li>
<p>填写一下信息:</p>
</li>
</ul>
<p>Name: Genymobile</p>
<p>Location: http://plugins.genymotion.com/eclipse</p>
<ul>
<li>接下来跟安装adt的流程一样,指导完成</li>
</ul>
<p>最后附上官网地址:http://www.genymotion.com/</p>
Android-Universal-Image-Loader
2013-12-01T00:00:00+00:00
/android/openandroid/2013/12/01/android-universal-image-loader
<p>之前项目中用到的图片异步加载库是<a href="https://github.com/thest1/LazyList">LazyList</a>。随着需求的不算增长,需求有点不太能满足,缺少可配置的选项,于是换到了强大的<a href="https://github.com/nostra13/Android-Universal-Image-Loader">UniversalImageLoader</a>。</p>
<h2 id="section">一、介绍</h2>
<p>Android-Universal-Image-Loader是一个开源的图片异步加载库,该项目的目的是提供一个可重复使用的仪器为异步图像加载,缓存和显示。该库非常强大,国内外很多有名的应用程序都有使用,在该类库的默认缓存文件夹中甚至发现了google, instagram, qq, baidu都有在用。</p>
<!-- more -->
<h2 id="section-1">二、特点</h2>
<ul>
<li>
<p>多线程的图像加载</p>
</li>
<li>
<p>尽可能多的配置选项(线程池,加载器,解析器,内存/磁盘缓存,显示参数等等)</p>
</li>
<li>
<p>图片可以缓存在内存中,或者设备文件目录下,或者SD卡中</p>
</li>
<li>
<p>可以添加图片加载监听器</p>
</li>
<li>
<p>可以自定义显示每一张图片时都带不同参数</p>
</li>
<li>
<p>支持Widget</p>
</li>
<li>
<p>Android 1.5以上支持</p>
</li>
</ul>
<h2 id="section-2">三、使用方法</h2>
<h4 id="android-manifest">1. Android Manifest</h4>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="nt"><manifest></span>
<span class="nt"><uses-permission</span> <span class="na">android:name=</span><span class="s">"android.permission.INTERNET"</span> <span class="nt">/></span>
<span class="c"><!-- Include next permission if you want to allow UIL to cache images on SD card --></span>
<span class="nt"><uses-permission</span> <span class="na">android:name=</span><span class="s">"android.permission.WRITE_EXTERNAL_STORAGE"</span> <span class="nt">/></span>
...
<span class="nt"><application</span> <span class="na">android:name=</span><span class="s">"MyApplication"</span><span class="nt">></span>
...
<span class="nt"></application></span>
<span class="nt"></manifest></span></code></pre></div>
<h4 id="application-class">2. Application class</h4>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyApplication</span> <span class="n">extends</span> <span class="no">Application</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">();</span>
<span class="sr">//</span> <span class="no">Create</span> <span class="n">global</span> <span class="n">configuration</span> <span class="ow">and</span> <span class="kp">initialize</span> <span class="no">ImageLoader</span> <span class="n">with</span> <span class="n">this</span> <span class="n">configuration</span>
<span class="no">ImageLoaderConfiguration</span> <span class="n">config</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">ImageLoaderConfiguration</span><span class="o">.</span><span class="n">Builder</span><span class="p">(</span><span class="n">getApplicationContext</span><span class="p">())</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="o">.</span><span class="n">build</span><span class="p">();</span>
<span class="no">ImageLoader</span><span class="o">.</span><span class="n">getInstance</span><span class="p">()</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">config</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h5 id="configuration">Configuration</h5>
<p>所有的选项都是可选的,只选择你真正想制定的去配置。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">File</span> <span class="n">cacheDir</span> <span class="o">=</span> <span class="no">StorageUtils</span><span class="o">.</span><span class="n">getCacheDirectory</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
<span class="no">ImageLoaderConfiguration</span> <span class="n">config</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">ImageLoaderConfiguration</span><span class="o">.</span><span class="n">Builder</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
<span class="sr">//</span><span class="err">如果图片尺寸大于了这个参数,那么就会这按照这个参数对图片大小进行限制并缓存</span>
<span class="o">.</span><span class="n">memoryCacheExtraOptions</span><span class="p">(</span><span class="mi">480</span><span class="p">,</span> <span class="mi">800</span><span class="p">)</span> <span class="sr">//</span> <span class="n">default</span><span class="o">=</span><span class="n">device</span> <span class="n">screen</span> <span class="n">dimensions</span>
<span class="o">.</span><span class="n">discCacheExtraOptions</span><span class="p">(</span><span class="mi">480</span><span class="p">,</span> <span class="mi">800</span><span class="p">,</span> <span class="no">CompressFormat</span><span class="o">.</span><span class="n">JPEG</span><span class="p">,</span> <span class="mi">75</span><span class="p">)</span>
<span class="o">.</span><span class="n">taskExecutor</span><span class="p">(</span><span class="no">AsyncTask</span><span class="o">.</span><span class="n">THREAD_POOL_EXECUTOR</span><span class="p">)</span>
<span class="o">.</span><span class="n">taskExecutorForCachedImages</span><span class="p">(</span><span class="no">AsyncTask</span><span class="o">.</span><span class="n">THREAD_POOL_EXECUTOR</span><span class="p">)</span>
<span class="o">.</span><span class="n">threadPoolSize</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">threadPriority</span><span class="p">(</span><span class="no">Thread</span><span class="o">.</span><span class="n">NORM_PRIORITY</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">tasksProcessingOrder</span><span class="p">(</span><span class="no">QueueProcessingType</span><span class="o">.</span><span class="n">FIFO</span><span class="p">)</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">denyCacheImageMultipleSizesInMemory</span><span class="p">()</span>
<span class="o">.</span><span class="n">memoryCache</span><span class="p">(</span><span class="kp">new</span> <span class="no">LruMemoryCache</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">))</span>
<span class="o">.</span><span class="n">memoryCacheSize</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">)</span>
<span class="o">.</span><span class="n">discCache</span><span class="p">(</span><span class="kp">new</span> <span class="no">UnlimitedDiscCache</span><span class="p">(</span><span class="n">cacheDir</span><span class="p">))</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">discCacheSize</span><span class="p">(</span><span class="mi">50</span> <span class="o">*</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">1024</span><span class="p">)</span>
<span class="o">.</span><span class="n">discCacheFileCount</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="o">.</span><span class="n">discCacheFileNameGenerator</span><span class="p">(</span><span class="kp">new</span> <span class="no">HashCodeFileNameGenerator</span><span class="p">())</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">imageDownloader</span><span class="p">(</span><span class="kp">new</span> <span class="no">BaseImageDownloader</span><span class="p">(</span><span class="n">context</span><span class="p">))</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">imageDecoder</span><span class="p">(</span><span class="kp">new</span> <span class="no">BaseImageDecoder</span><span class="p">())</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">defaultDisplayImageOptions</span><span class="p">(</span><span class="no">DisplayImageOptions</span><span class="o">.</span><span class="n">createSimple</span><span class="p">())</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">enableLogging</span><span class="p">()</span>
<span class="o">.</span><span class="n">build</span><span class="p">();</span></code></pre></div>
<h5 id="display-options">Display Options</h5>
<p>显示参数可以分别被每一个显示任务调用(ImageLoader.displayImage(…))</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">DisplayImageOptions</span> <span class="n">options</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">DisplayImageOptions</span><span class="o">.</span><span class="n">Builder</span><span class="p">()</span>
<span class="o">.</span><span class="n">showStubImage</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">drawable</span><span class="o">.</span><span class="n">ic_stub</span><span class="p">)</span> <span class="sr">//</span> <span class="err">在显示真正的图片前,会加载这个资源</span>
<span class="o">.</span><span class="n">showImageForEmptyUri</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">drawable</span><span class="o">.</span><span class="n">ic_empty</span><span class="p">)</span> <span class="sr">//</span><span class="err">空的</span><span class="no">Url</span><span class="err">时</span>
<span class="o">.</span><span class="n">showImageOnFail</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">drawable</span><span class="o">.</span><span class="n">ic_error</span><span class="p">)</span>
<span class="o">.</span><span class="n">resetViewBeforeLoading</span><span class="p">()</span> <span class="sr">//</span>
<span class="o">.</span><span class="n">delayBeforeLoading</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span> <span class="sr">//</span> <span class="err">延长</span><span class="mi">1000</span><span class="n">ms</span> <span class="err">加载图片</span> <span class="err">(想不出来用在什么场景下)</span>
<span class="o">.</span><span class="n">cacheInMemory</span><span class="p">()</span>
<span class="o">.</span><span class="n">cacheOnDisc</span><span class="p">()</span>
<span class="o">.</span><span class="n">preProcessor</span><span class="p">(</span><span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">)</span>
<span class="o">.</span><span class="n">postProcessor</span><span class="p">(</span><span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">)</span>
<span class="o">.</span><span class="n">extraForDownloader</span><span class="p">(</span><span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">)</span> <span class="sr">//</span><span class="err">可以向加载器携带一些参数</span>
<span class="o">.</span><span class="n">imageScaleType</span><span class="p">(</span><span class="no">ImageScaleType</span><span class="o">.</span><span class="n">IN_SAMPLE_POWER_OF_2</span><span class="p">)</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">bitmapConfig</span><span class="p">(</span><span class="no">Bitmap</span><span class="o">.</span><span class="n">Config</span><span class="o">.</span><span class="n">ARGB_8888</span><span class="p">)</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">decodingOptions</span><span class="p">(</span><span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">)</span>
<span class="o">.</span><span class="n">displayer</span><span class="p">(</span><span class="kp">new</span> <span class="no">SimpleBitmapDisplayer</span><span class="p">())</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">handler</span><span class="p">(</span><span class="kp">new</span> <span class="no">Handler</span><span class="p">())</span> <span class="sr">//</span> <span class="n">default</span>
<span class="o">.</span><span class="n">build</span><span class="p">();</span></code></pre></div>
<h5 id="url">可接收的URL</h5>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">String</span> <span class="n">imageUri</span> <span class="o">=</span> <span class="s2">"http://site.com/image.png"</span><span class="p">;</span> <span class="sr">//</span> <span class="n">from</span> <span class="no">Web</span>
<span class="nb">String</span> <span class="n">imageUri</span> <span class="o">=</span> <span class="s2">"file:///mnt/sdcard/image.png"</span><span class="p">;</span> <span class="sr">//</span> <span class="n">from</span> <span class="no">SD</span> <span class="n">card</span>
<span class="nb">String</span> <span class="n">imageUri</span> <span class="o">=</span> <span class="s2">"content://media/external/audio/albumart/13"</span><span class="p">;</span> <span class="sr">//</span> <span class="n">from</span> <span class="n">content</span> <span class="n">provider</span>
<span class="nb">String</span> <span class="n">imageUri</span> <span class="o">=</span> <span class="s2">"assets://image.png"</span><span class="p">;</span> <span class="sr">//</span> <span class="n">from</span> <span class="n">assets</span>
<span class="nb">String</span> <span class="n">imageUri</span> <span class="o">=</span> <span class="s2">"drawable://"</span> <span class="o">+</span> <span class="n">R</span><span class="o">.</span><span class="n">drawable</span><span class="o">.</span><span class="n">image</span><span class="p">;</span> <span class="sr">//</span> <span class="n">from</span> <span class="n">drawables</span> <span class="p">(</span><span class="n">only</span> <span class="n">images</span><span class="p">,</span> <span class="n">non</span><span class="o">-</span><span class="mi">9</span><span class="n">patch</span><span class="p">)</span></code></pre></div>
<h5 id="section-3">完整版</h5>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="no">Load</span> <span class="n">image</span><span class="p">,</span> <span class="n">decode</span> <span class="n">it</span> <span class="n">to</span> <span class="no">Bitmap</span> <span class="ow">and</span> <span class="nb">display</span> <span class="no">Bitmap</span> <span class="k">in</span> <span class="no">ImageView</span>
<span class="n">imageLoader</span><span class="o">.</span><span class="n">displayImage</span><span class="p">(</span><span class="n">imageUri</span><span class="p">,</span> <span class="n">imageView</span><span class="p">,</span> <span class="n">displayOptions</span><span class="p">,</span>
<span class="kp">new</span> <span class="no">ImageLoadingListener</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onLoadingStarted</span><span class="p">(</span><span class="nb">String</span> <span class="n">imageUri</span><span class="p">,</span> <span class="no">View</span> <span class="n">view</span><span class="p">)</span> <span class="p">{</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onLoadingFailed</span><span class="p">(</span><span class="nb">String</span> <span class="n">imageUri</span><span class="p">,</span> <span class="no">View</span> <span class="n">view</span><span class="p">,</span> <span class="no">FailReason</span> <span class="n">failReason</span><span class="p">)</span> <span class="p">{</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onLoadingComplete</span><span class="p">(</span><span class="nb">String</span> <span class="n">imageUri</span><span class="p">,</span> <span class="no">View</span> <span class="n">view</span><span class="p">,</span> <span class="no">Bitmap</span> <span class="n">loadedImage</span><span class="p">)</span> <span class="p">{</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onLoadingCancelled</span><span class="p">(</span><span class="nb">String</span> <span class="n">imageUri</span><span class="p">,</span> <span class="no">View</span> <span class="n">view</span><span class="p">)</span> <span class="p">{</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="no">ImageSize</span> <span class="n">targetSize</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">ImageSize</span><span class="p">(</span><span class="mi">120</span><span class="p">,</span> <span class="mi">80</span><span class="p">);</span> <span class="sr">//</span> <span class="n">result</span> <span class="no">Bitmap</span> <span class="n">will</span> <span class="n">be</span> <span class="n">fit</span> <span class="n">to</span> <span class="n">this</span> <span class="n">size</span>
<span class="n">imageLoader</span><span class="o">.</span><span class="n">loadImage</span><span class="p">(</span><span class="n">imageUri</span><span class="p">,</span> <span class="n">targetSize</span><span class="p">,</span> <span class="n">displayOptions</span><span class="p">,</span> <span class="kp">new</span> <span class="no">SimpleImageLoadingListener</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onLoadingComplete</span><span class="p">(</span><span class="nb">String</span> <span class="n">imageUri</span><span class="p">,</span> <span class="no">View</span> <span class="n">view</span><span class="p">,</span> <span class="no">Bitmap</span> <span class="n">loadedImage</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">Do</span> <span class="n">whatever</span> <span class="n">you</span> <span class="n">want</span> <span class="n">with</span> <span class="no">Bitmap</span>
<span class="p">}</span>
<span class="p">});</span></code></pre></div>
Android高效加载图片,有效避免程序OOM
2013-11-20T00:00:00+00:00
/android/2013/11/20/android-display-bitmaps-efficiently
<p>我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状、不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小。比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨率高得多。大家应该知道,我们编写的应用程序都是有一定内存限制的,程序占用了过高的内存就容易出现OOM(OutOfMemory)异常。我们可以通过下面的代码看出每个应用程序最高可用内存是多少。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">int</span> <span class="n">maxMemory</span> <span class="o">=</span> <span class="p">(</span><span class="n">int</span><span class="p">)</span> <span class="p">(</span><span class="no">Runtime</span><span class="o">.</span><span class="n">getRuntime</span><span class="p">()</span><span class="o">.</span><span class="n">maxMemory</span><span class="p">()</span> <span class="o">/</span> <span class="mi">1024</span><span class="p">);</span>
<span class="no">Log</span><span class="o">.</span><span class="n">d</span><span class="p">(</span><span class="s2">"TAG"</span><span class="p">,</span> <span class="s2">"Max memory is "</span> <span class="o">+</span> <span class="n">maxMemory</span> <span class="o">+</span> <span class="s2">"KB"</span><span class="p">);</span></code></pre></div>
<p>因此在展示高分辨率图片的时候,最好先将图片进行压缩。压缩后的图片大小应该和用来展示它的控件大小相近,在一个很小的ImageView上显示一张超大的图片不会带来任何视觉上的好处,但却会占用我们相当多宝贵的内存,而且在性能上还可能会带来负面影响。下面我们就来看一看,如何对一张大图片进行适当的压缩,让它能够以最佳大小显示的同时,还能防止OOM的出现。</p>
<!-- more -->
<p>BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。比如SD卡中的图片可以使用decodeFile方法,网络上的图片可以使用decodeStream方法,资源文件中的图片可以使用decodeResource方法。这些方法会尝试为已经构建的bitmap分配内存,这时就会很容易导致OOM出现。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。如下代码所示:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">BitmapFactory</span><span class="o">.</span><span class="n">Options</span> <span class="n">options</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">BitmapFactory</span><span class="o">.</span><span class="n">Options</span><span class="p">();</span>
<span class="n">options</span><span class="o">.</span><span class="n">inJustDecodeBounds</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="no">BitmapFactory</span><span class="o">.</span><span class="n">decodeResource</span><span class="p">(</span><span class="n">getResources</span><span class="p">(),</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">myimage</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>
<span class="n">int</span> <span class="n">imageHeight</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">outHeight</span><span class="p">;</span>
<span class="n">int</span> <span class="n">imageWidth</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">outWidth</span><span class="p">;</span>
<span class="nb">String</span> <span class="n">imageType</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">outMimeType</span><span class="p">;</span></code></pre></div>
<p>为了避免OOM异常,最好在解析每张图片的时候都先检查一下图片的大小,除非你非常信任图片的来源,保证这些图片都不会超出你程序的可用内存。现在图片的大小已经知道了,我们就可以决定是把整张图片加载到内存中还是加载一个压缩版的图片到内存中。以下几个因素是我们需要考虑的:</p>
<ul>
<li>
<p>预估一下加载整张图片所需占用的内存</p>
</li>
<li>
<p>为了加载这一张图片你所愿意提供多少内存</p>
</li>
<li>
<p>用于展示这张图片的控件的实际大小</p>
</li>
<li>
<p>当前设备的屏幕尺寸和分辨率</p>
</li>
</ul>
<p>
比如,你的ImageView只有128*96像素的大小,只是为了显示一张缩略图,这时候把一张1024*768像素的图片完全加载到内存中显然是不值得的。那我们怎样才能对图片进行压缩呢?通过设置BitmapFactory.Options中inSampleSize的值就可以实现。比如我们有一张2048*1536像素的图片,将inSampleSize的值设置为4,就可以把这张图片压缩成512*384像素。原本加载这张图片需要占用13M的内存,压缩后就只需要占用0.75M了(假设图片是ARGB_8888类型,即每个像素点占用4个字节)。下面的方法可以根据传入的宽和高,计算出合适的inSampleSize值:
</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="n">int</span> <span class="n">calculateInSampleSize</span><span class="p">(</span><span class="no">BitmapFactory</span><span class="o">.</span><span class="n">Options</span> <span class="n">options</span><span class="p">,</span>
<span class="n">int</span> <span class="n">reqWidth</span><span class="p">,</span> <span class="n">int</span> <span class="n">reqHeight</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">源图片的高度和宽度</span>
<span class="n">final</span> <span class="n">int</span> <span class="n">height</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">outHeight</span><span class="p">;</span>
<span class="n">final</span> <span class="n">int</span> <span class="n">width</span> <span class="o">=</span> <span class="n">options</span><span class="o">.</span><span class="n">outWidth</span><span class="p">;</span>
<span class="n">int</span> <span class="n">inSampleSize</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">height</span> <span class="o">></span> <span class="n">reqHeight</span> <span class="o">||</span> <span class="n">width</span> <span class="o">></span> <span class="n">reqWidth</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">计算出实际宽高和目标宽高的比率</span>
<span class="n">final</span> <span class="n">int</span> <span class="n">heightRatio</span> <span class="o">=</span> <span class="no">Math</span><span class="o">.</span><span class="n">round</span><span class="p">((</span><span class="n">float</span><span class="p">)</span> <span class="n">height</span> <span class="o">/</span> <span class="p">(</span><span class="n">float</span><span class="p">)</span> <span class="n">reqHeight</span><span class="p">);</span>
<span class="n">final</span> <span class="n">int</span> <span class="n">widthRatio</span> <span class="o">=</span> <span class="no">Math</span><span class="o">.</span><span class="n">round</span><span class="p">((</span><span class="n">float</span><span class="p">)</span> <span class="n">width</span> <span class="o">/</span> <span class="p">(</span><span class="n">float</span><span class="p">)</span> <span class="n">reqWidth</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">选择宽和高中最小的比率作为</span><span class="n">inSampleSize</span><span class="err">的值,这样可以保证最终图片的宽和高</span>
<span class="sr">//</span> <span class="err">一定都会大于等于目标的宽和高。</span>
<span class="n">inSampleSize</span> <span class="o">=</span> <span class="n">heightRatio</span> <span class="o"><</span> <span class="n">widthRatio</span> <span class="p">?</span> <span class="n">heightRatio</span> <span class="p">:</span> <span class="n">widthRatio</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">inSampleSize</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>使用这个方法,首先你要将BitmapFactory.Options的inJustDecodeBounds属性设置为true,解析一次图片。然后将BitmapFactory.Options连同期望的宽度和高度一起传递到到calculateInSampleSize方法中,就可以得到合适的inSampleSize值了。之后再解析一次图片,使用新获取到的inSampleSize值,并把inJustDecodeBounds设置为false,就可以得到压缩后的图片了。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="no">Bitmap</span> <span class="n">decodeSampledBitmapFromResource</span><span class="p">(</span><span class="no">Resources</span> <span class="n">res</span><span class="p">,</span> <span class="n">int</span> <span class="n">resId</span><span class="p">,</span>
<span class="n">int</span> <span class="n">reqWidth</span><span class="p">,</span> <span class="n">int</span> <span class="n">reqHeight</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">第一次解析将</span><span class="n">inJustDecodeBounds</span><span class="err">设置为</span><span class="kp">true</span><span class="err">,来获取图片大小</span>
<span class="n">final</span> <span class="no">BitmapFactory</span><span class="o">.</span><span class="n">Options</span> <span class="n">options</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">BitmapFactory</span><span class="o">.</span><span class="n">Options</span><span class="p">();</span>
<span class="n">options</span><span class="o">.</span><span class="n">inJustDecodeBounds</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="no">BitmapFactory</span><span class="o">.</span><span class="n">decodeResource</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="n">resId</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">调用上面定义的方法计算</span><span class="n">inSampleSize</span><span class="err">值</span>
<span class="n">options</span><span class="o">.</span><span class="n">inSampleSize</span> <span class="o">=</span> <span class="n">calculateInSampleSize</span><span class="p">(</span><span class="n">options</span><span class="p">,</span> <span class="n">reqWidth</span><span class="p">,</span> <span class="n">reqHeight</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">使用获取到的</span><span class="n">inSampleSize</span><span class="err">值再次解析图片</span>
<span class="n">options</span><span class="o">.</span><span class="n">inJustDecodeBounds</span> <span class="o">=</span> <span class="kp">false</span><span class="p">;</span>
<span class="k">return</span> <span class="no">BitmapFactory</span><span class="o">.</span><span class="n">decodeResource</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="n">resId</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>
下面的代码非常简单地将任意一张图片压缩成100*100的缩略图,并在ImageView上展示。
</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">mImageView</span><span class="o">.</span><span class="n">setImageBitmap</span><span class="p">(</span>
<span class="n">decodeSampledBitmapFromResource</span><span class="p">(</span><span class="n">getResources</span><span class="p">(),</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">myimage</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">));</span></code></pre></div>
<p>使用图片缓存技术在你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来。在很多情况下,(比如使用ListView, GridView 或者 ViewPager 这样的组件),屏幕上显示的图片可以通过滑动屏幕等事件不断地增加,最终导致OOM。为了保证内存的使用始终维持在一个合理的范围,通常会把被移除屏幕的图片进行回收处理。此时垃圾回收器也会认为你不再持有这些图片的引用,从而对这些图片进行GC操作。用这种思路来解决问题是非常好的,可是为了能让程序快速运行,在界面上迅速地加载图片,你又必须要考虑到某些图片被回收之后,用户又将它重新滑入屏幕这种情况。这时重新去加载一遍刚刚加载过的图片无疑是性能的瓶颈,你需要想办法去避免这个情况的发生。这个时候,使用内存缓存技术可以很好的解决这个问题,它可以让组件快速地重新加载和处理图片。下面我们就来看一看如何使用内存缓存技术来对图片进行缓存,从而让你的应用程序在加载很多图片的时候可以提高响应速度和流畅性。内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。其中最核心的类是LruCache (此类在android-support-v4的包中提供) 。这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。</p>
<p>在过去,我们经常会使用一种非常流行的内存缓存技术的实现,即软引用或弱引用 (SoftReference or WeakReference)。但是现在已经不再推荐使用这种方式了,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃。</p>
<p>为了能够选择一个合适的缓存大小给LruCache, 有以下多个因素应该放入考虑范围内,例如:</p>
<ul>
<li>
<p>你的设备可以为每个应用程序分配多大的内存?</p>
</li>
<li>
<p>设备屏幕上一次最多能显示多少张图片?有多少图片需要进行预加载,因为有可能很快也会显示在屏幕上?</p>
</li>
<li>
<p>你的设备的屏幕大小和分辨率分别是多少?一个超高分辨率的设备(例如 Galaxy Nexus) 比起一个较低分辨率的设备(例如 Nexus S),在持有相同数量图片的时候,需要更大的缓存空间。</p>
</li>
<li>
<p>图片的尺寸和大小,还有每张图片会占据多少内存空间。</p>
</li>
<li>
<p>图片被访问的频率有多高?会不会有一些图片的访问频率比其它图片要高?如果有的话,你也许应该让一些图片常驻在内存当中,或者使用多个LruCache 对象来区分不同组的图片。</p>
</li>
<li>
<p>你能维持好数量和质量之间的平衡吗?有些时候,存储多个低像素的图片,而在后台去开线程加载高像素的图片会更加的有效。</p>
</li>
</ul>
<p>并没有一个指定的缓存大小可以满足所有的应用程序,这是由你决定的。你应该去分析程序内存的使用情况,然后制定出一个合适的解决方案。一个太小的缓存空间,有可能造成图片频繁地被释放和重新加载,这并没有好处。而一个太大的缓存空间,则有可能还是会引起 java.lang.OutOfMemory 的异常。
下面是一个使用 LruCache 来缓存图片的例子:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">private</span> <span class="no">LruCache</span><span class="o"><</span><span class="nb">String</span><span class="p">,</span> <span class="no">Bitmap</span><span class="o">></span> <span class="n">mMemoryCache</span><span class="p">;</span>
<span class="vi">@Override</span>
<span class="kp">protected</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">获取到可用内存的最大值,使用内存超出这个值会引起</span><span class="no">OutOfMemory</span><span class="err">异常。</span>
<span class="sr">//</span> <span class="no">LruCache</span><span class="err">通过构造函数传入缓存值,以</span><span class="no">KB</span><span class="err">为单位。</span>
<span class="n">int</span> <span class="n">maxMemory</span> <span class="o">=</span> <span class="p">(</span><span class="n">int</span><span class="p">)</span> <span class="p">(</span><span class="no">Runtime</span><span class="o">.</span><span class="n">getRuntime</span><span class="p">()</span><span class="o">.</span><span class="n">maxMemory</span><span class="p">()</span> <span class="o">/</span> <span class="mi">1024</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">使用最大可用内存值的</span><span class="mi">1</span><span class="o">/</span><span class="mi">8</span><span class="err">作为缓存的大小。</span>
<span class="n">int</span> <span class="n">cacheSize</span> <span class="o">=</span> <span class="n">maxMemory</span> <span class="o">/</span> <span class="mi">8</span><span class="p">;</span>
<span class="n">mMemoryCache</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">LruCache</span><span class="o"><</span><span class="nb">String</span><span class="p">,</span> <span class="no">Bitmap</span><span class="o">></span><span class="p">(</span><span class="n">cacheSize</span><span class="p">)</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">protected</span> <span class="n">int</span> <span class="n">sizeOf</span><span class="p">(</span><span class="nb">String</span> <span class="n">key</span><span class="p">,</span> <span class="no">Bitmap</span> <span class="n">bitmap</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">重写此方法来衡量每张图片的大小,默认返回图片数量。</span>
<span class="k">return</span> <span class="n">bitmap</span><span class="o">.</span><span class="n">getByteCount</span><span class="p">()</span> <span class="o">/</span> <span class="mi">1024</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">addBitmapToMemoryCache</span><span class="p">(</span><span class="nb">String</span> <span class="n">key</span><span class="p">,</span> <span class="no">Bitmap</span> <span class="n">bitmap</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">getBitmapFromMemCache</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mMemoryCache</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">bitmap</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">Bitmap</span> <span class="n">getBitmapFromMemCache</span><span class="p">(</span><span class="nb">String</span> <span class="n">key</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">mMemoryCache</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>
在这个例子当中,使用了系统分配给应用程序的八分之一内存来作为缓存大小。在中高配置的手机当中,这大概会有4兆(32/8)的缓存空间。一个全屏幕的 GridView 使用4张 800x480分辨率的图片来填充,则大概会占用1.5兆的空间(800*480*4)。因此,这个缓存大小可以存储2.5页的图片。
当向 ImageView 中加载一张图片时,首先会在 LruCache 的缓存中进行检查。如果找到了相应的键值,则会立刻更新ImageView ,否则开启一个后台线程来加载这张图片。
</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">void</span> <span class="n">loadBitmap</span><span class="p">(</span><span class="n">int</span> <span class="n">resId</span><span class="p">,</span> <span class="no">ImageView</span> <span class="n">imageView</span><span class="p">)</span> <span class="p">{</span>
<span class="n">final</span> <span class="nb">String</span> <span class="n">imageKey</span> <span class="o">=</span> <span class="nb">String</span><span class="o">.</span><span class="n">valueOf</span><span class="p">(</span><span class="n">resId</span><span class="p">);</span>
<span class="n">final</span> <span class="no">Bitmap</span> <span class="n">bitmap</span> <span class="o">=</span> <span class="n">getBitmapFromMemCache</span><span class="p">(</span><span class="n">imageKey</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">bitmap</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">imageView</span><span class="o">.</span><span class="n">setImageBitmap</span><span class="p">(</span><span class="n">bitmap</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">imageView</span><span class="o">.</span><span class="n">setImageResource</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">drawable</span><span class="o">.</span><span class="n">image_placeholder</span><span class="p">);</span>
<span class="no">BitmapWorkerTask</span> <span class="n">task</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">BitmapWorkerTask</span><span class="p">(</span><span class="n">imageView</span><span class="p">);</span>
<span class="n">task</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">resId</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>BitmapWorkerTask 还要把新加载的图片的键值对放到缓存中。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">BitmapWorkerTask</span> <span class="n">extends</span> <span class="no">AsyncTask</span><span class="o"><</span><span class="nb">Integer</span><span class="p">,</span> <span class="no">Void</span><span class="p">,</span> <span class="no">Bitmap</span><span class="o">></span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">在后台加载图片。</span>
<span class="vi">@Override</span>
<span class="kp">protected</span> <span class="no">Bitmap</span> <span class="n">doInBackground</span><span class="p">(</span><span class="nb">Integer</span><span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="n">params</span><span class="p">)</span> <span class="p">{</span>
<span class="n">final</span> <span class="no">Bitmap</span> <span class="n">bitmap</span> <span class="o">=</span> <span class="n">decodeSampledBitmapFromResource</span><span class="p">(</span>
<span class="n">getResources</span><span class="p">(),</span> <span class="n">params</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
<span class="n">addBitmapToMemoryCache</span><span class="p">(</span><span class="nb">String</span><span class="o">.</span><span class="n">valueOf</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">),</span> <span class="n">bitmap</span><span class="p">);</span>
<span class="k">return</span> <span class="n">bitmap</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>掌握了以上两种方法,不管是要在程序中加载超大图片,还是要加载大量图片,都不用担心OOM的问题了!</p>
Android Custom Loading
2013-11-15T00:00:00+00:00
/openandroid/2013/11/15/android-custom-loading
<p>Android开发中我们经常会用到各种各样的loading,于是自己总结了常用的loading并分享出来。首先来看下具体效果图:</p>
<p>完整源码参见:<a href="https://github.com/stormzhang/CustomLoading">stormzhang / CustomLoading</a></p>
<p><img src="https://raw.github.com/stormzhang/CustomLoading/master/snap.jpg" /></p>
<!-- more -->
<p>下面主要说下代码的关键部分:</p>
<h2 id="frame-loading">1. Frame Loading</h2>
<p>第一个就是在app中常见的loading效果,主要是用帧动画实现的,所谓帧动画就是一组组图片顺序播放;
下面直接看下代码实现:</p>
<p>首先在drawable文件夹下建立一个xml文件,内容如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">frame_loading</span><span class="o">.</span><span class="n">xml</span>
<span class="o"><</span><span class="p">?</span><span class="n">xml</span> <span class="n">version</span><span class="o">=</span><span class="s2">"1.0"</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"UTF-8"</span><span class="sc">?></span>
<span class="o"><</span><span class="n">animation</span><span class="o">-</span><span class="n">list</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">oneshot</span><span class="o">=</span><span class="s2">"false"</span> <span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_01"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_02"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_03"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_04"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_05"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_06"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_07"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_08"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_09"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_10"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_11"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="n">item</span> <span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"150"</span><span class="o">></span>
<span class="o"><</span><span class="n">clip</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_12"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"left"</span> <span class="sr">/></span>
<span class="sr"> </i</span><span class="n">tem</span><span class="o">></span>
<span class="o"><</span><span class="sr">/animation-list></span></code></pre></div>
<p>注意上面item下得clip标签,这个是必须的,不然不同的屏幕尺寸适配会有问题。然后在布局文件里这样调用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">ProgressBar</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">indeterminateDrawable</span><span class="o">=</span><span class="s2">"@drawable/frame_loading"</span> <span class="sr">/></span></code></pre></div>
<h2 id="rotate-loading">2. Rotate Loading</h2>
<p>第二种主要是用rotate动画来实现的,具体代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">rotate_loading</span><span class="o">.</span><span class="n">xml</span>
<span class="o"><</span><span class="n">rotate</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">fromDegrees</span><span class="o">=</span><span class="s2">"0"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">interpolator</span><span class="o">=</span><span class="s2">"@android:anim/accelerate_decelerate_interpolator"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">pivotX</span><span class="o">=</span><span class="s2">"50%"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">pivotY</span><span class="o">=</span><span class="s2">"50%"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">toDegrees</span><span class="o">=</span><span class="s2">"360"</span> <span class="o">></span>
<span class="o"><</span><span class="n">bitmap</span>
<span class="ss">android</span><span class="p">:</span><span class="n">antialias</span><span class="o">=</span><span class="s2">"true"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">filter</span><span class="o">=</span><span class="s2">"true"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">src</span><span class="o">=</span><span class="s2">"@drawable/loading_360"</span> <span class="sr">/></span>
<span class="sr"></</span><span class="n">rotate</span><span class="o">></span></code></pre></div>
<p>然后调用方法和第一种一样。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">ProgressBar</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">indeterminateDrawable</span><span class="o">=</span><span class="s2">"@drawable/rotate_loading"</span> <span class="sr">/></span></code></pre></div>
<h2 id="clip-loading">3. Clip Loading</h2>
<p>第三种loading的是自定义了一个组件,主要用到了ClipDrawable的setLevel()方法,代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">CustomClipLoading</span> <span class="n">extends</span> <span class="no">FrameLayout</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">MAX_PROGRESS</span> <span class="o">=</span> <span class="mi">10000</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">ClipDrawable</span> <span class="n">mClipDrawable</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">int</span> <span class="n">mProgress</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">boolean</span> <span class="n">running</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">Handler</span> <span class="n">handler</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Handler</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">handleMessage</span><span class="p">(</span><span class="no">Message</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">如果消息是本程序发送的</span>
<span class="k">if</span> <span class="p">(</span><span class="n">msg</span><span class="o">.</span><span class="n">what</span> <span class="o">==</span> <span class="mh">0x123</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mClipDrawable</span><span class="o">.</span><span class="n">setLevel</span><span class="p">(</span><span class="n">mProgress</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="kp">public</span> <span class="no">CustomClipLoading</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">CustomClipLoading</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">attrs</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">attrs</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">CustomClipLoading</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">attrs</span><span class="p">,</span> <span class="n">int</span> <span class="n">defStyle</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">attrs</span><span class="p">,</span> <span class="n">defStyle</span><span class="p">);</span>
<span class="n">init</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">init</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="no">View</span> <span class="n">view</span> <span class="o">=</span> <span class="no">LayoutInflater</span><span class="o">.</span><span class="n">from</span><span class="p">(</span><span class="n">context</span><span class="p">)</span><span class="o">.</span><span class="n">inflate</span><span class="p">(</span><span class="n">layout</span><span class="o">.</span><span class="n">custom_loading</span><span class="p">,</span> <span class="n">null</span><span class="p">);</span>
<span class="n">addView</span><span class="p">(</span><span class="n">view</span><span class="p">);</span>
<span class="no">ImageView</span> <span class="n">imageView</span> <span class="o">=</span> <span class="p">(</span><span class="no">ImageView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="nb">id</span><span class="o">.</span><span class="n">iv_progress</span><span class="p">);</span>
<span class="n">mClipDrawable</span> <span class="o">=</span> <span class="p">(</span><span class="no">ClipDrawable</span><span class="p">)</span> <span class="n">imageView</span><span class="o">.</span><span class="n">getDrawable</span><span class="p">();</span>
<span class="no">Thread</span> <span class="n">s</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Thread</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
<span class="n">s</span><span class="o">.</span><span class="n">start</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">stop</span><span class="p">()</span> <span class="p">{</span>
<span class="n">mProgress</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">running</span> <span class="o">=</span> <span class="kp">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="no">Runnable</span> <span class="n">r</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Runnable</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="n">running</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="n">running</span><span class="p">)</span> <span class="p">{</span>
<span class="n">handler</span><span class="o">.</span><span class="n">sendEmptyMessage</span><span class="p">(</span><span class="mh">0x123</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mProgress</span> <span class="o">></span> <span class="no">MAX_PROGRESS</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mProgress</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">mProgress</span> <span class="o">+=</span> <span class="mi">100</span><span class="p">;</span>
<span class="n">try</span> <span class="p">{</span>
<span class="no">Thread</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">18</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">InterruptedException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStackTrace</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">}</span></code></pre></div>
<p>其中custom_loading布局文件的内容如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">custom_loading</span><span class="o">.</span><span class="n">xml</span>
<span class="o"><</span><span class="p">?</span><span class="n">xml</span> <span class="n">version</span><span class="o">=</span><span class="s2">"1.0"</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="sc">?></span>
<span class="o"><</span><span class="no">ImageView</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/iv_progress"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_gravity</span><span class="o">=</span><span class="s2">"center"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">background</span><span class="o">=</span><span class="s2">"@drawable/loading_bg"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">paddingLeft</span><span class="o">=</span><span class="s2">"3dp"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">paddingTop</span><span class="o">=</span><span class="s2">"3dp"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">scaleType</span><span class="o">=</span><span class="s2">"centerInside"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">src</span><span class="o">=</span><span class="s2">"@drawable/clip_loading"</span> <span class="sr">/></span></code></pre></div>
<p>然后clip_loading的内容如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">clip_loading</span><span class="o">.</span><span class="n">xml</span>
<span class="o"><</span><span class="p">?</span><span class="n">xml</span> <span class="n">version</span><span class="o">=</span><span class="s2">"1.0"</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="sc">?></span>
<span class="o"><</span><span class="n">clip</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">clipOrientation</span><span class="o">=</span><span class="s2">"vertical"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/loading_progress"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"bottom"</span> <span class="o">></span>
<span class="o"><</span><span class="sr">/clip></span></code></pre></div>
<p>至此这个组件就定义好了,那么接下来就是在avtivity布局文件的使用了:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">com</span><span class="o">.</span><span class="n">storm</span><span class="o">.</span><span class="n">ui</span><span class="o">.</span><span class="n">CustomClipLoading</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span> <span class="sr">/></span></code></pre></div>
<h2 id="progress-wheel">4. Progress Wheel</h2>
<p>第四和第五种loading只要是用到了一个开源的项目<a href="https://github.com/Todd-Davies/ProgressWheel">ProgressWheel</a>,使用方法也很简单,具体见项目说明。</p>
Java Thread Pool
2013-11-08T00:00:00+00:00
/java/2013/11/08/java-thread-pool
<p>介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用。</p>
<h2 id="new-thread">new Thread的弊端</h2>
<p>执行一个异步任务我们一般都是这样:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">new</span> <span class="no">Thread</span><span class="p">(</span><span class="kp">new</span> <span class="no">Runnable</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="nb">method</span> <span class="n">stub</span>
<span class="p">}</span>
<span class="p">})</span><span class="o">.</span><span class="n">start</span><span class="p">();</span></code></pre></div>
<p>弊端如下:</p>
<ul>
<li>每次new Thread新建对象性能差。</li>
<li>线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。</li>
<li>缺乏更多功能,如定时执行、定期执行、线程中断。</li>
</ul>
<p>相比new Thread,Java提供的四种线程池的好处在于:</p>
<ul>
<li>重用存在的线程,减少对象创建、消亡的开销,性能佳。</li>
<li>可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。</li>
<li>提供定时执行、定期执行、单线程、并发数控制等功能。</li>
</ul>
<!-- more -->
<h2 id="java-">Java 线程池</h2>
<p>Java通过Executors提供四种线程池,分别为:</p>
<ul>
<li>newCachedThreadPool: 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。</li>
<li>newFixedThreadPool: 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。</li>
<li>newScheduledThreadPool: 创建一个定长线程池,支持定时及周期性任务执行。</li>
<li>newSingleThreadExecutor: 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。</li>
</ul>
<h4 id="newcachedthreadpool">1. newCachedThreadPool</h4>
<p>创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ExecutorService</span> <span class="n">chacheThreadPool</span> <span class="o">=</span> <span class="no">Executors</span><span class="o">.</span><span class="n">newCachedThreadPool</span><span class="p">();</span>
<span class="k">for</span><span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">final</span> <span class="n">int</span> <span class="n">index</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
<span class="n">try</span><span class="p">{</span>
<span class="no">Thread</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">index</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">InterruptedException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStackTrace</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">chacheThreadPool</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="kp">new</span> <span class="no">Runnable</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="nb">method</span> <span class="n">stub</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="n">index</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span></code></pre></div>
<p>线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。</p>
<h4 id="newfixedthreadpool">2. newFixedThreadPool</h4>
<p>创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ExecutorService</span> <span class="n">fixedThreadPool</span> <span class="o">=</span> <span class="no">Executors</span><span class="o">.</span><span class="n">newFixedThreadPool</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">final</span> <span class="n">int</span> <span class="n">index</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
<span class="n">fixedThreadPool</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="kp">new</span> <span class="no">Runnable</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="n">try</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="n">index</span><span class="p">);</span>
<span class="no">Thread</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2000</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">InterruptedException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="kp">catch</span> <span class="n">block</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStackTrace</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span></code></pre></div>
<p>因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。</p>
<h4 id="newscheduledthreadpool">3. newScheduledThreadPool</h4>
<p>创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ScheduledExecutorService</span> <span class="n">scheduledThreadPool</span> <span class="o">=</span> <span class="no">Executors</span><span class="o">.</span><span class="n">newScheduledThreadPool</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
<span class="n">scheduledThreadPool</span><span class="o">.</span><span class="n">schedule</span><span class="p">(</span><span class="kp">new</span> <span class="no">Runnable</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">"delay 3 seconds"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">},</span> <span class="mi">3</span><span class="p">,</span> <span class="no">TimeUnit</span><span class="o">.</span><span class="n">SECONDS</span><span class="p">);</span></code></pre></div>
<p>表示延迟3秒执行。</p>
<p>定期执行示例代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">scheduledThreadPool</span><span class="o">.</span><span class="n">scheduleAtFixedRate</span><span class="p">(</span><span class="kp">new</span> <span class="no">Runnable</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">"delay 1 seconds, and excute every 3 seconds"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">},</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="no">TimeUnit</span><span class="o">.</span><span class="n">SECONDS</span><span class="p">);</span></code></pre></div>
<p>表示延迟1秒后每3秒执行一次。</p>
<p>ScheduledExecutorService比Timer更安全,功能更强大,后面会有一篇单独进行对比。</p>
<h4 id="newsinglethreadexecutor">4. newSingleThreadExecutor</h4>
<p>创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ExecutorService</span> <span class="n">singleThreadExecutor</span> <span class="o">=</span> <span class="no">Executors</span><span class="o">.</span><span class="n">newSingleThreadExecutor</span><span class="p">();</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">final</span> <span class="n">int</span> <span class="n">index</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
<span class="n">singleThreadExecutor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="kp">new</span> <span class="no">Runnable</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="n">try</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="n">index</span><span class="p">);</span>
<span class="no">Thread</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">2000</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">InterruptedException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="kp">catch</span> <span class="n">block</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStackTrace</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span></code></pre></div>
<p>结果依次输出,相当于顺序执行各个任务。</p>
<p>现行大多数GUI程序都是单线程的。Android中单线程可用于数据库操作,文件操作,应用批量安装,应用批量删除等不适合并发但可能IO阻塞性及影响UI线程响应的操作。</p>
<p>综上所述的4中方式,主要有以下几种见解:</p>
<ol>
<li>
<p>第一种方式跟第四种方式,运行了之后,发现结果是一样的,那是不是说这两种线程池的使用是一样的呢?如果是一样的,为什么会有两种而不是一种?其实大家仔细看下一开始这两个方法的介绍就知道了,第一种:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。这里觉得它可能是并发且是无序的。第四种:newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。是一个单线程的线程池,个人觉得它是有序的,因为有优先级。</p>
</li>
<li>
<p>第二种方式,可指定线程池的大小,并且每隔一段时间执行多少数据。比如上面的例子就是:因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。相信大家都能够理解了。</p>
</li>
<li>
<p>第三种方式就更简单了,这里介绍了两种情况,一种是只执行一次的情况,一种是延迟几秒再每隔几秒执行一次。可能这样说大家不太明白,我举个简单的例子。比如现在很多应用的首页有一个广告部分,每隔几秒后就会自动播放下一张图片,这里用的线程就是此线程。其实很多Android开发遇到这种情况,都会首先想到用定时器(Timer),其实不然,很多帖子都说明了,这种方式比用Timer更好。</p>
</li>
</ol>
Builder Pattern In Android
2013-11-05T00:00:00+00:00
/designpatterns/android/2013/11/05/builder-pattern-in-android
<p>看到一篇个人感觉不错的文章,分享出来。</p>
<p>When I started with android, I almost always asked Google how to do the smallest things. I want to highlight the examples I found for starting an activity.</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="no">SomeOtherActivity</span><span class="o">.</span><span class="n">class</span><span class="p">);</span>
<span class="n">startActivity</span><span class="p">(</span><span class="n">intent</span><span class="p">);</span></code></pre></div>
<p>I always wondered why many examples used a variable called intent. You could easily create the Intent on-the-fly in the startActivity() parameter. Maybe it looks better, if you need to add some parameters to the intent like this?</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="no">SomeOtherActivity</span><span class="o">.</span><span class="n">class</span><span class="p">);</span>
<span class="n">intent</span><span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="s2">"param1"</span><span class="p">,</span> <span class="n">extraInfo1</span><span class="p">);</span>
<span class="n">intent</span><span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="s2">"param2"</span><span class="p">,</span> <span class="n">extraInfo2</span><span class="p">);</span>
<span class="n">startActivity</span><span class="p">(</span><span class="n">intent</span><span class="p">);</span></code></pre></div>
<p>But then again, the method putExtra() returns an Intent. As described in the <a href="http://unreleased.de/code/example-for-method-chaining">example for method-chaining</a>, such chaining can be relatively useful. Androids call to startActivity() could be rewritten to this:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">startActivity</span><span class="p">(</span><span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="no">SomeOtherActivity</span><span class="o">.</span><span class="n">class</span><span class="p">)</span>
<span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="s2">"param1"</span><span class="p">,</span> <span class="n">extraInfo1</span><span class="p">)</span>
<span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="s2">"param2"</span><span class="p">,</span> <span class="n">extraInfo2</span><span class="p">));</span></code></pre></div>
<p>Since this doesn’t need a variable, I like that approach more. In Android, this pattern is used quite often. The so called builder-pattern is used with AlertDialog for example. When built, you use the create() method in order to show the dialog itself (equals the build() method of the builder-pattern). To create URIs one can use the buildUpon() method to get a Uri.Builder. There are tons of other examples where Android uses such builders. Intents seem to be an exception, since there is no startActivity() function or similar to it. Returning the object itself was not even done consequently, to make chaining of some methods impossible. Examples for these are setSourceBounds() or setExtrasClassLoader().</p>
<p>I see two options to address these problems:</p>
<ul>
<li>Creation of a sub-class of Intent, which provides a startAsActivity() function. It needs to return its own type in every putExtra() method, to preserve the startAsActivity() method. This class could look like this:</li>
</ul>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">BuilderIntent</span> <span class="n">extends</span> <span class="no">Intent</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">final</span> <span class="no">Context</span> <span class="n">context</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">BuilderIntent</span><span class="p">(</span><span class="no">Context</span> <span class="n">ctx</span><span class="p">,</span> <span class="no">Class</span><span class="o"><</span><span class="sc">?></span> <span class="n">cls</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">cls</span><span class="p">);</span>
<span class="n">this</span><span class="o">.</span><span class="n">context</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">;</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="no">BuilderIntent</span> <span class="n">putExtra</span><span class="p">(</span><span class="nb">String</span> <span class="nb">name</span><span class="p">,</span> <span class="nb">String</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="nb">name</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
<span class="k">return</span> <span class="n">this</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="n">more</span> <span class="n">putExtra</span><span class="o">-</span><span class="nb">methods</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">startAsActivity</span><span class="p">()</span> <span class="p">{</span>
<span class="n">context</span><span class="o">.</span><span class="n">startActivity</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="ss">Usage</span><span class="p">:</span>
<span class="kp">new</span> <span class="no">BuilderIntent</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="no">SomeOtherActivity</span><span class="o">.</span><span class="n">class</span><span class="p">)</span>
<span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="s2">"param1"</span><span class="p">,</span> <span class="s2">"info1"</span><span class="p">)</span>
<span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="s2">"param2"</span><span class="p">,</span> <span class="s2">"info2"</span><span class="p">)</span>
<span class="o">.</span><span class="n">startAsActivity</span><span class="p">();</span></code></pre></div>
<p>The problem with this class is, that methods like public void setExtrasClassLoader() cannot be changed to return the BuilderIntent type.</p>
<ul>
<li>An alternative would be the usage of an external builder, which uses its own Intent internally. Take all functions you want to provide and change them for your requirements, somehow similar to the Decorator-Pattern.</li>
</ul>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="k">class</span> <span class="nc">ActivityBuilder</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">final</span> <span class="no">Context</span> <span class="n">context</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">final</span> <span class="no">Intent</span> <span class="n">intent</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">ActivityBuilder</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">,</span> <span class="no">Class</span><span class="o"><</span><span class="p">?</span> <span class="n">extends</span> <span class="no">Activity</span><span class="o">></span> <span class="n">activity</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">context</span> <span class="o">=</span> <span class="n">context</span><span class="p">;</span>
<span class="n">intent</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">activity</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">ActivityBuilder</span> <span class="n">put</span><span class="p">(</span><span class="nb">String</span> <span class="n">param</span><span class="p">,</span> <span class="no">Long</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span>
<span class="n">intent</span><span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="n">param</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>
<span class="k">return</span> <span class="n">this</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="n">more</span> <span class="n">required</span> <span class="nb">methods</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">start</span><span class="p">()</span> <span class="p">{</span>
<span class="n">context</span><span class="o">.</span><span class="n">startActivity</span><span class="p">(</span><span class="n">intent</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>The advantage is that you can setup a public ActivityBuilder setExtrasClassLoader() method without hassle. The problem is, that you cannot use methods from Intent at all. With the first option, that would be cumbersome but at least possible.</p>
<p>What I wanted to say is that a method like startAsActivity() seems to be missing. However, you can create your own builders around existing classes.</p>
设计模式之建造者模式
2013-11-02T00:00:00+00:00
/designpatterns/java/2013/11/02/java-builder-pattern
<p>Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。</p>
<p>Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们。用户不知道内部的具体构建细节。Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到。</p>
<h2 id="section">为何使用建造者模式</h2>
<p>是为了将构建复杂对象的过程和它的部件解耦。注意:是解耦过程和部件。</p>
<p>因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮、方向盘、发动机,还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开。</p>
<h2 id="section-1">如何使用建造者模式</h2>
<p>首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示。</p>
<p>首先,需要一个接口,它定义如何创建复杂对象的各个部件:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">interface</span> <span class="no">Builder</span> <span class="p">{</span>
<span class="err"> </span><span class="o">//</span><span class="err">创建部件</span><span class="n">A</span><span class="err"> 比如创建汽车车轮</span>
<span class="err"> </span><span class="n">void</span> <span class="n">buildPartA</span><span class="p">();</span>
<span class="err"> </span><span class="o">//</span><span class="err">创建部件</span><span class="n">B</span> <span class="err">比如创建汽车方向盘</span>
<span class="err"> </span><span class="n">void</span> <span class="n">buildPartB</span><span class="p">();</span>
<span class="err"> </span><span class="o">//</span><span class="err">创建部件</span><span class="n">C</span> <span class="err">比如创建汽车发动机</span>
<span class="err"> </span><span class="n">void</span> <span class="n">buildPartC</span><span class="p">();</span>
<span class="err"> </span><span class="o">//</span><span class="err">返回最后组装成品结果</span> <span class="p">(</span><span class="err">返回最后装配好的汽车</span><span class="p">)</span>
<span class="err"> </span><span class="o">//</span><span class="err">成品的组装过程不在这里进行</span><span class="p">,</span><span class="err">而是转移到下面的</span><span class="no">Director</span><span class="err">类中进行</span><span class="o">.</span>
<span class="err"> </span><span class="o">//</span><span class="err">从而实现了解耦过程和部件</span>
<span class="err"> </span><span class="no">Product</span> <span class="n">getResult</span><span class="p">();</span>
<span class="p">}</span></code></pre></div>
<p>用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说Director的内容是如何将部件最后组装成成品:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Director</span> <span class="p">{</span>
<span class="err"> </span><span class="kp">private</span> <span class="no">Builder</span> <span class="n">builder</span><span class="p">;</span>
<span class="err"> </span><span class="kp">public</span> <span class="no">Director</span><span class="p">(</span> <span class="no">Builder</span> <span class="n">builder</span> <span class="p">)</span> <span class="p">{</span>
<span class="err"> </span><span class="n">this</span><span class="o">.</span><span class="n">builder</span> <span class="o">=</span> <span class="n">builder</span><span class="p">;</span>
<span class="err"> </span><span class="p">}</span>
<span class="err"> </span><span class="o">//</span> <span class="err">将部件</span><span class="n">partA</span> <span class="n">partB</span> <span class="n">partC</span><span class="err">最后组成复杂对象</span>
<span class="err"> </span><span class="o">//</span><span class="err">这里是将车轮</span> <span class="err">方向盘和发动机组装成汽车的过程</span>
<span class="err"> </span><span class="kp">public</span> <span class="n">void</span> <span class="n">construct</span><span class="p">()</span> <span class="p">{</span>
<span class="err"> </span><span class="n">builder</span><span class="o">.</span><span class="n">buildPartA</span><span class="p">();</span>
<span class="err"> </span><span class="n">builder</span><span class="o">.</span><span class="n">buildPartB</span><span class="p">();</span>
<span class="err"> </span><span class="n">builder</span><span class="o">.</span><span class="n">buildPartC</span><span class="p">();</span>
<span class="err"> </span><span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>Builder的具体实现ConcreteBuilder:
* 通过具体完成接口Builder来构建或装配产品的部件;
* 定义并明确它所要创建的是什么具体东西;
* 提供一个可以重新获取产品的接口。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">ConcreteBuilder</span> <span class="n">implements</span> <span class="no">Builder</span> <span class="p">{</span>
<span class="err"> </span><span class="no">Part</span> <span class="n">partA</span><span class="p">,</span> <span class="n">partB</span><span class="p">,</span> <span class="n">partC</span><span class="p">;</span>
<span class="err"> </span><span class="kp">public</span> <span class="n">void</span> <span class="n">buildPartA</span><span class="p">()</span> <span class="p">{</span>
<span class="err"> </span><span class="o">//</span><span class="err">这里是具体如何构建</span><span class="n">partA</span><span class="err">的代码</span>
<span class="err"> </span><span class="p">};</span>
<span class="err"> </span><span class="kp">public</span> <span class="n">void</span> <span class="n">buildPartB</span><span class="p">()</span> <span class="p">{</span>
<span class="err"> </span><span class="o">//</span><span class="err">这里是具体如何构建</span><span class="n">partB</span><span class="err">的代码</span>
<span class="err"> </span><span class="p">};</span>
<span class="err"> </span><span class="kp">public</span> <span class="n">void</span> <span class="n">buildPartC</span><span class="p">()</span> <span class="p">{</span>
<span class="err"> </span><span class="o">//</span><span class="err">这里是具体如何构建</span><span class="n">partB</span><span class="err">的代码</span>
<span class="err"> </span><span class="p">};</span>
<span class="err"> </span><span class="kp">public</span> <span class="no">Product</span> <span class="n">getResult</span><span class="p">()</span> <span class="p">{</span>
<span class="err"> </span><span class="o">//</span><span class="err">返回最后组装成品结果</span>
<span class="err"> </span><span class="p">};</span>
<span class="p">}</span></code></pre></div>
<p>复杂对象:产品Product:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">interface</span> <span class="no">Product</span> <span class="p">{</span> <span class="p">}</span></code></pre></div>
<p>复杂对象的部件:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">interface</span> <span class="no">Part</span> <span class="p">{</span> <span class="p">}</span></code></pre></div>
<p>我们看看如何调用Builder模式:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ConcreteBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">ConcreteBuilder</span><span class="p">();</span>
<span class="no">Director</span> <span class="n">director</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Director</span><span class="p">(</span> <span class="n">builder</span> <span class="p">);</span>
<span class="n">director</span><span class="o">.</span><span class="n">construct</span><span class="p">();</span>
<span class="no">Product</span> <span class="n">product</span> <span class="o">=</span> <span class="n">builder</span><span class="o">.</span><span class="n">getResult</span><span class="p">();</span></code></pre></div>
<p>通过上面我们可以看到:建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。</p>
Java Ten Object Oriented Design Principles
2013-10-26T00:00:00+00:00
/java/2013/10/26/java-10-object-oriented-design-principles
<p>看到一篇比较好的文章,故记录在此。</p>
<p>英文原文: <a href="http://javarevisited.blogspot.hk/2012/03/10-object-oriented-design-principles.html">10-object-oriented-design-principles</a>(友情提示,需翻墙)</p>
<p>面向对象理论是面向对象编程的核心,但是我发现大部分 Java 程序员热衷于像单例模式、装饰者模式或观察者模式这样的设计模式,而并没有十分注意学习面向对象的分析和设计。学习面向编程的基础(如抽象,封装,多态,继承等)是非常重要的,而运用它们来设计干净的模块也同样重要。我也认识很多不同等级的程序员,他们没有听过这些面向对象理论,或者不知道某个设计理论有什么好处,或者如何在编码中使用这些设计理论。</p>
<p>我们起码要设计出高度一致而且松散耦合的代码。Apache 和 Sun 的源代码就是学习 Java 面向对象理论的非常好的例子。JDK 遵循了一些设计模式,譬如在 BorderFactory 中使用工厂模式,Runtime 类中使用单例模式,java.io 中的许多类中使用装饰者模式。如果你真的对 Java 编程感兴趣,请先阅读 Joshua Bloch 的 Effective Java,正是他参与编写了 Java API。另外两本我喜欢的关于设计模式的书还有,Kathy Sierra 等编写的的 Head First Design Pattern 和 Head First Object Oriented Analysis and Design。这些书帮助理解面向对象理论,并帮助我写出更好的代码。</p>
<p>学习任何设计理论或模式最好的方法就是现实世界中的例子,这篇文章只是要给还在学习阶段的程序员介绍面相对象理论。我想以下每一条都需要用一篇文章来详细介绍,我以后也会逐一介绍的,只是现在先来个快速浏览一下。</p>
<h2 id="dry-dont-repeat-yourself">避免重复,DRY (Don’t repeat yourself)</h2>
<p>面相对设计理论的第一条就是避免重复,不要写重复的代码,尽量将共同的功能放在一个地方。如果你准备在不同地方写同一段代码,那么只写一个方法。如果你不止一次硬编码某个值,那么将其声明成 public final 常量。这么做的好处就是容易维护。但是不要滥用这一条,重复不是指代码的重复,而是指功能的重复。譬如你有一段相同的代码来验证 OrderID 和 SSN,但它们代表的意义并不相同。如果你将两个不同的功能合并在一起,当 OrderID 更改了格式之后,那么检验 SSN 的代码就会失效。所以要警觉这种耦合,不要讲任何相似但不相关的代码合并在一起。</p>
<h2 id="section">将变化封装起来</h2>
<p>在软件领域唯一不变的就是“变化”。所以最好将你觉得将来会有改变的代码封装起来。这样做的好处就是更容易测试和维护正确的被封装的代码。你应该先将变量声明成 private,然后有需要的话再扩大访问权限,如将 private 变成 protected。Java 中很多设计模式都使用了封装,工厂设计模式就是封装的一个例子,它封装了对象的创建,如果要引入新的“产品”,也不必更改现有的代码。</p>
<h2 id="open-closed-design-principle">开放且封闭的设计原则(Open Closed Design Principle)</h2>
<p>类、方法以及功能应该对扩展开放(新的功能),而对更改封闭。这是另一个优美的”SOLID”设计理论,这保证了有人更改已经经过测试了的代码。如果你要加入新的功能,你必须要先测试它,这正是开放且封闭的设计理论的目标。另外,Open Closed principle 正是 SOLID 中的O的缩写。</p>
<h2 id="single-responsibility-principle-srp">单一责任原则(Single Responsibility Principle (SRP))</h2>
<p>单一责任原理是另外一条”SOLID”设计理论,代表其中的“S”。每次一个类只有一个更改的原因,或者一个类只应该完成单一的功能。如果你将多过一个功能放在一个类中,它会将两个功能耦合在一起,如果你改变了其中的一个功能,可能会破坏另外一个功能,这样便需要更多的测试以确保上线时不出现什么岔子。</p>
<h2 id="section-1">依赖注入或反转原则</h2>
<p>容器会提供依赖注入,Spring 非常好的实现了依赖注入。这条原理的美妙之处在于,每个被注入的类很容易的测试和维护,因为这些对象的创建代码都集中在容器中,有多种方法都可以进行依赖注射,譬如一些 AOP 框架如 AspectJ 使用的字节码注入(bytecode instrumentation),以及 Spring 中使用的代理器(proxy)。来看看这个依赖注射的例子吧。这一条正是 SOLID 中的”D”。</p>
<h2 id="section-2">多用组合,少用继承</h2>
<p>如果可能的话,多用组合,少用继承。可能有的人会不同意,但我确实发现组合的灵活性高过继承。组合可以在运行时通过设置某个属性以及通过接口来组合某个类,我们可以使用多态,这样就能随时改变类的行为,大大提高了灵活性。Effective Java 也更倾向于使用组合。</p>
<h2 id="liskov-liskov-substitution-principle-lsp">Liskov 替代原则(Liskov Substitution Principle (LSP))</h2>
<p>根据 Liskov 替代原理,子类必须可以替代父类,也就是使用父类的方法,也能够没有任何问题的和子类对象也兼容。LSP 和单一责任原则以及接口分离原则的关系紧密。如果一个类比子类的功能要多,子类不能支持父类中的某些功能的话,就违反了 LSP。为了遵循 LSP 原理,子类需要改进父类的功能,而不是减少功能。LSP 代表 SOLID 中的”L”。</p>
<h2 id="interface-segregation-principle-isp">接口分离原则(Interface Segregation principle (ISP))</h2>
<p>接口分离理论强调,如果客户端没有使用一个接口的话,就不要实现它。当一个接口包含两个以上的功能,如果客户端仅仅需要其中某个功能,而不需要另外一个,那么就不要实现它。接口的设计是件非常复杂的工作,因为一旦你发布了接口之后,就再也无法保证不破坏现有实现的情况下更改接口。分离接口的另一个好处就是,因为必须要实现方法才能使用接口,所以如果仅仅只有单一的功能,那么要实现的方法也会减少。</p>
<h2 id="section-3">针对接口编程,而不是针对实现编程</h2>
<p>尽量针对接口编程,这样如果要引入任何新的接口,也有足够的灵活性。在变量的类型、方法的返回类型以及参量类型中使用接口类型。很多程序员都建议这么做,包括 Effective Java 和 head first design pattern 等书。</p>
<h2 id="delegation-principle">代理原则(Delegation principle)</h2>
<p>不要所有的事情都自己做,有时候要将任务代理给相应的类去做。运用代理模式最经典的例子就是 equals ()和 hashCode ()方法。为了比较两个对象的相等与否,我们没有用客户端代码去比较,而是让对象自己去比较。这么做的好处就是减少代码的重复,更容易更改行为。</p>
<p> 所有的这些面相对象理论都能帮助你写出更灵活、高度一致且低耦合的代码。理论是第一步,更重要的是运用这些设计理论的能力。找出违反这些设计理论的地方,但是就像这个世界上没有什么是完美的一样,不要尝试着用设计模式和理论解决一切问题,因为它们往往是针对大型的企业级项目,有着更长的运行周期。换句话说小型的项目不一定值得这么做。</p>
Android调用系统图库
2013-10-22T00:00:00+00:00
/android/2013/10/22/android-select-system-photos
<p>上面一篇讲到Android调用系统相机时遇到的兼容性问题,没想到选择系统图库的时候竟然也遇到了系统兼容性问题,郁闷之极无以言表,在这里记录下解决方案吧。</p>
<p>首先是调用系统默认图库代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">ACTION_PICK</span><span class="p">,</span> <span class="n">android</span><span class="o">.</span><span class="n">provider</span><span class="o">.</span><span class="n">MediaStore</span><span class="o">.</span><span class="n">Images</span><span class="o">.</span><span class="n">Media</span><span class="o">.</span><span class="n">EXTERNAL_CONTENT_URI</span><span class="p">);</span>
<span class="n">startActivityForResult</span><span class="p">(</span><span class="n">intent</span><span class="p">,</span> <span class="no">SELECT_PHOTOS_REQUEST_CODE</span><span class="p">);</span></code></pre></div>
<p>下面是关键的拿到图片的处理代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">void</span> <span class="n">onActivityResult</span><span class="p">(</span><span class="n">int</span> <span class="n">requestCode</span><span class="p">,</span> <span class="n">int</span> <span class="n">resultCode</span><span class="p">,</span> <span class="no">Intent</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onActivityResult</span><span class="p">(</span><span class="n">requestCode</span><span class="p">,</span> <span class="n">resultCode</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
<span class="n">switch</span> <span class="p">(</span><span class="n">requestCode</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="ss">SELECT_PHOTOS_REQUEST_CODE</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">resultCode</span> <span class="o">==</span> <span class="no">RESULT_OK</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Uri</span> <span class="n">uri</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">getData</span><span class="p">();</span>
<span class="sr">//</span> <span class="err">取得返回的</span><span class="no">Uri</span><span class="p">,</span><span class="err">基本上选择照片的时候返回的是以</span><span class="no">Uri</span><span class="err">形式,但是在拍照中有得机子呢</span><span class="no">Uri</span><span class="err">是空的,所以要特别注意</span>
<span class="k">if</span> <span class="p">(</span><span class="n">uri</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Bitmap</span> <span class="n">image</span><span class="p">;</span>
<span class="n">try</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">这个方法是根据</span><span class="no">Uri</span><span class="err">获取</span><span class="no">Bitmap</span><span class="err">图片的静态方法</span>
<span class="n">image</span> <span class="o">=</span> <span class="no">MediaStore</span><span class="o">.</span><span class="n">Images</span><span class="o">.</span><span class="n">Media</span><span class="o">.</span><span class="n">getBitmap</span><span class="p">(</span><span class="n">getContentResolver</span><span class="p">(),</span> <span class="n">uri</span><span class="p">);</span>
<span class="n">postImage</span><span class="o">.</span><span class="n">setImageBitmap</span><span class="p">(</span><span class="n">image</span><span class="p">);</span>
<span class="n">imageLayout</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">VISIBLE</span><span class="p">);</span>
<span class="n">mUri</span> <span class="o">=</span> <span class="n">uri</span><span class="p">;</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">Exception</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStackTrace</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="no">Bundle</span> <span class="n">extras</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">getExtras</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">extras</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">这里是有些拍照后的图片是直接存放到</span><span class="no">Bundle</span><span class="err">中的所以我们可以从这里面获取</span><span class="no">Bitmap</span><span class="err">图片</span>
<span class="no">Bitmap</span> <span class="n">image</span> <span class="o">=</span> <span class="n">extras</span><span class="o">.</span><span class="n">getParcelable</span><span class="p">(</span><span class="s2">"data"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">image</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">postImage</span><span class="o">.</span><span class="n">setImageBitmap</span><span class="p">(</span><span class="n">image</span><span class="p">);</span>
<span class="n">imageLayout</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">VISIBLE</span><span class="p">);</span>
<span class="n">mUri</span> <span class="o">=</span> <span class="no">BitmapUtil</span><span class="o">.</span><span class="n">getImageUri</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">image</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
Android调用系统相机拍照
2013-10-19T00:00:00+00:00
/android/2013/10/19/android-call-camera
<p>在这次的开发中用到了拍照上传图片的功能,还好Android中调用系统相机很方便,代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">cameraBtn</span><span class="o">.</span><span class="n">setOnClickListener</span><span class="p">(</span><span class="kp">new</span> <span class="no">OnClickListener</span><span class="p">()</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onClick</span><span class="p">()</span> <span class="p">{</span>
<span class="no">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="no">MediaStore</span><span class="o">.</span><span class="n">ACTION_IMAGE_CAPTURE</span><span class="p">);</span>
<span class="n">startActivityForResult</span><span class="p">(</span><span class="n">intent</span><span class="p">,</span> <span class="no">TAKE_PHOTO_REQUEST_CODE</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span></code></pre></div>
<p>获取拍照后图片数据,代码如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">void</span> <span class="n">onActivityResult</span><span class="p">(</span><span class="n">int</span> <span class="n">requestCode</span><span class="p">,</span> <span class="n">int</span> <span class="n">resultCode</span><span class="p">,</span> <span class="no">Intent</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onActivityResult</span><span class="p">(</span><span class="n">requestCode</span><span class="p">,</span> <span class="n">resultCode</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
<span class="n">switch</span> <span class="p">(</span><span class="n">requestCode</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="ss">TAKE_PHOTO_REQUEST_CODE</span><span class="p">:</span>
<span class="k">if</span><span class="p">(</span><span class="n">data!</span><span class="o">=</span><span class="n">null</span><span class="p">){</span>
<span class="no">Bundle</span> <span class="n">extras</span> <span class="o">=</span> <span class="n">data</span><span class="o">.</span><span class="n">getExtras</span><span class="p">();</span>
<span class="no">Bitmap</span> <span class="n">bmp</span> <span class="o">=</span> <span class="p">(</span><span class="no">Bitmap</span><span class="p">)</span> <span class="n">extras</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"data"</span><span class="p">);</span>
<span class="n">imageView</span><span class="o">.</span><span class="n">setImageBitmap</span><span class="p">(</span><span class="n">bmp</span><span class="p">);</span> <span class="sr">//</span><span class="err">设置照片现实在界面上</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>此时便遇到了问题,测试发现不同手机调用系统相机拍照时,有些并不会把照片存储起来,导致返回时无法获取照片数据,使用Log打印数据之后发现Bitmap bmp = (Bitmap) extras.get(“data”); 处出现异常。Android的兼容性真是一个头大的难题,但是问题依然是要解决,google了有了解决方案。就是拍照时会把数据指定存储在SDcard上,然后读取SDcard上的图片数据显示在ImageView上。下面就看下解决问题的代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">cameraBtn</span><span class="o">.</span><span class="n">setOnClickListener</span><span class="p">(</span><span class="kp">new</span> <span class="no">OnClickListener</span><span class="p">()</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onClick</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isHasSdCard</span><span class="p">())</span> <span class="p">{</span>
<span class="no">Helper</span><span class="o">.</span><span class="n">showToast</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">R</span><span class="o">.</span><span class="n">string</span><span class="o">.</span><span class="n">sdcard_no_avaiable</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="no">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="no">MediaStore</span><span class="o">.</span><span class="n">ACTION_IMAGE_CAPTURE</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">指定存储照片的路径</span>
<span class="no">Uri</span> <span class="n">imageUri</span> <span class="o">=</span> <span class="no">Uri</span><span class="o">.</span><span class="n">fromFile</span><span class="p">(</span><span class="n">getTempImage</span><span class="p">());</span>
<span class="n">intent</span><span class="o">.</span><span class="n">putExtra</span><span class="p">(</span><span class="no">MediaStore</span><span class="o">.</span><span class="n">EXTRA_OUTPUT</span><span class="p">,</span> <span class="n">imageUri</span><span class="p">);</span>
<span class="n">startActivityForResult</span><span class="p">(</span><span class="n">intent</span><span class="p">,</span> <span class="no">TAKE_PHOTO_REQUEST_CODE</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span></code></pre></div>
<p>上面代码会指定调用系统相机拍照时图片的存储路径,其中指定了一个临时文件temp.jpg来存储。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="no">File</span> <span class="n">getTempImage</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">android</span><span class="o">.</span><span class="n">os</span><span class="o">.</span><span class="n">Environment</span><span class="o">.</span><span class="n">getExternalStorageState</span><span class="p">()</span><span class="o">.</span><span class="n">equals</span><span class="p">(</span>
<span class="n">android</span><span class="o">.</span><span class="n">os</span><span class="o">.</span><span class="n">Environment</span><span class="o">.</span><span class="n">MEDIA_MOUNTED</span><span class="p">))</span> <span class="p">{</span>
<span class="no">File</span> <span class="n">tempFile</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">File</span><span class="p">(</span><span class="no">Environment</span><span class="o">.</span><span class="n">getExternalStorageDirectory</span><span class="p">(),</span> <span class="s2">"temp.jpg"</span><span class="p">);</span>
<span class="n">try</span> <span class="p">{</span>
<span class="n">tempFile</span><span class="o">.</span><span class="n">createNewFile</span><span class="p">();</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">IOException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStackTrace</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">tempFile</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">null</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>这时的onActivityResult的方法是下面这样的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">void</span> <span class="n">onActivityResult</span><span class="p">(</span><span class="n">int</span> <span class="n">requestCode</span><span class="p">,</span> <span class="n">int</span> <span class="n">resultCode</span><span class="p">,</span> <span class="no">Intent</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onActivityResult</span><span class="p">(</span><span class="n">requestCode</span><span class="p">,</span> <span class="n">resultCode</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
<span class="n">switch</span> <span class="p">(</span><span class="n">requestCode</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="ss">TAKE_PHOTO</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">resultCode</span> <span class="o">==</span> <span class="no">RESULT_OK</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Bitmap</span> <span class="n">bmp</span> <span class="o">=</span> <span class="no">BitmapFactory</span><span class="o">.</span><span class="n">decodeFile</span><span class="p">(</span><span class="n">getTempImage</span><span class="p">()</span><span class="o">.</span><span class="n">getPath</span><span class="p">());</span>
<span class="k">if</span> <span class="p">(</span><span class="n">null</span> <span class="o">!=</span> <span class="n">bmp</span><span class="p">)</span> <span class="p">{</span>
<span class="n">postImage</span><span class="o">.</span><span class="n">setImageBitmap</span><span class="p">(</span><span class="n">bmp</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>兼容性问题终于解决了,不过不幸的是新的问题又出现了。</p>
<p>由于目前Android手机的相机像素普遍已经比较高了,拍出来的照片可能都会达到1-2M,把这么大的一张图片加载进内存里肯定会报出OOM错误。即:</p>
<pre><code>Bitmap bmp = BitmapFactory.decodeFile(getTempImage().getPath());
</code></pre>
<p>这行代码会报出内存不足的错误。所以解决方案是在拍照后返回的bitmap必须要经过压缩,然后加载进内存从而渲染出来才不会报出内存不足的错误。下面就看代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">static</span> <span class="no">Bitmap</span> <span class="n">getScaleBitmap</span><span class="p">(</span><span class="no">Context</span> <span class="n">ctx</span><span class="p">,</span> <span class="nb">String</span> <span class="n">filePath</span><span class="p">)</span> <span class="p">{</span>
<span class="no">BitmapFactory</span><span class="o">.</span><span class="n">Options</span> <span class="n">opt</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">BitmapFactory</span><span class="o">.</span><span class="n">Options</span><span class="p">();</span>
<span class="n">opt</span><span class="o">.</span><span class="n">inJustDecodeBounds</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="no">Bitmap</span> <span class="n">bmp</span> <span class="o">=</span> <span class="no">BitmapFactory</span><span class="o">.</span><span class="n">decodeFile</span><span class="p">(</span><span class="n">filePath</span><span class="p">,</span> <span class="n">opt</span><span class="p">);</span>
<span class="n">int</span> <span class="n">bmpWidth</span> <span class="o">=</span> <span class="n">opt</span><span class="o">.</span><span class="n">outWidth</span><span class="p">;</span>
<span class="n">int</span> <span class="n">bmpHeght</span> <span class="o">=</span> <span class="n">opt</span><span class="o">.</span><span class="n">outHeight</span><span class="p">;</span>
<span class="no">WindowManager</span> <span class="n">windowManager</span> <span class="o">=</span> <span class="p">(</span><span class="no">WindowManager</span><span class="p">)</span> <span class="n">ctx</span><span class="o">.</span><span class="n">getSystemService</span><span class="p">(</span><span class="no">Context</span><span class="o">.</span><span class="n">WINDOW_SERVICE</span><span class="p">);</span>
<span class="no">Display</span> <span class="nb">display</span> <span class="o">=</span> <span class="n">windowManager</span><span class="o">.</span><span class="n">getDefaultDisplay</span><span class="p">();</span>
<span class="n">int</span> <span class="n">screenWidth</span> <span class="o">=</span> <span class="nb">display</span><span class="o">.</span><span class="n">getWidth</span><span class="p">();</span>
<span class="n">int</span> <span class="n">screenHeight</span> <span class="o">=</span> <span class="nb">display</span><span class="o">.</span><span class="n">getHeight</span><span class="p">();</span>
<span class="n">opt</span><span class="o">.</span><span class="n">inSampleSize</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">bmpWidth</span> <span class="o">></span> <span class="n">bmpHeght</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">bmpWidth</span> <span class="o">></span> <span class="n">screenWidth</span><span class="p">)</span>
<span class="n">opt</span><span class="o">.</span><span class="n">inSampleSize</span> <span class="o">=</span> <span class="n">bmpWidth</span> <span class="o">/</span> <span class="n">screenWidth</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">bmpHeght</span> <span class="o">></span> <span class="n">screenHeight</span><span class="p">)</span>
<span class="n">opt</span><span class="o">.</span><span class="n">inSampleSize</span> <span class="o">=</span> <span class="n">bmpHeght</span> <span class="o">/</span> <span class="n">screenHeight</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">opt</span><span class="o">.</span><span class="n">inJustDecodeBounds</span> <span class="o">=</span> <span class="kp">false</span><span class="p">;</span>
<span class="n">bmp</span> <span class="o">=</span> <span class="no">BitmapFactory</span><span class="o">.</span><span class="n">decodeFile</span><span class="p">(</span><span class="n">filePath</span><span class="p">,</span> <span class="n">opt</span><span class="p">);</span>
<span class="k">return</span> <span class="n">bmp</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>上述代码的关键在于inJustDecodeBounds属性。如果设置inJustDecodeBounds为true,仍可以获取到bitmap信息,但完全不用分配内存,因为没有获取像素,所以我们可以利用得到的Bitmap的大小,重新压缩图片,然后在内存中生成一个更小的Bitmap,这样即便是一个4MB的JPG,我们也可以随心所欲地把他压缩到任意大小,从而节省了内存。</p>
<p>下面看下更改后的代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">void</span> <span class="n">onActivityResult</span><span class="p">(</span><span class="n">int</span> <span class="n">requestCode</span><span class="p">,</span> <span class="n">int</span> <span class="n">resultCode</span><span class="p">,</span> <span class="no">Intent</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onActivityResult</span><span class="p">(</span><span class="n">requestCode</span><span class="p">,</span> <span class="n">resultCode</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
<span class="n">switch</span> <span class="p">(</span><span class="n">requestCode</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="ss">TAKE_PHOTO</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">resultCode</span> <span class="o">==</span> <span class="no">RESULT_OK</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Bitmap</span> <span class="n">bmp</span> <span class="o">=</span> <span class="n">getScaleBitmap</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">getTempImage</span><span class="p">()</span><span class="o">.</span><span class="n">getPath</span><span class="p">());</span>
<span class="k">if</span> <span class="p">(</span><span class="n">null</span> <span class="o">!=</span> <span class="n">bmp</span><span class="p">)</span> <span class="p">{</span>
<span class="n">postImage</span><span class="o">.</span><span class="n">setImageBitmap</span><span class="p">(</span><span class="n">bmp</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
多个github帐号的SSH key切换
2013-10-16T00:00:00+00:00
/other/2013/10/16/github-multiply-ssh-key
<h2 id="section">背景</h2>
<p>今天给我女朋友弄了个github账号,由于准备全部在一台mbp上管理,当准备给新的github账号添加ssh key的时候提示我该ssh key已经使用过了,因为我之前已经把该ssh key添加到我的github账号上了。</p>
<h2 id="section-1">原因</h2>
<p>
github使用SSH与客户端连接。如果是单用户(first),生成密钥对后,将公钥保存至github,每次连接时SSH客户端发送本地私钥(默认~/.ssh/id_rsa)到服务端验证。单用户情况下,连接的服务器上保存的公钥和发送的私钥自然是配对的。但是如果是多用户(first,second),我们在连接到second的帐号时,second保存的是自己的公钥,但是SSH客户端依然发送默认私钥,即first的私钥,那么这个验证自然无法通过。不过,要实现多帐号下的SSH key切换在客户端做一些配置即可。
</p>
<h2 id="section-2">解决方案</h2>
<p>首先,在新增私钥的时候,通过指定不同的文件名来生成不同的私钥文件</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">ssh-keygen -t rsa -f ~/.ssh/id_rsa_second -C "second@mail.com"</code></pre></div>
<p>默认SSH只会读取id_rsa,所以为了让SSH识别新的私钥,需要将其添加到SSH agent</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">ssh-add ~/.ssh/id_rsa_second</code></pre></div>
<p>该命令如果报错:Could not open a connection to your authentication agent.无法连接到ssh agent,可执行ssh-agent bash命令后再执行ssh-add命令。</p>
<p>
完成以上步骤后在~/.ssh目录创建config文件,该文件用于配置私钥对应的服务器。内容如下:
</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"># first.github (first@gmail.com)
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa
# second (second@gmail.com)
Host github-second
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_second</code></pre></div>
<p>Host随意即可,方便自己记忆,后续在添加remote是还需要用到。</p>
<p>配置完成后,在连接非默认帐号的github仓库时,远程库的地址要对应地做一些修改,比如现在添加second帐号下的一个仓库origin,则需要这样添加:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">remote</span> <span class="n">add</span> <span class="n">origin</span> <span class="n">git</span><span class="vi">@github</span><span class="o">-</span><span class="ss">second</span><span class="p">:</span><span class="n">second</span><span class="o">/</span><span class="nb">test</span><span class="o">.</span><span class="n">git</span>
<span class="c1"># 并非原来的git remote add origin git@github.com:second/test.git</span></code></pre></div>
<p>这样每次连接都会使用id_rsa_second与服务器进行连接。至此,大功告成!</p>
<p>注意:github根据配置文件的user.email来获取github帐号显示author信息,所以对于多帐号用户一定要记得将user.email改为相应的email(second@mail.com)。</p>
<h3 id="github">参考github帮助文档:</h3>
<ul>
<li>
<p><a href="http://help.github.com/win-set-up-git/">http://help.github.com/win-set-up-git/</a></p>
</li>
<li>
<p><a href="http://help.github.com/multiple-ssh-keys/">http://help.github.com/multiple-ssh-keys/</a></p>
</li>
</ul>
Android生成唯一标识符UUID
2013-10-11T21:13:00+00:00
/android/2013/10/11/java-generate-identity
<p>在这次新版本的app开发中,其中增加了游客账号访问,但需要为该游客生成一个唯一标识符identity,最初考虑通过mac地址或者imei来唯一标识,但是后面有一项需求,游客账号可以升级,升级之后退出重新游客访问的话就需要生成新的identity,这样一来就必须手动生成唯一的identity了,好在java提供了生成UUID唯一标示符。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">java</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">UUID</span><span class="o">.</span><span class="n">randomUUID</span><span class="p">()</span><span class="o">.</span><span class="n">toString</span><span class="p">();</span></code></pre></div>
<p>如果未升级的游客账号退出重新登录这个时候再重新访问还是用原来的identity,解决方法也很简单,把第一次生成的identity存起来,下次就直接取值了,看下代码吧:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="nb">String</span> <span class="n">getIdentity</span><span class="p">()</span> <span class="p">{</span>
<span class="no">SharedPreferences</span> <span class="n">preference</span> <span class="o">=</span> <span class="no">PreferenceManager</span><span class="o">.</span><span class="n">getDefaultSharedPreferences</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
<span class="nb">String</span> <span class="n">identity</span> <span class="o">=</span> <span class="n">preference</span><span class="o">.</span><span class="n">getString</span><span class="p">(</span><span class="s2">"identity"</span><span class="p">,</span> <span class="n">null</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">identity</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">identity</span> <span class="o">=</span> <span class="n">java</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">UUID</span><span class="o">.</span><span class="n">randomUUID</span><span class="p">()</span><span class="o">.</span><span class="n">toString</span><span class="p">();</span>
<span class="n">preference</span><span class="o">.</span><span class="n">edit</span><span class="p">()</span><span class="o">.</span><span class="n">putString</span><span class="p">(</span><span class="s2">"identity"</span><span class="p">,</span> <span class="n">identity</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">identity</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>然后只需要在升级成功后把identity remove掉就ok。</p>
小米2S开启USB调试模式
2013-10-09T00:00:00+00:00
/android/2013/10/09/open-usb-debug-mode-on-xiaomi2s
<p>拿了同事的新买的小米2S来调试应用程序,没想到已经在设置->开发者选项里已经打开了“USB调试模式”,但是不管是在eclipse的DDMS上还是在命令行下输入adb devices都找不到设备,最后google了才知道原来小米2s提供了另一种方法来开启USB模式,故记录在此:</p>
<p>1.首先在【设置】→【全部设置】→【开发者选项】→【调试】下开启【USB 调试】模式
2.然后打开手机拨号界面,在拨号界面按 </p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">*</span><span class="c1">#*#717717#*#*</span></code></pre></div>
<p>即可使 USB 接口生效,设置成功后会在当前界面弹出吐司 Diag USB port enable。</p>
<p>设置成功后即可查看设备的信息了,</p>
Android自定义点击效果的ImageView
2013-09-21T00:00:00+00:00
/android/2013/09/21/android-autobg-imageview
<p>我们知道在Android开发中一些可以点击的Button或者ImageView一般都会有一些特效,这样的设计比较友好,让用户确切的知道那个组件有没有成功点击。最简单最常用的办法就是设计两套背景图片,然后给Button或者ImageView设置一个xml的selector,从而达到这样的效果。但是如果整个应用下来每个可点击的组件都需要准备两张图片,未免有些太麻烦,而且一般的点击效果只是让透明度有些变化而已。那么针对只是透明度变化的点击效果,有没有可能自定义一个组件呢,从而达到方便重用的目的。带着整个想法,最后终于找到一个比较好的解决方案。废话不多说,见代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">AutoBgImageView</span> <span class="n">extends</span> <span class="no">ImageView</span> <span class="p">{</span>
<span class="kp">public</span> <span class="no">AutoBgImageView</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">AutoBgImageView</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">attrs</span><span class="p">,</span> <span class="n">int</span> <span class="n">defStyle</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">attrs</span><span class="p">,</span> <span class="n">defStyle</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">AutoBgImageView</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">attrs</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">attrs</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">setBackgroundDrawable</span><span class="p">(</span><span class="no">Drawable</span> <span class="n">d</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">Replace</span> <span class="n">the</span> <span class="n">original</span> <span class="n">background</span> <span class="n">drawable</span> <span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">g</span><span class="o">.</span> <span class="n">image</span><span class="p">)</span> <span class="n">with</span> <span class="n">a</span>
<span class="sr">//</span> <span class="no">LayerDrawable</span> <span class="n">that</span>
<span class="sr">//</span> <span class="n">contains</span> <span class="n">the</span> <span class="n">original</span> <span class="n">drawable</span><span class="o">.</span>
<span class="no">SAutoBgButtonBackgroundDrawable</span> <span class="n">layer</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">SAutoBgButtonBackgroundDrawable</span><span class="p">(</span><span class="n">d</span><span class="p">);</span>
<span class="k">super</span><span class="o">.</span><span class="n">setBackgroundDrawable</span><span class="p">(</span><span class="n">layer</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">protected</span> <span class="k">class</span> <span class="nc">SAutoBgButtonBackgroundDrawable</span> <span class="n">extends</span> <span class="no">LayerDrawable</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">The</span> <span class="n">color</span> <span class="n">filter</span> <span class="n">to</span> <span class="n">apply</span> <span class="k">when</span> <span class="n">the</span> <span class="n">button</span> <span class="n">is</span> <span class="n">pressed</span>
<span class="kp">protected</span> <span class="no">ColorFilter</span> <span class="n">_pressedFilter</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">LightingColorFilter</span><span class="p">(</span><span class="no">Color</span><span class="o">.</span><span class="n">LTGRAY</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="sr">//</span> <span class="no">Alpha</span> <span class="n">value</span> <span class="k">when</span> <span class="n">the</span> <span class="n">button</span> <span class="n">is</span> <span class="n">disabled</span>
<span class="kp">protected</span> <span class="n">int</span> <span class="n">_disabledAlpha</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">SAutoBgButtonBackgroundDrawable</span><span class="p">(</span><span class="no">Drawable</span> <span class="n">d</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="kp">new</span> <span class="no">Drawable</span><span class="o">[]</span> <span class="p">{</span> <span class="n">d</span> <span class="p">});</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">protected</span> <span class="n">boolean</span> <span class="n">onStateChange</span><span class="p">(</span><span class="n">int</span><span class="o">[]</span> <span class="n">states</span><span class="p">)</span> <span class="p">{</span>
<span class="n">boolean</span> <span class="n">enabled</span> <span class="o">=</span> <span class="kp">false</span><span class="p">;</span>
<span class="n">boolean</span> <span class="n">pressed</span> <span class="o">=</span> <span class="kp">false</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">state</span> <span class="p">:</span> <span class="n">states</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">state</span> <span class="o">==</span> <span class="n">android</span><span class="o">.</span><span class="n">R</span><span class="o">.</span><span class="n">attr</span><span class="o">.</span><span class="n">state_enabled</span><span class="p">)</span>
<span class="n">enabled</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">state</span> <span class="o">==</span> <span class="n">android</span><span class="o">.</span><span class="n">R</span><span class="o">.</span><span class="n">attr</span><span class="o">.</span><span class="n">state_pressed</span><span class="p">)</span>
<span class="n">pressed</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">mutate</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">enabled</span> <span class="o">&&</span> <span class="n">pressed</span><span class="p">)</span> <span class="p">{</span>
<span class="n">setColorFilter</span><span class="p">(</span><span class="n">_pressedFilter</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">enabled</span><span class="p">)</span> <span class="p">{</span>
<span class="n">setColorFilter</span><span class="p">(</span><span class="n">null</span><span class="p">);</span>
<span class="n">setAlpha</span><span class="p">(</span><span class="n">_disabledAlpha</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">setColorFilter</span><span class="p">(</span><span class="n">null</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">invalidateSelf</span><span class="p">();</span>
<span class="k">return</span> <span class="k">super</span><span class="o">.</span><span class="n">onStateChange</span><span class="p">(</span><span class="n">states</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">isStateful</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>上面代码定义了一个AutoBgImageView组件继承自ImageView,重写了ImageView的setBackgroundDrawable()方法,从而达到点击ImageView的时候透明度为100(最大为255)。这样整个系统只要是可点击的ImageView只需要用整个组件代替即可,非常方便。上述代码同样可以自定义Button达到类似的点击效果。</p>
Android MaskedImage
2013-09-15T00:00:00+00:00
/android/2013/09/15/android-maskedimage
<p>在这次one新版本的UI设计中,其中关于头像的设计中,一部分是圆角头像,一部分是圆形图像,关于这部分的实现有两种方法:第一种就是通过图片的叠加来实现,这种方法相对比较简单,但是如果有多种规格大小的就得提供相应这么多种的图片,第二种就是通过代码实现,这部分实现起来稍微麻烦点,但是如果一旦实现那么代码就可以重用,代码中用起来就比较方便。于是查找了各种资料,今天就来分享下代码实现的方法。</p>
<p>UI的开发最推荐的还是组件式开发,对于多个地方通用的东西抽成一个组件,这样不管是代码复用还是提供给别人使用都是比较方便的做法。在这次UI设计中,有圆形图片,圆角图片,不排除以后还有其他图片,如椭圆图片之类的,所以这次UI组件的开发也采用了继承的方法。</p>
<p><img src="/images/masked_image_design.png" /></p>
<p>下面就来看下代码实现:</p>
<h2 id="maskedimage">MaskedImage抽象基类</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">package</span> <span class="n">com</span><span class="o">.</span><span class="n">boohee</span><span class="o">.</span><span class="n">widgets</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">content</span><span class="o">.</span><span class="n">Context</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">graphics</span><span class="o">.</span><span class="n">Bitmap</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">graphics</span><span class="o">.</span><span class="n">Canvas</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">graphics</span><span class="o">.</span><span class="n">Paint</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">graphics</span><span class="o">.</span><span class="n">PorterDuff</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">graphics</span><span class="o">.</span><span class="n">PorterDuffXfermode</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">graphics</span><span class="o">.</span><span class="n">Xfermode</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">graphics</span><span class="o">.</span><span class="n">drawable</span><span class="o">.</span><span class="n">Drawable</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">AttributeSet</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">widget</span><span class="o">.</span><span class="n">ImageView</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">abstract</span> <span class="k">class</span> <span class="nc">MaskedImage</span> <span class="n">extends</span> <span class="no">ImageView</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="no">Xfermode</span> <span class="no">MASK_XFERMODE</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">Bitmap</span> <span class="n">mask</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">Paint</span> <span class="n">paint</span><span class="p">;</span>
<span class="n">static</span> <span class="p">{</span>
<span class="no">PorterDuff</span><span class="o">.</span><span class="n">Mode</span> <span class="n">localMode</span> <span class="o">=</span> <span class="no">PorterDuff</span><span class="o">.</span><span class="n">Mode</span><span class="o">.</span><span class="n">DST_IN</span><span class="p">;</span>
<span class="no">MASK_XFERMODE</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">PorterDuffXfermode</span><span class="p">(</span><span class="n">localMode</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">MaskedImage</span><span class="p">(</span><span class="no">Context</span> <span class="n">paramContext</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">paramContext</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">MaskedImage</span><span class="p">(</span><span class="no">Context</span> <span class="n">paramContext</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">paramAttributeSet</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">paramContext</span><span class="p">,</span> <span class="n">paramAttributeSet</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">MaskedImage</span><span class="p">(</span><span class="no">Context</span> <span class="n">paramContext</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">paramAttributeSet</span><span class="p">,</span> <span class="n">int</span> <span class="n">paramInt</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">paramContext</span><span class="p">,</span> <span class="n">paramAttributeSet</span><span class="p">,</span> <span class="n">paramInt</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">abstract</span> <span class="no">Bitmap</span> <span class="n">createMask</span><span class="p">();</span>
<span class="kp">protected</span> <span class="n">void</span> <span class="n">onDraw</span><span class="p">(</span><span class="no">Canvas</span> <span class="n">paramCanvas</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Drawable</span> <span class="n">localDrawable</span> <span class="o">=</span> <span class="n">getDrawable</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">localDrawable</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span>
<span class="k">return</span><span class="p">;</span>
<span class="n">try</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">paint</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Paint</span> <span class="n">localPaint1</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Paint</span><span class="p">();</span>
<span class="n">this</span><span class="o">.</span><span class="n">paint</span> <span class="o">=</span> <span class="n">localPaint1</span><span class="p">;</span>
<span class="n">this</span><span class="o">.</span><span class="n">paint</span><span class="o">.</span><span class="n">setFilterBitmap</span><span class="p">(</span><span class="kp">false</span><span class="p">);</span>
<span class="no">Paint</span> <span class="n">localPaint2</span> <span class="o">=</span> <span class="n">this</span><span class="o">.</span><span class="n">paint</span><span class="p">;</span>
<span class="no">Xfermode</span> <span class="n">localXfermode1</span> <span class="o">=</span> <span class="no">MASK_XFERMODE</span><span class="p">;</span>
<span class="no">Xfermode</span> <span class="n">localXfermode2</span> <span class="o">=</span> <span class="n">localPaint2</span><span class="o">.</span><span class="n">setXfermode</span><span class="p">(</span><span class="n">localXfermode1</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">float</span> <span class="n">f1</span> <span class="o">=</span> <span class="n">getWidth</span><span class="p">();</span>
<span class="n">float</span> <span class="n">f2</span> <span class="o">=</span> <span class="n">getHeight</span><span class="p">();</span>
<span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">paramCanvas</span><span class="o">.</span><span class="n">saveLayer</span><span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">0</span><span class="n">F</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">0</span><span class="n">F</span><span class="p">,</span> <span class="n">f1</span><span class="p">,</span> <span class="n">f2</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="mi">31</span><span class="p">);</span>
<span class="n">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">getWidth</span><span class="p">();</span>
<span class="n">int</span> <span class="n">k</span> <span class="o">=</span> <span class="n">getHeight</span><span class="p">();</span>
<span class="n">localDrawable</span><span class="o">.</span><span class="n">setBounds</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">k</span><span class="p">);</span>
<span class="n">localDrawable</span><span class="o">.</span><span class="n">draw</span><span class="p">(</span><span class="n">paramCanvas</span><span class="p">);</span>
<span class="k">if</span> <span class="p">((</span><span class="n">this</span><span class="o">.</span><span class="n">mask</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">mask</span><span class="o">.</span><span class="n">isRecycled</span><span class="p">()))</span> <span class="p">{</span>
<span class="no">Bitmap</span> <span class="n">localBitmap1</span> <span class="o">=</span> <span class="n">createMask</span><span class="p">();</span>
<span class="n">this</span><span class="o">.</span><span class="n">mask</span> <span class="o">=</span> <span class="n">localBitmap1</span><span class="p">;</span>
<span class="p">}</span>
<span class="no">Bitmap</span> <span class="n">localBitmap2</span> <span class="o">=</span> <span class="n">this</span><span class="o">.</span><span class="n">mask</span><span class="p">;</span>
<span class="no">Paint</span> <span class="n">localPaint3</span> <span class="o">=</span> <span class="n">this</span><span class="o">.</span><span class="n">paint</span><span class="p">;</span>
<span class="n">paramCanvas</span><span class="o">.</span><span class="n">drawBitmap</span><span class="p">(</span><span class="n">localBitmap2</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">0</span><span class="n">F</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">0</span><span class="n">F</span><span class="p">,</span> <span class="n">localPaint3</span><span class="p">);</span>
<span class="n">paramCanvas</span><span class="o">.</span><span class="n">restoreToCount</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">Exception</span> <span class="n">localException</span><span class="p">)</span> <span class="p">{</span>
<span class="no">StringBuilder</span> <span class="n">localStringBuilder</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">StringBuilder</span><span class="p">()</span>
<span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">"Attempting to draw with recycled bitmap. View ID = "</span><span class="p">);</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">"localStringBuilder=="</span><span class="o">+</span><span class="n">localStringBuilder</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="circularimage">CircularImage(圆形图片)实现类</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">CircularImage</span> <span class="n">extends</span> <span class="no">MaskedImage</span> <span class="p">{</span>
<span class="kp">public</span> <span class="no">CircularImage</span><span class="p">(</span><span class="no">Context</span> <span class="n">paramContext</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">paramContext</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">CircularImage</span><span class="p">(</span><span class="no">Context</span> <span class="n">paramContext</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">paramAttributeSet</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">paramContext</span><span class="p">,</span> <span class="n">paramAttributeSet</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">CircularImage</span><span class="p">(</span><span class="no">Context</span> <span class="n">paramContext</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">paramAttributeSet</span><span class="p">,</span> <span class="n">int</span> <span class="n">paramInt</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">paramContext</span><span class="p">,</span> <span class="n">paramAttributeSet</span><span class="p">,</span> <span class="n">paramInt</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">Bitmap</span> <span class="n">createMask</span><span class="p">()</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">getWidth</span><span class="p">();</span>
<span class="n">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">getHeight</span><span class="p">();</span>
<span class="no">Bitmap</span><span class="o">.</span><span class="n">Config</span> <span class="n">localConfig</span> <span class="o">=</span> <span class="no">Bitmap</span><span class="o">.</span><span class="n">Config</span><span class="o">.</span><span class="n">ARGB_8888</span><span class="p">;</span>
<span class="no">Bitmap</span> <span class="n">localBitmap</span> <span class="o">=</span> <span class="no">Bitmap</span><span class="o">.</span><span class="n">createBitmap</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">localConfig</span><span class="p">);</span>
<span class="no">Canvas</span> <span class="n">localCanvas</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Canvas</span><span class="p">(</span><span class="n">localBitmap</span><span class="p">);</span>
<span class="no">Paint</span> <span class="n">localPaint</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Paint</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">localPaint</span><span class="o">.</span><span class="n">setColor</span><span class="p">(</span><span class="o">-</span><span class="mi">16777216</span><span class="p">);</span>
<span class="n">float</span> <span class="n">f1</span> <span class="o">=</span> <span class="n">getWidth</span><span class="p">();</span>
<span class="n">float</span> <span class="n">f2</span> <span class="o">=</span> <span class="n">getHeight</span><span class="p">();</span>
<span class="no">RectF</span> <span class="n">localRectF</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">RectF</span><span class="p">(</span><span class="mi">0</span><span class="o">.</span><span class="mi">0</span><span class="n">F</span><span class="p">,</span> <span class="mi">0</span><span class="o">.</span><span class="mi">0</span><span class="n">F</span><span class="p">,</span> <span class="n">f1</span><span class="p">,</span> <span class="n">f2</span><span class="p">);</span>
<span class="n">localCanvas</span><span class="o">.</span><span class="n">drawOval</span><span class="p">(</span><span class="n">localRectF</span><span class="p">,</span> <span class="n">localPaint</span><span class="p">);</span>
<span class="k">return</span> <span class="n">localBitmap</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="roundedcornersimage">RoundedCornersImage(圆角图片)实现类</h2>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">public class RoundedCornersImage extends MaskedImage {
private static final int DEFAULT_CORNER_RADIUS = 8;
private int cornerRadius = DEFAULT_CORNER_RADIUS;
public RoundedCornersImage(Context paramContext) {
super(paramContext);
}
public RoundedCornersImage(Context paramContext, AttributeSet paramAttributeSet) {
super(paramContext, paramAttributeSet);
int[] arrayOfInt = R.styleable.RoundedCornersImage;
TypedArray a = paramContext.obtainStyledAttributes(paramAttributeSet, arrayOfInt);
int i = a.getDimensionPixelSize(0, DEFAULT_CORNER_RADIUS);
this.cornerRadius = i;
a.recycle();
}
public Bitmap createMask() {
int i = getWidth();
int j = getHeight();
Bitmap.Config localConfig = Bitmap.Config.ARGB_8888;
Bitmap localBitmap = Bitmap.createBitmap(i, j, localConfig);
Canvas localCanvas = new Canvas(localBitmap);
Paint localPaint = new Paint(1);
localPaint.setColor(-16777216);
float f1 = getWidth();
float f2 = getHeight();
RectF localRectF = new RectF(0.0F, 0.0F, f1, f2);
float f3 = this.cornerRadius;
float f4 = this.cornerRadius;
localCanvas.drawRoundRect(localRectF, f3, f4, localPaint);
return localBitmap;
}
}</code></pre></div>
<p>如果以后新增了椭圆图片,那么只须新建一个椭圆图片累继承自MaskedImage,然后重写createMask()方法即可。</p>
<p>至此,代码实现已完成,只需要在xml中像使用android自带组件的方式使用我们的自定义组件即可,如:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">com</span><span class="o">.</span><span class="n">boohee</span><span class="o">.</span><span class="n">widgets</span><span class="o">.</span><span class="n">RoundedCornersImage</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/rounded_image"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"60dp"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"60dp"</span> <span class="sr">/></span></code></pre></div>
Android SqliteAssetHelper
2013-09-02T00:00:00+00:00
/android/sqlite/2013/09/02/android-sqliteassethelper
<p>前面有一篇blog提到在Android开发中我们一般有两种方式使用sqlite,第一种是在application中手动创建,然后程序中管理数据库的升级;第二种是预先放置一份sqlite数据库,程序中使用的时候仅是查询功能,并不会涉及到更改、删除操作。这种情况下多是起到提供一个基础资源库的作用,如预先放置的一些提醒励志语句、以及预先放置的一些食物数据等。今天就来总结下如何管理assets文件夹下的sqlite数据库。</p>
<h2 id="section">使用场景与策略</h2>
<p>数据库管理一般都会伴随着升级,试想放在assets文件夹下的数据库升级是该怎么处理呢?</p>
<p>首先放在assets文件夹里的sqlite文件一定是我们事先经过处理好的数据库,包括里面的数据也是我们人为的生成的,如我们的app其实就是根据后端mysql转换成的sqlite,但是后端的数据是会不断完善以及不断变化的,所以伴随着我们的app端的sqlite也会是不断完善的,我想这种情况下大多数的策略是后端重新生成一份最新的sqlite文件,然后等到app发布的时候直接拷贝并覆盖原来旧的数据库。基于这种场景参考了github上一些资料,定义了一个SqliteAssetHelper来管理数据库的升级。下面看代码:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">public class SQLiteAssetHelper extends SQLiteOpenHelper {
static final String TAG = SQLiteAssetHelper.class.getSimpleName();
private static final String ASSET_DB_PATH = "databases";
private final Context mContext;
private final String mName;
private final CursorFactory mFactory;
private final int mNewVersion;
private SQLiteDatabase mDatabase = null;
private boolean mIsInitializing = false;
private String mDatabasePath;
private String mArchivePath;
public SQLiteAssetHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
if (version <span class="nt">< 1</span><span class="err">)</span> <span class="err">throw</span> <span class="err">new</span> <span class="err">IllegalArgumentException("Version</span> <span class="err">must</span> <span class="err">be</span> <span class="nt">></span>= 1, was " + version);
if (name == null) throw new IllegalArgumentException("Databse name cannot be null");
mContext = context;
mName = name;
mFactory = factory;
mNewVersion = version;
mArchivePath = ASSET_DB_PATH + "/" + name + ".zip";
mDatabasePath = context.getApplicationInfo().dataDir + "/databases";
}
@Override
public void onCreate(SQLiteDatabase db) {
// do nothing - createOrOpenDatabase() is called in
// getWritableDatabase() to handle database creation.
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
@Override
public synchronized SQLiteDatabase getWritableDatabase() {
if (mDatabase != null <span class="err">&&</span> mDatabase.isOpen() <span class="err">&&</span> !mDatabase.isReadOnly()) {
return mDatabase; // The database is already open for business
}
if (mIsInitializing) {
throw new IllegalStateException("getWritableDatabase called recursively");
}
// If we have a read-only database open, someone could be using it
// (though they shouldn't), which would cause a lock to be held on
// the file, and our attempts to open the database read-write would
// fail waiting for the file lock. To prevent that, we acquire the
// lock on the read-only database, which shuts out other users.
boolean success = false;
SQLiteDatabase db = null;
//if (mDatabase != null) mDatabase.lock();
try {
mIsInitializing = true;
db = createOrOpenDatabase(false);
int version = db.getVersion();
Log.e(TAG, "old version:" + version);
Log.e(TAG, "new version:" + mNewVersion);
// do force upgrade
if (version != 0 <span class="err">&&</span> version <span class="nt">< mNewVersion</span><span class="err">)</span> <span class="err">{</span>
<span class="na">db =</span> <span class="s">createOrOpenDatabase(true);</span>
<span class="na">version =</span> <span class="s">db.getVersion();</span>
<span class="err">}</span>
<span class="err">onOpen(db);</span>
<span class="na">success =</span> <span class="s">true;</span>
<span class="err">return</span> <span class="err">db;</span>
<span class="err">}</span> <span class="err">finally</span> <span class="err">{</span>
<span class="na">mIsInitializing =</span> <span class="s">false;</span>
<span class="err">if</span> <span class="err">(success)</span> <span class="err">{</span>
<span class="err">if</span> <span class="err">(mDatabase</span> <span class="err">!=</span> <span class="err">null)</span> <span class="err">{</span>
<span class="err">try</span> <span class="err">{</span> <span class="err">mDatabase.close();</span> <span class="err">}</span> <span class="err">catch</span> <span class="err">(Exception</span> <span class="err">e)</span> <span class="err">{</span> <span class="err">}</span>
<span class="err">//mDatabase.unlock();</span>
<span class="err">}</span>
<span class="na">mDatabase =</span> <span class="s">db;</span>
<span class="err">}</span> <span class="err">else</span> <span class="err">{</span>
<span class="err">//if</span> <span class="err">(mDatabase</span> <span class="err">!=</span> <span class="err">null)</span> <span class="err">mDatabase.unlock();</span>
<span class="err">if</span> <span class="err">(db</span> <span class="err">!=</span> <span class="err">null)</span> <span class="err">db.close();</span>
<span class="err">}</span>
<span class="err">}</span>
<span class="err">}</span>
<span class="err">@Override</span>
<span class="err">public</span> <span class="err">synchronized</span> <span class="err">SQLiteDatabase</span> <span class="err">getReadableDatabase()</span> <span class="err">{</span>
<span class="err">if</span> <span class="err">(mDatabase</span> <span class="err">!=</span> <span class="err">null</span> <span class="err">&&</span> <span class="err">mDatabase.isOpen())</span> <span class="err">{</span>
<span class="err">return</span> <span class="err">mDatabase;</span> <span class="err">//</span> <span class="err">The</span> <span class="err">database</span> <span class="err">is</span> <span class="err">already</span> <span class="err">open</span> <span class="err">for</span> <span class="err">business</span>
<span class="err">}</span>
<span class="err">if</span> <span class="err">(mIsInitializing)</span> <span class="err">{</span>
<span class="err">throw</span> <span class="err">new</span> <span class="err">IllegalStateException("getReadableDatabase</span> <span class="err">called</span> <span class="err">recursively");</span>
<span class="err">}</span>
<span class="err">try</span> <span class="err">{</span>
<span class="err">return</span> <span class="err">getWritableDatabase();</span>
<span class="err">}</span> <span class="err">catch</span> <span class="err">(SQLiteException</span> <span class="err">e)</span> <span class="err">{</span>
<span class="err">if</span> <span class="err">(</span><span class="na">mName =</span><span class="s">=</span> <span class="err">null)</span> <span class="err">throw</span> <span class="err">e;</span> <span class="err">//</span> <span class="err">Can't</span> <span class="err">open</span> <span class="err">a</span> <span class="err">temp</span> <span class="err">database</span> <span class="err">read-only!</span>
<span class="err">Log.e(TAG,</span> <span class="err">"Couldn't</span> <span class="err">open</span> <span class="err">"</span> <span class="err">+</span> <span class="err">mName</span> <span class="err">+</span> <span class="err">"</span> <span class="err">for</span> <span class="err">writing</span> <span class="err">(will</span> <span class="err">try</span> <span class="err">read-only):",</span> <span class="err">e);</span>
<span class="err">}</span>
<span class="err">SQLiteDatabase</span> <span class="na">db =</span> <span class="s">null;</span>
<span class="err">try</span> <span class="err">{</span>
<span class="na">mIsInitializing =</span> <span class="s">true;</span>
<span class="err">String</span> <span class="na">path =</span> <span class="s">mContext.getDatabasePath(mName).getPath();</span>
<span class="na">db =</span> <span class="s">SQLiteDatabase.openDatabase(path,</span> <span class="err">mFactory,</span> <span class="err">SQLiteDatabase.OPEN_READONLY);</span>
<span class="err">if</span> <span class="err">(db.getVersion()</span> <span class="err">!=</span> <span class="err">mNewVersion)</span> <span class="err">{</span>
<span class="err">throw</span> <span class="err">new</span> <span class="err">SQLiteException("Can't</span> <span class="err">upgrade</span> <span class="err">read-only</span> <span class="err">database</span> <span class="err">from</span> <span class="err">version</span> <span class="err">"</span> <span class="err">+</span>
<span class="err">db.getVersion()</span> <span class="err">+</span> <span class="err">"</span> <span class="err">to</span> <span class="err">"</span> <span class="err">+</span> <span class="err">mNewVersion</span> <span class="err">+</span> <span class="err">":</span> <span class="err">"</span> <span class="err">+</span> <span class="err">path);</span>
<span class="err">}</span>
<span class="err">onOpen(db);</span>
<span class="err">Log.w(TAG,</span> <span class="err">"Opened</span> <span class="err">"</span> <span class="err">+</span> <span class="err">mName</span> <span class="err">+</span> <span class="err">"</span> <span class="err">in</span> <span class="err">read-only</span> <span class="err">mode");</span>
<span class="na">mDatabase =</span> <span class="s">db;</span>
<span class="err">return</span> <span class="err">mDatabase;</span>
<span class="err">}</span> <span class="err">finally</span> <span class="err">{</span>
<span class="na">mIsInitializing =</span> <span class="s">false;</span>
<span class="err">if</span> <span class="err">(db</span> <span class="err">!=</span> <span class="err">null</span> <span class="err">&&</span> <span class="err">db</span> <span class="err">!=</span> <span class="err">mDatabase)</span> <span class="err">db.close();</span>
<span class="err">}</span>
<span class="err">}</span>
<span class="err">private</span> <span class="err">SQLiteDatabase</span> <span class="err">createOrOpenDatabase(boolean</span> <span class="err">force)</span> <span class="err">throws</span> <span class="err">SQLiteAssetException</span> <span class="err">{</span>
<span class="err">SQLiteDatabase</span> <span class="na">db =</span> <span class="s">returnDatabase();</span>
<span class="err">if</span> <span class="err">(db</span> <span class="err">!=</span> <span class="err">null)</span> <span class="err">{</span>
<span class="err">//</span> <span class="err">database</span> <span class="err">already</span> <span class="err">exists</span>
<span class="err">if</span> <span class="err">(force)</span> <span class="err">{</span>
<span class="err">Log.w(TAG,</span> <span class="err">"forcing</span> <span class="err">database</span> <span class="err">upgrade!");</span>
<span class="err">copyDatabaseFromAssets();</span>
<span class="na">db =</span> <span class="s">returnDatabase();</span>
<span class="err">db.setVersion(mNewVersion);</span>
<span class="err">}</span>
<span class="err">return</span> <span class="err">db;</span>
<span class="err">}</span> <span class="err">else</span> <span class="err">{</span>
<span class="err">//</span> <span class="err">database</span> <span class="err">does</span> <span class="err">not</span> <span class="err">exist,</span> <span class="err">copy</span> <span class="err">it</span> <span class="err">from</span> <span class="err">assets</span> <span class="err">and</span> <span class="err">return</span> <span class="err">it</span>
<span class="err">copyDatabaseFromAssets();</span>
<span class="na">db =</span> <span class="s">returnDatabase();</span>
<span class="err">db.setVersion(mNewVersion);</span>
<span class="err">return</span> <span class="err">db;</span>
<span class="err">}</span>
<span class="err">}</span>
<span class="err">private</span> <span class="err">SQLiteDatabase</span> <span class="err">returnDatabase(){</span>
<span class="err">try</span> <span class="err">{</span>
<span class="err">SQLiteDatabase</span> <span class="na">db =</span> <span class="s">SQLiteDatabase.openDatabase(mDatabasePath</span> <span class="err">+</span> <span class="err">"/"</span> <span class="err">+</span> <span class="err">mName,</span> <span class="err">mFactory,</span> <span class="err">SQLiteDatabase.OPEN_READWRITE);</span>
<span class="err">Log.i(TAG,</span> <span class="err">"successfully</span> <span class="err">opened</span> <span class="err">database</span> <span class="err">"</span> <span class="err">+</span> <span class="err">mName);</span>
<span class="err">return</span> <span class="err">db;</span>
<span class="err">}</span> <span class="err">catch</span> <span class="err">(SQLiteException</span> <span class="err">e)</span> <span class="err">{</span>
<span class="err">Log.w(TAG,</span> <span class="err">"could</span> <span class="err">not</span> <span class="err">open</span> <span class="err">database</span> <span class="err">"</span> <span class="err">+</span> <span class="err">mName</span> <span class="err">+</span> <span class="err">"</span> <span class="err">-</span> <span class="err">"</span> <span class="err">+</span> <span class="err">e.getMessage());</span>
<span class="err">return</span> <span class="err">null;</span>
<span class="err">}</span>
<span class="err">}</span>
<span class="err">private</span> <span class="err">void</span> <span class="err">copyDatabaseFromAssets()</span> <span class="err">throws</span> <span class="err">SQLiteAssetException</span> <span class="err">{</span>
<span class="err">Log.e(TAG,</span> <span class="err">"copying</span> <span class="err">database</span> <span class="err">from</span> <span class="err">assets...");</span>
<span class="err">try</span> <span class="err">{</span>
<span class="err">InputStream</span> <span class="na">zipFileStream =</span> <span class="s">mContext.getAssets().open(mArchivePath);</span>
<span class="err">File</span> <span class="na">f =</span> <span class="s">new</span> <span class="err">File(mDatabasePath</span> <span class="err">+</span> <span class="err">"/");</span>
<span class="err">if</span> <span class="err">(!f.exists())</span> <span class="err">{</span> <span class="err">f.mkdir();</span> <span class="err">}</span>
<span class="err">ZipInputStream</span> <span class="na">zis =</span> <span class="s">getFileFromZip(zipFileStream);</span>
<span class="err">if</span> <span class="err">(</span><span class="na">zis =</span><span class="s">=</span> <span class="err">null)</span> <span class="err">{</span>
<span class="err">throw</span> <span class="err">new</span> <span class="err">SQLiteAssetException("Archive</span> <span class="err">is</span> <span class="err">missing</span> <span class="err">a</span> <span class="err">SQLite</span> <span class="err">database</span> <span class="err">file");</span>
<span class="err">}</span>
<span class="err">writeExtractedFileToDisk(zis,</span> <span class="err">new</span> <span class="err">FileOutputStream(mDatabasePath</span> <span class="err">+</span> <span class="err">"/"</span> <span class="err">+</span> <span class="err">mName));</span>
<span class="err">Log.e(TAG,</span> <span class="err">"database</span> <span class="err">copy</span> <span class="err">complete");</span>
<span class="err">}</span> <span class="err">catch</span> <span class="err">(FileNotFoundException</span> <span class="err">fe)</span> <span class="err">{</span>
<span class="err">SQLiteAssetException</span> <span class="na">se =</span> <span class="s">new</span> <span class="err">SQLiteAssetException("Missing</span> <span class="err">"</span> <span class="err">+</span> <span class="err">mArchivePath</span> <span class="err">+</span> <span class="err">"</span> <span class="err">file</span> <span class="err">in</span> <span class="err">assets</span> <span class="err">or</span> <span class="err">target</span> <span class="err">folder</span> <span class="err">not</span> <span class="err">writable");</span>
<span class="err">se.setStackTrace(fe.getStackTrace());</span>
<span class="err">throw</span> <span class="err">se;</span>
<span class="err">}</span> <span class="err">catch</span> <span class="err">(IOException</span> <span class="err">e)</span> <span class="err">{</span>
<span class="err">SQLiteAssetException</span> <span class="na">se =</span> <span class="s">new</span> <span class="err">SQLiteAssetException("Unable</span> <span class="err">to</span> <span class="err">extract</span> <span class="err">"</span> <span class="err">+</span> <span class="err">mArchivePath</span> <span class="err">+</span> <span class="err">"</span> <span class="err">to</span> <span class="err">data</span> <span class="err">directory");</span>
<span class="err">se.setStackTrace(e.getStackTrace());</span>
<span class="err">throw</span> <span class="err">se;</span>
<span class="err">}</span>
<span class="err">}</span>
<span class="err">private</span> <span class="err">void</span> <span class="err">writeExtractedFileToDisk(ZipInputStream</span> <span class="err">zin,</span> <span class="err">OutputStream</span> <span class="err">outs)</span> <span class="err">throws</span> <span class="err">IOException</span> <span class="err">{</span>
<span class="err">byte[]</span> <span class="na">buffer =</span> <span class="s">new</span> <span class="err">byte[1024];</span>
<span class="err">int</span> <span class="err">length;</span>
<span class="err">while</span> <span class="err">((</span><span class="na">length =</span> <span class="s">zin.read(buffer))</span><span class="nt">></span>0){
outs.write(buffer, 0, length);
}
outs.flush();
outs.close();
zin.close();
}
private ZipInputStream getFileFromZip(InputStream zipFileStream) throws FileNotFoundException, IOException {
ZipInputStream zis = new ZipInputStream(zipFileStream);
ZipEntry ze = null;
while ((ze = zis.getNextEntry()) != null) {
Log.e(TAG, "extracting file: '" + ze.getName() + "'...");
return zis;
}
return null;
}
}</code></pre></div>
<h2 id="section-1">使用方法</h2>
<p>使用时只需把实现准备好的sqlite文件压缩成zip包放在assets文件夹下的databases目录,然后定义一个Helper继承自SqliteAssetHelper,如下代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">DBHelper</span> <span class="n">extends</span> <span class="no">SQLiteAssetHelper</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">DATABASE_NAME</span> <span class="o">=</span> <span class="s2">"bhdb.sqlite"</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">DATABASE_VERSION</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">DBHelper</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="no">DATABASE_NAME</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="no">DATABASE_VERSION</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>升级的时候只需要改DATABASE_VERSION的值就好了。</p>
<h2 id="section-2">优点与缺点</h2>
<p>优点:管理assets文件夹下的数据库简单方便,把sqlite文件以zip包的形式放在程序中,减少包大小。
缺点:每次更新只能覆盖原来的数据,及时是少量数据更新也是这种方式。如果少量数据更新的话打算以执行sql的方式来更新数据那可以参考下面这个项目,
<a href="https://github.com/jgilfelt/android-sqlite-asset-helper">android-sqlite-asset-helper</a></p>
Android BuildConfig.DEBUG的妙用
2013-08-28T00:00:00+00:00
/android/2013/08/28/android-use-build-config
<p>在Android开发中,我们使用android.util.Log来打印日志,方便我们的开发调试。但是这些代码不想在发布后执行,我们并不想在软件发布后调试日志被其他开发者看到,现在我的方法是设置一个全局变量,标记软件为Debug模式还是Release模式。来看下代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Log</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">boolean</span> <span class="no">DEBUG</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">i</span><span class="p">(</span><span class="nb">String</span> <span class="n">tag</span><span class="p">,</span> <span class="nb">String</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="no">DEBUG</span><span class="p">)</span>
<span class="n">android</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">Log</span><span class="o">.</span><span class="n">i</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">e</span><span class="p">(</span><span class="nb">String</span> <span class="n">tag</span><span class="p">,</span> <span class="nb">String</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="no">DEBUG</span><span class="p">)</span>
<span class="n">android</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">Log</span><span class="o">.</span><span class="n">e</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">d</span><span class="p">(</span><span class="nb">String</span> <span class="n">tag</span><span class="p">,</span> <span class="nb">String</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="no">DEBUG</span><span class="p">)</span>
<span class="n">android</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">Log</span><span class="o">.</span><span class="n">d</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">v</span><span class="p">(</span><span class="nb">String</span> <span class="n">tag</span><span class="p">,</span> <span class="nb">String</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="no">DEBUG</span><span class="p">)</span>
<span class="n">android</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">Log</span><span class="o">.</span><span class="n">v</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">w</span><span class="p">(</span><span class="nb">String</span> <span class="n">tag</span><span class="p">,</span> <span class="nb">String</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="no">DEBUG</span><span class="p">)</span>
<span class="n">android</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">Log</span><span class="o">.</span><span class="n">w</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>这样打包发布之前只要改下DEBUG=false就行了,但是每次在发布之前都要手动去改这个变量,不是很方便,而且不排除开发者忘记改的情况。那么有没有更好更方便的做法呢?</p>
<p>ADT(r17)发布以后,Google为我们提供了一种新的调试机制,即BuildConfig.DEBUG。</p>
<p>ADT 17.0.0的New build features第二条如下描述:</p>
<blockquote>
<p>Added a feature that allows you to run some code only in debug mode. Builds now generate a class called BuildConfig containing a DEBUGconstant that is automatically set according to your build type. You can check the (BuildConfig.DEBUG) constant in your code to run debug-only functions.</p>
</blockquote>
<p>即:新增了一个特性,允许开发者只在Debug模式下运行部分代码。Builds会生成一个叫做BuildConfig的类,该类包含一个名为DEBUG的常量,其常量值会依据开发者的Build类型自动设定。如此,便可以利用BuildConfig.DEBUG来实现只在Debug模式下运行的代码。</p>
<p>如果你的ADT已经更新到17及以上版本,可以尝试在Eclipse中新建一个Android工程,你会发现和R.java同级目录下多了一个叫做BuildConfig.java的类,其内容如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">/** Automatically generated file. DO NOT MODIFY */</span>
<span class="n">package</span> <span class="n">com</span><span class="o">.</span><span class="n">boohee</span><span class="o">.</span><span class="n">one</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">final</span> <span class="k">class</span> <span class="nc">BuildConfig</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">final</span> <span class="n">static</span> <span class="n">boolean</span> <span class="no">DEBUG</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>这样只需要改动一行代码就ok了,</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">boolean</span> <span class="no">DEBUG</span> <span class="o">=</span> <span class="no">BuildConifg</span><span class="o">.</span><span class="n">DEBUG</span><span class="p">;</span></code></pre></div>
<p>在上面提到,DEBUG会根据Build类型自动设定。那么Build类型又从哪里区分呢?很简单,点开Eclipse的Project菜单便可见分晓,如下图:</p>
<p><img src="/image/eclipse_build1.jpg" />
<img src="/image/eclipse_build2.jpg" /></p>
<p>可见,Build类型分为Build Project和Build Automatically,即手动和自动。</p>
<p>需要注意的是,如果直接通过Eclipse运行Project,则不论Build是手动还是自动,DEBUG均不会被设定为false。这是为什么呢?这就牵涉到Android 签名的问题,这里只简单提一下,不赘述:直接通过Eclipse运行Project,Eclipse会在工程Build完毕后在bin目录下生成一个apk,这个apk的签名是调试模式(debug mode),和发布模式(release mode)签名生成的apk略有不同。如此,该问题产生原因便浮出水面。</p>
<p>此时肯定会有人说,直接使用Android Tools–>Export Signed Application Package导出的release mode apk,其DEBUG就是false。这是不对的。在生成Release版时,需要区分Build的类型。如果选择的是自动Build,那么DEBUG仍然会被设定为true。所以在生成Release版时,请按照下面这个步骤进行打包,BuildConfig.DEBUG会被修改为false:</p>
<ol>
<li>
<p>Project -> Build Automatically,即取消Build Automatically</p>
</li>
<li>
<p>Project -> Clean</p>
</li>
<li>
<p>Project -> Build</p>
</li>
<li>
<p>Android Tools -> Export Android application</p>
</li>
</ol>
Android Copy Sqlite From Assets
2013-08-23T00:00:00+00:00
/android/sqlite/2013/08/23/android-copy-sqlite-from-assets
<h2 id="section">背景</h2>
<p>我们都知道,在Android开发中,一般都使用sqlite来进行数据的存储与访问,而使用sqlite也可分为两种方式,一是在app中手动创建database,这种情况下大多使用SQLiteOpenHelper来进行数据库的创建与升级管理;另一种则是我们事先处理一份只读的数据库放在assets文件夹里,使用的时候则必须先把sqlite文件从assets文件里里拷贝到程序中才可使用,这种情况下的升级则是通过判断版本号来直接覆盖拷贝。</p>
<h2 id="section-1">问题</h2>
<p>在one的项目中以上两种做法都用到了,本地放了一份只读的部分食物数据,大概1.1M左右,用户的记录数据如:饮食、运动、体重记录存储在自己创建的另一个数据库中。这看似没什么问题,实际测试中也没发现有什么异常。项目上线后后台监测会有一些异常出现,大概意思是food_groups找不到这个表之类的,由于异常次数比较少,加上本地测试也没重现,所以也没在意。但是这个异常每一个版本都会出现,加上偶尔有个别用户反馈食物查询不了,甚是纳闷。</p>
<h2 id="section-2">解决</h2>
<p>通过询问几位用户以及后台错误的版本分布来看,大多集中在2.2。这个时候便定位到了问题,2.2版本一下的已经非常少了,实际测试时公司也没有那么低版本的手机了,所以一直没有重现,于是在模拟器上用2.1版本进行测试,果然查询模块无法查看。通过监测日志发现,在2.1版本copy数据库时失败,但是在2.3以上版本却一直正常。经过google查看了很多资料,加上自己实际的测试才发现在2.3以下版本的系统中放在assets文件夹里的数据库文件大小不能超过1M,于是经过重新整理把文件缩小到900多k,再在2.1版本的模拟器上运行,一些ok了。</p>
<h2 id="section-3">总结</h2>
<p>安卓平台上的版本兼容问题确实是令人比较头痛的事,但是4.0之后这些兼容问题不再那么明显了,就目前来看我们的app还是要兼容大多数的系统版本的,后台的异常还是有很大作用的,在测试机器不充分的条件下,这种方式还是非常依赖的。相信等4.0以后的版本普及之后,兼容问题便不用花费太多精力了。</p>
Android批量插入数据性能优化
2013-08-19T00:00:00+00:00
/android/sqlite/2013/08/19/android-insert-performance-optimization
<p>最近做数据同步时遇到一个问题,在下载数据时需要批量的向sqlite插入数据,虽然数据不算多,但是实际测试中每插入一条数据需要将近50ms的时间,这意味着100条数据就需要花费5s左右的时间,对于用户来说,体验太差了,必须要优化。</p>
<p>在google了之后,发现了sqlite的事务处理问题,在sqlite插入数据的时候默认一条语句就是一个事务,有多少条数据就有多少次磁盘操作。明白了这个,解决方案就有了,在批量插入数据的时候,只开启一个事务,这样只会进行一次磁盘操作,代码如下:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">db.beginTransaction();
try {
for (...) {
db.execSQL("...", new Object[]{});
}
db.setTransactionSuccessful();
} catch (Exception e) {
} finally {
db.endTransaction();
}</code></pre></div>
<p>使用事务后性能有明显的提升,以批量操作100条为例,由原来的5s优化成了现在的1s。</p>
Android Single Sqlite Connection
2013-08-16T00:00:00+00:00
/android/sqlite/2013/08/16/android-single-sqlite-connection
<p>最近在做数据同步的功能,其中在用户第一次同步时会下载数据,如果数据量比较大的话会比较耗时,十几秒的时间很是正常,但是下载数据的时候让用户在那干等不让他进行任何操作用户体验是很差的,那这个时候就只有新开一个线程,让下载数据的一系列动作在后台进行。</p>
<p>所谓下载数据就是把数据从网络取回来的时候把输入insert到本地sqlite数据库中,这时候问题就来了,如果在下载数据的同时用户也在进行数据库的操作(如记录等),这个时候就会有冲突并报出异常,解决的办法是始终让整个Application保持一个database连接,这样的话即使多线程同时访问sqlite,database对象使用java锁会保持访问的序列化。那么如果保持Application是一个database连接呢?</p>
<p>我们一般都是用SqliteOpenHelper来管理数据库,而一个Helper实例会产生一个database连接,所以我们只需要让整个Application产生一个SqliteOpenHelper的实例就ok了,没错,就是单例模式,废话不多说,看代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">DBHelper</span> <span class="n">extends</span> <span class="no">SQLiteOpenHelper</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">DB_NAME</span> <span class="o">=</span> <span class="s2">"food.db"</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">DB_VERSION</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="no">DBHelper</span> <span class="n">helper</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">synchronized</span> <span class="no">DBHelper</span> <span class="n">getInstance</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">helper</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">helper</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">DBHelper</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">helper</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="no">DBHelper</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="no">DB_NAME</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="no">DB_VERSION</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>接下来就是在你的Application类或者Activity类调用了。值得一说的是下面这个代码是不正确的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyApplication</span> <span class="n">extends</span> <span class="no">Application</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">Context</span> <span class="n">context</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">DBHelper</span> <span class="n">dbHelper</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">();</span>
<span class="n">context</span> <span class="o">=</span> <span class="n">getApplicationContext</span><span class="p">();</span>
<span class="n">dbHelper</span> <span class="o">=</span> <span class="no">DBHelper</span><span class="o">.</span><span class="n">getInstance</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onTerminate</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onTerminate</span><span class="p">();</span>
<span class="n">dbHelper</span><span class="o">.</span><span class="n">close</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>因为onTerminate()方法并不会一直执行,在由于异常退出的时候这个方法就不会执行。所以我的解决方案是在主ActivityGroup的onDestroy方法里也会执行一次数据库的关闭,确保万无一失。</p>
<p>参考资料:
<a href="http://stackoverflow.com/questions/2493331/what-are-the-best-practices-for-sqlite-on-android">http://stackoverflow.com/questions/2493331/what-are-the-best-practices-for-sqlite-on-android</a></p>
Android Sqlite Database Upgrade
2013-08-11T00:00:00+00:00
/android/sqlite/2013/08/11/android-sqlite-database-upgrade
<p>本周着手开发数据同步的功能,但首先要解决的就是sqlite数据库升级的问题,关于数据库升级有蛮多方面涉及到,也许你是新增加了功能,所以新建了表,也许你为某些表增加了些字段,也许你是重构了数据模型与数据结构,不管如何升级,必须要满足用户正常升级的情况下原来的数据不会丢失。关于正确的数据库升级做法网上资料比较少,这次就来介绍下看到的国外一位大牛总结的数据库升级的正确做法。</p>
<h2 id="version-1-of-your-database">Version 1 of your database</h2>
<p>大多数我们都是用android提供的SQLiteOpenHelper来创建和管理数据库,如下代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">DbHelper</span> <span class="n">extends</span> <span class="no">SQLiteOpenHelper</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">DATABASE_NAME</span> <span class="o">=</span> <span class="s2">"mysample.db"</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">DATABASE_VERSION</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">DATABASE_CREATE_SAMPLE_TABLE</span> <span class="o">=</span> <span class="s2">"CREATE TABLE tblSample (_id integer primary key autoincrement, name varchar(32);"</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">DbHelper</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="no">DATABASE_NAME</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="no">DATABASE_VERSION</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">SQLiteDatabase</span> <span class="n">database</span><span class="p">)</span> <span class="p">{</span>
<span class="n">database</span><span class="o">.</span><span class="n">execSQL</span><span class="p">(</span><span class="no">DATABASE_CREATE_SAMPLE_TABLE</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onUpgrade</span><span class="p">(</span><span class="no">SQLiteDatabase</span> <span class="n">db</span><span class="p">,</span> <span class="n">int</span> <span class="n">oldVersion</span><span class="p">,</span> <span class="n">int</span> <span class="n">newVersion</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">Do</span> <span class="n">nothing</span> <span class="k">for</span> <span class="n">now</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>上述代码每次执行的时候都会检查当前版本的数据库是否存在,如果不存在则会执行onCreate方法来创建新的数据库,然后会把数据库的名字和版本号存储起来;如果已经存在,则会比较当前版本和DATABASE_VERSION的大小,然后就会去执行onUpgrade方法。</p>
<h2 id="version-2-of-your-database">Version 2 of your database</h2>
<p>问题是,最好的处理升级的方法是什么,这里认为最好的方法是循环处理每一个版本的数据库变化,看示例:</p>
<p>假设下一版本想为tblSample表新增一个“address”的字段,新的创建语句应该像这样:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">CREATE</span> <span class="no">TABLE</span> <span class="n">tblSample</span>
<span class="p">(</span>
<span class="n">_id</span> <span class="n">integer</span> <span class="n">primary</span> <span class="n">key</span> <span class="n">autoincrement</span><span class="p">,</span>
<span class="nb">name</span> <span class="n">varchar</span><span class="p">(</span><span class="mi">32</span><span class="p">),</span>
<span class="n">address</span> <span class="n">varchar</span><span class="p">(</span><span class="mi">128</span><span class="p">)</span>
<span class="p">);</span></code></pre></div>
<p>那么看下新的代码会是什么样的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">DbHelper</span> <span class="n">extends</span> <span class="no">SQLiteOpenHelper</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">DATABASE_NAME</span> <span class="o">=</span> <span class="s2">"mysample.db"</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">DATABASE_VERSION</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">DATABASE_CREATE_SAMPLE_TABLE</span> <span class="o">=</span> <span class="s2">"CREATE TABLE tblSample (_id integer primary key autoincrement, name varchar(32), address varchar(128);"</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">DbHelper</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="no">DATABASE_NAME</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="no">DATABASE_VERSION</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">SQLiteDatabase</span> <span class="n">database</span><span class="p">)</span> <span class="p">{</span>
<span class="n">database</span><span class="o">.</span><span class="n">execSQL</span><span class="p">(</span><span class="no">DATABASE_CREATE_SAMPLE_TABLE</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onUpgrade</span><span class="p">(</span><span class="no">SQLiteDatabase</span> <span class="n">db</span><span class="p">,</span> <span class="n">int</span> <span class="n">oldVersion</span><span class="p">,</span> <span class="n">int</span> <span class="n">newVersion</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">oldVersion</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">newVersion</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">switch</span> <span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">db</span><span class="o">.</span><span class="n">execSQL</span><span class="p">(</span><span class="s2">"ALTER TABLE tblSample ADD address varchar(128)"</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>代码逻辑很简单,就是一个for循环加上switch…case…语句,然后上述代码却能处理所有的数据库升级,不管你是从版本1升级到版本9也好,还是从版本4升级到版本5也好,都可以从容的解决,确切的说,它将能解决从之前的所有版本升级到当前最新的版本。</p>
<p>须要说明的是,如果有些莫名其妙的用户从高版本升级到低版本(确切的说是降级),例如从版本3不小心降级到版本1了,这种情况下如果只是有了上述代码则就会抛出异常,造成系统崩溃。android中数据库降级则会执行onDowngrade方法,为防止有这种情况发生,同样须要重新这个方法防止程序的异常。</p>
Android为自定义View添加属性
2013-08-04T00:00:00+00:00
/android/2013/08/04/android-custom-xml-attributes-for-your-custom-widgets
<p>Android 自定义View 己经不是什么新鲜话题,Android Api提供了一大堆基础组件给我们,需要什么特定功能还需要我们继承它们然后定制更加丰富的功能。那么如何给自定义的View添加一些自定义xml属性呢,如one:textTitle=”“,不仅如此,我们知道xml中有一个android:onClick=”onClickMethod”,这样在Activity中就不需要给该View设置监听器了,那么有没有类似的自定义listener的属性呢?答案是肯定的。</p>
<p>先来看下我们最后想要定义的格式:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">com</span><span class="o">.</span><span class="n">boohee</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">Navbar</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/navbar"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">one</span><span class="p">:</span><span class="n">textTitle</span><span class="o">=</span><span class="s2">"@string/tab_more_text"</span>
<span class="ss">one</span><span class="p">:</span><span class="n">onAction</span><span class="o">=</span><span class="s2">"onAction"</span> <span class="sr">/></span></code></pre></div>
<p>接着便会有如下的效果:</p>
<p><img src="/image/one_navbar.png" /></p>
<p>其中textTitle是定义navbar的标题,onAction是navbar上“保存”按钮的事件。好了,下面就来看下代码实现:</p>
<h2 id="attrsxml">定义attrs.xml</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="p">?</span><span class="n">xml</span> <span class="n">version</span><span class="o">=</span><span class="s2">"1.0"</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="sc">?></span>
<span class="o"><</span><span class="n">resources</span><span class="o">></span>
<span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"Navbar"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"textTitle"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"string|reference"</span> <span class="sr">/></span>
<span class="sr"> <attr name="onAction" format="string" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/declare-styleable></span>
<span class="sr"></</span><span class="n">resources</span><span class="o">></span></code></pre></div>
<p>上面attrs.xml文件中定义了两个属性,关于自定义属性格式,见这篇blog<a href="/blog/2013/07/30/android-custome-attribute-format/">Android中自定义属性格式详解</a></p>
<h2 id="view">自定义View的初始化下添加代码</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Navbar</span> <span class="n">extends</span> <span class="no">FrameLayout</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">Context</span> <span class="n">ctx</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">Button</span> <span class="n">left_btn</span><span class="p">,</span> <span class="n">right_btn</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">TextView</span> <span class="n">title</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">Navbar</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">);</span>
<span class="n">setUp</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">Navbar</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">attrs</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">attrs</span><span class="p">);</span>
<span class="n">setUp</span><span class="p">();</span>
<span class="no">TypedArray</span> <span class="n">a</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">obtainStyledAttributes</span><span class="p">(</span><span class="n">attrs</span><span class="p">,</span> <span class="n">R</span><span class="o">.</span><span class="n">styleable</span><span class="o">.</span><span class="n">Navbar</span><span class="p">);</span>
<span class="n">final</span> <span class="n">int</span> <span class="n">N</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">getIndexCount</span><span class="p">();</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">N</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="n">int</span> <span class="kp">attr</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">getIndex</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
<span class="n">switch</span> <span class="p">(</span><span class="kp">attr</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">R</span><span class="o">.</span><span class="n">styleable</span><span class="o">.</span><span class="n">Navbar_textTitle</span><span class="p">:</span>
<span class="n">title</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="n">getString</span><span class="p">(</span><span class="kp">attr</span><span class="p">));</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="n">R</span><span class="o">.</span><span class="n">styleable</span><span class="o">.</span><span class="n">Navbar_onAction</span><span class="p">:</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">a</span><span class="o">.</span><span class="n">recycle</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">void</span> <span class="n">setUp</span><span class="p">()</span> <span class="p">{</span>
<span class="n">ctx</span> <span class="o">=</span> <span class="n">getContext</span><span class="p">();</span>
<span class="n">addView</span><span class="p">(</span><span class="no">LayoutInflater</span><span class="o">.</span><span class="n">from</span><span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">getContext</span><span class="p">())</span><span class="o">.</span><span class="n">inflate</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">navbar</span><span class="p">,</span> <span class="n">null</span><span class="p">));</span>
<span class="n">left_btn</span> <span class="o">=</span> <span class="p">(</span><span class="no">Button</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">left_btn</span><span class="p">);</span>
<span class="n">title</span> <span class="o">=</span> <span class="p">(</span><span class="no">TextView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">title</span><span class="p">);</span>
<span class="n">right_btn</span> <span class="o">=</span> <span class="p">(</span><span class="no">Button</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">right_btn</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="xml">在XML布局文件中使用</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="p">?</span><span class="n">xml</span> <span class="n">version</span><span class="o">=</span><span class="s2">"1.0"</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="sc">?></span>
<span class="o"><</span><span class="no">LinearLayout</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">xmlns</span><span class="p">:</span><span class="n">one</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/com.boohee.one"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">background</span><span class="o">=</span><span class="s2">"@drawable/main_bg"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">orientation</span><span class="o">=</span><span class="s2">"vertical"</span> <span class="o">></span>
<span class="o"><</span><span class="n">com</span><span class="o">.</span><span class="n">boohee</span><span class="o">.</span><span class="n">myview</span><span class="o">.</span><span class="n">Navbar</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/navbar"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">one</span><span class="p">:</span><span class="n">textTitle</span><span class="o">=</span><span class="s2">"@string/tab_more_text"</span> <span class="sr">/></span>
<span class="sr"></</span><span class="no">LinearLayout</span><span class="o">></span></code></pre></div>
<p>需要注意的是根布局要加上命名空间xmlns:one=”http://schemas.android.com/apk/res/com.boohee.one”</p>
<h2 id="section">添加一个回调属性</h2>
<p>上面说明了如何自定义一些基础属性,那么如何像android:onClick属性一样自定义一个方法回调属性呢,这个一开始实在不晓得如何下手,还好android是开源的,通过看源码后终于有了方法,废话不多说,看代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">case</span> <span class="n">R</span><span class="o">.</span><span class="n">styleable</span><span class="o">.</span><span class="n">Navbar_onAction</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">isRestricted</span><span class="p">())</span> <span class="p">{</span>
<span class="kp">throw</span> <span class="kp">new</span> <span class="no">IllegalStateException</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">right_btn</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">VISIBLE</span><span class="p">);</span>
<span class="n">final</span> <span class="nb">String</span> <span class="n">handlerName</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">getString</span><span class="p">(</span><span class="kp">attr</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">handlerName</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">right_btn</span><span class="o">.</span><span class="n">setOnClickListener</span><span class="p">(</span><span class="kp">new</span> <span class="no">OnClickListener</span><span class="p">()</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">Method</span> <span class="n">mHandler</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onClick</span><span class="p">(</span><span class="no">View</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mHandler</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">try</span> <span class="p">{</span>
<span class="n">mHandler</span> <span class="o">=</span> <span class="n">getContext</span><span class="p">()</span><span class="o">.</span><span class="n">getClass</span><span class="p">()</span><span class="o">.</span><span class="n">getMethod</span><span class="p">(</span><span class="n">handlerName</span><span class="p">,</span>
<span class="no">View</span><span class="o">.</span><span class="n">class</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">NoSuchMethodException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kp">throw</span> <span class="kp">new</span> <span class="no">IllegalStateException</span><span class="p">(</span><span class="s2">"NoSuchMethodException"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">try</span> <span class="p">{</span>
<span class="n">mHandler</span><span class="o">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">getContext</span><span class="p">(),</span> <span class="n">right_btn</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">IllegalAccessException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kp">throw</span> <span class="kp">new</span> <span class="no">IllegalStateException</span><span class="p">();</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">InvocationTargetException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kp">throw</span> <span class="kp">new</span> <span class="no">IllegalStateException</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span></code></pre></div>
<p>代码倒是不难理解,只是上述代码用到了java的反射机制,这篇blog则讲述了<a href="/blog/2013/07/29/java-reflection/">java的反射机制</a></p>
Android性能优化之使用SparseArray代替HashMap
2013-08-01T00:00:00+00:00
/android/2013/08/01/android-use-sparsearray-for-performance-optimization
<p>最近在重构one的项目,其中用HashMap来缓存ActivityGroup加载过的View,Eclipse给出了一个警告,之前考虑项目进度没怎么在意,这次仔细看了下提示,如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Use</span> <span class="kp">new</span> <span class="no">SparseArray</span><span class="o"><</span><span class="no">View</span><span class="o">></span> <span class="p">(</span><span class="o">.</span><span class="n">.</span><span class="o">.</span><span class="p">)</span> <span class="n">instead</span> <span class="k">for</span> <span class="n">better</span> <span class="n">performance</span></code></pre></div>
<p class="paragraph">
意思就是说用SparseArray来替代,以获取更好的性能。对SparseArray根本不熟悉,甚至都没听过,第一感觉应该是Android提供的类,于是F3进入SparseArray的源码,果不其然,确实是Android提供的一个工具类,部分源码如下:
</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">*</span> <span class="no">Copyright</span> <span class="p">(</span><span class="n">C</span><span class="p">)</span> <span class="mi">2006</span> <span class="no">The</span> <span class="no">Android</span> <span class="no">Open</span> <span class="no">Source</span> <span class="no">Project</span>
<span class="o">*</span>
<span class="o">*</span> <span class="no">Licensed</span> <span class="n">under</span> <span class="n">the</span> <span class="no">Apache</span> <span class="no">License</span><span class="p">,</span> <span class="no">Version</span> <span class="mi">2</span><span class="o">.</span><span class="mi">0</span> <span class="p">(</span><span class="n">the</span> <span class="s2">"License"</span><span class="p">);</span>
<span class="o">*</span> <span class="n">you</span> <span class="n">may</span> <span class="ow">not</span> <span class="n">use</span> <span class="n">this</span> <span class="n">file</span> <span class="n">except</span> <span class="k">in</span> <span class="n">compliance</span> <span class="n">with</span> <span class="n">the</span> <span class="no">License</span><span class="o">.</span>
<span class="o">*</span> <span class="no">You</span> <span class="n">may</span> <span class="n">obtain</span> <span class="n">a</span> <span class="n">copy</span> <span class="n">of</span> <span class="n">the</span> <span class="no">License</span> <span class="n">at</span>
<span class="o">*</span>
<span class="o">*</span> <span class="ss">http</span><span class="p">:</span><span class="sr">//</span><span class="n">www</span><span class="o">.</span><span class="n">apache</span><span class="o">.</span><span class="n">org</span><span class="o">/</span><span class="n">licenses</span><span class="o">/</span><span class="no">LICENSE</span><span class="o">-</span><span class="mi">2</span><span class="o">.</span><span class="mi">0</span>
<span class="o">*</span>
<span class="o">*</span> <span class="no">Unless</span> <span class="n">required</span> <span class="n">by</span> <span class="n">applicable</span> <span class="n">law</span> <span class="ow">or</span> <span class="n">agreed</span> <span class="n">to</span> <span class="k">in</span> <span class="n">writing</span><span class="p">,</span> <span class="n">software</span>
<span class="o">*</span> <span class="n">distributed</span> <span class="n">under</span> <span class="n">the</span> <span class="no">License</span> <span class="n">is</span> <span class="n">distributed</span> <span class="n">on</span> <span class="n">an</span> <span class="s2">"AS IS"</span> <span class="no">BASIS</span><span class="p">,</span>
<span class="o">*</span> <span class="no">WITHOUT</span> <span class="no">WARRANTIES</span> <span class="no">OR</span> <span class="no">CONDITIONS</span> <span class="no">OF</span> <span class="no">ANY</span> <span class="no">KIND</span><span class="p">,</span> <span class="n">either</span> <span class="n">express</span> <span class="ow">or</span> <span class="n">implied</span><span class="o">.</span>
<span class="o">*</span> <span class="no">See</span> <span class="n">the</span> <span class="no">License</span> <span class="k">for</span> <span class="n">the</span> <span class="n">specific</span> <span class="n">language</span> <span class="n">governing</span> <span class="n">permissions</span> <span class="ow">and</span>
<span class="o">*</span> <span class="n">limitations</span> <span class="n">under</span> <span class="n">the</span> <span class="no">License</span><span class="o">.</span>
<span class="o">*/</span>
<span class="n">package</span> <span class="n">android</span><span class="o">.</span><span class="n">util</span><span class="p">;</span>
<span class="n">import</span> <span class="n">com</span><span class="o">.</span><span class="n">android</span><span class="o">.</span><span class="n">internal</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">ArrayUtils</span><span class="p">;</span>
<span class="sr">/**</span>
<span class="sr"> * SparseArrays map integers to Objects. Unlike a normal array of Objects,</span>
<span class="sr"> * there can be gaps in the indices. It is intended to be more efficient</span>
<span class="sr"> * than using a HashMap to map Integers to Objects.</span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">SparseArray</span><span class="o"><</span><span class="n">E</span><span class="o">></span> <span class="n">implements</span> <span class="no">Cloneable</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="no">Object</span> <span class="no">DELETED</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Object</span><span class="p">();</span>
<span class="kp">private</span> <span class="n">boolean</span> <span class="n">mGarbage</span> <span class="o">=</span> <span class="kp">false</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">int</span><span class="o">[]</span> <span class="n">mKeys</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">Object</span><span class="o">[]</span> <span class="n">mValues</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">int</span> <span class="n">mSize</span><span class="p">;</span>
<span class="sr">/**</span>
<span class="sr"> * Creates a new SparseArray containing no mappings.</span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="no">SparseArray</span><span class="p">()</span> <span class="p">{</span>
<span class="n">this</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">/**</span>
<span class="sr"> * Creates a new SparseArray containing no mappings that will not</span>
<span class="sr"> * require any additional memory allocation to store the specified</span>
<span class="sr"> * number of mappings.</span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="no">SparseArray</span><span class="p">(</span><span class="n">int</span> <span class="n">initialCapacity</span><span class="p">)</span> <span class="p">{</span>
<span class="n">initialCapacity</span> <span class="o">=</span> <span class="no">ArrayUtils</span><span class="o">.</span><span class="n">idealIntArraySize</span><span class="p">(</span><span class="n">initialCapacity</span><span class="p">);</span>
<span class="n">mKeys</span> <span class="o">=</span> <span class="kp">new</span> <span class="n">int</span><span class="o">[</span><span class="n">initialCapacity</span><span class="o">]</span><span class="p">;</span>
<span class="n">mValues</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Object</span><span class="o">[</span><span class="n">initialCapacity</span><span class="o">]</span><span class="p">;</span>
<span class="n">mSize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="vi">@SuppressWarnings</span><span class="p">(</span><span class="s2">"unchecked"</span><span class="p">)</span>
<span class="kp">public</span> <span class="no">SparseArray</span><span class="o"><</span><span class="n">E</span><span class="o">></span> <span class="nb">clone</span><span class="p">()</span> <span class="p">{</span>
<span class="no">SparseArray</span><span class="o"><</span><span class="n">E</span><span class="o">></span> <span class="nb">clone</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="n">try</span> <span class="p">{</span>
<span class="nb">clone</span> <span class="o">=</span> <span class="p">(</span><span class="no">SparseArray</span><span class="o"><</span><span class="n">E</span><span class="o">></span><span class="p">)</span> <span class="k">super</span><span class="o">.</span><span class="n">clone</span><span class="p">();</span>
<span class="nb">clone</span><span class="o">.</span><span class="n">mKeys</span> <span class="o">=</span> <span class="n">mKeys</span><span class="o">.</span><span class="n">clone</span><span class="p">();</span>
<span class="nb">clone</span><span class="o">.</span><span class="n">mValues</span> <span class="o">=</span> <span class="n">mValues</span><span class="o">.</span><span class="n">clone</span><span class="p">();</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">CloneNotSupportedException</span> <span class="n">cnse</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">/* ignore */</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">clone</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">/**</span>
<span class="sr"> * Gets the Object mapped from the specified key, or <code>null</</span><span class="n">code</span><span class="o">></span>
<span class="o">*</span> <span class="k">if</span> <span class="n">no</span> <span class="n">such</span> <span class="n">mapping</span> <span class="n">has</span> <span class="n">been</span> <span class="n">made</span><span class="o">.</span>
<span class="o">*/</span>
<span class="kp">public</span> <span class="n">E</span> <span class="n">get</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">null</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">/**</span>
<span class="sr"> * Gets the Object mapped from the specified key, or the specified Object</span>
<span class="sr"> * if no such mapping has been made.</span>
<span class="sr"> */</span>
<span class="vi">@SuppressWarnings</span><span class="p">(</span><span class="s2">"unchecked"</span><span class="p">)</span>
<span class="kp">public</span> <span class="n">E</span> <span class="n">get</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">,</span> <span class="n">E</span> <span class="n">valueIfKeyNotFound</span><span class="p">)</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">binarySearch</span><span class="p">(</span><span class="n">mKeys</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">mSize</span><span class="p">,</span> <span class="n">key</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">mValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">==</span> <span class="no">DELETED</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">valueIfKeyNotFound</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="n">E</span><span class="p">)</span> <span class="n">mValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="sr">/**</span>
<span class="sr"> * Removes the mapping from the specified key, if there was any.</span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">delete</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">)</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">binarySearch</span><span class="p">(</span><span class="n">mKeys</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">mSize</span><span class="p">,</span> <span class="n">key</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">>=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">!=</span> <span class="no">DELETED</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="no">DELETED</span><span class="p">;</span>
<span class="n">mGarbage</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="sr">/**</span>
<span class="sr"> * Alias for {@link #delete(int)}.</span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">remove</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">)</span> <span class="p">{</span>
<span class="n">delete</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">/**</span>
<span class="sr"> * Removes the mapping at the specified index.</span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">removeAt</span><span class="p">(</span><span class="n">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mValues</span><span class="o">[</span><span class="n">index</span><span class="o">]</span> <span class="o">!=</span> <span class="no">DELETED</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mValues</span><span class="o">[</span><span class="n">index</span><span class="o">]</span> <span class="o">=</span> <span class="no">DELETED</span><span class="p">;</span>
<span class="n">mGarbage</span> <span class="o">=</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span></code></pre></div>
<pre><code>mSize++;单纯从字面上来理解,SparseArray指的是稀疏数组(Sparse array),所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容。
</code></pre>
<p>继续阅读SparseArray的源码,从构造方法我们可以看出,它和一般的List一样,可以预先设置容器大小,默认的大小是10:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="no">SparseArray</span><span class="p">()</span> <span class="p">{</span>
<span class="n">this</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">SparseArray</span><span class="p">(</span><span class="n">int</span> <span class="n">initialCapacity</span><span class="p">)</span> <span class="p">{</span>
<span class="n">initialCapacity</span> <span class="o">=</span> <span class="no">ArrayUtils</span><span class="o">.</span><span class="n">idealIntArraySize</span><span class="p">(</span><span class="n">initialCapacity</span><span class="p">);</span>
<span class="n">mKeys</span> <span class="o">=</span> <span class="kp">new</span> <span class="n">int</span><span class="o">[</span><span class="n">initialCapacity</span><span class="o">]</span><span class="p">;</span>
<span class="n">mValues</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Object</span><span class="o">[</span><span class="n">initialCapacity</span><span class="o">]</span><span class="p">;</span>
<span class="n">mSize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>再来看看它对数据的“增删改查”。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">void</span> <span class="n">put</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">,</span> <span class="n">E</span> <span class="n">value</span><span class="p">)</span> <span class="p">{}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">append</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">,</span> <span class="n">E</span> <span class="n">value</span><span class="p">){}</span></code></pre></div>
<p>修改数据起初以为只有setValueAt(int index, E value)可以修改数据,但后来发现put(int key, E value)也可以修改数据,我们查看put(int key, E value)的源码可知,在put数据之前,会先查找要put的数据是否已经存在,如果存在就是修改,不存在就添加。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">void</span> <span class="n">put</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">,</span> <span class="n">E</span> <span class="n">value</span><span class="p">)</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">binarySearch</span><span class="p">(</span><span class="n">mKeys</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">mSize</span><span class="p">,</span> <span class="n">key</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">>=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">i</span> <span class="o">=</span> <span class="o">~</span><span class="n">i</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="n">mSize</span> <span class="o">&&</span> <span class="n">mValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">==</span> <span class="no">DELETED</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mKeys</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">key</span><span class="p">;</span>
<span class="n">mValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mGarbage</span> <span class="o">&&</span> <span class="n">mSize</span> <span class="o">>=</span> <span class="n">mKeys</span><span class="o">.</span><span class="n">length</span><span class="p">)</span> <span class="p">{</span>
<span class="n">gc</span><span class="p">();</span>
<span class="sr">//</span> <span class="no">Search</span> <span class="n">again</span> <span class="n">because</span> <span class="n">indices</span> <span class="n">may</span> <span class="n">have</span> <span class="n">changed</span><span class="o">.</span>
<span class="n">i</span> <span class="o">=</span> <span class="o">~</span><span class="n">binarySearch</span><span class="p">(</span><span class="n">mKeys</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">mSize</span><span class="p">,</span> <span class="n">key</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mSize</span> <span class="o">>=</span> <span class="n">mKeys</span><span class="o">.</span><span class="n">length</span><span class="p">)</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">n</span> <span class="o">=</span> <span class="no">ArrayUtils</span><span class="o">.</span><span class="n">idealIntArraySize</span><span class="p">(</span><span class="n">mSize</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">int</span><span class="o">[]</span> <span class="n">nkeys</span> <span class="o">=</span> <span class="kp">new</span> <span class="n">int</span><span class="o">[</span><span class="n">n</span><span class="o">]</span><span class="p">;</span>
<span class="no">Object</span><span class="o">[]</span> <span class="n">nvalues</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Object</span><span class="o">[</span><span class="n">n</span><span class="o">]</span><span class="p">;</span>
<span class="sr">//</span> <span class="no">Log</span><span class="o">.</span><span class="n">e</span><span class="p">(</span><span class="s2">"SparseArray"</span><span class="p">,</span> <span class="s2">"grow "</span> <span class="o">+</span> <span class="n">mKeys</span><span class="o">.</span><span class="n">length</span> <span class="o">+</span> <span class="s2">" to "</span> <span class="o">+</span> <span class="n">n</span><span class="p">);</span>
<span class="no">System</span><span class="o">.</span><span class="n">arraycopy</span><span class="p">(</span><span class="n">mKeys</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">nkeys</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">mKeys</span><span class="o">.</span><span class="n">length</span><span class="p">);</span>
<span class="no">System</span><span class="o">.</span><span class="n">arraycopy</span><span class="p">(</span><span class="n">mValues</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">nvalues</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">mValues</span><span class="o">.</span><span class="n">length</span><span class="p">);</span>
<span class="n">mKeys</span> <span class="o">=</span> <span class="n">nkeys</span><span class="p">;</span>
<span class="n">mValues</span> <span class="o">=</span> <span class="n">nvalues</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mSize</span> <span class="o">-</span> <span class="n">i</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">Log</span><span class="o">.</span><span class="n">e</span><span class="p">(</span><span class="s2">"SparseArray"</span><span class="p">,</span> <span class="s2">"move "</span> <span class="o">+</span> <span class="p">(</span><span class="n">mSize</span> <span class="o">-</span> <span class="n">i</span><span class="p">));</span>
<span class="no">System</span><span class="o">.</span><span class="n">arraycopy</span><span class="p">(</span><span class="n">mKeys</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">mKeys</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">mSize</span> <span class="o">-</span> <span class="n">i</span><span class="p">);</span>
<span class="no">System</span><span class="o">.</span><span class="n">arraycopy</span><span class="p">(</span><span class="n">mValues</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">mValues</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">mSize</span> <span class="o">-</span> <span class="n">i</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">mKeys</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">key</span><span class="p">;</span>
<span class="n">mValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>所以,修改数据实际也有两种方法:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">void</span> <span class="n">put</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">,</span> <span class="n">E</span> <span class="n">value</span><span class="p">)</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">setValueAt</span><span class="p">(</span><span class="n">int</span> <span class="n">index</span><span class="p">,</span> <span class="n">E</span> <span class="n">value</span><span class="p">)</span></code></pre></div>
<p>最后再来看看如何查找数据。有两个方法可以查询取值:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">E</span> <span class="n">get</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">)</span>
<span class="kp">public</span> <span class="n">E</span> <span class="n">get</span><span class="p">(</span><span class="n">int</span> <span class="n">key</span><span class="p">,</span> <span class="n">E</span> <span class="n">valueIfKeyNotFound</span><span class="p">)</span></code></pre></div>
<p>其中get(int key)也只是调用了 get(int key,E valueIfKeyNotFound),最后一个从传参的变量名就能看出,传入的是找不到的时候返回的值.get(int key)当找不到的时候,默认返回null。</p>
<p>查看第几个位置的键:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">int</span> <span class="n">keyAt</span><span class="p">(</span><span class="n">int</span> <span class="n">index</span><span class="p">)</span></code></pre></div>
<p>有一点需要注意的是,查看键所在位置,由于是采用二分法查找键的位置,所以找不到时返回小于0的数值,而不是返回-1。返回的负值是表示它在找不到时所在的位置。</p>
<p>查看第几个位置的值:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">E</span> <span class="n">valueAt</span><span class="p">(</span><span class="n">int</span> <span class="n">index</span><span class="p">)</span></code></pre></div>
<p>查看值所在位置,没有的话返回-1:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">int</span> <span class="n">indexOfValue</span><span class="p">(</span><span class="n">E</span> <span class="n">value</span><span class="p">)</span></code></pre></div>
<p>最后,发现其核心就是折半查找函数(binarySearch),算法设计的很不错。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">private</span> <span class="n">static</span> <span class="n">int</span> <span class="n">binarySearch</span><span class="p">(</span><span class="n">int</span><span class="o">[]</span> <span class="n">a</span><span class="p">,</span> <span class="n">int</span> <span class="n">start</span><span class="p">,</span> <span class="n">int</span> <span class="n">len</span><span class="p">,</span> <span class="n">int</span> <span class="n">key</span><span class="p">)</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">high</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="n">len</span><span class="p">,</span> <span class="n">low</span> <span class="o">=</span> <span class="n">start</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">guess</span><span class="p">;</span>
<span class="k">while</span> <span class="p">(</span><span class="n">high</span> <span class="o">-</span> <span class="n">low</span> <span class="o">&</span><span class="n">gt</span><span class="p">;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="n">guess</span> <span class="o">=</span> <span class="p">(</span><span class="n">high</span> <span class="o">+</span> <span class="n">low</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="o">[</span><span class="n">guess</span><span class="o">]</span> <span class="o">&</span><span class="n">lt</span><span class="p">;</span> <span class="n">key</span><span class="p">)</span>
<span class="n">low</span> <span class="o">=</span> <span class="n">guess</span><span class="p">;</span>
<span class="k">else</span>
<span class="n">high</span> <span class="o">=</span> <span class="n">guess</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">high</span> <span class="o">==</span> <span class="n">start</span> <span class="o">+</span> <span class="n">len</span><span class="p">)</span>
<span class="k">return</span> <span class="o">~</span><span class="p">(</span><span class="n">start</span> <span class="o">+</span> <span class="n">len</span><span class="p">);</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">a</span><span class="o">[</span><span class="n">high</span><span class="o">]</span> <span class="o">==</span> <span class="n">key</span><span class="p">)</span>
<span class="k">return</span> <span class="n">high</span><span class="p">;</span>
<span class="k">else</span>
<span class="k">return</span> <span class="o">~</span><span class="n">high</span><span class="p">;</span>
<span class="p">}</span>
<span class="err">相应的也有</span><span class="no">SparseBooleanArray</span><span class="err">,用来取代</span><span class="no">HashMap</span><span class="o"><</span><span class="nb">Integer</span><span class="p">,</span> <span class="no">Boolean</span><span class="o">></span><span class="err">,</span><span class="no">SparseIntArray</span><span class="err">用来取代</span><span class="no">HashMap</span><span class="o"><</span><span class="nb">Integer</span><span class="p">,</span> <span class="nb">Integer</span><span class="o">></span><span class="err">,大家有兴趣的可以研究。</span></code></pre></div>
<h2 id="section">总结</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">SparseArray</span><span class="err">是</span><span class="n">android</span><span class="err">里为</span><span class="o"><</span><span class="no">Interger</span><span class="p">,</span><span class="no">Object</span><span class="o">></span><span class="err">这样的</span><span class="no">Hashmap</span><span class="err">而专门写的类</span><span class="p">,</span><span class="err">目的是提高效率,其核心是折半查找函数(</span><span class="n">binarySearch</span><span class="err">)。在</span><span class="no">Android</span><span class="err">中,当我们需要定义</span>
<span class="no">HashMap</span><span class="o"><</span><span class="nb">Integer</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> <span class="n">hashMap</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">HashMap</span><span class="o"><</span><span class="nb">Integer</span><span class="p">,</span> <span class="n">E</span><span class="o">></span><span class="p">();</span>
<span class="err">时,我们可以使用如下的方式来取得更好的性能</span><span class="o">.</span>
<span class="no">SparseArray</span><span class="o"><</span><span class="n">E</span><span class="o">></span> <span class="n">sparseArray</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">SparseArray</span><span class="o"><</span><span class="n">E</span><span class="o">></span><span class="p">();</span></code></pre></div>
<p>最后,看看重构后的ActivityGroupManager:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">ActivityGroupManager</span> <span class="p">{</span>
<span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">TAG</span> <span class="o">=</span> <span class="no">ActivityGroupManager</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">getName</span><span class="p">();</span>
<span class="kp">private</span> <span class="no">SparseArray</span><span class="o"><</span><span class="no">View</span><span class="o">></span> <span class="n">array</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">ViewGroup</span> <span class="n">container</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">ActivityGroupManager</span><span class="p">()</span> <span class="p">{</span>
<span class="n">array</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">SparseArray</span><span class="o"><</span><span class="no">View</span><span class="o">></span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">setContainer</span><span class="p">(</span><span class="no">ViewGroup</span> <span class="n">container</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">container</span> <span class="o">=</span> <span class="n">container</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">showContainer</span><span class="p">(</span><span class="n">int</span> <span class="n">num</span><span class="p">,</span> <span class="no">View</span> <span class="n">view</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">array</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">num</span><span class="p">)</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">array</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="n">view</span><span class="p">);</span>
<span class="n">container</span><span class="o">.</span><span class="n">addView</span><span class="p">(</span><span class="n">view</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">array</span><span class="o">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="no">View</span> <span class="n">v</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="n">valueAt</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
<span class="n">v</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">GONE</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">view</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">VISIBLE</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
Android中自定义属性格式详解
2013-07-30T00:00:00+00:00
/android/2013/07/30/android-custome-attribute-format
<p>在Android项目的实际开发中,免不了要自定义一些控件或者view,更高深一点的自定义view也应该可以直接在xml自定义属性,今天就来分享下自定义属性的格式。</p>
<h2 id="referenceid">1. reference:参考某一资源ID</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"名称"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"background"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"reference"</span> <span class="sr">/></span>
<span class="sr"></</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span><span class="o">></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">ImageView</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">background</span><span class="o">=</span><span class="s2">"@drawable/图片ID"</span> <span class="sr">/></span></code></pre></div>
<h2 id="color">2. color:颜色值</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"名称"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"textColor"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"color"</span> <span class="sr">/></span>
<span class="sr"></</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span><span class="o">></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">TextView</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">textColor</span><span class="o">=</span><span class="s2">"#00FF00"</span> <span class="sr">/></span></code></pre></div>
<h2 id="boolean">3. boolean:布尔值</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"名称"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"focusable"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"boolean"</span> <span class="sr">/></span>
<span class="sr"></</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span><span class="o">></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">Button</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">focusable</span><span class="o">=</span><span class="s2">"true"</span> <span class="sr">/></span></code></pre></div>
<h2 id="dimension">4. dimension:尺寸值</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"名称"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"layout_width"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"dimension"</span> <span class="sr">/></span>
<span class="sr"></</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span><span class="o">></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">Button</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"42dip"</span> <span class="sr">/></span></code></pre></div>
<h2 id="float">5. float:浮点值</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"AlphaAnimation"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"fromAlpha"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"float"</span> <span class="sr">/></span>
<span class="sr"> <attr name="toAlpha" format="float" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/declare-styleable></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">alpha</span>
<span class="ss">android</span><span class="p">:</span><span class="n">fromAlpha</span><span class="o">=</span><span class="s2">"1.0"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">toAlpha</span><span class="o">=</span><span class="s2">"0.7"</span> <span class="sr">/></span></code></pre></div>
<h2 id="integer">6. integer:整型值</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"AnimatedRotateDrawable"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"visible"</span> <span class="sr">/></span>
<span class="sr"> <attr name="frameDuration" format="integer" /</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"framesCount"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"integer"</span> <span class="sr">/></span>
<span class="sr"> <attr name="pivotX" /</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"pivotY"</span> <span class="sr">/></span>
<span class="sr"> <attr name="drawable" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/declare-styleable></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">animated</span><span class="o">-</span><span class="n">rotate</span>
<span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawable</span><span class="o">=</span><span class="s2">"@drawable/图片ID"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">frameDuration</span><span class="o">=</span><span class="s2">"100"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">framesCount</span><span class="o">=</span><span class="s2">"12"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">pivotX</span><span class="o">=</span><span class="s2">"50%"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">pivotY</span><span class="o">=</span><span class="s2">"50%"</span> <span class="sr">/></span></code></pre></div>
<h2 id="string">7. string:字符串</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"MapView"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"apiKey"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"string"</span> <span class="sr">/></span>
<span class="sr"></</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span><span class="o">></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">com</span><span class="o">.</span><span class="n">google</span><span class="o">.</span><span class="n">android</span><span class="o">.</span><span class="n">maps</span><span class="o">.</span><span class="n">MapView</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">apiKey</span><span class="o">=</span><span class="s2">"0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"</span> <span class="sr">/></span></code></pre></div>
<h2 id="fraction">8. fraction:百分数</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"RotateDrawable"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"visible"</span> <span class="sr">/></span>
<span class="sr"> <attr name="fromDegrees" format="float" /</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"toDegrees"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"float"</span> <span class="sr">/></span>
<span class="sr"> <attr name="pivotX" format="fraction" /</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"pivotY"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"fraction"</span> <span class="sr">/></span>
<span class="sr"> <attr name="drawable" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/declare-styleable></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">rotate</span>
<span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">duration</span><span class="o">=</span><span class="s2">"5000"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">fromDegrees</span><span class="o">=</span><span class="s2">"0"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">interpolator</span><span class="o">=</span><span class="s2">"@anim/动画ID"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">pivotX</span><span class="o">=</span><span class="s2">"200%"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">pivotY</span><span class="o">=</span><span class="s2">"300%"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">repeatCount</span><span class="o">=</span><span class="s2">"infinite"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">repeatMode</span><span class="o">=</span><span class="s2">"restart"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">toDegrees</span><span class="o">=</span><span class="s2">"360"</span> <span class="sr">/></span></code></pre></div>
<h2 id="enum">9. enum:枚举值</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"名称"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"orientation"</span><span class="o">></span>
<span class="o"><</span><span class="n">enum</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"horizontal"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"0"</span> <span class="sr">/></span>
<span class="sr"> <enum name="vertical" value="1" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/attr></span>
<span class="sr"></</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span><span class="o">></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">LinearLayout</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">orientation</span><span class="o">=</span><span class="s2">"vertical"</span> <span class="sr">/></span></code></pre></div>
<h2 id="flag">10. flag:位或运算</h2>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"名称"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"windowSoftInputMode"</span><span class="o">></span>
<span class="o"><</span><span class="n">flag</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"stateUnspecified"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"0"</span> <span class="sr">/></span>
<span class="sr"> <flag name="stateUnchanged" value="1" /</span><span class="o">></span>
<span class="o"><</span><span class="n">flag</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"stateHidden"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"2"</span> <span class="sr">/></span>
<span class="sr"> <flag name="stateAlwaysHidden" value="3" /</span><span class="o">></span>
<span class="o"><</span><span class="n">flag</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"stateVisible"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"4"</span> <span class="sr">/></span>
<span class="sr"> <flag name="stateAlwaysVisible" value="5" /</span><span class="o">></span>
<span class="o"><</span><span class="n">flag</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"adjustUnspecified"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"0x00"</span> <span class="sr">/></span>
<span class="sr"> <flag name="adjustResize" value="0x10" /</span><span class="o">></span>
<span class="o"><</span><span class="n">flag</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"adjustPan"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"0x20"</span> <span class="sr">/></span>
<span class="sr"> <flag name="adjustNothing" value="0x30" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/attr></span>
<span class="sr"></</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span><span class="o">></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">activity</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">".StyleAndThemeActivity"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">label</span><span class="o">=</span><span class="s2">"@string/app_name"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">windowSoftInputMode</span><span class="o">=</span><span class="s2">"stateUnspecified | stateUnchanged | stateHidden"</span> <span class="o">></span>
<span class="o"><</span><span class="n">intent</span><span class="o">-</span><span class="n">filter</span><span class="o">></span>
<span class="o"><</span><span class="n">action</span> <span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">"android.intent.action.MAIN"</span> <span class="sr">/></span>
<span class="sr"> <category android:name="android.intent.category.LAUNCHER" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/intent-filter></span>
<span class="sr"></</span><span class="n">activity</span><span class="o">></span></code></pre></div>
<h2 id="section">注意</h2>
<p>属性定义时可以指定多种类型值,如:</p>
<p>属性定义:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"名称"</span><span class="o">></span>
<span class="o"><</span><span class="kp">attr</span> <span class="nb">name</span><span class="o">=</span><span class="s2">"background"</span> <span class="nb">format</span><span class="o">=</span><span class="s2">"reference|color"</span> <span class="sr">/></span>
<span class="sr"></</span><span class="n">declare</span><span class="o">-</span><span class="n">styleable</span><span class="o">></span></code></pre></div>
<p>属性使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="no">ImageView</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"42dip"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">background</span><span class="o">=</span><span class="s2">"@drawable/图片ID|#00FF00"</span> <span class="sr">/></span></code></pre></div>
java反射机制
2013-07-29T00:00:00+00:00
/java/2013/07/29/java-reflection
<p>Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”。</p>
<p>在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。</p>
<p>Java 反射机制主要提供了以下功能:</p>
<ul>
<li>在运行时判断任意一个对象所属的类。</li>
<li>在运行时构造任意一个类的对象。</li>
<li>在运行时判断任意一个类所具有的成员变量和方法。</li>
<li>在运行时调用任意一个对象的方法。</li>
</ul>
<p>Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods。</p>
<p>一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。</p>
<p>尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。</p>
<p>在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:</p>
<ul>
<li>Class类:代表一个类。</li>
<li>Field 类:代表类的成员变量(成员变量也称为类的属性)。</li>
<li>Method类:代表类的方法。</li>
<li>Constructor 类:代表类的构造方法。</li>
<li>Array类:提供了动态创建数组,以及访问数组的元素的静态方法。</li>
</ul>
<p>下面给出几个例子看看Reflection API的实际运用:</p>
<h2 id="section">1.得到某个对象的属性</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="no">Object</span> <span class="n">getProperty</span><span class="p">(</span><span class="no">Object</span> <span class="n">owner</span><span class="p">,</span> <span class="nb">String</span> <span class="n">fieldName</span><span class="p">)</span> <span class="n">throws</span> <span class="no">Exception</span> <span class="p">{</span>
<span class="no">Class</span> <span class="n">ownerClass</span> <span class="o">=</span> <span class="n">owner</span><span class="o">.</span><span class="n">getClass</span><span class="p">();</span>
<span class="no">Field</span> <span class="n">field</span> <span class="o">=</span> <span class="n">ownerClass</span><span class="o">.</span><span class="n">getField</span><span class="p">(</span><span class="n">fieldName</span><span class="p">);</span>
<span class="no">Object</span> <span class="n">property</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">owner</span><span class="p">);</span>
<span class="k">return</span> <span class="n">property</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>Class ownerClass = owner.getClass():得到该对象的Class。</p>
<p>Field field = ownerClass.getField(fieldName):通过Class得到类声明的属性。</p>
<p>Object property = field.get(owner):通过对象得到该属性的实例,如果这个属性是非公有的,这里会报IllegalAccessException。</p>
<h2 id="section-1">2.得到某个类的静态属性</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="no">Object</span> <span class="n">getStaticProperty</span><span class="p">(</span><span class="nb">String</span> <span class="n">className</span><span class="p">,</span> <span class="nb">String</span> <span class="n">fieldName</span><span class="p">)</span>
<span class="n">throws</span> <span class="no">Exception</span> <span class="p">{</span>
<span class="no">Class</span> <span class="n">ownerClass</span> <span class="o">=</span> <span class="no">Class</span><span class="o">.</span><span class="n">forName</span><span class="p">(</span><span class="n">className</span><span class="p">);</span>
<span class="no">Field</span> <span class="n">field</span> <span class="o">=</span> <span class="n">ownerClass</span><span class="o">.</span><span class="n">getField</span><span class="p">(</span><span class="n">fieldName</span><span class="p">);</span>
<span class="no">Object</span> <span class="n">property</span> <span class="o">=</span> <span class="n">field</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">ownerClass</span><span class="p">);</span>
<span class="k">return</span> <span class="n">property</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>Class ownerClass = Class.forName(className) :首先得到这个类的Class。</p>
<p>Field field = ownerClass.getField(fieldName):和上面一样,通过Class得到类声明的属性。</p>
<p>Object property = field.get(ownerClass) :这里和上面有些不同,因为该属性是静态的,所以直接从类的Class里取。</p>
<h2 id="section-2">3.执行某对象的方法</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="no">Object</span> <span class="n">invokeMethod</span><span class="p">(</span><span class="no">Object</span> <span class="n">owner</span><span class="p">,</span> <span class="nb">String</span> <span class="n">methodName</span><span class="p">,</span> <span class="no">Object</span><span class="o">[]</span> <span class="n">args</span><span class="p">)</span> <span class="n">throws</span> <span class="no">Exception</span> <span class="p">{</span>
<span class="no">Class</span> <span class="n">ownerClass</span> <span class="o">=</span> <span class="n">owner</span><span class="o">.</span><span class="n">getClass</span><span class="p">();</span>
<span class="no">Class</span><span class="o">[]</span> <span class="n">argsClass</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Class</span><span class="o">[</span><span class="n">args</span><span class="o">.</span><span class="n">length</span><span class="o">]</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">length</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">j</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">argsClass</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="n">getClass</span><span class="p">();</span>
<span class="p">}</span>
<span class="no">Method</span> <span class="nb">method</span> <span class="o">=</span> <span class="n">ownerClass</span><span class="o">.</span><span class="n">getMethod</span><span class="p">(</span><span class="n">methodName</span><span class="p">,</span><span class="n">argsClass</span><span class="p">);</span>
<span class="k">return</span> <span class="nb">method</span><span class="o">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">owner</span><span class="p">,</span> <span class="n">args</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>Class owner_class = owner.getClass() :首先还是必须得到这个对象的Class。</p>
<p>5~9行:配置参数的Class数组,作为寻找Method的条件。</p>
<p>Method method = ownerClass.getMethod(methodName, argsClass):通过methodName和参数的argsClass(方法中的参数类型集合)数组得到要执行的Method。</p>
<p>method.invoke(owner, args):执行该Method.invoke方法的参数是执行这个方法的对象owner,和参数数组args,可以这么理解:owner对象中带有参数args的method方法。返回值是Object,也既是该方法的返回值。</p>
<h2 id="section-3">4.执行某个类的静态方法</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="no">Object</span> <span class="n">invokeStaticMethod</span><span class="p">(</span><span class="nb">String</span> <span class="n">className</span><span class="p">,</span> <span class="nb">String</span> <span class="n">methodName</span><span class="p">,</span>
<span class="no">Object</span><span class="o">[]</span> <span class="n">args</span><span class="p">)</span> <span class="n">throws</span> <span class="no">Exception</span> <span class="p">{</span>
<span class="no">Class</span> <span class="n">ownerClass</span> <span class="o">=</span> <span class="no">Class</span><span class="o">.</span><span class="n">forName</span><span class="p">(</span><span class="n">className</span><span class="p">);</span>
<span class="no">Class</span><span class="o">[]</span> <span class="n">argsClass</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Class</span><span class="o">[</span><span class="n">args</span><span class="o">.</span><span class="n">length</span><span class="o">]</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">length</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">j</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">argsClass</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="n">getClass</span><span class="p">();</span>
<span class="p">}</span>
<span class="no">Method</span> <span class="nb">method</span> <span class="o">=</span> <span class="n">ownerClass</span><span class="o">.</span><span class="n">getMethod</span><span class="p">(</span><span class="n">methodName</span><span class="p">,</span><span class="n">argsClass</span><span class="p">);</span>
<span class="k">return</span> <span class="nb">method</span><span class="o">.</span><span class="n">invoke</span><span class="p">(</span><span class="n">null</span><span class="p">,</span> <span class="n">args</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>基本的原理和实例3相同,不同点是最后一行,invoke的一个参数是null,因为这是静态方法,不需要借助实例运行。</p>
<h2 id="section-4">5.新建实例</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="no">Object</span> <span class="n">newInstance</span><span class="p">(</span><span class="nb">String</span> <span class="n">className</span><span class="p">,</span> <span class="no">Object</span><span class="o">[]</span> <span class="n">args</span><span class="p">)</span> <span class="n">throws</span> <span class="no">Exception</span> <span class="p">{</span>
<span class="no">Class</span> <span class="n">newoneClass</span> <span class="o">=</span> <span class="no">Class</span><span class="o">.</span><span class="n">forName</span><span class="p">(</span><span class="n">className</span><span class="p">);</span>
<span class="no">Class</span><span class="o">[]</span> <span class="n">argsClass</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Class</span><span class="o">[</span><span class="n">args</span><span class="o">.</span><span class="n">length</span><span class="o">]</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">j</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">length</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">j</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">argsClass</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="n">getClass</span><span class="p">();</span>
<span class="p">}</span>
<span class="no">Constructor</span> <span class="n">cons</span> <span class="o">=</span> <span class="n">newoneClass</span><span class="o">.</span><span class="n">getConstructor</span><span class="p">(</span><span class="n">argsClass</span><span class="p">);</span>
<span class="k">return</span> <span class="n">cons</span><span class="o">.</span><span class="n">newInstance</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数,可以直接使用newoneClass.newInstance()来实现。</p>
<p>Class newoneClass = Class.forName(className):第一步,得到要构造的实例的Class。</p>
<p>第5~第9行:得到参数的Class数组。</p>
<p>Constructor cons = newoneClass.getConstructor(argsClass):得到构造子。</p>
<p>cons.newInstance(args):新建实例。</p>
<h2 id="section-5">6.判断是否为某个类的实例</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">boolean</span> <span class="n">isInstance</span><span class="p">(</span><span class="no">Object</span> <span class="n">obj</span><span class="p">,</span> <span class="no">Class</span> <span class="n">cls</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">cls</span><span class="o">.</span><span class="n">isInstance</span><span class="p">(</span><span class="n">obj</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-6">7.得到数组中的某个元素</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="no">Object</span> <span class="n">getByArray</span><span class="p">(</span><span class="no">Object</span> <span class="n">array</span><span class="p">,</span> <span class="n">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Array</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">array</span><span class="p">,</span><span class="n">index</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
Android中ScrollView嵌套WebView
2013-07-27T00:00:00+00:00
/android/2013/07/27/android-scrollview-nested-webview
<p>Android中WebView用来加载html页面,自带滑动效果。ScrollView同样也是自带滑动效果,在项目中如果需要WebView和一些其他view比如TextView一起滑动的话就必须外面嵌套一层ScrollView,这时问题就来了,嵌套之后ScrollView的滑动和WebView的滑动就会有冲突,WebView的滑动不流畅。下面就是解决方案:</p>
<p>自定义一个ScrollView</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyScrollView</span> <span class="n">extends</span> <span class="no">ScrollView</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">GestureDetector</span> <span class="n">mGestureDetector</span><span class="p">;</span>
<span class="no">View</span><span class="o">.</span><span class="n">OnTouchListener</span> <span class="n">mGestureListener</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">MyScrollView</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">,</span> <span class="no">AttributeSet</span> <span class="n">attrs</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">attrs</span><span class="p">);</span>
<span class="n">mGestureDetector</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">GestureDetector</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="kp">new</span> <span class="no">YScrollDetector</span><span class="p">());</span>
<span class="n">setFadingEdgeLength</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onInterceptTouchEvent</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">ev</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">super</span><span class="o">.</span><span class="n">onInterceptTouchEvent</span><span class="p">(</span><span class="n">ev</span><span class="p">)</span> <span class="o">&&</span> <span class="n">mGestureDetector</span><span class="o">.</span><span class="n">onTouchEvent</span><span class="p">(</span><span class="n">ev</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="no">Return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">we</span><span class="err">'</span><span class="n">re</span> <span class="n">scrolling</span> <span class="k">in</span> <span class="n">the</span> <span class="n">x</span> <span class="n">direction</span>
<span class="k">class</span> <span class="nc">YScrollDetector</span> <span class="n">extends</span> <span class="no">SimpleOnGestureListener</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onScroll</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e1</span><span class="p">,</span> <span class="no">MotionEvent</span> <span class="n">e2</span><span class="p">,</span> <span class="n">float</span> <span class="n">distanceX</span><span class="p">,</span> <span class="n">float</span> <span class="n">distanceY</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="no">Math</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">distanceY</span><span class="p">)</span> <span class="o">></span> <span class="no">Math</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">distanceX</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kp">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>上面代码中onInterceptTouchEvent方法是关键,重写这个方法使如果ScrollView有touch事件时不被拦截,这样只要ScrollView有touch事件优先处理,这样就保证了滑动的流畅。</p>
<p>之后就在自己的xml布局文件里用MyScrollView代替ScrollView来布局就ok了。如:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">com</span><span class="o">.</span><span class="n">boohee</span><span class="o">.</span><span class="n">widgets</span><span class="o">.</span><span class="n">MyScrollView</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">background</span><span class="o">=</span><span class="s2">"@drawable/main_bg"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_marginTop</span><span class="o">=</span><span class="s2">"@dimen/default_shadow_margin"</span> <span class="o">></span>
<span class="o"><</span><span class="no">LinearLayout</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">orientation</span><span class="o">=</span><span class="s2">"vertical"</span> <span class="o">></span>
<span class="o"><</span><span class="no">RelativeLayout</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"180dp"</span> <span class="o">></span>
<span class="o"><</span><span class="n">android</span><span class="o">.</span><span class="n">support</span><span class="o">.</span><span class="n">v4</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">ViewPager</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/vp_top_show"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"fill_parent"</span> <span class="sr">/></span>
<span class="sr"> <LinearLayout</span>
<span class="sr"> android:id="@+id/</span><span class="n">dot_layout</span><span class="s2">"</span>
<span class="s2"> android:layout_width="</span><span class="n">fill_parent</span><span class="s2">"</span>
<span class="s2"> android:layout_height="</span><span class="n">wrap_content</span><span class="s2">"</span>
<span class="s2"> android:layout_alignParentBottom="</span><span class="kp">true</span><span class="s2">"</span>
<span class="s2"> android:gravity="</span><span class="n">center_horizontal</span><span class="s2">"</span>
<span class="s2"> android:orientation="</span><span class="n">horizontal</span><span class="s2">"</span>
<span class="s2"> android:padding="</span><span class="mi">10</span><span class="n">dp</span><span class="s2">" ></span>
<span class="s2"> </LinearLayout></span>
<span class="s2"> </RelativeLayout></span>
<span class="s2"> <WebView</span>
<span class="s2"> android:id="</span><span class="err">@</span><span class="o">+</span><span class="nb">id</span><span class="o">/</span><span class="n">wv_show</span><span class="s2">"</span>
<span class="s2"> android:layout_width="</span><span class="n">fill_parent</span><span class="s2">"</span>
<span class="s2"> android:layout_height="</span><span class="n">fill_parent</span><span class="s2">"</span>
<span class="s2"> android:layerType="</span><span class="n">software</span><span class="s2">"</span>
<span class="s2"> android:scrollbars="</span><span class="n">none</span><span class="s2">" /></span>
<span class="s2"> </LinearLayout></span>
<span class="s2"></com.boohee.widgets.MyScrollView></span></code></pre></div>
Android设置WebView背景透明
2013-07-25T00:00:00+00:00
/android/2013/07/25/android-webview-background
<p>Android4.0环境下WebView背景一直都是白色的,和App的整体风格不一致,所以需要把背景设为透明,本来以为非常简单的一个background属性设为透明就好了,可事情并没有这么简单,经过一番周折最后终于搞定,在这里记录下来。</p>
<h2 id="section">网上的解决方案</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="ss">android</span><span class="p">:</span><span class="n">layerType</span><span class="o">=</span><span class="s2">"software"</span><span class="err">(没效果)</span>
<span class="n">mWebView</span><span class="o">.</span><span class="n">setBackgroundColor</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><span class="err">(没效果)</span>
<span class="n">mWebView</span><span class="o">.</span><span class="n">setBackgroundDrawable</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">drawable</span><span class="o">.</span><span class="n">main_bg</span><span class="p">);</span><span class="err">(没效果)</span></code></pre></div>
<h2 id="section-1">总结方法</h2>
<ol>
<li>首先检查配置文件里application设置android:hardwareAccelerated=”false”,自己尝试后必须这样设置才行;</li>
<li>在loadUrl后设置mWebView.setBackgroundColor(0);</li>
<li>检查xml布局文件里的WebView的父层布局,也要设置背景为透明的;(之前也因为这个问题没发现,绕了很大一个圈)</li>
</ol>
Android中Cursor关闭的问题
2013-07-23T00:00:00+00:00
/android/2013/07/23/android-cursor-close
<p>Cursor是Android查询数据后得到的一个管理数据集合的类,正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉。</p>
<p>然而如果Cursor的数据量特表大,特别是如果里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android明显是倾向于编程者手动的将Cursor close掉,因为在源代码中我们发现,如果等到垃圾回收器来回收时,会给用户以错误提示。</p>
<p>所以我们使用Cursor的方式一般如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Cursor</span> <span class="n">cursor</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="n">try</span> <span class="p">{</span>
<span class="n">cursor</span> <span class="o">=</span> <span class="n">mContext</span><span class="o">.</span><span class="n">getContentResolver</span><span class="p">()</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="n">uri</span><span class="p">,</span><span class="n">null</span><span class="p">,</span><span class="n">null</span><span class="p">,</span><span class="n">null</span><span class="p">,</span><span class="n">null</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="n">cursor</span> <span class="o">!=</span> <span class="n">null</span><span class="p">){</span>
<span class="n">cursor</span><span class="o">.</span><span class="n">moveToFirst</span><span class="p">();</span>
<span class="sr">//</span><span class="k">do</span> <span class="n">something</span>
<span class="p">}</span>
<span class="p">}</span> <span class="kp">catch</span><span class="p">(</span><span class="no">Exception</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStatckTrace</span><span class="p">();</span>
<span class="p">}</span> <span class="n">finally</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">cursor</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">cursor</span><span class="o">.</span><span class="n">close</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vi">@Override</span>
<span class="kp">protected</span> <span class="n">void</span> <span class="n">onDestroy</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mAdapter</span> <span class="o">!=</span> <span class="n">null</span> <span class="o">&&</span> <span class="n">mAdapter</span><span class="o">.</span><span class="n">getCurosr</span><span class="p">()</span> <span class="o">!=</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mAdapter</span><span class="o">.</span><span class="n">getCursor</span><span class="p">()</span><span class="o">.</span><span class="n">close</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">super</span><span class="o">.</span><span class="n">onDestroy</span><span class="p">();</span>
<span class="p">}</span></code></pre></div>
<p>CursorAdapter中的changeCursor函数,会将原来的Cursor释放掉,并替换为新的Cursor,所以你不用担心原来的Cursor没有被关闭。</p>
<p>你可能会想到使用Activity的managedQuery来生成Cursor,这样Cursor就会与Acitivity的生命周期一致了,多么完美的解决方法!然而事实上managedQuery也有很大的局限性。</p>
<p>managedQuery生成的Cursor必须确保不会被替换,因为可能很多程序事实上查询条件都是不确定的,因此我们经常会用新查询的Cursor来替换掉原先的Cursor。因此这种方法适用范围也是很小。</p>
mac配置adb环境变量
2013-07-23T00:00:00+00:00
/android/2013/07/23/adb-config-for-mac
<p>android环境搭建完成之后需要配置android环境变量,这对以后的运行调试很有帮助。</p>
<p>下面我将一下mac环境下的配置步骤:</p>
<p>1.在本地目录(home directory)中创建文件.bash_profile</p>
<p>2.在文件中写入以下内容:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">export</span> <span class="no">ANDROID_HOME</span><span class="o">=</span><span class="sr">/Users/s</span><span class="n">torm</span><span class="o">/</span><span class="n">android_tools</span><span class="o">/</span><span class="n">adt</span><span class="o">-</span><span class="n">bundle</span><span class="o">-</span><span class="n">mac</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="mi">20130522</span><span class="o">/</span><span class="n">sdk</span>
<span class="n">export</span> <span class="no">PATH</span><span class="o">=</span><span class="vg">$PATH</span><span class="p">:</span><span class="vg">$ANDROID_HOME</span><span class="o">/</span><span class="n">tools</span>
<span class="n">export</span> <span class="no">PATH</span><span class="o">=</span><span class="vg">$PATH</span><span class="p">:</span><span class="vg">$ANDROID_HOME</span><span class="o">/</span><span class="n">platform</span><span class="o">-</span><span class="n">tools</span></code></pre></div>
<p>其中:/Users/storm/android_tools/adt-bundle-mac-x86_64-20130522/sdk不是固定的,它指向android SDK的目录</p>
<p>3.执行如下命令:source .bash_profile</p>
<p>4.验证:输入adb回车。如果未显示command not found,说明此命令有效,环境便亮设置完成。</p>
Android编程规范与常用技巧
2013-07-21T00:00:00+00:00
/android/2013/07/21/android-coding-standards
<h2 id="android">一、Android编码规范</h2>
<h4 id="java">1.java代码中不出现中文,最多注释中可以出现中文</h4>
<h4 id="section">2.局部变量命名、静态成员变量命名</h4>
<p>只能包含字母,单词首字母出第一个外,都为大写,其他字母都为小写</p>
<h4 id="section-1">3.常量命名</h4>
<p>只能包含字母和_,字母全部大写,单词之间用_隔开</p>
<h4 id="layoutid">4.layout中的id命名</h4>
<p>命名模式为:view缩写_模块名称_view的逻辑名称</p>
<p>view的缩写详情如下</p>
<p>LayoutView:lv</p>
<p>RelativeView:rv</p>
<p>TextView:tv</p>
<p>ImageView:iv</p>
<p>ImageButton:im</p>
<p>Button:btn</p>
<h4 id="activityview">5.activity中的view变量命名</h4>
<p>命名模式为:逻辑名称+view缩写</p>
<p>建议:如果layout文件很复杂,建议将layout分成多个模块,每个模块定义一个moduleViewHolder,其成员变量包含所属view</p>
<h4 id="stringsxmlid">6.strings.xml中的id命名</h4>
<p>命名模式:activity名称_功能模块名称_逻辑名称/activity名称_逻辑名称/common_逻辑名称</p>
<p>strings.xml中,使用activity名称注释,将文件内容区分开来</p>
<h4 id="drawable">7.drawable中的图片命名</h4>
<p>命名模式:activity名称_逻辑名称/common_逻辑名称</p>
<h4 id="stylesxmllayoutstylestylestylesxml">8.styles.xml:将layout中不断重现的style提炼出通用的style通用组件,放到styles.xml中;</h4>
<h4 id="layer-listselector">9.使用layer-list和selector</h4>
<h4 id="section-2">10.图片尽量分拆成多个可重用的图片</h4>
<h4 id="section-3">11.服务端可以实现的,就不要放在客户端</h4>
<h4 id="section-4">12.引用第三方库要慎重,避免应用大容量的第三方库,导致客户端包非常大</h4>
<h4 id="section-5">13.处理应用全局异常和错误,将错误以邮件的形式发送给服务端</h4>
<h4 id="section-6">14.图片的.9处理</h4>
<h4 id="section-7">15.使用静态变量方式实现界面间共享要慎重</h4>
<h4 id="log--">16.Log(系统名称 模块名称 接口名称,详细描述)</h4>
<h4 id="section-8">17.单元测试(逻辑测试、界面测试)</h4>
<h4 id="handlerhandlermessagewhat">18.不要重用父类的handler,对应一个类的handler也不应该让其子类用到,否则会导致message.what冲突</h4>
<h4 id="activityviewonclicklistener">19.activity中在一个View.OnClickListener中处理所有的逻辑</h4>
<h4 id="stringsxml1s">20.strings.xml中使用%1$s实现字符串的通配</h4>
<h4 id="activityuicommonactivityactivity">21.如果多个Activity中包含共同的UI处理,那么可以提炼一个CommonActivity,把通用部分叫由它来处理,其他activity只要继承它即可</h4>
<h4 id="buttonactivitgrouptabbuttonsetselectedtrueactivitygroupactivitybutton">22.使用button+activitgroup实现tab效果时,使用Button.setSelected(true),确保按钮处于选择状态,并使activitygroup的当前activity与该button对应</h4>
<h4 id="drawablelayoutmenuvalues">23.如果所开发的为通用组件,为避免冲突,将drawable/layout/menu/values目录下的文件名增加前缀</h4>
<h4 id="section-9">24.数据一定要效验,例如</h4>
<p>字符型转数字型,如果转换失败一定要有缺省值;</p>
<p>服务端响应数据是否有效判断;</p>
<h4 id="section-10">25.同一个客户端如果要放在不同的市场,而且要统计各个市场下载及使用数据时</h4>
<p>针对不同的客户端打不同的包,唯一的区别是versionName,apk文件名为versionName.apk</p>
<p>在升级时,要将自己的versionCode和versionName一并传给服务端,如果需要升级,则下载versionName相对应的apk</p>
<p>关于是否要强制升级:</p>
<p>1).不管何种情况都强制升级</p>
<p>2).判断用户的版本和当前最新版本,如果兼容则强制升级,否则可选;</p>
<h4 id="section-11">26.有的按钮要避免重复点击</h4>
<h2 id="android-1">二、Android性能优化</h2>
<h4 id="httpgzip">1.http用gzip压缩,设置连接超时时间和响应超时时间</h4>
<p>http请求按照业务需求,分为是否可以缓存和不可缓存,那么在无网络的环境中,仍然通过缓存的httpresponse浏览部分数据,实现离线阅读。</p>
<h4 id="listview-">2.listview 性能优化</h4>
<h6 id="convertview">1)复用convertView</h6>
<p>在getItemView中,判断convertView是否为空,如果不为空,可复用。如果couvertview中的view需要添加listerner,代码一定要在if(convertView==null){}之外。</p>
<h6 id="section-12">2)异步加载图片</h6>
<p>item中如果包含有webimage,那么最好异步加载</p>
<h6 id="section-13">3)快速滑动时不显示图片</h6>
<p>当快速滑动列表时(SCROLL_STATE_FLING),item中的图片或获取需要消耗资源的view,可以不显示出来;而处于其他两种状态(SCROLL_STATE_IDLE 和SCROLL_STATE_TOUCH_SCROLL),则将那些view显示出来</p>
<h6 id="list10item">4)list中异步加载的图片,当不在可视范围内,按照一定的算法及时回收(如在当前可视范围的上下10条item以外的图片进行回收,或者将图片进行缓存,设置一个大小,按照最近最少使用原则超过部分进行回收)</h6>
<h6 id="baseadapter">5)BaseAdapter避免内存溢出</h6>
<p>如果BaseAdapter的实体类有属性非常消耗内存,可以将保存到文件;为提高性能,可以进行缓存,并限制缓存大小。</p>
<h4 id="section-14">3.使用线程池,分为核心线程池和普通线程池,下载图片等耗时任务放置在普通线程池,避免耗时任务阻塞线程池后,导致所有异步任务都必须等待</h4>
<h4 id="uiactivity">4.异步任务,分为核心任务和普通任务,只有核心任务中出现的系统级错误才会报错,异步任务的ui操作需要判断原activity是否处于激活状态</h4>
<h6 id="section-15">1)主线程不要进行网络处理;</h6>
<h6 id="section-16">2)主线程不要进行数据库处理;</h6>
<h6 id="section-17">3)主线程不要进行文件处理;</h6>
<h4 id="staticcontext">5.尽量避免static成员变量引用资源耗费过多的实例,比如Context</h4>
<h4 id="weakreferencegcgc">6.使用WeakReference代替强引用,弱引用可以让您保持对对象的引用,同时允许GC在必要时释放对象,回收内存。对于那些创建便宜但耗费大量内存的对象,即希望保持该对象,又要在应用程序需要时使用,同时希望GC必要时回收时,可以考虑使用弱引用。</h4>
<h4 id="bitmap">7.超级大胖子Bitmap</h4>
<p>及时的销毁(Activity的onDestroy时将bitmap回收,在被UI组件使用后马上进行回收会抛RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap)
设置一定的采样率(有开发者提供的图片无需进行采样,对于有用户上传或第三方的大小不可控图片,可进行采样减少图片所占的内存),从服务端返回图片,建议同时反馈图片的size
巧妙的运用软引用
drawable对应resid的资源,bitmap对应其他资源
任何类型的图片,如果获取不到(例如文件不存在,或者读取文件时跑OutOfMemory异常),应该有对应的默认图片(默认图片放在在apk中,通过resid获取);</p>
<h4 id="cursor-gc-android-cursor-close">8.保证Cursor 占用的内存被及时的释放掉,而不是等待GC来处理。并且 Android明显是倾向于编 程者手动的将Cursor close掉</h4>
<h4 id="section-18">9.线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程 生命周期的不可控</h4>
<h4 id="imageview">10.如果ImageView的图片是来自网络,进行异步加载</h4>
<h4 id="viewtouchlistener">11.应用开发中自定义View的时候,交互部分,千万不要写成线程不断刷新界面显示,而是根据TouchListener事件主动触发界面的更新</h4>
<h4 id="drawable-1">12.Drawable</h4>
<p>ui组件需要用到的图片是apk包自带的,那么一律用setImageResource或者setBackgroundResource,而不要根据resourceid</p>
<p>注意:get(getResources(), R.drawable.btn_achievement_normal)该方法通过resid转换为drawable,需要考虑回收的问题,如果drawable是对象私有对象,在对象销毁前是肯定不会释放内存的。</p>
<h4 id="activity">13.复用、回收Activity对象</h4>
<p>临时的activity及时finish</p>
<p>主界面设置为singleTask</p>
<p>一般界面设置为singleTop</p>
<h4 id="section-19">14.位置信息</h4>
<p>获取用户的地理位置信息时,在需要获取数据的时候打开GPS,之后及时关闭掉</p>
<h4 id="onresumeonpause">15.在onResume时设置该界面的电源管理,在onPause时取消设置</h4>
<h2 id="androidui">三、AndroidUI优化</h2>
<h4 id="layoutmergeinclude">1.layout组件化,尽量使用merge及include复用</h4>
<h4 id="styles">2.使用styles,复用样式定义</h4>
<h4 id="section-20">3.软键盘的弹出控制,不要让其覆盖输入框</h4>
<h4 id="section-21">4.数字、字母和汉字混排占位问题:将数字和字母全角化。由于现在大多数情况下我们的输入都是半角,所以 字母和数字的占位无法确定,但是一旦全角化之后,数字、字母的占位就和一个汉字的占位相同了,这样就可以避免由于占位导致的排版问题。</h4>
<h4 id="textviewn">5.英文文档排版:textview自动换行时要保持单词的完整性,解决方案是计算字符串长度,然后手动设定每一行显示多少个字母并加上‘\n‘</h4>
<h4 id="relativelayout">6.复杂布局使用RelativeLayout</h4>
<h4 id="dppix">7.自适应屏幕,使用dp替代pix</h4>
<h4 id="androidlayoutweighttablelayout">8.使用android:layout_weight或者TableLayout制作等分布局</h4>
<h4 id="animation-list">9.使用animation-list制作动画效果</h4>
Java编程规范
2013-07-20T00:00:00+00:00
/java/2013/07/20/java-coding-standards
<p>说到编程,就不得不提到编程规范,好的编程规范应该是每个人都要遵守的好习惯,个人觉得也应该是我们从开始学习编程就应该掌握的一个技能,至于为什么要严格遵守编程规范,我就不做过多的解释,这里有一篇文章讲的非常不错<a href="http://www.aqee.net/google-coding-standards/">为什么谷歌要执行严格的代码编写规范</a>,今天整理了下java领域的编程规范,记录并分享在此。</p>
<h2 id="javadoc">使用Javadoc标准注释</h2>
<p>每个文件的开头都应该有一句版权说明。然后下面应该是package包语句和import语句,每个语句块之间用空行分隔,然后是类或接口的定义,在Javadoc注释中,应描述类或接口的用途。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">/*</span>
<span class="sr"> * @creator Storm</span>
<span class="sr"> * @created_at 2013-7-16 上午9:54:34</span>
<span class="sr"> * Copyright (C) 2013 BOOHEE. All rights reserved.</span>
<span class="sr"> */</span>
<span class="n">package</span> <span class="n">com</span><span class="o">.</span><span class="n">boohee</span><span class="o">.</span><span class="n">recipe</span><span class="p">;</span>
<span class="n">import</span> <span class="n">org</span><span class="o">.</span><span class="n">json</span><span class="o">.</span><span class="n">JSONException</span><span class="p">;</span>
<span class="n">import</span> <span class="n">org</span><span class="o">.</span><span class="n">json</span><span class="o">.</span><span class="n">JSONObject</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">os</span><span class="o">.</span><span class="n">Bundle</span><span class="p">;</span>
<span class="n">import</span> <span class="n">android</span><span class="o">.</span><span class="n">widget</span><span class="o">.</span><span class="n">ListView</span><span class="p">;</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">OrderActivity</span> <span class="n">extends</span> <span class="no">PayActivityBase</span> <span class="p">{</span>
<span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span></code></pre></div>
<p>个人不推荐在每个类或者自建的public方法标注过多的Javadoc注释,如果你的方法或者类需要标注非常详细的注释才能让人理解,那么我想你的类或者方法应该考虑进行重构了,或许你应该起一个更好的名字来代替。当然,某些特殊情况,如包含很复杂的业务逻辑与算法,这就不得不标注详细点的Javadoc注释了。</p>
<h2 id="section">编写简短的方法</h2>
<p>为了把规模控制在合理范围内,方法应该保持简短和重点突出。不过,有时较长的方法也是合适的,所以对方法的代码长度并没有硬性的限制。如果方法代码超过了15行(最多不应该超过20行),就该考虑是否可以在不损害程序结构的前提下进行分拆。</p>
<h2 id="section-1">在标准的位置定义字段</h2>
<p>字段应该定义在文件开头,或者紧挨着使用这些字段的方法之前。</p>
<h2 id="section-2">限制变量的作用范围</h2>
<p>局部变量的作用范围应该是限制为最小的(Effective Java第29条)。使用局部变量,可以增加代码的可读性和可维护性,并且降低发生错误的可能性。每个变量都应该在最小范围的代码块中进行声明,该代码块的大小只要能够包含所有对该变量的使用即可。</p>
<p>应该在第一次用到局部变量的地方对其进行声明。几乎所有局部变量声明都应该进行初始化。如果还缺少足够的信息来正确地初始化变量,那就应该推迟声明,直至可以初始化为止。</p>
<p>本规则存在一个例外,就是涉及try-catch语句的情况。如果变量是用方法的返回值来初始化的,而该方法可能会抛出一个checked异常,那么必须在try块中进行变量声明。如果需在try块之外使用该变量,那它就必须在try块之前就进行声明了,这时它是不可能进行正确的初始化的。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="no">Instantiate</span> <span class="k">class</span> <span class="n">cl</span><span class="p">,</span> <span class="n">which</span> <span class="n">represents</span> <span class="n">some</span> <span class="n">sort</span> <span class="n">of</span> <span class="no">Set</span>
<span class="no">Set</span> <span class="n">s</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="n">try</span> <span class="p">{</span>
<span class="n">s</span> <span class="o">=</span> <span class="p">(</span><span class="no">Set</span><span class="p">)</span> <span class="n">cl</span><span class="o">.</span><span class="n">newInstance</span><span class="p">();</span>
<span class="p">}</span> <span class="kp">catch</span><span class="p">(</span><span class="no">IllegalAccessException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kp">throw</span> <span class="kp">new</span> <span class="no">IllegalArgumentException</span><span class="p">(</span><span class="n">cl</span> <span class="o">+</span> <span class="s2">" not accessible"</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span><span class="p">(</span><span class="no">InstantiationException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kp">throw</span> <span class="kp">new</span> <span class="no">IllegalArgumentException</span><span class="p">(</span><span class="n">cl</span> <span class="o">+</span> <span class="s2">" not instantiable"</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="no">Exercise</span> <span class="n">the</span> <span class="n">set</span>
<span class="n">s</span><span class="o">.</span><span class="n">addAll</span><span class="p">(</span><span class="no">Arrays</span><span class="o">.</span><span class="n">asList</span><span class="p">(</span><span class="n">args</span><span class="p">));</span></code></pre></div>
<p>但即便是这种情况也是可以避免的,把try-catch 块封装在一个方法内即可:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Set</span> <span class="n">createSet</span><span class="p">(</span><span class="no">Class</span> <span class="n">cl</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">Instantiate</span> <span class="k">class</span> <span class="n">cl</span><span class="p">,</span> <span class="n">which</span> <span class="n">represents</span> <span class="n">some</span> <span class="n">sort</span> <span class="n">of</span> <span class="no">Set</span>
<span class="n">try</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="no">Set</span><span class="p">)</span> <span class="n">cl</span><span class="o">.</span><span class="n">newInstance</span><span class="p">();</span>
<span class="p">}</span> <span class="kp">catch</span><span class="p">(</span><span class="no">IllegalAccessException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kp">throw</span> <span class="kp">new</span> <span class="no">IllegalArgumentException</span><span class="p">(</span><span class="n">cl</span> <span class="o">+</span> <span class="s2">" not accessible"</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span><span class="p">(</span><span class="no">InstantiationException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kp">throw</span> <span class="kp">new</span> <span class="no">IllegalArgumentException</span><span class="p">(</span><span class="n">cl</span> <span class="o">+</span> <span class="s2">" not instantiable"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="no">Exercise</span> <span class="n">the</span> <span class="n">set</span>
<span class="no">Set</span> <span class="n">s</span> <span class="o">=</span> <span class="n">createSet</span><span class="p">(</span><span class="n">cl</span><span class="p">);</span>
<span class="n">s</span><span class="o">.</span><span class="n">addAll</span><span class="p">(</span><span class="no">Arrays</span><span class="o">.</span><span class="n">asList</span><span class="p">(</span><span class="n">args</span><span class="p">));</span></code></pre></div>
<p>除非理由十分充分,否则循环变量都应该在for语句内进行声明:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">doSomething</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>和</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">for</span> <span class="p">(</span><span class="no">Iterator</span> <span class="n">i</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">iterator</span><span class="p">();</span> <span class="n">i</span><span class="o">.</span><span class="n">hasNext</span><span class="p">();</span> <span class="p">)</span> <span class="p">{</span>
<span class="n">doSomethingElse</span><span class="p">(</span><span class="n">i</span><span class="o">.</span><span class="n">next</span><span class="p">());</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-3">使用空格进行缩进</h2>
<p>我们的代码块缩进使用4个空格。尽量不使用制表符tab。如果存在疑惑,与前后的其它代码保持一致即可。
我们用8个空格作为换行后的缩进,包括函数调用和赋值。例如这是正确的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Instrument</span> <span class="n">i</span> <span class="o">=</span> <span class="n">someLongExpression</span><span class="p">(</span><span class="n">that</span><span class="p">,</span> <span class="n">wouldNotFit</span><span class="p">,</span> <span class="n">on</span><span class="p">,</span> <span class="n">one</span><span class="p">,</span> <span class="n">line</span><span class="p">);</span></code></pre></div>
<p>而这是错误的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Instrument</span> <span class="n">i</span> <span class="o">=</span> <span class="n">someLongExpression</span><span class="p">(</span><span class="n">that</span><span class="p">,</span> <span class="n">wouldNotFit</span><span class="p">,</span> <span class="n">on</span><span class="p">,</span> <span class="n">one</span><span class="p">,</span> <span class="n">line</span><span class="p">);</span></code></pre></div>
<h2 id="section-4">遵守字段命名惯例</h2>
<ul>
<li>非public的、非static的字段名称以m开头。</li>
<li>static字段名称以s开头。</li>
<li>其它字段以小写字母开头。</li>
<li>public static final字段(常量)全部字母大写并用下划线分隔。</li>
</ul>
<p>例如:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyClass</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">SOME_CONSTANT</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">int</span> <span class="n">publicField</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="no">MyClass</span> <span class="n">sSingleton</span><span class="p">;</span>
<span class="n">int</span> <span class="n">mPackagePrivate</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">int</span> <span class="n">mPrivate</span><span class="p">;</span>
<span class="kp">protected</span> <span class="n">int</span> <span class="n">mProtected</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-5">使用标准的大括号风格</h2>
<p>大括号不单独占用一行;它们紧接着上一行书写。就像这样:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">MyClass</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">func</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">something</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">somethingElse</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="sr">//</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>我们需要用大括号来包裹条件语句块。不过也有例外,如果整个条件语句块(条件和语句本身)都能容纳在一行内,也可以(但不是必须)把它们放入同一行中。也就是说,这是合法的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">if</span> <span class="p">(</span><span class="n">condition</span><span class="p">)</span> <span class="p">{</span>
<span class="n">body</span><span class="p">();</span>
<span class="p">}</span></code></pre></div>
<p>这也是合法的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">if</span> <span class="p">(</span><span class="n">condition</span><span class="p">)</span> <span class="n">body</span><span class="p">();</span></code></pre></div>
<p>但这是非法的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">if</span> <span class="p">(</span><span class="n">condition</span><span class="p">)</span>
<span class="n">body</span><span class="p">();</span> <span class="sr">//</span> <span class="n">bad!</span></code></pre></div>
<h2 id="section-6">限制代码行的长度</h2>
<p>每行代码的长度应该不超过100个字符。</p>
<p>有关本规则的讨论有很多,最后的结论还是最多不超过100个字符。</p>
<p>例外:如果注释行包含了超过100个字符的命令示例或者URL文字,为了便于剪切和复制,其长度可以超过100个字符。</p>
<p>例外:import行可以超过限制,因为很少有人会去阅读它。这也简化了编程工具的写入操作。</p>
<h2 id="java-annotation">使用标准的Java Annotation</h2>
<p>Annotation应该位于Java语言元素的其它修饰符之前。 简单的marker annotation(@Override等)可以和语言元素放在同一行。 如果存在多个annotation,或者annotation是参数化的,则应按字母顺序各占一行来列出。</p>
<p>对于Java 内建的三种annotation,Android标准的实现如下:</p>
<ul>
<li>
<p>Deprecated:只要某个语言元素已不再建议使用了,就必须使用Deprecated annotation。如果使用了Deprecated annotation,则必须同时进行deprecated Javadoc标记,并且给出一个替代的实现方式。此外请记住,被Deprecated的方法仍然是能正常执行的。</p>
</li>
<li>
<p>Override:只要某个方法覆盖了已过时的或继承自超类的方法,就必须使用Override annotation。</p>
</li>
</ul>
<p>例如,如果方法使用了@inheritdocs Javadoc标记,且继承自超类(而不是interface),则必须同时用@Override标明覆盖了父类方法。</p>
<ul>
<li>SuppressWarnings:SuppressWarnings annotation仅用于无法消除编译警告的场合。 如果警告确实经过测试“不可能消除”,则必须使用SuppressWarnings annotation,以确保所有的警告都能真实反映代码中的问题。</li>
</ul>
<p>当需要使用@SuppressWarnings annotation时,必须在前面加上TODO注释行,用于解释“不可能消除”警告的条件。通常是标明某个令人讨厌的类用到了某个拙劣的接口。比如:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="ss">TODO</span><span class="p">:</span> <span class="no">The</span> <span class="n">third</span><span class="o">-</span><span class="n">party</span> <span class="k">class</span> <span class="n">com</span><span class="o">.</span><span class="n">third</span><span class="o">.</span><span class="n">useful</span><span class="o">.</span><span class="n">Utility</span><span class="o">.</span><span class="n">rotate</span><span class="p">()</span> <span class="n">needs</span> <span class="n">generics</span>
<span class="vi">@SuppressWarnings</span><span class="p">(</span><span class="s2">"generic-cast"</span><span class="p">)</span>
<span class="no">List</span><span class="o"><</span><span class="nb">String</span><span class="o">></span> <span class="n">blix</span> <span class="o">=</span> <span class="no">Utility</span><span class="o">.</span><span class="n">rotate</span><span class="p">(</span><span class="n">blax</span><span class="p">);</span></code></pre></div>
<p>如果需要使用SuppressWarnings annotation,应该重新组织一下代码,把需要应用annotation的语言元素独立出来。</p>
<h2 id="section-7">简称等同于单词</h2>
<p>Good Bad</p>
<p>XmlHttpRequest XMLHTTPRequest</p>
<p>getCustomerId getCustomerID</p>
<p>class Html class HTML</p>
<p>String url String URL</p>
<p>long id long ID</p>
<p>如何对待简称,JDK和Android底层代码存在很大的差异。因此,你几乎不大可能与其它代码取得一致。别无选择,把简称当作完整的单词看待吧。</p>
<h2 id="todo">使用TODO注释</h2>
<p>对那些临时性的、短期的、够棒但不完美的代码,请使用TODO注释。</p>
<p>TODO注释应该包含全部大写的TODO,后跟一个冒号:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="ss">TODO</span><span class="p">:</span> <span class="no">Remove</span> <span class="n">this</span> <span class="n">code</span> <span class="n">after</span> <span class="n">the</span> <span class="no">UrlTable2</span> <span class="n">has</span> <span class="n">been</span> <span class="n">checked</span> <span class="k">in</span><span class="o">.</span></code></pre></div>
<p>和</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="ss">TODO</span><span class="p">:</span> <span class="no">Change</span> <span class="n">this</span> <span class="n">to</span> <span class="n">use</span> <span class="n">a</span> <span class="n">flag</span> <span class="n">instead</span> <span class="n">of</span> <span class="n">a</span> <span class="n">constant</span><span class="o">.</span></code></pre></div>
<h2 id="log">慎用Log</h2>
<p>记录日志会对性能产生显著的负面影响。如果日志内容不够简炼的话,很快会丧失可用性。日志功能支持五种不同的级别。以下列出了各个级别及其使用场合和方式。</p>
<ul>
<li>
<p>ERROR: 该级别日志应该在致命错误发生时使用,也就是说,错误的后果能被用户看到,但是不明确删除部分数据、卸装程序、清除数据区或重新刷机(或更糟糕)就无法恢复。该级别总是记录日志。需要记录ERROR级别日志的事件一般都应该向统计信息收集(statistics-gathering )服务器报告。</p>
</li>
<li>
<p>WARNING: 该级别日志应该用于那些重大的、意外的事件,也就是说,错误的后果能被用户看到,但是不采取明确的动作可能就无法无损恢复,从等待或重启应用开始,直至重新下载新版程序或重启设备。该级别总是记录日志。需记录WARNING级别日志的事件也可以考虑向统计信息收集服务器报告。</p>
</li>
<li>
<p>INFORMATIVE: 该级别的日志应该用于记录大部分人都会感兴趣的事件,也就是说,如果检测到事件的影响面可能很广,但不一定是错误。应该只有那些拥有本区域内最高级别身份认证的模块才能记录这些日志(为了避免级别不足的模块重复记录日志)。该级别总是记录日志。</p>
</li>
<li>
<p>DEBUG: 该级别的日志应该用于进一步记录有关调查、调试意外现象的设备事件。应该只记录那些有关控件运行所必需的信息。如果debug日志占用了太多的日志空间,那就应该使用详细级别日志(verbose)才更为合适。</p>
</li>
</ul>
<p>即使是发行版本(release build),该级别也会被记录,并且需用if (LOCAL_LOG)或if (LOCAL_LOGD)语句块包裹,这里的LOCAL_LOG[D]在你的类或子控件中定义。这样就能够一次性关闭所有的调试日志。因此在if (LOCAL_LOG)语句块中不允许存在逻辑判断语句。所有日志所需的文字组织工作也应在if (LOCAL_LOG)语句块内完成。如果对记录日志的调用会导致在if (LOCAL_LOG)语句块之外完成文字组织工作,那该调用就必须控制在一个方法内完成。</p>
<p>还存在一些代码仍然在使用if (localLOGV)。这也是可以接受的,虽然名称不是标准的。</p>
<ul>
<li>VERBOSE: 该级别日志应用于所有其余的事件。该级别仅会在调试版本(debug build)下记录日志,并且需用if (LOCAL_LOGV)语句块(或等效语句)包裹,这样该部分代码默认就不会编译进发行版本中去了。所有构建日志文字的代码将会在发行版本中剥离出去,并且需包含在if (LOCAL_LOGV)语句块中。</li>
</ul>
<p>注意:</p>
<p>除了VERBOSE级别外,在同一个模块中同一个错误应该尽可能只报告一次:在同一个模块内的一系列层层嵌套的函数调用中,只有最内层的函数才返回错误;并且只有能为解决问题提供明显帮助的时候,同一模块中的调用方才写入一些日志。</p>
<p>除了VERBOSE级别外,在一系列嵌套的模块中,当较低级别的模块对来自较高级别模块的非法数据进行检测时,应该只把检测情况记录在DEBUG日志中,并且只记录那些调用者无法获取的信息。特别是不需要记录已经抛出异常的情况(异常中应该包含了全部有价值的信息),也不必记录那些只包含错误代码的信息。当应用程序与系统框架间进行交互时,这一点尤为重要。系统框架已能正确处理的第三方应用程序,也不应该记录大于DEBUG级别的日志。仅当一个模块或应用程序检测到自身或来自更低级别模块的错误时,才应该记录INFORMATIVE及以上级别的日志。</p>
<p>如果一个通常要记录日志的事件可能会多次发生,则采取一些频次限制措施或许是个好主意,以防日志被很多重复(或类似)的信息给撑爆了。</p>
<p>网络连接的丢失可被视为常见现象,也是完全可以预见的,不应该无缘无故就记录进日志。影响范围限于应用程序内部的网络中断应该记录在DEBUG或VERBOSE级别的日志中(根据影响的严重程度及意外程度,再来确定是否在发行版本中也记录日志)。</p>
<p>有权访问的文件系统或第三方应用程序发起的系统空间满,应该记录大于INFORMATIVE级别的日志。</p>
<p>来自任何未授信源的非法数据(包括共享存储上的任何文件,或来自任何网络连接的数据)可被视为可预见的,如果检测到非法数据也不应该记录大于DEBUG级别的日志(即使记录也应尽可能少)。</p>
<p>请记住,对字符串使用+操作符时,会在后台以默认大小(16个字符)缓冲区创建一个StringBuilder对象,并且可能还会创建一些其它的临时String对象。换句话说,显式创建StringBuilders对象的代价并不会比用’+’操作符更高(事实上效率还将会提高很多)。还要记住,即使不会再去读取这些日志,调用Log.v()的代码也将编译进发行版中并获得执行,包括创建字符串的代码。</p>
<p>所有要被人阅读并存在于发行版本中的日志,都应该简洁明了、没有秘密、容易理解。这里包括所有DEBUG以上级别的日志。</p>
<p>只要有可能,日志就应该一句一行。行长最好不超过80或100个字符,尽可能避免超过130或160个字符(包括标识符)的行。</p>
<p>报告成功的日志记录绝不应该出现在大于VERBOSE级别的日志中。</p>
<p>用于诊断难以重现事件的临时日志应该限于DEBUG或VERBOSE级别,并且应该用if语句块包裹,以便在编译时能够一次全部关闭。</p>
<p>小心日志会泄漏隐私。应该避免将私人信息记入日志,受保护的内容肯定也不允许记录。这在编写系统框架级代码时尤为重要,因为很难预知哪些是私人信息和受保护信息。</p>
<p>绝对不要使用System.out.println() (或本地代码中的printf())。System.out 和 System.err会重定向到/dev/null,因此print语句不会产生任何可见的效果。可是,这些调用中的所有字符串创建工作都仍然会执行。</p>
<p>日志的黄金法则是:你的日志记录不会导致其它日志的缓冲区溢出,正如其他人的日志也不会让你的溢出一样。</p>
<h2 id="section-8">保持一致</h2>
<p>我们的最终想法是:保持一致。如果你正在编写代码,请花几分钟浏览一下前后的其它代码,以确定它们的风格。如果它们在if语句前后使用了空格,那你也应该遵循。如果它们的注释是用星号组成的框框围起来的,那也请你照办。</p>
<p>保持风格规范的重点是有一个公共的代码词汇表,这样大家就可以把注意力集中于你要说什么,而不是你如何说。我们在这里列出了全部的风格规范,于是大家也知道了这些词汇。不过本地化的风格也很重要。如果你要加入的代码和已存在的代码风格迥异,那就会突然打破阅读的节奏。请努力避免这种情况的发生。</p>
Android OnTouchListener OnGestureListener
2013-07-14T00:00:00+00:00
/android/2013/07/14/android-ontouchlistener-and-ongesturelistener
<p>Android事件处理机制是基于Listener实现的,比如触摸屏相关的事件,是通过OnTouchListener实现的;而手势是通过OnGestureListener实现的,那么这两者有什么关联呢?</p>
<h2 id="ontouchlistener">OnTouchListener</h2>
<p>OnTouchListener接口中包含一个onTouch()方法,直接看一个例子:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MainActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="n">implements</span> <span class="no">OnTouchListener</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">outState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">outState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">main</span><span class="p">);</span>
<span class="no">TextView</span> <span class="n">tv</span> <span class="o">=</span> <span class="p">(</span><span class="no">TextView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">tv</span><span class="p">);</span>
<span class="n">tv</span><span class="o">.</span><span class="n">setOnTouchListener</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onTouch</span><span class="p">(</span><span class="no">View</span> <span class="n">v</span><span class="p">,</span> <span class="no">MotionEvent</span> <span class="n">event</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"Touch Touch"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_SHORT</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="k">return</span> <span class="kp">false</span> <span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>我们可以通过MotionEvent的getAction()方法来获取Touch事件的类型,包括 ACTION_DOWN(按下触摸屏), ACTION_MOVE(按下触摸屏后移动受力点), ACTION_UP(松开触摸屏)和ACTION_CANCEL(不会由用户直接触发)。借助对于用户不同操作的判断,结合getRawX()、 getRawY()、getX()和getY()等方法来获取坐标后,我们可以实现诸如拖动某一个按钮,拖动滚动条等功能。</p>
<p>可以看到OnTouchListener只能监听到三种触摸事件,即按下,移动,松开,如果想要监听到双击、滑动、长按等复杂的手势操作,这个时候就必须得用到OnGestureListener了。</p>
<h2 id="ongesturelistener">OnGestureListener</h2>
<p>接着上面的例子,这次加入手势监听:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MainActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="n">implements</span> <span class="no">OnTouchListener</span><span class="p">,</span> <span class="no">OnGestureListener</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">GestureDetector</span> <span class="n">mGestureDetector</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">outState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">outState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">main</span><span class="p">);</span>
<span class="n">mGestureDetector</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">GestureDetector</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="no">TextView</span> <span class="n">tv</span> <span class="o">=</span> <span class="p">(</span><span class="no">TextView</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">tv</span><span class="p">);</span>
<span class="n">tv</span><span class="o">.</span><span class="n">setOnTouchListener</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onTouch</span><span class="p">(</span><span class="no">View</span> <span class="n">v</span><span class="p">,</span> <span class="no">MotionEvent</span> <span class="n">event</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">mGestureDetector</span><span class="o">.</span><span class="n">onTouchEvent</span><span class="p">(</span><span class="n">event</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">用户轻触触摸屏,由</span><span class="mi">1</span><span class="err">个</span><span class="no">MotionEvent</span> <span class="no">ACTION_DOWN</span><span class="err">触发</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onDown</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">arg0</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Log</span><span class="o">.</span><span class="n">i</span><span class="p">(</span><span class="s2">"MyGesture"</span><span class="p">,</span> <span class="s2">"onDown"</span><span class="p">);</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"onDown"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_SHORT</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">用户轻触触摸屏,尚未松开或拖动,由一个</span><span class="mi">1</span><span class="err">个</span><span class="no">MotionEvent</span> <span class="no">ACTION_DOWN</span><span class="err">触发</span><span class="p">,</span> <span class="err">注意和</span><span class="n">onDown</span><span class="p">()</span><span class="err">的区别,强调的是没有松开或者拖动的状态</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onShowPress</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Log</span><span class="o">.</span><span class="n">i</span><span class="p">(</span><span class="s2">"MyGesture"</span><span class="p">,</span> <span class="s2">"onShowPress"</span><span class="p">);</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"onShowPress"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_SHORT</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">用户(轻触触摸屏后)松开,由一个</span><span class="mi">1</span><span class="err">个</span><span class="no">MotionEvent</span> <span class="no">ACTION_UP</span><span class="err">触发</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onSingleTapUp</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Log</span><span class="o">.</span><span class="n">i</span><span class="p">(</span><span class="s2">"MyGesture"</span><span class="p">,</span> <span class="s2">"onSingleTapUp"</span><span class="p">);</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"onSingleTapUp"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_SHORT</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">用户按下触摸屏、快速移动后松开,由</span><span class="mi">1</span><span class="err">个</span><span class="no">MotionEvent</span> <span class="no">ACTION_DOWN</span><span class="p">,</span> <span class="err">多个</span><span class="no">ACTION_MOVE</span><span class="p">,</span> <span class="mi">1</span><span class="err">个</span><span class="no">ACTION_UP</span><span class="err">触发</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onFling</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e1</span><span class="p">,</span> <span class="no">MotionEvent</span> <span class="n">e2</span><span class="p">,</span> <span class="n">float</span> <span class="n">velocityX</span><span class="p">,</span> <span class="n">float</span> <span class="n">velocityY</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Log</span><span class="o">.</span><span class="n">i</span><span class="p">(</span><span class="s2">"MyGesture"</span><span class="p">,</span> <span class="s2">"onFling"</span><span class="p">);</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"onFling"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_LONG</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">用户按下触摸屏,并拖动,由</span><span class="mi">1</span><span class="err">个</span><span class="no">MotionEvent</span> <span class="no">ACTION_DOWN</span><span class="p">,</span> <span class="err">多个</span><span class="no">ACTION_MOVE</span><span class="err">触发</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onScroll</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e1</span><span class="p">,</span> <span class="no">MotionEvent</span> <span class="n">e2</span><span class="p">,</span> <span class="n">float</span> <span class="n">distanceX</span><span class="p">,</span> <span class="n">float</span> <span class="n">distanceY</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Log</span><span class="o">.</span><span class="n">i</span><span class="p">(</span><span class="s2">"MyGesture"</span><span class="p">,</span> <span class="s2">"onScroll"</span><span class="p">);</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"onScroll"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_LONG</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">用户长按触摸屏,由多个</span><span class="no">MotionEvent</span> <span class="no">ACTION_DOWN</span><span class="err">触发</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onLongPress</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Log</span><span class="o">.</span><span class="n">i</span><span class="p">(</span><span class="s2">"MyGesture"</span><span class="p">,</span> <span class="s2">"onLongPress"</span><span class="p">);</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="s2">"onLongPress"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_LONG</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>本示例中,用到了OnGestureListener接口的onScroll()和OnFling()方法,涉及到了Android系统坐标及触摸MotionEvent e1和e2、速度velocityX、velocityY等值,那么这里就顺便补充下Android屏幕坐标系如下图:</p>
<p><img src="http://my.csdn.net/uploads/201206/13/1339581389_3855.png" /></p>
<p>(1)MotionEvent中 e1是手指第一次按上屏幕的起点,e2是抬起手指离开屏幕的终点,根据上图Android屏幕坐标系可知:</p>
<p>手指向右滑动,终点(e2)在起点(e1)的右侧,有e2.getX() - e1.getX() 大于0
手指向左滑动,终点(e2)在起点(e1)的左侧,有e2.getX() - e1.getX() 小于0
手指向下滑动,终点(e2)在起点(e1)的下侧,有e2.getY() - e1.getY() 大于0
手指向上滑动,终点(e2)在起点(e1)的上侧,有e2.getY() - e1.getY() 小于0</p>
<p>(2)onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)</p>
<p>distanceX,是前后两次call的X距离,不是e2与e1的水平距离
distanceX,是前后两次call的Y距离,不是e2与e1的垂直距离
具体数值的方向,请详见上图(中)</p>
<p>(3)onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) </p>
<p>velocityX,是X轴的每秒速度
velocityY,是Y轴的每秒速度
具体数值的方向,请详见上图(右)
仔细观察可以发现:velocityX、velocityY的方向与distanceX、distanceY方向正好相反</p>
8个常用的Android开发工具
2013-07-07T00:00:00+00:00
/android/2013/07/07/eight-android-develop-tools
<p>周末发现一些比较有用的android开发常用的工具,里面大部分是自己经常用的,还有一些暂时很少用,暂且在这里记录下,以后一定同样会经常用到的。</p>
<h2 id="the-sdk-and-avd-managerhttpdeveloperandroidcomsdkexploringhtml">1 <a href="http://developer.android.com/sdk/exploring.html">The SDK and AVD Manager</a></h2>
<p>这个工具是用来添加、更新Android SDK的组件的,例如新的API。</p>
<p><img src="http://static.oschina.net/uploads/img/201110/05010012_nJHi.png" /></p>
<h2 id="android-adthttpdeveloperandroidcomtoolssdkeclipse-adthtml">2 <a href="http://developer.android.com/tools/sdk/eclipse-adt.html">Android ADT</a></h2>
<p>这是Eclipse的Android开发者查件,为Android开发提供了一个可视化的集成开发环境。</p>
<p><img src="http://static.oschina.net/uploads/img/201110/05010014_f3IY.png" /></p>
<h2 id="android-ddmshttpdeveloperandroidcomtoolsdebuggingddmshtml">3 <a href="http://developer.android.com/tools/debugging/ddms.html">Android DDMS</a></h2>
<p>在Android开发工具包当中有一个调试工具,Dalvik Debug Monitor Server (DDMS)。这个工具提供了端口转发,截屏,堆栈,进程信息,日志,信号状态信息,模拟来电,短信,模拟地理位置信息等。</p>
<p><img src="http://static.oschina.net/uploads/img/201110/05010015_Hf0J.png" /></p>
<h2 id="logcathttpdeveloperandroidcomtoolshelplogcathtml">4 <a href="http://developer.android.com/tools/help/logcat.html">Logcat</a></h2>
<p>这是Android提供的日志系统。这个系统提供了一个收集、查看系统调试信息的机制。不同的App,不同的系统组件生成的日志将被同一收集、存储起来。我们可以通过logcat的命令去筛选,查看日志信息。</p>
<h2 id="hierarchy-viewerhttpdeveloperandroidcomtoolshelphierarchy-viewerhtml">5 <a href="http://developer.android.com/tools/help/hierarchy-viewer.html">Hierarchy Viewer</a></h2>
<p>这个工具可以帮助开发者调试、优化用户界面。它可以为App的用户界面结构生成一个图形的展示方式,并且提供了显示的放大功能。</p>
<p><img src="http://static.oschina.net/uploads/img/201110/05010016_VJQP.png" /></p>
<h2 id="zipalignhttpdeveloperandroidcomtoolshelpzipalignhtml">6 <a href="http://developer.android.com/tools/help/zipalign.html">Zipalign</a></h2>
<p>这个工具可以优化Android程序文件(.apk),可以使应用程序运行更快。在Android平台中,数据文件存储在apk文件中,可以多进程的访问,如果你开发过Win32可能知道程序的粒度对齐问题,不错虽然不是PE格式的文件,在Zip中一样,资源的访问可以通过更好的对其优化,而zipalign使用了4字节的边界对齐方式来影射内存,通过空间换时间的方式提高执行效率。</p>
<h2 id="emulatorhttpdeveloperandroidcomtoolshelpemulatorhtml">7 <a href="http://developer.android.com/tools/help/emulator.html">Emulator</a></h2>
<p>这个很简单啦,就是模拟器!</p>
<h2 id="android-debug-bridgehttpdeveloperandroidcomtoolshelpadbhtml">8 <a href="http://developer.android.com/tools/help/adb.html">Android Debug Bridge</a></h2>
<p>Android Debug Bridge(adb) 是一个通用的命令行工具用来和模拟器或者连接到计算机的Android设备通信。</p>
<p>ADB常用的几个命令</p>
<p>1.查看设备</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">devices</span></code></pre></div>
<p>这个命令是查看当前连接的设备, 连接到计算机的android设备或者模拟器将会列出显示</p>
<p>2.安装软件</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">install</span> <span class="o"><</span><span class="n">apk</span><span class="err">文件路径</span><span class="o">></span></code></pre></div>
<p>这个命令将指定的apk文件安装到设备上</p>
<p>3.卸载软件</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">uninstall</span> <span class="o"><</span><span class="err">软件名</span><span class="o">></span>
<span class="n">adb</span> <span class="n">uninstall</span> <span class="o">-</span><span class="n">k</span> <span class="o"><</span><span class="err">软件名</span><span class="o">></span></code></pre></div>
<p>如果加 -k 参数,为卸载软件但是保留配置和缓存文件.</p>
<p>4.登录设备shell</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">adb shell
adb shell <span class="nt"><command></span></code></pre></div>
<p>这个命令将登录设备的shell.
后面加command将是直接运行设备命令, 相当于执行远程命令</p>
<p>5.从电脑上发送文件到设备</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">push</span> <span class="o"><</span><span class="err">本地路径</span><span class="o">></span> <span class="o"><</span><span class="err">远程路径</span><span class="o">></span></code></pre></div>
<p>用push命令可以把本机电脑上的文件或者文件夹复制到设备(手机)</p>
<p>6.从设备上下载文件到电脑</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">pull</span> <span class="o"><</span><span class="err">远程路径</span><span class="o">></span> <span class="o"><</span><span class="err">本地路径</span><span class="o">></span></code></pre></div>
<p>用pull命令可以把设备(手机)上的文件或者文件夹复制到本机电脑</p>
<p>7.重新挂载文件系统</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">remount</span></code></pre></div>
<p>8.重启手机</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">reboot</span></code></pre></div>
<p>9.重启到Recovery界面</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">reboot</span> <span class="n">recovery</span></code></pre></div>
<p>10.显示帮助信息</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">adb</span> <span class="n">help</span></code></pre></div>
<p>这个命令将显示帮助信息</p>
linux下proxy设定的一般方法
2013-07-05T00:00:00+00:00
/2013/07/05/set-linux-proxy
<p class="paragraph">
在linux下配置测试环境时,经常遇到代理服务器配置的相关问题,在这里总结一些,为以后节省些时间。
也希望对需要的人有所帮助。
</p>
<h2 id="linuxproxy">linux下proxy的常规设置</h2>
<p class="paragraph">
一般是把如下环境变量的设置放到/etc/profile.d/proxy.sh文件中。
对于没有系统权限的用户,可以将下面的内容添加到自己用户目录下的.profile文件中。
这样登录后,大多数的应用程序都可以正常上网。
</p>
<div class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c">#proxy=http://用户名:密码@ProxyURL或IP地址:端口号</span>
<span class="nv">proxy</span><span class="o">=</span>http://ProxyURL或IP地址:端口号
<span class="nb">export </span><span class="nv">http_proxy</span><span class="o">=</span><span class="nv">$proxy</span>
<span class="nb">export </span><span class="nv">https_proxy</span><span class="o">=</span><span class="nv">$proxy</span>
<span class="nb">export </span><span class="nv">ftp_proxy</span><span class="o">=</span><span class="nv">$proxy</span>
<span class="nb">export </span><span class="nv">no_proxy</span><span class="o">=</span>以逗号分隔的除外列表</code></pre></div>
<h2 id="centosyumproxy">CentOS的yum工具的proxy设置</h2>
<p class="paragraph">
CentOS下,设置了上述proxy后,yum还是有不能正确下载有些资源包的情况,可以通过在
/etc/yum.conf文件中添加如下内容来单独设置proxy。
</p>
<div class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">proxy</span><span class="o">=</span>http://ProxyURL或IP地址:端口号
<span class="nv">proxy_username</span><span class="o">=</span>用户名
<span class="nv">proxy_password</span><span class="o">=</span>密码</code></pre></div>
Android Manifest.xml中的meta-data属性
2013-06-30T00:00:00+00:00
/android/2013/06/30/android-metadata
<h2 id="section">语法</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">meta</span><span class="o">-</span><span class="n">data</span> <span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">"string"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">resource</span><span class="o">=</span><span class="s2">"resource specification"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">value</span><span class="o">=</span><span class="s2">"string"</span> <span class="sr">/></span></code></pre></div>
<p>这是该元素的基本结构.可以包含在activity, activity-alias, service, receiver四个元素中。</p>
<p>这个名字值是额外的任意的可以提供给父组件的数据。一个组件元素能够包含任意数量的meta-data子元素。它们所有的值都会被收集在Bundle对象中并且使其可以作为组件的 PackageItemInfo.metaData 字段。</p>
<p>一般的值可以通过value属性来指定,但是如果要指定一个资源id作为一个值,那么就要用resource属性来代替。例如:下面的代码就是指定存储在@string/kangaroo 资源中的zoo名字。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">meta</span><span class="o">-</span><span class="n">data</span> <span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">"zoo"</span> <span class="ss">android</span><span class="p">:</span><span class="n">value</span><span class="o">=</span><span class="s2">"@string/kangaroo"</span> <span class="sr">/></span></code></pre></div>
<p>另一方面,利用resource属性将指定zoo的资源id号,并不是存储在资源中的资源值。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="n">meta</span><span class="o">-</span><span class="n">data</span> <span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">"zoo"</span> <span class="ss">android</span><span class="p">:</span><span class="n">resource</span><span class="o">=</span><span class="s2">"@string/kangaroo"</span> <span class="sr">/></span></code></pre></div>
<p>当要给组件提供多个复杂的数据时,在这里并不推荐使用多重meta-data元素,推荐你存储这些数据在一个资源文件中并且利用resource属性来通知它的id给组件。</p>
<p>android:name</p>
<p>元数据项的名字,为了保证这个名字是唯一的,采用java风格的命名规范。例如:
com.example.project.activity.fred </p>
<p>android:resource</p>
<p>资源的一个引用,指定给这个项的值是该资源的id。该id可以通过方法Bundle.getInt()来从meta-data中找到。</p>
<p>android:value</p>
<p>指定给这一项的值。可以作为值来指定的数据类型并且组件用来找回那些值的Bundle方法列在了下面的表中。</p>
<h2 id="section-1">工具类</h2>
<p>自己写了个工具类来获取meta-data字段值:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Util</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">static</span> <span class="nb">String</span> <span class="n">getMetaValue</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">,</span> <span class="nb">String</span> <span class="n">metaKey</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Bundle</span> <span class="n">metaData</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="nb">String</span> <span class="n">metaValue</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">context</span> <span class="o">==</span> <span class="n">null</span> <span class="o">||</span> <span class="n">metaKey</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">try</span> <span class="p">{</span>
<span class="no">ApplicationInfo</span> <span class="n">ai</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="n">getPackageManager</span><span class="p">()</span><span class="o">.</span><span class="n">getApplicationInfo</span><span class="p">(</span>
<span class="n">context</span><span class="o">.</span><span class="n">getPackageName</span><span class="p">(),</span> <span class="no">PackageManager</span><span class="o">.</span><span class="n">GET_META_DATA</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">null</span> <span class="o">!=</span> <span class="n">ai</span><span class="p">)</span> <span class="p">{</span>
<span class="n">metaData</span> <span class="o">=</span> <span class="n">ai</span><span class="o">.</span><span class="n">metaData</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">null</span> <span class="o">!=</span> <span class="n">metaData</span><span class="p">)</span> <span class="p">{</span>
<span class="n">metaValue</span> <span class="o">=</span> <span class="n">metaData</span><span class="o">.</span><span class="n">getString</span><span class="p">(</span><span class="n">metaKey</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">NameNotFoundException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">metaValue</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
Android中ScrollView嵌套ListView
2013-06-29T00:00:00+00:00
/android/2013/06/29/android-scorllview-nested-listview
<p>这几天项目需要在ScrollView中放入ListView,一开始还以为很轻松的,就是一个xml的布局问题。但是实际操作才发现问题,会发现ListView会显示不完全,它的高度始终有问题。网上同样有很多人遇到这样的问题,大多数人不推荐这样的设计,因为默认情况下Android是禁止在ScrollView中放入另外的ScrollView的,它的高度是无法计算的。</p>
<p>但是既然已经有这样的需求,就要实现。StackOverFlow真是个好东西,发现已经有牛人解决了这个问题,经过试验是可以解决这个问题的,它的思路就是在设置完ListView的Adapter后,根据ListView的子项目重新计算ListView的高度,然后把高度再作为LayoutParams设置给ListView,这样它的高度就正确了,以下是源码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">UIHelper</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">setListViewHeightBasedOnChildren</span><span class="p">(</span><span class="no">ListView</span> <span class="n">listView</span><span class="p">)</span> <span class="p">{</span>
<span class="no">ListAdapter</span> <span class="n">listAdapter</span> <span class="o">=</span> <span class="n">listView</span><span class="o">.</span><span class="n">getAdapter</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">listAdapter</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="n">pre</span><span class="o">-</span><span class="n">condition</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">int</span> <span class="n">totalHeight</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">listAdapter</span><span class="o">.</span><span class="n">getCount</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="no">View</span> <span class="n">listItem</span> <span class="o">=</span> <span class="n">listAdapter</span><span class="o">.</span><span class="n">getView</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="n">listView</span><span class="p">);</span>
<span class="n">listItem</span><span class="o">.</span><span class="n">measure</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="n">totalHeight</span> <span class="o">+=</span> <span class="n">listItem</span><span class="o">.</span><span class="n">getMeasuredHeight</span><span class="p">();</span>
<span class="p">}</span>
<span class="no">ViewGroup</span><span class="o">.</span><span class="n">LayoutParams</span> <span class="n">params</span> <span class="o">=</span> <span class="n">listView</span><span class="o">.</span><span class="n">getLayoutParams</span><span class="p">();</span>
<span class="n">params</span><span class="o">.</span><span class="n">height</span> <span class="o">=</span> <span class="n">totalHeight</span> <span class="o">+</span> <span class="p">(</span><span class="n">listView</span><span class="o">.</span><span class="n">getDividerHeight</span><span class="p">()</span> <span class="o">*</span> <span class="p">(</span><span class="n">listAdapter</span><span class="o">.</span><span class="n">getCount</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">));</span>
<span class="n">listView</span><span class="o">.</span><span class="n">setLayoutParams</span><span class="p">(</span><span class="n">params</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>只要在设置ListView的Adapter后调用此静态方法即可让ListView正确的显示在其父ListView的ListItem中。但是要注意的是,子ListView的每个Item必须是LinearLayout,不能是其他的,因为其他的Layout(如RelativeLayout)没有重写onMeasure(),所以会在onMeasure()时抛出异常。</p>
<p>在ScrollView中嵌套ListView(或者ScrollView)的另外一个问题就是,子ScrollView中无法滑动的(如果它没有显示完全的话),因为滑动事件会被父ScrollView吃掉,如果想要让子ScrollView也可以滑动,只能强行截取滑动事件,有牛人在论坛中发过代码说可以。虽然我没有亲自试过,但估计是可行的。</p>
<p>虽然在ScrollView中显示ScrollView在技术上的难题可以攻破,但是这样的设计却是非常差的用户体验因为用户会不容易看到和操作子ScrollView中的内容。比如好的设计是,父ListView的每个Item只显示概括性的描述,然后点击其Item会进入另外一个页面来详细描述和展示以及对这个Item的操作。</p>
<p>参考资料:<a href="http://stackoverflow.com/questions/3495890/how-can-i-put-a-listview-into-a-scrollview-without-it-collapsing">http://stackoverflow.com/questions/3495890/how-can-i-put-a-listview-into-a-scrollview-without-it-collapsing</a></p>
android中调用App市场对自身App评分
2013-06-27T00:00:00+00:00
/android/2013/06/27/android-appraise-for-your-app
<p>苹果的app评价很容易,直接请求AppStore的一个链接就好了,android市场这么多,请求一个链接肯定不行。之前一直以为很麻烦,也没有仔细研究,今天竟然发现原来很简单。上代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Uri</span> <span class="n">uri</span> <span class="o">=</span> <span class="no">Uri</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">"market://details?id="</span> <span class="o">+</span> <span class="n">context</span><span class="o">.</span><span class="n">getPackageName</span><span class="p">());</span>
<span class="no">Intent</span> <span class="n">goToMarket</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="no">Intent</span><span class="o">.</span><span class="n">ACTION_VIEW</span><span class="p">,</span> <span class="n">uri</span><span class="p">);</span>
<span class="n">try</span> <span class="p">{</span>
<span class="n">startActivity</span><span class="p">(</span><span class="n">goToMarket</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">ActivityNotFoundException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Toast</span><span class="o">.</span><span class="n">makeText</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="s2">"Couldn't launch the market !"</span><span class="p">,</span> <span class="no">Toast</span><span class="o">.</span><span class="n">LENGTH_SHORT</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">();</span>
<span class="p">}</span></code></pre></div>
Android Push
2013-06-23T00:00:00+00:00
/android/2013/06/23/android-push
<p>之前一直困扰着的android端推送服务这次终于解决了,这里总结下android平台下几种消息推送方案以及这次所采用的策略。</p>
<h2 id="gcmgoogle-cloud-messaging">方案一:使用GCM服务(Google Cloud Messaging)</h2>
<p>简介:Google在Android上标配了自己的推送GCM(Google Cloud Messageing),可以帮助开发人员给他们的Android应用程序发送数据。它是一个轻量级的消息,告诉Android应用程序有新的数据要获取从服务器,或者它可能是一个消息,其中包含了4KB的payload data(像即时通讯这类应用程序可以直接使用该payload消息)。GCM服务处理排队的消息,并把消息传递到目标设备上运行的Android应用程序。</p>
<p>优点:Google提供的服务、原生、简单,无需实现和部署服务端。 </p>
<p>缺点:</p>
<p>1.GCM要求Android系统必须是2.2以上的版本,所以对于不少2.2以前的系统没法推送</p>
<p>2.国内服务不稳定。而且不少国内的终端厂商纷纷把Google的服务去掉,替换上自己的。</p>
<p>3.需要用户绑定Google账号,但不少国内用户没有Google账号。</p>
<h2 id="xmppopenfire--spark--smack">方案二:使用XMPP协议(Openfire + Spark + Smack)</h2>
<p>简介:XMPP是一种基于XML的协议,它继承了在XML环境中灵活的发展性,有很强的可扩展性。包括上面讲的GCM服务器底层也是采用XMPP协议封装的。</p>
<p>优点:协议成熟、强大、可扩展性强、目前主要应用于许多聊天系统中,且已有开源的Java版的开发实例androidpn。 </p>
<p>缺点:协议较复杂、冗余(基于XML)、费流量、费电,部署硬件成本高。</p>
<p>而androidpn(Android Push Notification)就是基于 XMPP 开源组件的一套整合方案,服务端基于Openfire、客户端基于Smack。到AndroidPN项目主页( http://sourceforge.net/projects/androidpn/ ) 下载2个文件: androidpn-server-0.5.0-bin.zip 和 androidpn-client-0.5.0.zip 分别是服务器和客户端的代码。详细的实现方式网上有不少文章。</p>
<p>androidpn是韩国人放在sourceforge.net 的项目,已经有两年多没有更新了,项目应该是个人维护的,不是很成熟。有意思的是,网站上这个项目有82%的下载者的ip是中国的。androidpn有如下一些不足,开发的时候需要权衡:</p>
<p>1、androidpn服务端重启后客户端不会重连,这个非常悲剧</p>
<p>2、由于服务器不保存消息,造成了如果客户端当前离线就收不到消息。</p>
<p>3、androidpn发送完消息就不管了,所以没有消息回执报表之类,造成没法做应用后续的数据分析用户体验的改善,这对于企业级的应用是个致命伤。</p>
<p>XMPP协议比较费电费流量,这个对当前智能机的消耗太大,在窄带网络和不稳定的(手机)网络都不是最优的选择。但总体来说,XMPP协议还是比较成熟的。</p>
<h2 id="mqtt-httpmqttorghttpmqttorg">方案三:使用MQTT协议(更多信息见: <a href="http://mqtt.org/">http://mqtt.org/</a>)</h2>
<p>简介:轻量级的、基于代理的“发布/订阅”模式的消息传输协议。 </p>
<p>优点:协议简洁、小巧、可扩展性强、省流量、省电,目前已经应用到企业领域(参考: http://mqtt.org/software),且已有C++版的服务端组件rsmb。 </p>
<p>缺点:不够成熟、实现较复杂、服务端组件rsmb不开源,部署硬件成本较高。</p>
<h2 id="http">方案四:使用HTTP轮循方式</h2>
<p>简介:定时向HTTP服务端接口(Web Service API)获取最新消息。 </p>
<p>优点:实现简单、可控性强,部署硬件成本低。 </p>
<p>缺点:实时性差。</p>
<h2 id="section">方案五:采用第三方服务</h2>
<p>目前有不少第三方提供了类似服务,客户端只需要嵌入第三方提供的lib库,由第三方建立长连接,负责消息的接收/发送。同时对于消息都有比较详细的报表数据,可以用于做数据分析挖掘和用户体验的改善。目前国内提供推送服务的有好几家,比较成熟的主要有<a href="http://developer.baidu.com/">百度云推送</a>, <a href="http://www.jpush.cn/">极光推送</a>, <a href="http://www.igetui.com/">个推服务</a>。</p>
<p>还是出于对大公司的信任吧,所以这次选择了百度的云推送,总的来说官方文档使用说明还是蛮详细的。百度的云推送共提供三种形式的推送:</p>
<p>1.顶部通知栏消息提醒,当然也是提供自定义;</p>
<p>2.消息,可以是无界面的,也可以是用户自己定义的消息处理方式;</p>
<p>3.富媒体推送,如图片,声音等。</p>
<p>值得说明的是第一种提醒方式是比较常见的,这次的app中也是这种方式,但是SDK提供的这种顶部通知栏消息点击事件每次都会重新打开一次app,哪怕之前你的app是打开过的。这种交互方法非常不友好,为了解决这个问题试了很久也没能找到相应的方法。</p>
<p>于是在第二种消息提醒方式时,才发现这种定制性更强,只是处理的时候有些麻烦。这里客户端拿到推送消息时手动处理使在顶部通知栏显示,并把这些提醒信息保存进客户端Sqlite文件里,从而使交互更加友好。</p>
<p>当然基于以后的推送新的需求可能会涉及到富媒体推送之类的,以后用到时再看下实现方式。当然以后有机会也会了解下其他的推送服务,来了解各自推送服务的优劣,从而能确定一个最优的服务。</p>
SQLite加密--SQLCipher
2013-06-16T00:00:00+00:00
/android/sqlite/2013/06/16/sqlite-encrypt
<p>最近项目中要预先放置一部分food的sqlite数据在程序里,android项目资源文件的破译非常简单,出于安全的考虑,要对sqlite文件进行加密处理,于是就用到了SQLCipher。</p>
<p>SQLCipher is an open source library that provides transparent, secure 256-bit AES encryption of SQLite database files.</p>
<p>虽然SQLCipher是开源的,但是仅仅是开源的而已——你要自己编译,不想自己编译就得付费购买已经编译好的二进制文件~~木有钱,只好自己编译了,这里来讲下编译过程。</p>
<h2 id="section">1.下载源代码</h2>
<p>官方源代码:<a href="https://github.com/sqlcipher/sqlcipher">https://github.com/sqlcipher/sqlcipher</a></p>
<h2 id="section-1">2.编译</h2>
<p>进入源代码目录:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">.</span><span class="n">/configure</span> <span class="o">--</span><span class="n">enable</span><span class="o">-</span><span class="n">tempstore</span><span class="o">=</span><span class="n">yes</span> <span class="no">CFLAGS</span><span class="o">=</span><span class="s2">"-DSQLITE_HAS_CODEC"</span> <span class="no">LDFLAGS</span><span class="o">=</span><span class="s2">"-lcrypto"</span>
<span class="n">make</span></code></pre></div>
<p>注意:由于SQLCipher是SQLite的另外一个版本,所以为了不影响系统的SQLite,和其他SQLCipher版本间的兼容问题,所以不能将编译生成的直接install到系统,可以做符号链接等方式来管理二进制版本。我这里建立了一个软链接:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">ln</span> <span class="o">-</span><span class="n">s</span> <span class="sr">/Users/s</span><span class="n">torm</span><span class="o">/</span><span class="n">sqlcipher</span><span class="o">/</span><span class="n">sqlite3</span> <span class="sr">/usr/</span><span class="n">bin</span><span class="o">/</span><span class="n">sqlcipher</span></code></pre></div>
<h2 id="section-2">3.验证编译是否成功</h2>
<p>创建一个加密的数据,密码是aaa:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="err">$</span> <span class="n">sqlcipher</span> <span class="nb">test</span><span class="o">.</span><span class="n">sqlite</span>
<span class="no">SQLite</span> <span class="n">version</span> <span class="mi">3</span><span class="o">.</span><span class="mi">7</span><span class="o">.</span><span class="mi">14</span><span class="o">.</span><span class="mi">1</span> <span class="mi">2012</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mo">04</span> <span class="mi">19</span><span class="p">:</span><span class="mi">37</span><span class="p">:</span><span class="mi">12</span>
<span class="no">Enter</span> <span class="s2">".help"</span> <span class="k">for</span> <span class="n">instructions</span>
<span class="no">Enter</span> <span class="no">SQL</span> <span class="n">statements</span> <span class="n">terminated</span> <span class="n">with</span> <span class="n">a</span> <span class="s2">";"</span>
<span class="n">sqlite</span><span class="o">></span> <span class="no">PRAGMA</span> <span class="n">key</span> <span class="o">=</span> <span class="s1">'aaa'</span><span class="p">;</span>
<span class="n">sqlite</span><span class="o">></span> <span class="n">create</span> <span class="n">table</span> <span class="n">a</span><span class="p">(</span><span class="n">ind</span> <span class="n">int</span><span class="p">);</span>
<span class="n">sqlite</span><span class="o">></span> <span class="o">.</span><span class="n">tables</span>
<span class="n">a</span>
<span class="n">sqlite</span><span class="o">></span> <span class="o">.</span><span class="n">quit</span></code></pre></div>
<p>尝试不输入密码,直接读取数据库,理论上是读不到数据,或者报错:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="err">$</span> <span class="n">sqlcipher</span> <span class="nb">test</span><span class="o">.</span><span class="n">sqlite</span>
<span class="no">SQLite</span> <span class="n">version</span> <span class="mi">3</span><span class="o">.</span><span class="mi">7</span><span class="o">.</span><span class="mi">14</span><span class="o">.</span><span class="mi">1</span> <span class="mi">2012</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mo">04</span> <span class="mi">19</span><span class="p">:</span><span class="mi">37</span><span class="p">:</span><span class="mi">12</span>
<span class="no">Enter</span> <span class="s2">".help"</span> <span class="k">for</span> <span class="n">instructions</span>
<span class="no">Enter</span> <span class="no">SQL</span> <span class="n">statements</span> <span class="n">terminated</span> <span class="n">with</span> <span class="n">a</span> <span class="s2">";"</span>
<span class="n">sqlite</span><span class="o">></span> <span class="o">.</span><span class="n">tables</span>
<span class="n">sqlite</span><span class="o">></span> <span class="o">.</span><span class="n">quit</span></code></pre></div>
<p>尝试正确输入密码,应该成功读取:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="err">$</span> <span class="n">sqlcipher</span> <span class="nb">test</span><span class="o">.</span><span class="n">sqlite</span>
<span class="no">SQLite</span> <span class="n">version</span> <span class="mi">3</span><span class="o">.</span><span class="mi">7</span><span class="o">.</span><span class="mi">14</span><span class="o">.</span><span class="mi">1</span> <span class="mi">2012</span><span class="o">-</span><span class="mi">10</span><span class="o">-</span><span class="mo">04</span> <span class="mi">19</span><span class="p">:</span><span class="mi">37</span><span class="p">:</span><span class="mi">12</span>
<span class="no">Enter</span> <span class="s2">".help"</span> <span class="k">for</span> <span class="n">instructions</span>
<span class="no">Enter</span> <span class="no">SQL</span> <span class="n">statements</span> <span class="n">terminated</span> <span class="n">with</span> <span class="n">a</span> <span class="s2">";"</span>
<span class="n">sqlite</span><span class="o">></span> <span class="no">PRAGMA</span> <span class="n">key</span> <span class="o">=</span> <span class="s1">'aaa'</span><span class="p">;</span>
<span class="n">sqlite</span><span class="o">></span> <span class="o">.</span><span class="n">tables</span>
<span class="n">a</span>
<span class="n">sqlite</span><span class="o">></span> <span class="o">.</span><span class="n">quit</span></code></pre></div>
<p>上面三个流程都过说明编译成功!</p>
<h2 id="section-3">给现有数据进行加密</h2>
<p>如何给现有的sqlite文件进行加密,没有别的简单的方法:</p>
<p>1.先把数据导出:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="err">$</span> <span class="n">sqlite3</span> <span class="n">ifood</span><span class="o">.</span><span class="n">sqlite</span>
<span class="o">>.</span><span class="n">output</span> <span class="n">ifood</span><span class="o">.</span><span class="n">sql</span>
<span class="o">>.</span><span class="n">dump</span></code></pre></div>
<p>2.创建一个新的加密的数据库:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="err">$</span> <span class="n">sqlcipher</span> <span class="n">ifood_lock</span><span class="o">.</span><span class="n">sqlite</span>
<span class="n">sqlite</span><span class="o">></span> <span class="no">PRAGMA</span> <span class="n">key</span> <span class="o">=</span> <span class="s1">'abcdef'</span><span class="p">;</span> <span class="c1"># 设置密码</span></code></pre></div>
<p>3.导入数据</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">>.</span><span class="n">read</span> <span class="n">ifood</span><span class="o">.</span><span class="n">sql</span></code></pre></div>
<p>如果项目中经常用到数据加密,可以写个脚步。</p>
<h2 id="sqlcipher-for-android">SQLCipher For Android</h2>
<p>SQLCipher官网有详细的android项目中的使用教程,地址在:<a href="http://sqlcipher.net/sqlcipher-for-android/">http://sqlcipher.net/sqlcipher-for-android/</a></p>
<p>实际项目中遇到的问题是密码设置不要含单引号,不然会解密不对。而且加密过后有一个弊端,就是程序会增加将近4M左右的大小,使你的apk包看起来比较大,但是为了数据安全,这点牺牲还是值得的!</p>
Android AChartEngine
2013-06-02T00:00:00+00:00
/android/2013/06/02/android-achartengine
<p>最近一段时间完成“体重记录”的功能,需要实现日历和曲线的效果。也花费不少精力吧,这里就先把曲线的实现分享出来,俗话说的好:“好记忆不如烂笔头”!</p>
<h2 id="achartengine">AChartEngine是什么?</h2>
<p>AChartEngine是一个android应用的图表库,他支持一些常见的一些图表,如线状图,区域图,散点图,时间图,柱状图,饼状图,气泡图等。当然这次只用到了线状图。项目地址在<a href="http://code.google.com/p/achartengine/">http://code.google.com/p/achartengine/</a></p>
<p>下面先看下这次项目中实现的效果吧:</p>
<p><img src="/image/weight_curve.png" /></p>
<h2 id="section">实现</h2>
<p>总的来说,AChartEngine提供的api还是很全的,使用起来是很方便的,但是唯一的缺点就是api文档描述的不够详细,很多自己想要的效果都只能自己根据api的命名去推测,更有甚者得必须自己亲自一点点尝试才能实现出自己想要的效果,为了以后用到,这次也在代码中用到的接口表明了清晰的注释,废话不多说,直接上代码。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">WeightCurveActivity</span> <span class="n">extends</span> <span class="no">ActivityBase</span> <span class="p">{</span>
<span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">TAG</span> <span class="o">=</span> <span class="no">WeightCurveActivity</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">getName</span><span class="p">();</span>
<span class="kp">private</span> <span class="no">LinearLayout</span> <span class="n">rootLayout</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">XYMultipleSeriesRenderer</span> <span class="n">mRenderer</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">XYMultipleSeriesDataset</span> <span class="n">mDataset</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">XYMultipleSeriesDataset</span><span class="p">();</span>
<span class="kp">private</span> <span class="n">int</span> <span class="n">month</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">Date</span> <span class="n">date</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">ArrayList</span><span class="o"><</span><span class="no">WeightRecord</span><span class="o">></span> <span class="n">records</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">double</span><span class="o">[]</span> <span class="n">xValues</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">double</span><span class="o">[]</span> <span class="n">yValues</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">outState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">outState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">weight_curve</span><span class="p">);</span>
<span class="n">handleIntent</span><span class="p">();</span>
<span class="n">initRender</span><span class="p">();</span>
<span class="n">initData</span><span class="p">();</span>
<span class="n">initUI</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">handleIntent</span><span class="p">()</span> <span class="p">{</span>
<span class="nb">String</span> <span class="n">dateString</span> <span class="o">=</span> <span class="n">getIntent</span><span class="p">()</span><span class="o">.</span><span class="n">getStringExtra</span><span class="p">(</span><span class="no">Const</span><span class="o">.</span><span class="n">DATE</span><span class="p">);</span>
<span class="n">date</span> <span class="o">=</span> <span class="no">DateHelper</span><span class="o">.</span><span class="n">parseString</span><span class="p">(</span><span class="n">dateString</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">initRender</span><span class="p">()</span> <span class="p">{</span>
<span class="n">mRenderer</span> <span class="o">=</span> <span class="n">buildRenderer</span><span class="p">();</span>
<span class="n">setChartSettings</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">31</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">120</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">initData</span><span class="p">()</span> <span class="p">{</span>
<span class="n">month</span> <span class="o">=</span> <span class="no">DateHelper</span><span class="o">.</span><span class="n">getMonth</span><span class="p">(</span><span class="n">date</span><span class="p">);</span>
<span class="no">WeightRecordDao</span> <span class="n">dao</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">WeightRecordDao</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="n">records</span> <span class="o">=</span> <span class="n">dao</span><span class="o">.</span><span class="n">getMonthLists</span><span class="p">(</span><span class="n">date</span><span class="p">);</span>
<span class="no">Helper</span><span class="o">.</span><span class="n">showLog</span><span class="p">(</span><span class="no">TAG</span><span class="p">,</span> <span class="n">records</span><span class="o">.</span><span class="n">size</span><span class="p">());</span>
<span class="n">dao</span><span class="o">.</span><span class="n">closeDB</span><span class="p">();</span>
<span class="n">initValues</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">initValues</span><span class="p">()</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">count</span> <span class="o">=</span> <span class="n">records</span><span class="o">.</span><span class="n">size</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">count</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">xValues</span> <span class="o">=</span> <span class="kp">new</span> <span class="n">double</span><span class="o">[</span><span class="n">count</span><span class="o">]</span><span class="p">;</span>
<span class="n">yValues</span> <span class="o">=</span> <span class="kp">new</span> <span class="n">double</span><span class="o">[</span><span class="n">count</span><span class="o">]</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">count</span><span class="p">;</span> <span class="n">i</span> <span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="no">WeightRecord</span> <span class="n">record</span> <span class="o">=</span> <span class="n">records</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
<span class="n">xValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="no">DateHelper</span><span class="o">.</span><span class="n">getDay</span><span class="p">(</span><span class="n">record</span><span class="o">.</span><span class="n">record_on</span><span class="p">);</span>
<span class="n">yValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="p">(</span><span class="no">Math</span><span class="o">.</span><span class="n">round</span><span class="p">(</span><span class="n">record</span><span class="o">.</span><span class="n">weight</span> <span class="o">*</span> <span class="mi">10</span><span class="p">)</span> <span class="o">/</span> <span class="mi">10</span><span class="o">.</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">setXLabel</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">initUI</span><span class="p">()</span> <span class="p">{</span>
<span class="n">addXYSeries</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="n">rootLayout</span> <span class="o">=</span> <span class="p">(</span><span class="no">LinearLayout</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">root</span><span class="p">);</span>
<span class="no">View</span> <span class="n">view</span> <span class="o">=</span> <span class="no">ChartFactory</span><span class="o">.</span><span class="n">getLineChartView</span><span class="p">(</span><span class="n">this</span><span class="p">,</span> <span class="n">mDataset</span><span class="p">,</span> <span class="n">mRenderer</span><span class="p">);</span>
<span class="n">rootLayout</span><span class="o">.</span><span class="n">addView</span><span class="p">(</span><span class="n">view</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">setXLabel</span><span class="p">()</span> <span class="p">{</span>
<span class="n">mRenderer</span><span class="o">.</span><span class="n">setXLabels</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置</span><span class="n">X</span><span class="err">轴标签不显示</span>
<span class="n">int</span> <span class="n">length</span> <span class="o">=</span> <span class="n">xValues</span><span class="o">.</span><span class="n">length</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">length</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mRenderer</span><span class="o">.</span><span class="n">addXTextLabel</span><span class="p">(</span><span class="n">i</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">month</span> <span class="o">+</span> <span class="s2">"/"</span> <span class="o">+</span> <span class="p">(</span><span class="n">int</span><span class="p">)</span><span class="n">xValues</span><span class="o">[</span><span class="n">i</span><span class="o">]</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="no">XYMultipleSeriesRenderer</span> <span class="n">buildRenderer</span><span class="p">()</span> <span class="p">{</span>
<span class="no">XYMultipleSeriesRenderer</span> <span class="n">renderer</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">XYMultipleSeriesRenderer</span><span class="p">();</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setAxisTitleTextSize</span><span class="p">(</span><span class="mi">16</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置坐标轴字体大小</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setChartTitleTextSize</span><span class="p">(</span><span class="mi">20</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置标题大小</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setLabelsTextSize</span><span class="p">(</span><span class="mi">20</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置标签字体大小</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setLegendTextSize</span><span class="p">(</span><span class="mi">15</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置底部曲线说明字体大小</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setShowGridX</span><span class="p">(</span><span class="kp">true</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置</span><span class="n">X</span><span class="err">方向表格显示</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setShowLegend</span><span class="p">(</span><span class="kp">false</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置底部曲线说明显示</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setGridColor</span><span class="p">(</span><span class="no">Color</span><span class="o">.</span><span class="n">LTGRAY</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置表格颜色</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setPointSize</span><span class="p">(</span><span class="mi">5</span><span class="n">f</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setMargins</span><span class="p">(</span><span class="kp">new</span> <span class="n">int</span><span class="o">[]</span> <span class="p">{</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">30</span> <span class="p">});</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setPanEnabled</span><span class="p">(</span><span class="kp">true</span><span class="p">,</span> <span class="kp">false</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置曲线可滑动</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setApplyBackgroundColor</span><span class="p">(</span><span class="kp">true</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置图表背景</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setBackgroundColor</span><span class="p">(</span><span class="no">Color</span><span class="o">.</span><span class="n">TRANSPARENT</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setChartTitle</span><span class="p">(</span><span class="s2">"体重曲线"</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setXTitle</span><span class="p">(</span><span class="s2">"日期"</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setYTitle</span><span class="p">(</span><span class="s2">"体重"</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setXLabelsColor</span><span class="p">(</span><span class="n">getResources</span><span class="p">()</span><span class="o">.</span><span class="n">getColor</span><span class="p">(</span>
<span class="n">R</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">main_font_color</span><span class="p">));</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setXLabelsAlign</span><span class="p">(</span><span class="no">Align</span><span class="o">.</span><span class="n">CENTER</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setXLabelsPadding</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setYLabelsColor</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span>
<span class="n">getResources</span><span class="p">()</span><span class="o">.</span><span class="n">getColor</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">main_font_color</span><span class="p">));</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setYLabelsPadding</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setYLabelsAlign</span><span class="p">(</span><span class="no">Align</span><span class="o">.</span><span class="n">RIGHT</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setAxesColor</span><span class="p">(</span><span class="no">Color</span><span class="o">.</span><span class="n">GRAY</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置坐标轴颜色</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setMarginsColor</span><span class="p">(</span><span class="n">getResources</span><span class="p">()</span><span class="o">.</span><span class="n">getColor</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">main_bg_color</span><span class="p">));</span> <span class="sr">//</span> <span class="err">设置图表周围颜色</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">setLabelsColor</span><span class="p">(</span><span class="no">Color</span><span class="o">.</span><span class="n">GRAY</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置标签颜色</span>
<span class="no">XYSeriesRenderer</span> <span class="n">r</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">XYSeriesRenderer</span><span class="p">();</span>
<span class="n">r</span><span class="o">.</span><span class="n">setColor</span><span class="p">(</span><span class="n">getResources</span><span class="p">()</span><span class="o">.</span><span class="n">getColor</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">color</span><span class="o">.</span><span class="n">stress_font_color</span><span class="p">));</span>
<span class="n">r</span><span class="o">.</span><span class="n">setFillPoints</span><span class="p">(</span><span class="kp">true</span><span class="p">);</span>
<span class="n">r</span><span class="o">.</span><span class="n">setPointStyle</span><span class="p">(</span><span class="no">PointStyle</span><span class="o">.</span><span class="n">CIRCLE</span><span class="p">);</span>
<span class="n">renderer</span><span class="o">.</span><span class="n">addSeriesRenderer</span><span class="p">(</span><span class="n">r</span><span class="p">);</span>
<span class="n">r</span><span class="o">.</span><span class="n">setDisplayChartValues</span><span class="p">(</span><span class="kp">true</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置显示图表值</span>
<span class="n">r</span><span class="o">.</span><span class="n">setDisplayChartValuesDistance</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="n">r</span><span class="o">.</span><span class="n">setChartValuesTextSize</span><span class="p">(</span><span class="mi">16</span><span class="p">);</span>
<span class="n">r</span><span class="o">.</span><span class="n">setChartValuesSpacing</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="n">r</span><span class="o">.</span><span class="n">setHighlighted</span><span class="p">(</span><span class="kp">true</span><span class="p">);</span>
<span class="k">return</span> <span class="n">renderer</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">setChartSettings</span><span class="p">(</span><span class="n">double</span> <span class="n">xMin</span><span class="p">,</span> <span class="n">double</span> <span class="n">xMax</span><span class="p">,</span> <span class="n">double</span> <span class="n">yMin</span><span class="p">,</span>
<span class="n">double</span> <span class="n">yMax</span><span class="p">)</span> <span class="p">{</span>
<span class="n">mRenderer</span><span class="o">.</span><span class="n">setXAxisMin</span><span class="p">(</span><span class="n">xMin</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置</span><span class="n">X</span><span class="err">轴最小值</span>
<span class="n">mRenderer</span><span class="o">.</span><span class="n">setXAxisMax</span><span class="p">(</span><span class="n">xMax</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置</span><span class="n">X</span><span class="err">轴最大值</span>
<span class="n">mRenderer</span><span class="o">.</span><span class="n">setYAxisMin</span><span class="p">(</span><span class="n">yMin</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置</span><span class="n">Y</span><span class="err">轴最小值</span>
<span class="n">mRenderer</span><span class="o">.</span><span class="n">setYAxisMax</span><span class="p">(</span><span class="n">yMax</span><span class="p">);</span> <span class="sr">//</span> <span class="err">设置</span><span class="n">Y</span><span class="err">轴最大值</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">addXYSeries</span><span class="p">(</span><span class="n">int</span> <span class="n">scale</span><span class="p">)</span> <span class="p">{</span>
<span class="no">XYSeries</span> <span class="n">series</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">XYSeries</span><span class="p">(</span><span class="s2">""</span><span class="p">,</span> <span class="n">scale</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">records</span><span class="o">.</span><span class="n">size</span><span class="p">()</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">int</span> <span class="n">seriesLength</span> <span class="o">=</span> <span class="n">xValues</span><span class="o">.</span><span class="n">length</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="n">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="o"><</span> <span class="n">seriesLength</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="n">series</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">k</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">yValues</span><span class="o">[</span><span class="n">k</span><span class="o">]</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">mDataset</span><span class="o">.</span><span class="n">addSeries</span><span class="p">(</span><span class="n">series</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>现在看来代码倒是很简单,但是为了实现现在这个样子,当初费了不少精力来一步步尝试。当然AChartEngine能实现的不止这些,可以充分发挥自己的想象力与创造力,实现更加复杂的效果与功能。</p>
设计模式之单例模式
2013-05-01T00:00:00+00:00
/designpatterns/2013/05/01/singleton
<h2 id="section">单例模式解释</h2>
<p>单例模式是一种对象创建性模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。</p>
<p>单例模式的要点有三个:一是某个类只能有一个实例;而是必须自行创建整个实例;三是它必须自行向整个系统提供整个实例。</p>
<p>英文定义为:Ensure a class only has one instance, and provide a global point of access to it.</p>
<h2 id="section-1">单例模式深入分析</h2>
<p>单例模式适合一个类只有一个实例的情况, 比如窗口管理器,打印缓冲池和文件系统,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是总所周知的单例模式的应用。当然这只有在你确信你不再需要任何多于一个的实例的情况下。</p>
<h2 id="section-2">使用场景及代码实现</h2>
<p>下面就举例来说明下:</p>
<p>单例模式的第一个版本,“饿汉式”,也就是当类加载进来的就立即实例化对象,但是这种方式比较的消耗计算机资源。如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">在类被加载进入内存的时候就创建单一的</span><span class="no">Foo</span><span class="err">对象</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="no">Foo</span> <span class="n">foo</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Foo</span><span class="p">();</span>
<span class="sr">//</span> <span class="err">构造函数私有化</span>
<span class="kp">private</span> <span class="no">Foo</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">提供一个全局的静态方法</span>
<span class="kp">public</span> <span class="n">static</span> <span class="no">Foo</span> <span class="n">getFoo</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">foo</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>单例模式的第二个版本,“懒汉式”,在单线程下能够非常好的工作,但是在多线程下存在线程安全问题,如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">//</span> <span class="err">这种方式在需要使用的时候才实例化</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="no">Foo</span> <span class="n">foo</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">构造函数私有化</span>
<span class="kp">private</span> <span class="no">Foo</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">提供一个全局的静态方法</span>
<span class="kp">public</span> <span class="n">static</span> <span class="no">Foo</span> <span class="n">getFoo</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">foo</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">foo</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Foo</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">foo</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>单例模式的第三个版本,为解决多线程问题,采用了对函数进行同步的方式,但是比较浪费资源,因为每次都要进行同步检查,而实际中真正需要检查只是第一次实例化的时候,如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="no">Foo</span> <span class="n">foo</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">构造函数私有化</span>
<span class="kp">private</span> <span class="no">Foo</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">提供一个全局的静态方法,使用同步方法</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">synchronized</span> <span class="no">Foo</span> <span class="n">getFoo</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">foo</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">foo</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Foo</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">foo</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>单例模式的第四个版本,既解决了”懒汉式“的多线程问题,又解决了资源浪费的现象,看上去是一种不错的选择,如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="no">Foo</span> <span class="n">foo</span><span class="p">;</span>
<span class="sr">//</span> <span class="err">构造函数私有化</span>
<span class="kp">private</span> <span class="no">Foo</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">提供一个全局的静态方法</span>
<span class="kp">public</span> <span class="n">static</span> <span class="no">Foo</span> <span class="n">getFoo</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">foo</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">synchronized</span> <span class="p">(</span><span class="no">Foo</span><span class="o">.</span><span class="n">class</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">foo</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">foo</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Foo</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">foo</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="section-3">单例模式的优缺点分析</h2>
<p>优点:客户端使用单例模式的实例的时候,只需要调用一个单一的方法即可生成一个唯一的实例,有利于节约资源。</p>
<p>缺点:首先单例模式很难实现序列化,这就导致采用单例模式的类很难被持久化,当然也很难通过网络传输;其次由于单例采用静态方法,无法在继承结构中使用。</p>
Android手势
2013-04-17T00:00:00+00:00
/android/2013/04/17/android-gesture-listener
<p>之前做的App是完全没有任何手势支持的,对于现在的程序来说,如果没有一些手势的支持,感觉实在是有点落后了,支持手势的App才叫cool。于是在这次重新搭建ifood for android框架的同时下决心让自己的App完全支持手势。下面就来看下自己实现的一个全局滑动切换窗口的例子。</p>
<p>在android系统中,手势的识别是通过 GestureDetector.OnGestureListener接口来实现的。如果要自定义手势需要重写这个接口里的一些方法,废话不多说,下面上代码:</p>
<p>自定义的一个GestureLisntener:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">MyGestureListener</span><span class="o">.</span><span class="n">java</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">MyGestureListener</span> <span class="n">implements</span> <span class="no">OnGestureListener</span> <span class="p">{</span>
<span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">TAG</span> <span class="o">=</span> <span class="s2">"MyGestureListener"</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">SWIPE_MAX_OFF_PATH</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">SWIPE_MIN_DISTANCE</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">SWIPE_THRESHOLD_VELOCITY</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">Context</span> <span class="n">context</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">MyGestureListener</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">context</span> <span class="o">=</span> <span class="n">context</span><span class="p">;</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onDown</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="nb">method</span> <span class="n">stub</span>
<span class="k">return</span> <span class="kp">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onShowPress</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="nb">method</span> <span class="n">stub</span>
<span class="no">Log</span><span class="o">.</span><span class="n">e</span><span class="p">(</span><span class="no">TAG</span><span class="p">,</span> <span class="s2">"onShowPress"</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onSingleTapUp</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="nb">method</span> <span class="n">stub</span>
<span class="k">return</span> <span class="kp">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onScroll</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e1</span><span class="p">,</span> <span class="no">MotionEvent</span> <span class="n">e2</span><span class="p">,</span> <span class="n">float</span> <span class="n">distanceX</span><span class="p">,</span>
<span class="n">float</span> <span class="n">distanceY</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="nb">method</span> <span class="n">stub</span>
<span class="k">return</span> <span class="kp">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onLongPress</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="nb">method</span> <span class="n">stub</span>
<span class="no">Log</span><span class="o">.</span><span class="n">e</span><span class="p">(</span><span class="no">TAG</span><span class="p">,</span> <span class="s2">"onLongPress"</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onFling</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">e1</span><span class="p">,</span> <span class="no">MotionEvent</span> <span class="n">e2</span><span class="p">,</span> <span class="n">float</span> <span class="n">velocityX</span><span class="p">,</span>
<span class="n">float</span> <span class="n">velocityY</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="no">Math</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">e1</span><span class="o">.</span><span class="n">getY</span><span class="p">()</span> <span class="o">-</span> <span class="n">e2</span><span class="o">.</span><span class="n">getY</span><span class="p">())</span> <span class="o">></span> <span class="no">SWIPE_MAX_OFF_PATH</span><span class="p">)</span>
<span class="k">return</span> <span class="kp">false</span><span class="p">;</span>
<span class="k">if</span> <span class="p">((</span><span class="n">e1</span><span class="o">.</span><span class="n">getX</span><span class="p">()</span> <span class="o">-</span> <span class="n">e2</span><span class="o">.</span><span class="n">getX</span><span class="p">())</span> <span class="o">></span> <span class="no">SWIPE_MIN_DISTANCE</span>
<span class="o">&&</span> <span class="no">Math</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">velocityX</span><span class="p">)</span> <span class="o">></span> <span class="no">SWIPE_THRESHOLD_VELOCITY</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Log</span><span class="o">.</span><span class="n">e</span><span class="p">(</span><span class="no">TAG</span><span class="p">,</span> <span class="s2">"onFling left"</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">e2</span><span class="o">.</span><span class="n">getX</span><span class="p">()</span> <span class="o">-</span> <span class="n">e1</span><span class="o">.</span><span class="n">getX</span><span class="p">())</span> <span class="o">></span> <span class="no">SWIPE_MIN_DISTANCE</span>
<span class="o">&&</span> <span class="no">Math</span><span class="o">.</span><span class="n">abs</span><span class="p">(</span><span class="n">velocityX</span><span class="p">)</span> <span class="o">></span> <span class="no">SWIPE_THRESHOLD_VELOCITY</span><span class="p">)</span> <span class="p">{</span>
<span class="no">Log</span><span class="o">.</span><span class="n">e</span><span class="p">(</span><span class="no">TAG</span><span class="p">,</span> <span class="s2">"onFling right"</span><span class="p">);</span>
<span class="p">((</span><span class="no">Activity</span><span class="p">)</span> <span class="n">context</span><span class="p">)</span><span class="o">.</span><span class="n">finish</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>在这个类中的onFling()方法中从左向右滑动时实现了界面的切换,如果有更复杂的手势支持,同样可以在这个基类中进行添加。</p>
<p>接下来新建一个GestureActivity实现Gesture滑动切换界面,让支持手势的Activity继承它,这样就继承了它的手势支持功能,提高代码复用。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">GestureActivity</span><span class="o">.</span><span class="n">java</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">GestureActivity</span> <span class="n">extends</span> <span class="no">ActivityBase</span> <span class="p">{</span>
<span class="no">MyGestureListener</span> <span class="n">listener</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">MyGestureListener</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="kp">protected</span> <span class="no">GestureDetector</span> <span class="n">gestureDetector</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">GestureDetector</span><span class="p">(</span><span class="n">listener</span><span class="p">);</span>
<span class="kp">public</span> <span class="n">boolean</span> <span class="n">onTouchEvent</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">event</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">gestureDetector</span><span class="o">.</span><span class="n">onTouchEvent</span><span class="p">(</span><span class="n">event</span><span class="p">))</span>
<span class="k">return</span> <span class="kp">true</span><span class="p">;</span>
<span class="k">else</span>
<span class="k">return</span> <span class="kp">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>这样就实现了一个简单的滑动切换页面的框架,如果想支持更多的手势,只需要重写MyGestureListener的方法就可以了。</p>
<p>不过不要高兴的太早,在一般的Activity手势支持是正常的,可是碰到一些包含ScrollView或者ListView的Activity时,手势就不相应了。原因是因为这些滑动的组件本身就已经具有了手势的支持,这样就会产生了冲突,导致自定义的手势没有被识别到。google了很久,似乎也没个具体的方法,后来看到说用dispatchTouchEvent(MotionEvent ev) 的方法,果然可以。于是在GestureActivity里就多了这样一个方法:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="n">boolean</span> <span class="n">dispatchTouchEvent</span><span class="p">(</span><span class="no">MotionEvent</span> <span class="n">ev</span><span class="p">)</span> <span class="p">{</span>
<span class="n">gestureDetector</span><span class="o">.</span><span class="n">onTouchEvent</span><span class="p">(</span><span class="n">ev</span><span class="p">);</span>
<span class="k">return</span> <span class="k">super</span><span class="o">.</span><span class="n">dispatchTouchEvent</span><span class="p">(</span><span class="n">ev</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>此时再试一下,果然所有Activity都实现了自定义的手势事件。但是为什么加上这个方法就可以了呢,必须要搞明白。</p>
<p>android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。</p>
<p>一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE…->ACTION_MOVE->ACTION_UP</p>
<p>当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?答案是:不一定。</p>
<p>android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:</p>
<p>1.public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent</p>
<p>2.public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent</p>
<p>3.public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent</p>
<p>当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View, TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。</p>
<p>看到这终于清楚了上面的疑问,dispatchTouchEvent()方法直接将触摸事件交给了gestureDetector的触摸事件,这样就解决了冲突问题。</p>
Android ActivityGroup
2013-04-07T00:00:00+00:00
/android/2013/04/07/android-activity-group
<p>在android应用中底部导航栏可以说是十分常见的,如新浪微博,微信等都是这种设计,大家在做这种应用第一反应就是使用TabActivity,今天就来分享下如何用ActivityGroup来代替TabActivity,以及这样使用的优点。</p>
<p>ActivityGroup是Google提供的一个非常优秀的API,而TabActivity是ActivityGroup唯一的一个子类。</p>
<h2 id="activitygroup">ActivityGroup的优点</h2>
<p>首先来说ActivityGroup的优秀之处以及它的必要性,它为开发者提供了一种可能,这种可能不将Activity作为屏幕的顶级元素(Context)呈现,而是嵌入到ActivityGroup当中。这是一种极大的飞跃,它将场景(Context)细分化了,ActivityGroup是一个主场景,而用户可以通过导航按钮来切换想要的子场景。如使用微博功能,它是一个相当宏大的场景,具有看最新的广播信息、自己发微博、修改资料等子场景,用户可以通过按钮来切换到想要的子场景,而这个子场景仍活动于主场景之中。让一个主场景能拥有多个逻辑处理模块,主场景不再负责子场景逻辑,主场景只负责切换场景的逻辑,即每一个Activity(子场景)拥有一个逻辑处理模块,一个ActivityGroup有多个Activity,却不干预Activity的逻辑,这无疑细分化和模块化了逻辑代码。ActivityGroup和它将要内嵌的Activity所要实现的功能完全可以只用一个Activity来完成,你可以试想,当你把一个ActivityGroup和它所拥有的Activity的逻辑代码放在一个Activity中时,那这个Activity会拥有多少行代码,为维护带来非常的不便。</p>
<h2 id="tabactivity">TabActivity的不足</h2>
<p>再来说说TabActivity的不足之处,首先,TabActivity自己独有的视图几乎没人使用(也就是难看的标签页按钮形式),大多数开发者用到的特性几乎都是从ActivityGroup继承下来的。还有就是TabActivity的强制依赖关系,它的布局文件必须将TabHost作根标签,并且id必须为”@android:id/tabhost”,必须有TabWidget标签,且它的id必须是”@android:id/tabs”,还有加载Activity的View容器,id必须为@android:id/tabcontent。光是强制依赖关系,我就觉得不是很舒服。而且在android3.0以后已经建议用Fragment来代替TabActivity了。只是由于目前为了要兼容3.0之前的版本,Fragment还没有被开发者们所普及。</p>
<p>下面就来看下这次食物库重构后的用ActivityGroup来实现的主页面架构吧:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">MainActivityGroup</span><span class="o">.</span><span class="n">java</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">MainActivityGroup</span> <span class="n">extends</span> <span class="no">ActivityGroup</span> <span class="n">implements</span>
<span class="no">OnCheckedChangeListener</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">RECORD</span> <span class="o">=</span> <span class="s2">"record"</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">CATEGORY</span> <span class="o">=</span> <span class="s2">"category"</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">MORE</span> <span class="o">=</span> <span class="s2">"more"</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">ActivityGroupManager</span> <span class="n">manager</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">FrameLayout</span> <span class="n">container</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">RadioGroup</span> <span class="n">radioGroup</span><span class="p">;</span>
<span class="vi">@Override</span>
<span class="kp">protected</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">test_main</span><span class="p">);</span>
<span class="n">initView</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">initView</span><span class="p">()</span> <span class="p">{</span>
<span class="n">radioGroup</span> <span class="o">=</span> <span class="p">(</span><span class="no">RadioGroup</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">main_radio</span><span class="p">);</span>
<span class="n">radioGroup</span><span class="o">.</span><span class="n">setOnCheckedChangeListener</span><span class="p">(</span><span class="n">this</span><span class="p">);</span>
<span class="n">manager</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">ActivityGroupManager</span><span class="p">();</span>
<span class="n">container</span> <span class="o">=</span> <span class="p">(</span><span class="no">FrameLayout</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">container</span><span class="p">);</span>
<span class="n">manager</span><span class="o">.</span><span class="n">setContainer</span><span class="p">(</span><span class="n">container</span><span class="p">);</span>
<span class="n">switchActivity</span><span class="p">(</span><span class="no">ActivityGroupManager</span><span class="o">.</span><span class="n">RECORD_ACTIVITY_VIEW</span><span class="p">,</span> <span class="no">RECORD</span><span class="p">,</span> <span class="no">RecordActivity</span><span class="o">.</span><span class="n">class</span><span class="p">);</span>
<span class="p">}</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCheckedChanged</span><span class="p">(</span><span class="no">RadioGroup</span> <span class="n">group</span><span class="p">,</span> <span class="n">int</span> <span class="n">checkedId</span><span class="p">)</span> <span class="p">{</span>
<span class="n">switch</span> <span class="p">(</span><span class="n">checkedId</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">record_button</span><span class="p">:</span>
<span class="n">switchActivity</span><span class="p">(</span><span class="no">ActivityGroupManager</span><span class="o">.</span><span class="n">RECORD_ACTIVITY_VIEW</span><span class="p">,</span> <span class="no">RECORD</span><span class="p">,</span> <span class="no">RecordActivity</span><span class="o">.</span><span class="n">class</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">category_button</span><span class="p">:</span>
<span class="n">switchActivity</span><span class="p">(</span><span class="no">ActivityGroupManager</span><span class="o">.</span><span class="n">CATEGORY_ACTIVITY_VIEW</span><span class="p">,</span> <span class="no">CATEGORY</span><span class="p">,</span> <span class="no">CategoryActivity</span><span class="o">.</span><span class="n">class</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">more_button</span><span class="p">:</span>
<span class="n">switchActivity</span><span class="p">(</span><span class="no">ActivityGroupManager</span><span class="o">.</span><span class="n">MORE_AVTIVITY_VIEW</span><span class="p">,</span> <span class="no">MORE</span><span class="p">,</span> <span class="no">MoreActivity</span><span class="o">.</span><span class="n">class</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="ss">default</span><span class="p">:</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="no">View</span> <span class="n">getActivityView</span><span class="p">(</span><span class="nb">String</span> <span class="n">activityName</span><span class="p">,</span> <span class="no">Class</span><span class="o"><</span><span class="sc">?></span> <span class="n">activityClass</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">getLocalActivityManager</span><span class="p">()</span><span class="o">.</span><span class="n">startActivity</span><span class="p">(</span><span class="n">activityName</span><span class="p">,</span>
<span class="kp">new</span> <span class="no">Intent</span><span class="p">(</span><span class="no">MainActivityGroup</span><span class="o">.</span><span class="n">this</span><span class="p">,</span> <span class="n">activityClass</span><span class="p">))</span>
<span class="o">.</span><span class="n">getDecorView</span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">void</span> <span class="n">switchActivity</span><span class="p">(</span><span class="n">int</span> <span class="n">num</span><span class="p">,</span> <span class="nb">String</span> <span class="n">activityName</span><span class="p">,</span> <span class="no">Class</span><span class="o"><</span><span class="sc">?></span> <span class="n">activityClass</span><span class="p">)</span> <span class="p">{</span>
<span class="n">manager</span><span class="o">.</span><span class="n">showContainer</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="n">getActivityView</span><span class="p">(</span><span class="n">activityName</span><span class="p">,</span> <span class="n">activityClass</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>可以看到在“主场景”MainActivityGroup中基本没有任何逻辑代码,只有各个“子场景”切换的逻辑,而子场景的切换用了一个ActivityGroupManager类来管理,这样又起到了代码分离的作用,</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">ActivityGroupManager</span><span class="o">.</span><span class="n">java</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">ActivityGroupManager</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">TAG</span> <span class="o">=</span> <span class="s2">"frag_manager"</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">RECORD_ACTIVITY_VIEW</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">CATEGORY_ACTIVITY_VIEW</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">final</span> <span class="n">int</span> <span class="no">MORE_AVTIVITY_VIEW</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">HashMap</span><span class="o"><</span><span class="nb">Integer</span><span class="p">,</span> <span class="no">View</span><span class="o">></span> <span class="n">hashMap</span><span class="p">;</span>
<span class="kp">private</span> <span class="no">ViewGroup</span> <span class="n">container</span><span class="p">;</span>
<span class="kp">public</span> <span class="no">ActivityGroupManager</span><span class="p">()</span> <span class="p">{</span>
<span class="n">hashMap</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">HashMap</span><span class="o"><</span><span class="nb">Integer</span><span class="p">,</span> <span class="no">View</span><span class="o">></span><span class="p">();</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">setContainer</span><span class="p">(</span><span class="no">ViewGroup</span> <span class="n">container</span><span class="p">)</span> <span class="p">{</span>
<span class="n">this</span><span class="o">.</span><span class="n">container</span> <span class="o">=</span> <span class="n">container</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">showContainer</span><span class="p">(</span><span class="n">int</span> <span class="n">num</span><span class="p">,</span> <span class="no">View</span> <span class="n">view</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">hashMap</span><span class="o">.</span><span class="n">containsKey</span><span class="p">(</span><span class="n">num</span><span class="p">))</span> <span class="p">{</span>
<span class="n">hashMap</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="n">num</span><span class="p">,</span> <span class="n">view</span><span class="p">);</span>
<span class="n">container</span><span class="o">.</span><span class="n">addView</span><span class="p">(</span><span class="n">view</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="no">Iterator</span><span class="o"><</span><span class="nb">Integer</span><span class="o">></span> <span class="n">iter</span> <span class="o">=</span> <span class="n">hashMap</span><span class="o">.</span><span class="n">keySet</span><span class="p">()</span><span class="o">.</span><span class="n">iterator</span><span class="p">();</span> <span class="n">iter</span><span class="o">.</span><span class="n">hasNext</span><span class="p">();)</span> <span class="p">{</span>
<span class="no">Object</span> <span class="n">key</span> <span class="o">=</span> <span class="n">iter</span><span class="o">.</span><span class="n">next</span><span class="p">();</span>
<span class="no">View</span> <span class="n">v</span> <span class="o">=</span> <span class="n">hashMap</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">);</span>
<span class="n">v</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">INVISIBLE</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">view</span><span class="o">.</span><span class="n">setVisibility</span><span class="p">(</span><span class="no">View</span><span class="o">.</span><span class="n">VISIBLE</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>值得一说的是在ActivityGroupManager类中的showContainer ()方法并没有像网上的做法这样:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">container</span><span class="o">.</span><span class="n">removeAllViews</span><span class="p">();</span>
<span class="n">container</span><span class="o">.</span><span class="n">addView</span><span class="p">(</span><span class="n">view</span><span class="p">);</span></code></pre></div>
<p>这种做法看似代码逻辑更简单,但是这样就会导致每次切换“子场景”的时候都会把已经加载过的View remove掉,一方面性能有所欠缺,另一个方面“子场景”的状态无法记住。而现在的做法就很好的解决了上面的问题。</p>
<p>好了,然后就是各个“子场景”(Activity)的代码了,每个“子场景”的逻辑各自独立,这里就不上代码了。</p>
<p>这里还有一个需要提到的是底部导航栏的实现,由于android没有像iOS那样预定好的组件,只有自己定义一个布局了,这里我用到的是用RadioGroup来实现类似微信的底部导航栏效果,下面是布局代码,在其他使用到的地方直接include进来就可以了。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="p">?</span><span class="n">xml</span> <span class="n">version</span><span class="o">=</span><span class="s2">"1.0"</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="sc">?></span>
<span class="o"><</span><span class="no">RadioGroup</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/main_radio"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_gravity</span><span class="o">=</span><span class="s2">"bottom|center"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_marginBottom</span><span class="o">=</span><span class="s2">"-20dp"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"center"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">orientation</span><span class="o">=</span><span class="s2">"horizontal"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">padding</span><span class="o">=</span><span class="s2">"0dp"</span> <span class="o">></span>
<span class="o"><</span><span class="no">RadioButton</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">id</span><span class="o">=</span><span class="s2">"@+id/record_button"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_width</span><span class="o">=</span><span class="s2">"fill_parent"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_height</span><span class="o">=</span><span class="s2">"wrap_content"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_gravity</span><span class="o">=</span><span class="s2">"center"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">layout_weight</span><span class="o">=</span><span class="s2">"1.0"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">background</span><span class="o">=</span><span class="s2">"@null"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">button</span><span class="o">=</span><span class="s2">"@null"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">checked</span><span class="o">=</span><span class="s2">"true"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">drawableTop</span><span class="o">=</span><span class="s2">"@drawable/main_tab_record_selector"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">gravity</span><span class="o">=</span><span class="s2">"center"</span>
<span class="ss">android</span><span class="p">:</span><span class="n">tag</span><span class="o">=</span><span class="s2">"record"</span> <span class="sr">/></span>
<span class="sr"> <RadioButton</span>
<span class="sr"> android:id="@+id/</span><span class="n">category_button</span><span class="s2">"</span>
<span class="s2"> android:layout_width="</span><span class="n">fill_parent</span><span class="s2">"</span>
<span class="s2"> android:layout_height="</span><span class="n">wrap_content</span><span class="s2">"</span>
<span class="s2"> android:layout_weight="</span><span class="mi">1</span><span class="o">.</span><span class="mi">0</span><span class="s2">"</span>
<span class="s2"> android:background="</span><span class="vi">@null</span><span class="s2">"</span>
<span class="s2"> android:button="</span><span class="vi">@null</span><span class="s2">"</span>
<span class="s2"> android:drawableTop="</span><span class="vi">@drawable</span><span class="o">/</span><span class="n">main_tab_category_selector</span><span class="s2">"</span>
<span class="s2"> android:gravity="</span><span class="n">center_horizontal</span><span class="s2">"</span>
<span class="s2"> android:tag="</span><span class="n">category</span><span class="s2">" /></span>
<span class="s2"> <RadioButton</span>
<span class="s2"> android:id="</span><span class="err">@</span><span class="o">+</span><span class="nb">id</span><span class="o">/</span><span class="n">more_button</span><span class="s2">"</span>
<span class="s2"> android:layout_width="</span><span class="n">fill_parent</span><span class="s2">"</span>
<span class="s2"> android:layout_height="</span><span class="n">wrap_content</span><span class="s2">"</span>
<span class="s2"> android:layout_weight="</span><span class="mi">1</span><span class="o">.</span><span class="mi">0</span><span class="s2">"</span>
<span class="s2"> android:background="</span><span class="vi">@null</span><span class="s2">"</span>
<span class="s2"> android:button="</span><span class="vi">@null</span><span class="s2">"</span>
<span class="s2"> android:drawableTop="</span><span class="vi">@drawable</span><span class="o">/</span><span class="n">main_tab_more_selector</span><span class="s2">"</span>
<span class="s2"> android:gravity="</span><span class="n">center_horizontal</span><span class="s2">"</span>
<span class="s2"> android:tag="</span><span class="n">more</span><span class="s2">" /></span>
<span class="s2"></RadioGroup></span></code></pre></div>
Android Handler
2013-03-28T00:00:00+00:00
/android/2013/03/28/android-handler
<h2 id="handler">Handler的定义</h2>
<p>Handler主要接受子线程发送的数据, 并用此数据配合主线程更新UI。 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) ,主线程为管理界面中的UI控件,进行事件分发, 比如说,你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 “强制关闭”。</p>
<p>这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程不是线程安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。这个时候,Handler就出现了,来解决这个复杂的问题。Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传递)Message对象(里面包含数据),把这些消息放入主线程队列中,配合主线程进行更新UI。</p>
<h2 id="handler-1">Handler的一些特点</h2>
<p>Handler 为Android操作系统中的线程通信工具,包为android.os.Handler。</p>
<p>与Handler绑定的有两个队列,一个为消息队列,另一个为线程队列。Handler可以通过这两个队列来分别:</p>
<p>1.发送、接受、处理消息–消息队列;</p>
<p>2.启动、结束、休眠线程–线程队列;</p>
<p>Android OS中,一个进程被创建之后,主线程(可理解为当前Activity)创建一个消息队列,这个消息队列维护所有顶层应用对象(Activities, Broadcast receivers等)以及主线程创建的窗口。你可以在主线程中创建新的线程,这些新的线程都通过Handler与主线程进行通信。通信通过新线程调用 Handler的post()方法和sendMessage()方法实现,分别对应功能:</p>
<p>1.post() 将一个线程加入线程队列;</p>
<p>2.sendMessage() 发送一个消息对象到消息队列;</p>
<p>当然,post()方法还有一些变体,比如
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)</p>
<h2 id="handler-2">Handler实例</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyHandlerActivity</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="p">{</span>
<span class="no">Button</span> <span class="n">button</span><span class="p">;</span>
<span class="no">MyHandler</span> <span class="n">myHandler</span><span class="p">;</span>
<span class="kp">protected</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">handlertest</span><span class="p">);</span>
<span class="n">button</span> <span class="o">=</span> <span class="p">(</span><span class="no">Button</span><span class="p">)</span> <span class="n">findViewById</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">id</span><span class="o">.</span><span class="n">button</span><span class="p">);</span>
<span class="n">myHandler</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">MyHandler</span><span class="p">();</span>
<span class="sr">//</span> <span class="err">当创建一个新的</span><span class="no">Handler</span><span class="err">实例时</span><span class="p">,</span> <span class="err">它会绑定到当前线程和消息的队列中</span><span class="p">,</span><span class="err">开始分发数据</span>
<span class="sr">//</span> <span class="no">Handler</span><span class="err">有两个作用</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">:</span> <span class="err">定时执行</span><span class="no">Message</span><span class="err">和</span><span class="no">Runnalbe</span> <span class="err">对象</span>
<span class="sr">//</span> <span class="p">(</span><span class="mi">2</span><span class="p">):</span> <span class="err">让一个动作</span><span class="p">,</span><span class="err">在不同的线程中执行</span><span class="o">.</span>
<span class="sr">//</span> <span class="err">它安排消息</span><span class="p">,</span><span class="err">用以下方法</span>
<span class="sr">//</span> <span class="n">post</span><span class="p">(</span><span class="no">Runnable</span><span class="p">)</span>
<span class="sr">//</span> <span class="n">postAtTime</span><span class="p">(</span><span class="no">Runnable</span><span class="p">,</span><span class="n">long</span><span class="p">)</span>
<span class="sr">//</span> <span class="n">postDelayed</span><span class="p">(</span><span class="no">Runnable</span><span class="p">,</span><span class="n">long</span><span class="p">)</span>
<span class="sr">//</span> <span class="n">sendEmptyMessage</span><span class="p">(</span><span class="n">int</span><span class="p">)</span>
<span class="sr">//</span> <span class="n">sendMessage</span><span class="p">(</span><span class="no">Message</span><span class="p">);</span>
<span class="sr">//</span> <span class="n">sendMessageAtTime</span><span class="p">(</span><span class="no">Message</span><span class="p">,</span><span class="n">long</span><span class="p">)</span>
<span class="sr">//</span> <span class="n">sendMessageDelayed</span><span class="p">(</span><span class="no">Message</span><span class="p">,</span><span class="n">long</span><span class="p">)</span>
<span class="sr">//</span> <span class="err">以上方法以</span> <span class="n">post</span><span class="err">开头的允许你处理</span><span class="no">Runnable</span><span class="err">对象</span>
<span class="sr">//sen</span><span class="n">dMessage</span><span class="p">()</span><span class="err">允许你处理</span><span class="no">Message</span><span class="err">对象</span><span class="p">(</span><span class="no">Message</span><span class="err">里可以包含数据</span><span class="p">,)</span>
<span class="no">MyThread</span> <span class="n">m</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">MyThread</span><span class="p">();</span>
<span class="kp">new</span> <span class="no">Thread</span><span class="p">(</span><span class="n">m</span><span class="p">)</span><span class="o">.</span><span class="n">start</span><span class="p">();</span>
<span class="p">}</span>
<span class="sr">/**</span>
<span class="sr"> * 接受消息,处理消息 ,此Handler会与当前主线程一块运行</span>
<span class="sr"> * */</span>
<span class="k">class</span> <span class="nc">MyHandler</span> <span class="n">extends</span> <span class="no">Handler</span> <span class="p">{</span>
<span class="kp">public</span> <span class="no">MyHandler</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="no">MyHandler</span><span class="p">(</span><span class="no">Looper</span> <span class="n">L</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="n">L</span><span class="p">);</span>
<span class="p">}</span>
<span class="sr">//</span> <span class="err">子类必须重写此方法</span><span class="p">,</span><span class="err">接受数据</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">handleMessage</span><span class="p">(</span><span class="no">Message</span> <span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="nb">method</span> <span class="n">stub</span>
<span class="no">Log</span><span class="o">.</span><span class="n">d</span><span class="p">(</span><span class="s2">"MyHandler"</span><span class="p">,</span> <span class="s2">"handleMessage......"</span><span class="p">);</span>
<span class="k">super</span><span class="o">.</span><span class="n">handleMessage</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">此处可以更新</span><span class="no">UI</span>
<span class="no">Bundle</span> <span class="n">b</span> <span class="o">=</span> <span class="n">msg</span><span class="o">.</span><span class="n">getData</span><span class="p">();</span>
<span class="nb">String</span> <span class="n">color</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">getString</span><span class="p">(</span><span class="s2">"color"</span><span class="p">);</span>
<span class="no">MyHandlerActivity</span><span class="o">.</span><span class="n">this</span><span class="o">.</span><span class="n">button</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">color</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">MyThread</span> <span class="n">implements</span> <span class="no">Runnable</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="n">try</span> <span class="p">{</span>
<span class="no">Thread</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">10000</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">InterruptedException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="kp">catch</span> <span class="n">block</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStackTrace</span><span class="p">();</span>
<span class="p">}</span>
<span class="no">Log</span><span class="o">.</span><span class="n">d</span><span class="p">(</span><span class="s2">"thread......."</span><span class="p">,</span> <span class="s2">"mThread........"</span><span class="p">);</span>
<span class="no">Message</span> <span class="n">msg</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Message</span><span class="p">();</span>
<span class="no">Bundle</span> <span class="n">b</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Bundle</span><span class="p">();</span><span class="o">//</span> <span class="err">存放数据</span>
<span class="n">b</span><span class="o">.</span><span class="n">putString</span><span class="p">(</span><span class="s2">"color"</span><span class="p">,</span> <span class="s2">"我的"</span><span class="p">);</span>
<span class="n">msg</span><span class="o">.</span><span class="n">setData</span><span class="p">(</span><span class="n">b</span><span class="p">);</span>
<span class="no">MyHandlerActivity</span><span class="o">.</span><span class="n">this</span><span class="o">.</span><span class="n">myHandler</span><span class="o">.</span><span class="n">sendMessage</span><span class="p">(</span><span class="n">msg</span><span class="p">);</span> <span class="sr">//</span> <span class="err">向</span><span class="no">Handler</span><span class="err">发送消息</span><span class="p">,</span><span class="err">更新</span><span class="no">UI</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>简单的说,Activity的onCreate方法里启动一个线程,在这个线程的run方法里使用一个Message对象并使用Handler的sendMessage方法发送到队列中,最后在Activity里new一个Handler的内部类实现handMessage方法,使用这个方法把队列中的Message对象取出来以实现异步操作。</p>
<p>然后是post的例子,这里稍微说一下,直接使用new Handler().post(Runnable)这样的写法并没有新开线程,也就是说依然是在主线程中执行,相当于简单调用了线程的run方法而不是start方法。这个有人说是android的bug,解决方案是这样使用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">HandlerThread</span> <span class="n">handlerThread</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">HandlerThread</span><span class="p">(</span><span class="s2">"myHandlerThread"</span><span class="p">);</span>
<span class="n">handlerThread</span><span class="o">.</span><span class="n">start</span><span class="p">();</span>
<span class="n">handler</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Handler</span><span class="p">(</span><span class="n">handlerThread</span><span class="o">.</span><span class="n">getLooper</span><span class="p">());</span></code></pre></div>
<p>来看一个完整的post例子:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="kp">public</span> <span class="k">class</span> <span class="nc">MyThread</span> <span class="n">extends</span> <span class="no">Activity</span> <span class="p">{</span>
<span class="kp">private</span> <span class="no">Handler</span> <span class="n">handler</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">(</span><span class="no">Bundle</span> <span class="n">savedInstanceState</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">(</span><span class="n">savedInstanceState</span><span class="p">);</span>
<span class="no">HandlerThread</span> <span class="n">handlerThread</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">HandlerThread</span><span class="p">(</span><span class="s2">"myHandlerThread"</span><span class="p">);</span>
<span class="n">handlerThread</span><span class="o">.</span><span class="n">start</span><span class="p">();</span>
<span class="n">handler</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">Handler</span><span class="p">(</span><span class="n">handlerThread</span><span class="o">.</span><span class="n">getLooper</span><span class="p">());</span>
<span class="n">handler</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="kp">new</span> <span class="no">MyRunnable</span><span class="p">());</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">"Oncreate---The Thread id is :"</span>
<span class="o">+</span> <span class="no">Thread</span><span class="o">.</span><span class="n">currentThread</span><span class="p">()</span><span class="o">.</span><span class="n">getId</span><span class="p">());</span>
<span class="n">setContentView</span><span class="p">(</span><span class="n">R</span><span class="o">.</span><span class="n">layout</span><span class="o">.</span><span class="n">main</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="k">class</span> <span class="nc">MyRunnable</span> <span class="n">implements</span> <span class="no">Runnable</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">run</span><span class="p">()</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">"Runnable---The Thread is running"</span><span class="p">);</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">"Runnable---The Thread id is :"</span>
<span class="o">+</span> <span class="no">Thread</span><span class="o">.</span><span class="n">currentThread</span><span class="p">()</span><span class="o">.</span><span class="n">getId</span><span class="p">());</span>
<span class="n">try</span> <span class="p">{</span>
<span class="no">Thread</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">6000</span><span class="p">);</span>
<span class="p">}</span> <span class="kp">catch</span> <span class="p">(</span><span class="no">InterruptedException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">TODO</span> <span class="no">Auto</span><span class="o">-</span><span class="n">generated</span> <span class="kp">catch</span> <span class="n">block</span>
<span class="n">e</span><span class="o">.</span><span class="n">printStackTrace</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>在这个demo中,用到了HandlerThread,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制。于是就有以下结果;</p>
<p>1:控制台的输出:</p>
<p>Oncreate—The Thread id is :1</p>
<p>Runnable—The Thread is running</p>
<p>Runnable—The Thread id is :10</p>
<p>2:程序启动后,我们立刻看到main.xml中的内容。
这样就达到了多线程的结果。</p>
Active Record Associations
2013-03-04T00:00:00+00:00
/rubyonrails/2013/03/04/active-record-associations
<h2 id="section">为什么要关联?</h2>
<p>为什么我们需要在两个model之间建立关联?因为它让通用操作变得简单和容易。例如,考虑有一个rails应用程序包含一个customer model和一个order model。每一个customer有很多的order。没有关联时,model声明如下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Customer</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Order</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="k">end</span></code></pre></div>
<p>现在,假设我们想为一个已存在的客户添加一个新的订单,我们需要像这样做:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vi">@order</span> <span class="o">=</span> <span class="no">Order</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="ss">:order_date</span> <span class="o">=></span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span><span class="p">,</span> <span class="ss">:customer_id</span> <span class="o">=></span> <span class="vi">@customer</span><span class="o">.</span><span class="n">id</span><span class="p">)</span></code></pre></div>
<p>再考虑在删除一个客户时,确保他的订单也被删除了。</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml">@orders = Order.find_by_customer_id(@customer.id)
@orders.each do |order|
order.destroy
end
@customer.destroy</code></pre></div>
<p>使用Active Record关联,我们通过声明告诉Rails在这两个model之间存在的关联使这些及其他一些操作流线化,这里是建立customer model和order model的改进代码</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Customer</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">has_many</span> <span class="ss">:orders</span><span class="p">,</span> <span class="ss">:dependent</span> <span class="o">=></span> <span class="ss">:destroy</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Order</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">belongs_to</span> <span class="ss">:customer</span>
<span class="k">end</span></code></pre></div>
<p>有了这些改变,很容易实现为一个特定的客户创建一个订单</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vi">@order</span> <span class="o">=</span> <span class="vi">@customer</span><span class="o">.</span><span class="n">orders</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="ss">:order_date</span> <span class="o">=></span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span><span class="p">)</span></code></pre></div>
<p>删除一个客户和它的订单则更加容易 </p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="vi">@customer</span><span class="o">.</span><span class="n">destroy</span></code></pre></div>
<h2 id="rails">Rails中的关联类型</h2>
<p>在Rails中,关联是两个Active Record Model之间的连接,关联通过macro-style的调用来实现的,因此你可以声明来添加特性到你的model。例如,通过声明一个model belongs_to另一个,你的Rails指令去维护在两个model的实例之间的primay_key, foreign_key信息,然后你同时有许多有用的方法添加到了你的model中。Rails支持六种类型的关联:</p>
<ul>
<li>belongs_to 从属关系</li>
<li>has_one 拥有(一对一)</li>
<li>has_many 拥有(一对多)</li>
<li>has_many :through 一对多,通过中间关联</li>
<li>has_one :through 一对一,通过中间关联</li>
<li>has_and_belongs_to_many 多对多</li>
</ul>
<h3 id="belongsto">belongs_to关联</h3>
<p>belongs_to关联与另一个model建立一对一联系,这样子声明的模型的每一个实例belongs_to其他模型的一个实例。例如,如果你的应用程序包含客户和订单,且每一个订单会被精确的分配给一个客户,你可像这样声明这个订单model:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Order</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">belongs_to</span> <span class="ss">:customer</span>
<span class="k">end</span></code></pre></div>
<p><img src="http://guides.rubyonrails.org/images/belongs_to.png" /></p>
<h3 id="hasone">has_one关联</h3>
<p>has_one关联同样是与另一个model建立一对一关联,但语义上有些不同(还有结果)。这种关联表明每一个model实例包含或者持有另一个model的实例。例如,如果你的应用程序里的每一个供应商仅拥有一个账号,你可像这样声明供应商model:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Supplier</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">has_one</span> <span class="ss">:account</span>
<span class="k">end</span></code></pre></div>
<p><img src="http://guides.rubyonrails.org/images/has_one.png" /></p>
<h3 id="hasmany">has_many关联</h3>
<p>has_many关联表明与另一个model的一对多关系。你会经常在belongs_to关系的“另一边”找到这种关系。这种关系表明这种model的每个实例拥有0或多个的另一个model的实例。例如,在一个应用程序里包含客户和订单,客户model可以这样声明:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Customer</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">has_many</span> <span class="ss">:orders</span>
<span class="k">end</span></code></pre></div>
<p><img src="http://guides.rubyonrails.org/images/has_many.png" /></p>
<h3 id="hasmany-through">has_many :through关联</h3>
<p>has_many :through关联通常用于和另一个model建立多对多关联。这种关系表明这样声明的model可以通过through处理匹配0或多个另一个model的实例。例如,考虑一个有关病人预约内科医生的医学练习,相关的声明可能像这样:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Physician</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">has_many</span> <span class="ss">:appointments</span>
<span class="n">has_many</span> <span class="ss">:patients</span><span class="p">,</span> <span class="ss">:through</span> <span class="o">=></span> <span class="ss">:appointments</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Appointment</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">belongs_to</span> <span class="ss">:physician</span>
<span class="n">belongs_to</span> <span class="ss">:patient</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Patient</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">has_many</span> <span class="ss">:appointments</span>
<span class="n">has_many</span> <span class="ss">:physicians</span><span class="p">,</span> <span class="ss">:through</span> <span class="o">=></span> <span class="ss">:appointments</span>
<span class="k">end</span></code></pre></div>
<p><img src="http://guides.rubyonrails.org/images/has_many_through.png" /></p>
<p>has_many :through关联同样有益于建立”快捷方式”通过嵌套的has_many关联。例如,如果一个文章有多个章节,而每个章节有很多段落,你也许有时想得到一个文档中所有段落的简单集合。你可以这种方式设置:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Document</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">has_many</span> <span class="ss">:sections</span>
<span class="n">has_many</span> <span class="ss">:paragraphs</span><span class="p">,</span> <span class="ss">:through</span> <span class="o">=></span> <span class="ss">:sections</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Section</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">belongs_to</span> <span class="ss">:document</span>
<span class="n">has_many</span> <span class="ss">:paragraphs</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Paragraph</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
<span class="n">belongs_to</span> <span class="ss">:section</span>
<span class="k">end</span></code></pre></div>
设计模式之TemplateMethod
2013-03-03T00:00:00+00:00
/designpatterns/2013/03/03/template-method
<p>最近开始看《Ruby设计模式》一书,结合“追mm与设计模式”,虽然有些YD,不过也有助于帮助理解设计模式 ,边学习边记录一下吧。Template method模式是最简单的一种设计模式了.</p>
<p>Template method——看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现);</p>
<h2 id="section">模板方法模式</h2>
<p>模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。</p>
<h2 id="section-1">实例</h2>
<p>以一个例子来说, 写一个输出一个不同格式的report generater。可以输出html格式,也可以输出纯文本的内容。</p>
<p>这个例子是hard-coded输出html格式的report类,但是无法处理纯文本格式。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Report</span>
<span class="k">def</span> <span class="nf">initialize</span>
<span class="vi">@title</span> <span class="o">=</span> <span class="s1">'Monthly Report'</span>
<span class="vi">@text</span> <span class="o">=</span> <span class="o">[</span> <span class="s1">'Things are going'</span><span class="p">,</span> <span class="s1">'really, really well.'</span> <span class="o">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_report</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">'<html>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' <head>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s2">" <title></span><span class="si">#{</span><span class="vi">@title</span><span class="si">}</span><span class="s2"></title>"</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' </head>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' <body>'</span><span class="p">)</span>
<span class="vi">@text</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
<span class="nb">puts</span><span class="p">(</span><span class="s2">" <p></span><span class="si">#{</span><span class="n">line</span><span class="si">}</span><span class="s2"></p>"</span> <span class="p">)</span>
<span class="k">end</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' </body>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">'</html>'</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>如果你要处理纯文本,那么只能重新写一个类:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Report</span>
<span class="k">def</span> <span class="nf">initialize</span>
<span class="vi">@title</span> <span class="o">=</span> <span class="s1">'Monthly Report'</span>
<span class="vi">@text</span> <span class="o">=</span> <span class="o">[</span><span class="s1">'Things are going'</span><span class="p">,</span> <span class="s1">'really, really well.'</span><span class="o">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_report</span><span class="p">(</span><span class="nb">format</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">format</span> <span class="o">==</span> <span class="ss">:plain</span>
<span class="nb">puts</span><span class="p">(</span><span class="s2">"*** </span><span class="si">#{</span><span class="vi">@title</span><span class="si">}</span><span class="s2"> ***"</span><span class="p">)</span>
<span class="k">elsif</span> <span class="nb">format</span> <span class="o">==</span> <span class="ss">:html</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">'<html>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' <head>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s2">" <title></span><span class="si">#{</span><span class="vi">@title</span><span class="si">}</span><span class="s2"></title>"</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' </head>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' <body>'</span><span class="p">)</span>
<span class="k">else</span>
<span class="k">raise</span> <span class="s2">"Unknown format: </span><span class="si">#{</span><span class="nb">format</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="vi">@text</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
<span class="k">if</span> <span class="nb">format</span> <span class="o">==</span> <span class="ss">:plain</span>
<span class="nb">puts</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="k">else</span>
<span class="nb">puts</span><span class="p">(</span><span class="s2">" <p></span><span class="si">#{</span><span class="n">line</span><span class="si">}</span><span class="s2"></p>"</span> <span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">if</span> <span class="nb">format</span> <span class="o">==</span> <span class="ss">:html</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' </body>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">'</html>'</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>上面的代码是可以工作的,但是如果将来又有需要让你处理其他格式呢,再重写一遍啊?这个时候template method就起作用了。我们可以把公共的部分抽象出来,也就是说先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。Ruby本身并没有抽象类,那么我们可以像下面方式一样去模拟:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Report</span>
<span class="k">def</span> <span class="nf">initialize</span>
<span class="vi">@title</span> <span class="o">=</span> <span class="s1">'Monthly Report'</span>
<span class="vi">@text</span> <span class="o">=</span> <span class="o">[</span><span class="s1">'Things are going'</span><span class="p">,</span> <span class="s1">'really, really well.'</span><span class="o">]</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_report</span>
<span class="n">output_start</span>
<span class="n">output_head</span>
<span class="n">output_body_start</span>
<span class="n">output_body</span>
<span class="n">output_body_end</span>
<span class="n">output_end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_body</span>
<span class="vi">@text</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
<span class="n">output_line</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_start</span>
<span class="k">raise</span> <span class="s1">'Called abstract method: output_start'</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_head</span>
<span class="k">raise</span> <span class="s1">'Called abstract method: output_head'</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_body_start</span>
<span class="k">raise</span> <span class="s1">'Called abstract method: output_body_start'</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_line</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="k">raise</span> <span class="s1">'Called abstract method: output_line'</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_body_end</span>
<span class="k">raise</span> <span class="s1">'Called abstract method: output_body_end'</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_end</span>
<span class="k">raise</span> <span class="s1">'Called abstract method: output_end'</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>那么我们可以实现处理html的子类了:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">HTMLReport</span> <span class="o"><</span> <span class="no">Report</span>
<span class="k">def</span> <span class="nf">output_start</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">'<html>'</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_head</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' <head>'</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s2">" <title></span><span class="si">#{</span><span class="vi">@title</span><span class="si">}</span><span class="s2"></title>"</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">' </head>'</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_body_start</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">'<body>'</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_line</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="s2">" <p></span><span class="si">#{</span><span class="n">line</span><span class="si">}</span><span class="s2"></p>"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_body_end</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">'</body>'</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_end</span>
<span class="nb">puts</span><span class="p">(</span><span class="s1">'</html>'</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>同样,处理文本的子类:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">PlainTextReport</span> <span class="o"><</span> <span class="no">Report</span>
<span class="k">def</span> <span class="nf">output_start</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_head</span>
<span class="nb">puts</span><span class="p">(</span><span class="s2">"**** </span><span class="si">#{</span><span class="vi">@title</span><span class="si">}</span><span class="s2"> ****"</span><span class="p">)</span>
<span class="nb">puts</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_body_start</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_line</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="nb">puts</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_body_end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">output_end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>看看结果:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">report</span> <span class="o">=</span> <span class="no">HTMLReport</span><span class="o">.</span><span class="n">new</span>
<span class="n">report</span><span class="o">.</span><span class="n">output_report</span>
<span class="c1">#=></span>
<span class="o"><</span><span class="n">html</span><span class="o">></span>
<span class="o"><</span><span class="n">head</span><span class="o">></span>
<span class="o"><</span><span class="n">title</span><span class="o">></span><span class="no">Monthly</span> <span class="no">Report</span><span class="o"><</span><span class="sr">/title></span>
<span class="sr"></</span><span class="n">head</span><span class="o">></span>
<span class="o"><</span><span class="n">body</span><span class="o">></span>
<span class="o"><</span><span class="nb">p</span><span class="o">></span><span class="no">Things</span> <span class="n">are</span> <span class="n">going</span><span class="o"><</span><span class="sr">/p></span>
<span class="sr"><p>really, really well.</</span><span class="nb">p</span><span class="o">></span>
<span class="o"><</span><span class="sr">/body></span>
<span class="sr"></</span><span class="n">html</span><span class="o">></span>
<span class="n">report</span> <span class="o">=</span> <span class="no">PlainTextReport</span><span class="o">.</span><span class="n">new</span>
<span class="n">report</span><span class="o">.</span><span class="n">output_report</span>
<span class="c1">#=></span>
<span class="o">**</span> <span class="no">Monthly</span> <span class="no">Report</span> <span class="o">****</span>
<span class="no">Things</span> <span class="n">are</span> <span class="n">going</span>
<span class="n">really</span><span class="p">,</span> <span class="n">really</span> <span class="n">well</span><span class="o">.</span></code></pre></div>
Android Source Code
2013-02-18T00:00:00+00:00
/android/2013/02/18/android-source-code
<p>我们知道,源代码是最好的学习资料,所以今天就来记录下android中下载并查看源代码的方法:</p>
<ol>
<li>
<p>安装Git,如果你还没有用Git,那么建议去Google下学习下相关知识,Git是现如今最流行的版本控制工具,没有之一。</p>
</li>
<li>
<p>新建源代码文件夹:</p>
</li>
</ol>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">mkdir</span> <span class="n">android_4</span><span class="o">.</span><span class="mi">0</span><span class="n">_src</span>
<span class="n">cd</span> <span class="n">android_4</span><span class="o">.</span><span class="mi">0</span><span class="n">_src</span></code></pre></div>
<ol>
<li>克隆sdk 远程仓库</li>
</ol>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="nb">clone</span> <span class="ss">http</span><span class="p">:</span><span class="sr">//</span><span class="n">android</span><span class="o">.</span><span class="n">googlesource</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">platform</span><span class="o">/</span><span class="n">frameworks</span><span class="o">/</span><span class="n">base</span><span class="o">.</span><span class="n">git</span></code></pre></div>
<ol>
<li>找到android sdk里的android.jar所在目录, 在此目录下新建sources文件夹, 将下载的源码中的core/android和core/com目录复制到source文件夹里, 重启Eclipse. 即可查看SDK中的Java源码.</li>
</ol>
<p>或者直接到<a href="http://repository.grepcode.com/java/ext/com/google/android/android/">这里</a>也可以直接下载jar文件</p>
Android全局异常处理
2013-02-03T00:00:00+00:00
/android/2013/02/03/android-catch-global-exception
<p>在做android项目开发时,大家都知道如果程序出错了,会弹出来一个强制退出的弹出框,这个本身没什么问题,但是这个UI实在是太丑了,别说用户接受不了,就连我们自己本身可能都接受不了。虽然我们在发布程序时总会经过仔细的测试,但是难免会碰到预料不到的错误。</p>
<p>今天就来自定义一个程序出错时的处理,类似iphone的闪退。(虽然闪退也是用户不愿意看到的,但是在用户体验上明显比那个原生的弹窗好多了)</p>
<p>废话不多说,直接上代码:</p>
<h2 id="crashhandler">CrashHandler</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">/** </span>
<span class="sr"> * 自定义的 异常处理类 , 实现了 UncaughtExceptionHandler接口 </span>
<span class="sr"> * </span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">CrashHandler</span> <span class="n">implements</span> <span class="no">UncaughtExceptionHandler</span> <span class="p">{</span>
<span class="sr">//</span> <span class="err">需求是</span> <span class="err">整个应用程序</span> <span class="err">只有一个</span> <span class="no">MyCrash</span><span class="o">-</span><span class="no">Handler</span>
<span class="kp">private</span> <span class="n">static</span> <span class="no">CrashHandler</span> <span class="no">INSTANCE</span> <span class="p">;</span>
<span class="kp">private</span> <span class="no">Context</span> <span class="n">context</span><span class="p">;</span>
<span class="sr">//</span><span class="mi">1</span><span class="o">.</span><span class="err">私有化构造方法</span>
<span class="kp">private</span> <span class="no">CrashHandler</span><span class="p">(){</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">synchronized</span> <span class="no">CrashHandler</span> <span class="n">getInstance</span><span class="p">(){</span>
<span class="k">if</span> <span class="p">(</span><span class="no">INSTANCE</span> <span class="o">==</span> <span class="n">null</span><span class="p">)</span>
<span class="no">INSTANCE</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">CrashHandler</span><span class="p">();</span>
<span class="k">return</span> <span class="no">INSTANCE</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">init</span><span class="p">(</span><span class="no">Context</span> <span class="n">context</span><span class="p">){</span>
<span class="n">this</span><span class="o">.</span><span class="n">context</span> <span class="o">=</span> <span class="n">context</span><span class="p">;</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">uncaughtException</span><span class="p">(</span><span class="no">Thread</span> <span class="n">arg0</span><span class="p">,</span> <span class="no">Throwable</span> <span class="n">arg1</span><span class="p">)</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="s2">"程序挂掉了 "</span><span class="p">);</span>
<span class="sr">//</span> <span class="err">在此可以把用户手机的一些信息以及异常信息捕获并上传</span><span class="p">,</span><span class="err">由于</span><span class="no">UMeng</span><span class="err">在这方面有很程序的</span><span class="n">api</span><span class="err">接口来调用,故没有考虑</span>
<span class="sr">//</span><span class="err">干掉当前的程序</span>
<span class="n">android</span><span class="o">.</span><span class="n">os</span><span class="o">.</span><span class="n">Process</span><span class="o">.</span><span class="n">killProcess</span><span class="p">(</span><span class="n">android</span><span class="o">.</span><span class="n">os</span><span class="o">.</span><span class="n">Process</span><span class="o">.</span><span class="n">myPid</span><span class="p">());</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="crashapplication">CrashApplication</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="sr">/** </span>
<span class="sr"> * 在开发应用时都会和Activity打交道,而Application使用的就相对较少了。 </span>
<span class="sr"> * Application是用来管理应用程序的全局状态的,比如载入资源文件。 </span>
<span class="sr"> * 在应用程序启动的时候Application会首先创建,然后才会根据情况(Intent)启动相应的Activity或者Service。 </span>
<span class="sr"> * 在本文将在Application中注册未捕获异常处理器。 </span>
<span class="sr"> */</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">CrashApplication</span> <span class="n">extends</span> <span class="no">Application</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onCreate</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">onCreate</span><span class="p">();</span>
<span class="no">CrashHandler</span> <span class="n">handler</span> <span class="o">=</span> <span class="no">CrashHandler</span><span class="o">.</span><span class="n">getInstance</span><span class="p">();</span>
<span class="n">handler</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">getApplicationContext</span><span class="p">());</span>
<span class="no">Thread</span><span class="o">.</span><span class="n">setDefaultUncaughtExceptionHandler</span><span class="p">(</span><span class="n">handler</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<h2 id="androidmanifestxml">在AndroidManifest.xml中注册</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o"><</span><span class="p">?</span><span class="n">xml</span> <span class="n">version</span><span class="o">=</span><span class="s2">"1.0"</span> <span class="n">encoding</span><span class="o">=</span><span class="s2">"utf-8"</span><span class="sc">?></span>
<span class="o"><</span><span class="n">manifest</span> <span class="ss">xmlns</span><span class="p">:</span><span class="n">android</span><span class="o">=</span><span class="s2">"http://schemas.android.com/apk/res/android"</span>
<span class="n">package</span><span class="o">=</span><span class="s2">"org.wp.activity"</span> <span class="ss">android</span><span class="p">:</span><span class="n">versionCode</span><span class="o">=</span><span class="s2">"1"</span> <span class="ss">android</span><span class="p">:</span><span class="n">versionName</span><span class="o">=</span><span class="s2">"1.0"</span><span class="o">></span>
<span class="o"><</span><span class="n">application</span> <span class="ss">android</span><span class="p">:</span><span class="n">icon</span><span class="o">=</span><span class="s2">"@drawable/icon"</span> <span class="ss">android</span><span class="p">:</span><span class="n">label</span><span class="o">=</span><span class="s2">"@string/app_name"</span>
<span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">".CrashApplication"</span> <span class="ss">android</span><span class="p">:</span><span class="n">debuggable</span><span class="o">=</span><span class="s2">"true"</span><span class="o">></span>
<span class="o"><</span><span class="n">activity</span> <span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">".MainActivity"</span> <span class="ss">android</span><span class="p">:</span><span class="n">label</span><span class="o">=</span><span class="s2">"@string/app_name"</span><span class="o">></span>
<span class="o"><</span><span class="n">intent</span><span class="o">-</span><span class="n">filter</span><span class="o">></span>
<span class="o"><</span><span class="n">action</span> <span class="ss">android</span><span class="p">:</span><span class="nb">name</span><span class="o">=</span><span class="s2">"android.intent.action.MAIN"</span> <span class="sr">/> </span>
<span class="sr"> <category android:name="android.intent.category.LAUNCHER" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/intent-filter> </span>
<span class="sr"> </</span><span class="n">activity</span><span class="o">></span>
<span class="o"><</span><span class="sr">/application> </span>
<span class="sr"> <uses-sdk android:minSdkVersion="8" /</span><span class="o">></span>
<span class="o"><</span><span class="sr">/manifest></span></code></pre></div>
<p>至此,可以测试下在出错的时候程序会直接闪退,并杀死后台进程。当然也可以自定义一些比较友好的出错UI提示,进一步提升用户体验。</p>
Android Asynchronous Http Client
2013-01-27T00:00:00+00:00
/android/2013/01/27/android-async-http
<p>在Android的SDK中封装一些请求http的包,其中最常用的便是使用HttpClient了,我们一般都是自己定义一个http的工具类,然后把get和post方法封装起来,然后自己手动处理一些http的异常,值得注意的是http请求在android中是阻碍UI主线程的,所以必须开启新的线程在后台请求,所以这一来,发现只是发起一个http的请求就必须要做这么多事,而且自己封装的工具类也不一定是最好用的。</p>
<p>上面便是我以前的做法,本周在做moya的app时,重新审视了以前的代码,进行了一些重构。其中的一个地方便是使用了一个比较成熟的组件android-async-http来代替自己封装的http工具类,使用起来非常方便,省去了一些臃肿的代码,使代码总体看起来比较清晰。</p>
<h2 id="overview">Overview</h2>
<p>android-async-http是基于Apache的HttpClient异步Http请求封装类,全部request都是脱离UI主线程的。这个包已经非常成熟了,国外有名的Instagram,Pinterest等apps都在使用。</p>
<h2 id="installation--basic-usage">Installation & Basic Usage</h2>
<p>下载最新的.jar文件,在自己的android app中引用。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">import</span> <span class="n">com</span><span class="o">.</span><span class="n">loopj</span><span class="o">.</span><span class="n">android</span><span class="o">.</span><span class="n">http</span><span class="o">.</span><span class="n">*</span><span class="p">;</span></code></pre></div>
<p>创建一个新的AsyncHttpClient实例并发起一个请求:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">AsyncHttpClient</span> <span class="n">client</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">AsyncHttpClient</span><span class="p">();</span>
<span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"http://www.google.com"</span><span class="p">,</span> <span class="kp">new</span> <span class="no">AsyncHttpResponseHandler</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onSuccess</span><span class="p">(</span><span class="nb">String</span> <span class="n">response</span><span class="p">)</span> <span class="p">{</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="n">response</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span></code></pre></div>
<h2 id="http-client">建议的用法:创建一个静态基类Http Client</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">import</span> <span class="n">com</span><span class="o">.</span><span class="n">loopj</span><span class="o">.</span><span class="n">android</span><span class="o">.</span><span class="n">http</span><span class="o">.</span><span class="n">*</span><span class="p">;</span>
<span class="kp">public</span> <span class="k">class</span> <span class="nc">BooheeClient</span> <span class="p">{</span>
<span class="kp">private</span> <span class="n">static</span> <span class="n">final</span> <span class="nb">String</span> <span class="no">BASE_URL</span> <span class="o">=</span> <span class="s2">"http://www.boohee.com/api/"</span><span class="p">;</span>
<span class="kp">private</span> <span class="n">static</span> <span class="no">AsyncHttpClient</span> <span class="n">client</span> <span class="o">=</span> <span class="kp">new</span> <span class="no">AsyncHttpClient</span><span class="p">();</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">get</span><span class="p">(</span><span class="nb">String</span> <span class="n">url</span><span class="p">,</span> <span class="no">RequestParams</span> <span class="n">params</span><span class="p">,</span> <span class="no">AsyncHttpResponseHandler</span> <span class="n">responseHandler</span><span class="p">)</span> <span class="p">{</span>
<span class="n">client</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">getAbsoluteUrl</span><span class="p">(</span><span class="n">url</span><span class="p">),</span> <span class="n">params</span><span class="p">,</span> <span class="n">responseHandler</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">public</span> <span class="n">static</span> <span class="n">void</span> <span class="n">post</span><span class="p">(</span><span class="nb">String</span> <span class="n">url</span><span class="p">,</span> <span class="no">RequestParams</span> <span class="n">params</span><span class="p">,</span> <span class="no">AsyncHttpResponseHandler</span> <span class="n">responseHandler</span><span class="p">)</span> <span class="p">{</span>
<span class="n">client</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">getAbsoluteUrl</span><span class="p">(</span><span class="n">url</span><span class="p">),</span> <span class="n">params</span><span class="p">,</span> <span class="n">responseHandler</span><span class="p">);</span>
<span class="p">}</span>
<span class="kp">private</span> <span class="n">static</span> <span class="nb">String</span> <span class="n">getAbsoluteUrl</span><span class="p">(</span><span class="nb">String</span> <span class="n">relativeUrl</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">BASE_URL</span> <span class="o">+</span> <span class="n">relativeUrl</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>然后在整个工程的其他地方便很方便的调用:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">import</span> <span class="n">org</span><span class="o">.</span><span class="n">json</span><span class="o">.</span><span class="n">*</span><span class="p">;</span>
<span class="n">import</span> <span class="n">com</span><span class="o">.</span><span class="n">loopj</span><span class="o">.</span><span class="n">android</span><span class="o">.</span><span class="n">http</span><span class="o">.</span><span class="n">*</span><span class="p">;</span>
<span class="k">class</span> <span class="nc">BooheeClientUsage</span> <span class="p">{</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">getPublicTimeline</span><span class="p">()</span> <span class="n">throws</span> <span class="no">JSONException</span> <span class="p">{</span>
<span class="no">BooheeClient</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">"moya_video"</span><span class="p">,</span> <span class="n">null</span><span class="p">,</span> <span class="kp">new</span> <span class="no">JsonHttpResponseHandler</span><span class="p">()</span> <span class="p">{</span>
<span class="vi">@Override</span>
<span class="kp">public</span> <span class="n">void</span> <span class="n">onSuccess</span><span class="p">(</span><span class="no">JSONObject</span> <span class="n">video</span><span class="p">)</span> <span class="p">{</span>
<span class="sr">//</span> <span class="no">Pull</span> <span class="n">out</span> <span class="n">the</span> <span class="n">first</span> <span class="n">event</span> <span class="n">on</span> <span class="n">the</span> <span class="kp">public</span> <span class="n">timeline</span>
<span class="nb">String</span> <span class="n">normal_url</span> <span class="o">=</span> <span class="n">video</span><span class="o">.</span><span class="n">getString</span><span class="p">(</span><span class="s2">"normal_url"</span><span class="p">);</span>
<span class="sr">//</span> <span class="no">Do</span> <span class="n">something</span> <span class="n">with</span> <span class="n">the</span> <span class="n">response</span>
<span class="no">System</span><span class="o">.</span><span class="n">out</span><span class="o">.</span><span class="n">println</span><span class="p">(</span><span class="n">normal_url</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p>上面的例子看到,对于json数据的处理也非常方便。不仅如此,还有其他更多的用法,如处理cookie,上传、下载文件等。github项目地址:<a href="https://github.com/stormzhang/android-async-http">android-async-http</a></p>
Rails3 Route常用用法
2013-01-20T00:00:00+00:00
/rubyonrails/2013/01/20/rails-route
<p>我们都知道在Rails中的config/route.rb文件定义了路由的设定,这次充分学习了Rails3中Route的常用用法,整理并分享在这里。</p>
<h2 id="section">默认路由:</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">match</span> <span class="s1">'/:controller(/:action(/:id))'</span></code></pre></div>
<h2 id="section-1">自定义路由:</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">match</span> <span class="s1">'products/:id'</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="s1">'catalog#view'</span></code></pre></div>
<h2 id="section-2">命名路由:</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">match</span> <span class="s1">'logout'</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="s1">'sessions#destroy'</span><span class="p">,</span> <span class="ss">:as</span> <span class="o">=></span> <span class="s1">'logout'</span></code></pre></div>
<h2 id="section-3">根路由:</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">root</span> <span class="ss">:to</span> <span class="o">=></span> <span class="s1">'welcome#show'</span></code></pre></div>
<h2 id="section-4">路由简写技巧:</h2>
<h3 id="to-">:to 键的省略</h3>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">match</span> <span class="s1">'account'</span> <span class="o">=></span> <span class="s1">'account#index'</span>
<span class="c1"># 相当于:</span>
<span class="n">match</span> <span class="s1">'account'</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="s1">'account#index'</span>
<span class="n">match</span> <span class="s1">'info'</span> <span class="o">=></span> <span class="s1">'projects#info'</span><span class="p">,</span> <span class="ss">:as</span> <span class="o">=></span> <span class="s1">'info'</span></code></pre></div>
<p>注意:
:as 在rails3中是改变 helper, 在rails2中是改变 path</p>
<p>当路径和控制器(及action)一至时,可省略指派控制器部分</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">match</span> <span class="s1">'account/overview'</span>
<span class="c1"># 相当于: </span>
<span class="n">match</span> <span class="s1">'account/overview'</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="s1">'account#overview'</span></code></pre></div>
<h2 id="verb">Verb路由</h2>
<p>当需要限制http请求方法的时候通过键 :via ,也可以直接把方法写在最前面:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">get</span> <span class="s1">'account/overview'</span>
<span class="c1"># 相当于: </span>
<span class="n">match</span> <span class="s1">'account/overview'</span><span class="p">,</span> <span class="ss">:via</span> <span class="o">=></span> <span class="s1">'get'</span>
<span class="n">match</span> <span class="s1">'account/setup'</span><span class="p">,</span> <span class="ss">:via</span> <span class="o">=></span> <span class="o">[</span><span class="ss">:get</span><span class="p">,</span> <span class="ss">:post</span><span class="o">]</span>
<span class="c1"># 支持get\post\put\delete四种HTTP方法</span></code></pre></div>
<h2 id="scope">scope路由</h2>
<h3 id="path-pathmodule-controller-nameprefix--as---helper">:path 改变Path,:module 改变Controller, :name_prefix || :as 改变 helper</h3>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">scope</span> <span class="s1">'admin'</span> <span class="k">do</span>
<span class="n">resources</span> <span class="ss">:posts</span>
<span class="k">end</span>
<span class="c1"># 行当于:</span>
<span class="n">scope</span> <span class="ss">:path</span> <span class="o">=></span> <span class="s1">'admin'</span> <span class="k">do</span>
<span class="n">resources</span> <span class="ss">:posts</span>
<span class="k">end</span></code></pre></div>
<p>生成路由:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">posts</span> <span class="no">GET</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"index"</span><span class="p">}</span>
<span class="n">posts</span> <span class="no">POST</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"create"</span><span class="p">}</span>
<span class="n">new_post</span> <span class="no">GET</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="kp">new</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"new"</span><span class="p">}</span>
<span class="n">edit_post</span> <span class="no">GET</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="o">/</span><span class="n">edit</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"edit"</span><span class="p">}</span>
<span class="n">post</span> <span class="no">GET</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"show"</span><span class="p">}</span>
<span class="n">post</span> <span class="no">PUT</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"update"</span><span class="p">}</span>
<span class="n">post</span> <span class="no">DELETE</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"destroy"</span><span class="p">}</span>
<span class="n">scope</span> <span class="ss">:module</span> <span class="o">=></span> <span class="s1">'admin'</span> <span class="k">do</span>
<span class="n">resources</span> <span class="ss">:posts</span>
<span class="k">end</span>
<span class="c1"># 相当于:</span>
<span class="n">resources</span> <span class="ss">:posts</span><span class="p">,</span> <span class="ss">:module</span> <span class="o">=></span> <span class="s1">'admin'</span></code></pre></div>
<p>生成路由:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">posts</span> <span class="no">GET</span> <span class="sr">/posts(.:format) {:controller=>"admin/</span><span class="n">posts</span><span class="s2">", :action=>"</span><span class="n">index</span><span class="s2">"}</span>
<span class="s2">posts POST /posts(.:format) {:controller=>"</span><span class="n">admin</span><span class="o">/</span><span class="n">posts</span><span class="s2">", :action=>"</span><span class="n">create</span><span class="s2">"}</span>
<span class="s2">new_post GET /posts/new(.:format) {:controller=>"</span><span class="n">admin</span><span class="o">/</span><span class="n">posts</span><span class="s2">", :action=>"</span><span class="kp">new</span><span class="s2">"}</span>
<span class="s2">edit_post GET /posts/:id/edit(.:format) {:controller=>"</span><span class="n">admin</span><span class="o">/</span><span class="n">posts</span><span class="s2">", :action=>"</span><span class="n">edit</span><span class="s2">"}</span>
<span class="s2">post GET /posts/:id(.:format) {:controller=>"</span><span class="n">admin</span><span class="o">/</span><span class="n">posts</span><span class="s2">", :action=>"</span><span class="n">show</span><span class="s2">"}</span>
<span class="s2">post PUT /posts/:id(.:format) {:controller=>"</span><span class="n">admin</span><span class="o">/</span><span class="n">posts</span><span class="s2">", :action=>"</span><span class="n">update</span><span class="s2">"}</span>
<span class="s2">post DELETE /posts/:id(.:format) {:controller=>"</span><span class="n">admin</span><span class="o">/</span><span class="n">posts</span><span class="s2">", :action=>"</span><span class="n">destroy</span><span class="s2">"}</span>
<span class="s2">scope :name_prefix => 'admin' do</span>
<span class="s2"> resources :posts</span>
<span class="s2">end</span>
<span class="s2"># 相当于:</span>
<span class="s2">resources :posts, :name_prefix => 'admin'</span></code></pre></div>
<p>生成路由:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">admin_posts</span> <span class="no">GET</span> <span class="sr">/posts(.:format) {:controller=>"posts", :action=>"index"}</span>
<span class="sr">admin_posts POST /</span><span class="n">posts</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"create"</span><span class="p">}</span>
<span class="n">new_admin_post</span> <span class="no">GET</span> <span class="sr">/posts/ne</span><span class="n">w</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"new"</span><span class="p">}</span>
<span class="n">edit_admin_post</span> <span class="no">GET</span> <span class="sr">/posts/</span><span class="ss">:id</span><span class="o">/</span><span class="n">edit</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"edit"</span><span class="p">}</span>
<span class="n">admin_post</span> <span class="no">GET</span> <span class="sr">/posts/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"show"</span><span class="p">}</span>
<span class="n">admin_post</span> <span class="no">PUT</span> <span class="sr">/posts/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"update"</span><span class="p">}</span>
<span class="n">admin_post</span> <span class="no">DELETE</span> <span class="sr">/posts/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"destroy"</span><span class="p">}</span>
<span class="n">scope</span> <span class="s1">'admin'</span><span class="p">,</span> <span class="ss">:module</span> <span class="o">=></span> <span class="s1">'admin'</span><span class="p">,</span> <span class="ss">:name_prefix</span> <span class="o">=></span> <span class="s1">'admin'</span> <span class="k">do</span>
<span class="n">resources</span> <span class="ss">:posts</span>
<span class="k">end</span>
<span class="c1"># 相当于:</span>
<span class="n">namespace</span> <span class="s1">'admin'</span> <span class="k">do</span>
<span class="n">resources</span> <span class="ss">:posts</span>
<span class="k">end</span></code></pre></div>
<p>生成路由:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">admin_posts</span> <span class="no">GET</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"admin/posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"index"</span><span class="p">}</span>
<span class="n">admin_posts</span> <span class="no">POST</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"admin/posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"create"</span><span class="p">}</span>
<span class="n">new_admin_post</span> <span class="no">GET</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="kp">new</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"admin/posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"new"</span><span class="p">}</span>
<span class="n">edit_admin_post</span> <span class="no">GET</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="o">/</span><span class="n">edit</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"admin/posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"edit"</span><span class="p">}</span>
<span class="n">admin_post</span> <span class="no">GET</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"admin/posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"show"</span><span class="p">}</span>
<span class="n">admin_post</span> <span class="no">PUT</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"admin/posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"update"</span><span class="p">}</span>
<span class="n">admin_post</span> <span class="no">DELETE</span> <span class="sr">/admin/</span><span class="n">posts</span><span class="o">/</span><span class="ss">:id</span><span class="p">(</span><span class="o">.</span><span class="ss">:format</span><span class="p">)</span> <span class="p">{</span><span class="ss">:controller</span><span class="o">=></span><span class="s2">"admin/posts"</span><span class="p">,</span> <span class="ss">:action</span><span class="o">=></span><span class="s2">"destroy"</span><span class="p">}</span></code></pre></div>
<h2 id="section-5">在路由中定义跳转:</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">match</span> <span class="s2">"/posts/github"</span> <span class="o">=></span> <span class="n">redirect</span><span class="p">(</span><span class="s2">"http://github.com/rails.atom"</span><span class="p">)</span>
<span class="c1"># 地址 /foo/1 会自动跳转到 /bar/1s</span>
<span class="n">match</span> <span class="s2">"/foo/:id"</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="n">redirect</span><span class="p">(</span><span class="s2">"/bar/%{id}s"</span><span class="p">)</span>
<span class="c1"># /account/proc/inosin 会自动跳转到 /inosins</span>
<span class="n">match</span> <span class="s1">'account/proc/:name'</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="n">redirect</span> <span class="p">{</span><span class="o">|</span><span class="n">params</span><span class="o">|</span>
<span class="s2">"/</span><span class="si">#{</span><span class="n">params</span><span class="o">[</span><span class="ss">:name</span><span class="o">].</span><span class="n">pluralize</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
<span class="n">match</span> <span class="s2">"/stories"</span> <span class="o">=></span> <span class="n">redirect</span> <span class="p">{</span><span class="o">|</span><span class="nb">p</span><span class="p">,</span> <span class="n">req</span><span class="o">|</span> <span class="s2">"/posts/</span><span class="si">#{</span><span class="n">req</span><span class="o">.</span><span class="n">subdomain</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span></code></pre></div>
<h2 id="section-6">路由中的限制:</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># 限制 id 只能为数字 </span>
<span class="n">match</span> <span class="s2">"/posts/show/:id"</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="s2">"posts#index"</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=></span> <span class="sr">/\d+/</span>
<span class="n">match</span> <span class="s2">"/posts/show/:id"</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="s2">"posts#index"</span><span class="p">,</span> <span class="ss">:constraints</span> <span class="o">=></span> <span class="p">{</span><span class="ss">:id</span> <span class="o">=></span> <span class="sr">/\d+/</span><span class="p">}</span>
<span class="c1"># 限制子域名 </span>
<span class="n">match</span> <span class="s2">"photos"</span><span class="p">,</span> <span class="ss">:constraints</span> <span class="o">=></span> <span class="p">{</span><span class="ss">:subdomain</span> <span class="o">=></span> <span class="s2">"admin"</span><span class="p">}</span>
<span class="c1"># 限制访问者 IP </span>
<span class="n">constraints</span><span class="p">(</span><span class="ss">:ip</span> <span class="o">=></span> <span class="sr">/127.0.0.1/</span><span class="p">)</span> <span class="k">do</span>
<span class="n">match</span> <span class="s1">'/questions'</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=></span> <span class="n">redirect</span><span class="p">(</span><span class="s2">"http://www.stackoverflow.com/"</span><span class="p">)</span>
<span class="k">end</span>
<span class="c1"># 当访问者 ip 是 192.168.1.* 的来访者访问 子域名为 "test" </span>
<span class="n">match</span> <span class="s2">"/ttt"</span> <span class="o">=></span> <span class="nb">proc</span><span class="p">{</span><span class="o">|</span><span class="n">env</span><span class="o">|</span> <span class="o">[</span><span class="mi">200</span><span class="p">,</span> <span class="p">{},</span> <span class="o">[</span><span class="s2">"hello test"</span><span class="o">]]</span><span class="p">},</span> <span class="p">\</span>
<span class="ss">:constraints</span> <span class="o">=></span> <span class="p">{</span><span class="ss">:subdomain</span> <span class="o">=></span> <span class="s2">"test"</span><span class="p">,</span> <span class="ss">:ip</span> <span class="o">=></span> <span class="sr">/192\.168\.1\.\d+/</span><span class="p">}</span></code></pre></div>
<h2 id="section-7">路由通配符:</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">resources</span> <span class="ss">:photos</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=></span> <span class="sr">/\d+/</span>
<span class="n">match</span> <span class="s1">'photos/*other'</span> <span class="o">=></span> <span class="s1">'photos#unknown'</span>
<span class="c1">#上面这两行路由则会把不符合7种path的其他url全部解析到PhotoController#unknown中去处理,params[:other]可得到path中/photos/之后的部分,注意这两行的顺序不能颠倒 </span>
<span class="n">match</span> <span class="s1">'books/*section/:title'</span> <span class="o">=></span> <span class="s1">'books#show'</span>
<span class="c1"># 例如:books/some/section/last-words-a-memoir 中 params[:section] = "some/section", params[:title] = "last-words-a-memoir". </span>
<span class="n">match</span> <span class="s1">'*a/foo/*b'</span> <span class="o">=></span> <span class="s1">'test#index'</span>
<span class="c1"># 例如:zoo/woo/foo/bar/baz 中 params[:a] = "zoo/woo", params[:b] = "bar/baz"</span></code></pre></div>
HTTP协议之状态码
2013-01-17T00:00:00+00:00
/rubyonrails/2013/01/17/http-code
<p>一篇不错的讲解HTTP状态码的Blog,收藏了!</p>
<p><a href="http://www.v5brower.iteye.com/blog/1769789">Http协议之状态码详解</a></p>
7-patterns-to-refactor-active-record
2013-01-10T00:00:00+00:00
/rubyonrails/2013/01/10/7-patterns-to-refactor-active-record
<p>Vincent推荐的一篇非常不错的文章,看后非常受益。</p>
<p><a href="http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/">7 Patterns to Refactor Fat ActiveRecord Models</a></p>
Sublime Text 2 Plugin
2012-12-30T00:00:00+00:00
/devtools/2012/12/30/sublime-plugin
<p>之前做android开发一直用eclipse做编辑器,之后接触ruby之后便开始用Sublime Text 2,渐渐的才发现此等编辑器的好处,也越来越依赖它。</p>
<p>Sublime Text 2是一个轻量、简洁、高效、跨平台的编辑器,方便的配色以及兼容vim快捷键等各种优点博得了很多前端开发人员的喜爱。之前也不并知道它有这么多插件的扩展与支持,直到vincent问到有没有在用cTags插件,才知道原来Sublime通过插件也可以实现一些大型IDE的功能,遂google一下,本篇Blog就来介绍下Sublime下经常使用的插件。</p>
<h2 id="package-control">安装包控制(Package Control)</h2>
<p>打开Sublime Text 2,点击 Tools -> Command Palette 调出控制台Console;</p>
<p>将以下代码粘贴进命令行中并回车:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">import</span> <span class="n">urllib2</span><span class="p">,</span><span class="n">os</span><span class="p">;</span><span class="n">pf</span><span class="o">=</span><span class="s1">'Package Control.sublime-package'</span><span class="p">;</span><span class="n">ipp</span><span class="o">=</span><span class="n">sublime</span><span class="o">.</span><span class="n">installed_packages_path</span><span class="p">();</span><span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">ipp</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">ipp</span><span class="p">)</span> <span class="k">else</span> <span class="no">None</span><span class="p">;</span><span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">ipp</span><span class="p">,</span><span class="n">pf</span><span class="p">),</span><span class="s1">'wb'</span><span class="p">)</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="s1">'http://sublime.wbond.net/'</span><span class="o">+</span><span class="n">pf</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">' '</span><span class="p">,</span><span class="s1">'%20'</span><span class="p">))</span><span class="o">.</span><span class="n">read</span><span class="p">())</span></code></pre></div>
<p>重启 Sublime Text 2,如果在 Preferences -> Package Settings中见到Package Control这一项,就说明安装成功了。</p>
<h2 id="alignment">安装Alignment插件</h2>
<p>对于某些喜欢整齐的程序员来说,看到下面这种情况可能是让其无法忍受的:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">var</span> <span class="n">joe</span> <span class="o">=</span> <span class="s1">'joe'</span><span class="p">;</span>
<span class="n">var</span> <span class="n">johnny</span> <span class="o">=</span> <span class="s1">'johnny'</span><span class="p">;</span>
<span class="n">var</span> <span class="n">quaid</span> <span class="o">=</span> <span class="s1">'quaid'</span><span class="p">;</span></code></pre></div>
<p>一定要改成这样才会安心:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">var</span> <span class="n">joe</span> <span class="o">=</span> <span class="s1">'joe'</span><span class="p">;</span>
<span class="n">var</span> <span class="n">johnny</span> <span class="o">=</span> <span class="s1">'johnny'</span><span class="p">;</span>
<span class="n">var</span> <span class="n">quaid</span> <span class="o">=</span> <span class="s1">'quaid'</span><span class="p">;</span></code></pre></div>
<p>Sublime Text 2 之中,一个 Sublime Alignment 插件也可以轻松实现。</p>
<p>1.按下 Ctrl + Shift + P 调出命令面板。</p>
<p>2.输入 install 调出 Package Control: Install Package 选项,按下回车。</p>
<p>3.在列表中找到 Alignment,按下回车进行安装。</p>
<p>4.重启 Sublime Text 2 使之生效。现在通过选中文本并按 Ctrl + Shift + A 就可以进行对齐操作了。</p>
<h2 id="vim">Vim模式</h2>
<p>是的,Sublime Text 2已经支持 Vim 的编辑模式了,如果更喜欢 Vim 的编辑模式,可以通过以下方法来激活 Vintage mode:</p>
<p>1.按下 Ctrl + Shift + P 调出命令面板。</p>
<p>2.输入 settings user 调出 Preferences:Settings - User,并按下回车。</p>
<p>3.这时会打开一个 Preferences.sublime-settings 的文件, 如果是第一次修改,它应该是个空文件,把以下文本粘贴进去:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="p">{</span>
<span class="s2">"ignored_packages"</span><span class="p">:</span> <span class="o">[]</span>
<span class="p">}</span></code></pre></div>
<p>4.保存这个文件,这时按下 ESC 键,再按下一些你熟悉的 Vim 命令,是不是很有亲切感?</p>
<h2 id="soda-">安装 Soda 主题</h2>
<p>这里所讲的主题不同于针对代码的 Color Scheme,是指针对 Sublime 程序本身的主题,目前可以安装的是 Ian Hill 的 Soda。</p>
<p>因为源中已经添加,所以这款主题的安装同样可以通过 Package Control,非常方便。目前 Soda 主题提供了明暗两种风格。</p>
<p>激活方法,同样要修改 Preferences:Settings - User:</p>
<p>1.按下 Ctrl + Shift + P 调出命令面板。</p>
<p>2.输入 user settings 调出 Preferences:Settings - User,并按下回车。</p>
<p>3.添加以下代码激活 Soda Light 主题:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="p">{</span>
<span class="s2">"theme"</span><span class="p">:</span> <span class="s2">"Soda Light.sublime-theme"</span>
<span class="p">}</span></code></pre></div>
<p>添加以下代码激活 Soda Dark 主题:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="p">{</span>
<span class="s2">"theme"</span><span class="p">:</span> <span class="s2">"Soda Dark.sublime-theme"</span>
<span class="p">}</span></code></pre></div>
<p>4.保存生效。</p>
<h2 id="ctags">安装cTags插件</h2>
<p>这个插件能跨文件跳转,实现像eclipse可那样以追踪函数的功能,从此更喜欢上Sublime了。安装方法:</p>
<p>1.按下 Ctrl + Shift + P 调出命令面板。</p>
<p>2.输入 install 调出 Package Control: Install Package 选项,按下回车。</p>
<p>3.在列表中找到 ctags,按下回车进行安装。</p>
<p>4.ubuntu下安装运行命令:sudo apt-get install exuberant-ctags。</p>
<p>5.在sublime项目文件夹右键, 会出现Ctag:Rebuild Tags 的菜单。点击它,然后会生成.tags的文件。</p>
<p>然后在你代码中, 光标放在某个函数上, 点击ctrl+shift+鼠标左键 就可以跳转到函数声明的地方。</p>
<h2 id="zen-coding">Zen Coding</h2>
<p>如果经常要写一些前端的代码,这个插件也是必不可少的,还不知道ZenCoding的同学推荐去看一下:<a href="http://www.qianduan.net/zen-coding-a-new-way-to-write-html-code.html">《Zen Coding: 一种快速编写HTML/CSS代码的方法》</a></p>
<h2 id="git">Git</h2>
<p>一个整合GIT和Sublime Text的插件,执行了很多你需要使用的命令。</p>
Ruby: eval && binding
2012-12-20T00:00:00+00:00
/rubyonrails/2012/12/20/ruby-eval-and-binding
<p>Ruby语言中有许多方法,灵活的运用这些方法,可以帮助我们轻松的掌握Ruby的编程技巧。最近遇到eval与binding,对于我这个ruby新手来说很陌生,于是就学习了下,并在这里记录并分享下来。</p>
<h2 id="eval">eval</h2>
<p>其实eval方法很简单,就是把字符串当作ruby程序来执行并返回结果,例子说话:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">a</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">str</span> <span class="o">=</span> <span class="s2">"if a == 1 then puts true else puts false end"</span>
<span class="nb">eval</span><span class="p">(</span><span class="n">str</span><span class="p">)</span> <span class="c1">#=> true</span></code></pre></div>
<h2 id="binding">binding</h2>
<p>生成并返回Binding对象。该对象包含变量、方法等的环境信息,它通常用作Eval的第二参数。来看官方提供示例:</p>
<h1 id="example1">Example1</h1>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Demo</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="vi">@secret</span> <span class="o">=</span> <span class="n">n</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">get_binding</span>
<span class="k">return</span> <span class="nb">binding</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">k1</span> <span class="o">=</span> <span class="no">Demo</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="mi">99</span><span class="p">)</span>
<span class="n">b1</span> <span class="o">=</span> <span class="n">k1</span><span class="o">.</span><span class="n">get_binding</span>
<span class="n">k2</span> <span class="o">=</span> <span class="no">Demo</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="o">-</span><span class="mi">3</span><span class="p">)</span>
<span class="n">b2</span> <span class="o">=</span> <span class="n">k2</span><span class="o">.</span><span class="n">get_binding</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">"@secret"</span><span class="p">,</span> <span class="n">b1</span><span class="p">)</span> <span class="c1">#=> 99</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">"@secret"</span><span class="p">,</span> <span class="n">b2</span><span class="p">)</span> <span class="c1">#=> -3</span>
<span class="nb">eval</span><span class="p">(</span><span class="s2">"@secret"</span><span class="p">)</span> <span class="c1">#=> nil</span></code></pre></div>
<h1 id="example2">Example2</h1>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">get_binding</span><span class="p">(</span><span class="n">param</span><span class="p">)</span>
<span class="nb">binding</span>
<span class="k">end</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">get_binding</span><span class="p">(</span><span class="s2">"hello"</span><span class="p">)</span>
<span class="n">b</span><span class="o">.</span><span class="n">eval</span><span class="p">(</span><span class="s2">"param"</span><span class="p">)</span> <span class="c1">#=> "hello"</span></code></pre></div>
<p>至此,eval和binding应该理解什么意思了,又一次体现了ruby语法的灵活性。</p>
Ruby闭包: Block, Proc, lambda
2012-12-19T00:00:00+00:00
/rubyonrails/2012/12/19/ruby-closure
<p>Ruby中的闭包非常强大,也非常重要,所以决定系统的学习下。</p>
<p>闭包(Closure),是指未绑定到任何对象的自由代码,闭包中的代码与任何对象和全局变量无关,只与执行此段代码的上下文相关。</p>
<p>Ruby中闭包的实现有:Block,Proc,Lambada。在学习它们的区别的时候,网上讲的都不是很全面,突然记起<a href="http://v.baiyulan.net">vincent</a>在以前的一篇开发周记中专门讲到这个主题,遂又仔细的通读了一遍,讲的非常详细全面,顿时就彻底弄懂了,于是也记录在这里,以便分享与以后查阅。</p>
<h2 id="block">首先,我们来看Block。</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">arr</span> <span class="o">=</span> <span class="o">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="o">]</span>
<span class="n">arr</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">item</span><span class="o">|</span> <span class="nb">puts</span> <span class="n">item</span> <span class="p">}</span></code></pre></div>
<p>这段代码,我们使用了Array对象的block方法,将ary中的每个元素打印出来。从例子中我们可以看到block使用起来很方便,想必传统的Java以及C编码,省略掉了冗余的循环,让你更加专注业务代码,这也是ruby的好处之一。</p>
<p>使用block的最大好处就是可以省略重复的冗余的无用的代码,我们再来看一个读文件的例子:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>
<span class="nb">puts</span> <span class="n">f</span><span class="o">.</span><span class="n">readlines</span>
<span class="k">end</span></code></pre></div>
<p>对比用Java代码来读文件,每次都很反感那个冗长的try-catch-finally。从上面的ruby代码中我么可以看到,ruby语言给你做了处理,通过block你可以不用写无用的系统代码了。</p>
<p>从上面的例子我们可以看到,Ruby中的Block对开发者来说,不用再写那些冗余无用的系统代码,而是更加专注业务代码,提升开发效率。</p>
<h2 id="proc">其次,我们再看Proc。</h2>
<p>如果你经常使用Block,你会发现一个问题:代码中会有很多重复的Block,比如好多地方需要打印文件内容。怎么解决呢?你第一个想到的是写一个公共函数,不错,可以解决。但是记住你使用的是ruby语言,不要重新造轮子。Ruby提供了函数化的Block:Proc。</p>
<p>我们看一个简单的Proc例子:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">p</span> <span class="o">=</span> <span class="no">Proc</span><span class="o">.</span><span class="n">new</span><span class="p">{</span><span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="nb">puts</span> <span class="n">f</span><span class="o">.</span><span class="n">readlines</span><span class="p">}</span>
<span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">,</span> <span class="o">&</span><span class="nb">p</span><span class="p">)</span></code></pre></div>
<p>上面例子可以看到,将Block代码定义为一个Proc对象,然后在使用Block的地方用Proc替换,这样就做到了完美的代码复用。</p>
<h2 id="lambda">最后我们看看lambda。</h2>
<p>Lambda:拉姆达表达式,ruby中可以通过lambda表达式创建Proc对象,可以把它理解成一种匿名函数。我们先看例子:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">new_p</span> <span class="o">=</span> <span class="nb">lambda</span><span class="p">{</span><span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="nb">puts</span> <span class="n">f</span><span class="o">.</span><span class="n">readlines</span><span class="p">}</span>
<span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">,</span> <span class="o">&</span><span class="n">new_p</span><span class="p">)</span></code></pre></div>
<p>上面例子中,我们使用系统的lambda方法创建了一个Proc对象,其效果与Proc.new创建出来的是一样的。其实使用lambda与使用Proc.new效果基本是一样的。下面就来讲下他们的区别:</p>
<h2 id="yield--block-call-">yield 和 block call 的区别</h2>
<p>yield 和 block call 两种都能实现运行闭包,从实际运行效果来说,区别不大。其区别主要在于:</p>
<h4 id="section">1. 闭包的传递和保存</h4>
<p>因为闭包已经传递到参数里,所以可以继续传递或保存起来,例如:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">A</span>
<span class="k">def</span> <span class="nf">foo1</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">)</span>
<span class="vi">@proc</span> <span class="o">=</span> <span class="n">b</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">foo2</span>
<span class="vi">@proc</span><span class="o">.</span><span class="n">call</span> <span class="k">if</span> <span class="vi">@proc</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">A</span><span class="o">.</span><span class="n">new</span>
<span class="n">a</span><span class="o">.</span><span class="n">foo1</span> <span class="p">{</span> <span class="nb">puts</span> <span class="s2">"proc from foo1"</span> <span class="p">}</span>
<span class="n">a</span><span class="o">.</span><span class="n">foo2</span></code></pre></div>
<h4 id="section-1">2. 性能</h4>
<p>yield不是方法调用,而是 Ruby 的关键字,yield 要比 block call 比快 1 倍左右。</p>
<h2 id="block--proc-lambda-">block 和 proc, lambda 的区别</h2>
<p>很简单直接,引入 proc 和 lambda 就是为了复用,避免重复定义,例如在上例中,使用 proc 变量存储闭包,避免重复定义两个 block 。</p>
<h2 id="proc--lambda-">proc 和 lambda 的区别</h2>
<p>这大概是最让人困惑的地方,从行为上看,他们的效果是一致的,为什么要用两种不同的表达方式。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">proc</span> <span class="o">=</span> <span class="no">Proc</span><span class="o">.</span><span class="n">new</span> <span class="p">{</span> <span class="nb">puts</span> <span class="s2">"foo in proc"</span> <span class="p">}</span>
<span class="n">lambda_proc</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="nb">puts</span> <span class="s2">"foo in lambda"</span> <span class="p">}</span></code></pre></div>
<p>确实,对于简单的情况,他们的行为是一致的,但是主要在两个地方有明显差异:</p>
<h4 id="lambdasprocs">1. 参数检查,lambdas检查参数的个数,Procs不会。</h4>
<p>还是例子说话</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">foo</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">100</span>
<span class="k">yield</span> <span class="n">x</span>
<span class="k">end</span>
<span class="nb">proc</span> <span class="o">=</span> <span class="no">Proc</span><span class="o">.</span><span class="n">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">|</span> <span class="nb">puts</span> <span class="s2">"a is </span><span class="si">#{</span><span class="n">a</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2"> b is </span><span class="si">#{</span><span class="n">b</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
<span class="n">foo</span><span class="p">(</span><span class="o">&</span><span class="nb">proc</span><span class="p">)</span> <span class="c1">#=> a is 100 b is nil</span>
<span class="n">lambda_proc1</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="o">|</span> <span class="nb">puts</span> <span class="s2">"a is </span><span class="si">#{</span><span class="n">a</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
<span class="n">foo</span><span class="p">(</span><span class="o">&</span><span class="n">lambda_proc1</span><span class="p">)</span> <span class="c1">#=> a is 100</span>
<span class="n">lambda_proc2</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="o">|</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="o">|</span> <span class="nb">puts</span> <span class="s2">"a is </span><span class="si">#{</span><span class="n">a</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2"> b is </span><span class="si">#{</span><span class="n">b</span><span class="o">.</span><span class="n">inspect</span><span class="si">}</span><span class="s2">"</span> <span class="p">}</span>
<span class="n">foo</span><span class="p">(</span><span class="o">&</span><span class="n">lambda_proc2</span><span class="p">)</span> <span class="c1">#=> ArgumentError: wrong number of arguments (1 for 2)</span></code></pre></div>
<p>可见,proc 不会对参数进行个数匹配检查,而 lambda 会,如果不匹配还会报异常,所以安全起见,建议优先用 lambda。</p>
<h4 id="returnlambdasreturn-procreturn">2. return不同。lambdas的return是返回值给方法,方法会继续执行。 Proc的return会终止方法并返回得到的值。</h4>
<p>还是例子说话</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">foo</span>
<span class="n">f</span> <span class="o">=</span> <span class="no">Proc</span><span class="o">.</span><span class="n">new</span> <span class="p">{</span> <span class="k">return</span> <span class="s2">"return from foo from inside proc"</span> <span class="p">}</span>
<span class="n">f</span><span class="o">.</span><span class="n">call</span> <span class="c1"># control leaves foo here</span>
<span class="k">return</span> <span class="s2">"return from foo"</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">bar</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">lambda</span> <span class="p">{</span> <span class="k">return</span> <span class="s2">"return from lambda"</span> <span class="p">}</span>
<span class="nb">puts</span> <span class="n">f</span><span class="o">.</span><span class="n">call</span> <span class="c1"># control does not leave bar here</span>
<span class="k">return</span> <span class="s2">"return from bar"</span>
<span class="k">end</span>
<span class="nb">puts</span> <span class="n">foo</span> <span class="c1">#=> return from foo from inside proc</span>
<span class="nb">puts</span> <span class="n">bar</span> <span class="c1">#=> return from lambda</span>
<span class="c1">#=> return from bar</span></code></pre></div>
<p>可见,proc 中,return 相当于执行了闭包环境里的 return,而 lambda 只是返回call 闭包所在环境。</p>
<p>为什么会有这样的不同?</p>
<p>答案在于procedures和methods概念上的不同。
Ruby中的Procs是代码片段(code snippets),不是方法。因此,Proc的return就是整个方法的return。
但lambdas就像是单独的methods(只不过是匿名的),所以它要检查参数个数,且不会覆盖整个方法的返回。
因此,最好把lambdas当作另一种methods的写法,一种匿名的方式。</p>
<h2 id="section-2">总结</h2>
<p>闭包是 Ruby 的强大特性,它的几种表达方式block,proc 和 lambda有细微差别。blocks和Procs看起来像在代码中插入代码片段。而lambdas和Methods看起来像方法。通过几个例子和比较,希望能了解如何灵活运用闭包,游刃有余!</p>
Ruby Range
2012-12-13T00:00:00+00:00
/rubyonrails/2012/12/13/ruby-range
<p>最近一直在做“生理周期”,不过不要担心,这里只会探讨技术领域,不会涉及到神圣的MC领域。在做生理周期指数预测、周期日报的时候大量用到了Range对象,所以干脆就把Range对象系统的学习下,在这里记录并分享出来。</p>
<h2 id="ruby-range">Ruby Range</h2>
<p>Range在概念上很直观,即范围,但是在实际使用中可能会遇到一些容易混淆的东西,看下面代码:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># 注意:..操作符将包含上限,而...不包含上限。</span>
<span class="p">(</span><span class="o">-</span><span class="mi">1</span> <span class="o">.</span><span class="n">.</span> <span class="o">-</span><span class="mi">5</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span> <span class="c1">#=> []</span>
<span class="p">(</span><span class="o">-</span><span class="mi">5</span> <span class="o">.</span><span class="n">.</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span> <span class="c1">#=> [-5, -4, -3, -2, -1]</span>
<span class="p">(</span><span class="s1">'a'</span> <span class="o">.</span><span class="n">.</span> <span class="s1">'e'</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span> <span class="c1">#=> ["a", "b", "c", "d", "e"]</span>
<span class="p">(</span><span class="s1">'a'</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="s1">'e'</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span> <span class="c1">#=> ["a", "b", "c", "d"]</span></code></pre></div>
<h2 id="beginend-firstlast">begin/end first/last</h2>
<p>使用first和last方法(或同义方法begin和end),可以获取一个Range的开始和结束元素:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">r1</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">.</span><span class="n">.</span> <span class="mi">6</span>
<span class="n">r2</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="mi">6</span>
<span class="n">r1a</span><span class="p">,</span> <span class="n">r1b</span> <span class="o">=</span> <span class="n">r1</span><span class="o">.</span><span class="n">first</span><span class="p">,</span> <span class="n">r1</span><span class="o">.</span><span class="n">last</span> <span class="c1">#=> 3, 6</span>
<span class="n">r1c</span><span class="p">,</span> <span class="n">r1d</span> <span class="o">=</span> <span class="n">r1</span><span class="o">.</span><span class="n">begin</span><span class="p">,</span> <span class="n">r1</span><span class="o">.</span><span class="n">end</span> <span class="c1">#=> 3, 6</span>
<span class="n">r2a</span><span class="p">,</span> <span class="n">r2b</span> <span class="o">=</span> <span class="n">r2</span><span class="o">.</span><span class="n">begin</span><span class="p">,</span> <span class="n">r2</span><span class="o">.</span><span class="n">end</span> <span class="c1">#=> 3, 6 (注意:不是3和5)</span>
<span class="n">r1</span><span class="o">.</span><span class="n">first</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1">#=> [3, 4]</span></code></pre></div>
<h2 id="excludeend">exclude_end?</h2>
<p>exclude_end?方法可以得到Range是否排除上限项(即是否是…的Range)</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">r1</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">.</span><span class="n">.</span> <span class="mi">6</span>
<span class="n">r2</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">.</span><span class="n">.</span><span class="o">.</span> <span class="mi">6</span>
<span class="n">r1</span><span class="o">.</span><span class="n">exclude_end?</span> <span class="c1">#=> false</span>
<span class="n">r2</span><span class="o">.</span><span class="n">exclude_end?</span> <span class="c1">#=> true</span></code></pre></div>
<h2 id="includecover">include?/cover?</h2>
<p>include?方法(同义方法member?)或者cover?方法可以判断一个值是否处在当前的Range中:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">r</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">.</span><span class="n">.</span> <span class="mi">5</span>
<span class="n">r</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">#=> true</span>
<span class="n">r</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1">#=> false</span>
<span class="n">r</span><span class="o">.</span><span class="n">cover?</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">#=> true</span>
<span class="n">r</span><span class="o">.</span><span class="n">cover?</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1">#=> false</span></code></pre></div>
<p>区别:include?方法是把Range的元素迭代取出进行比较,而cover?方法只是把给出的值和该Range的上下限做比较得出的,所以cover?性能比include?要好。</p>
<h2 id="step">step</h2>
<p>如果我们想要从0 .. 20中取出这样的元素0,5,10,20怎么办?这时候step方法就用到了</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">r</span> <span class="o">=</span> <span class="mi">0</span> <span class="o">.</span><span class="n">.</span> <span class="mi">20</span>
<span class="n">r</span><span class="o">.</span><span class="n">step</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span> <span class="c1">#=> [0, 5, 10, 15, 20]</span></code></pre></div>
<h2 id="range">浮点数的Range</h2>
<p>浮点数的Range可以进行迭代么?我们来看一下:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">fr</span> <span class="o">=</span> <span class="mi">2</span><span class="o">.</span><span class="mi">0</span><span class="o">.</span><span class="n">.</span><span class="mi">2</span><span class="o">.</span><span class="mi">2</span>
<span class="n">fr</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span><span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="nb">puts</span> <span class="n">x</span> <span class="p">}</span> <span class="c1">#=> TypeError: can't iterate from Float</span></code></pre></div>
<p>为什么浮点数不可以迭代呢?是因为不能实现么?理论上,这是没有问题的,但是,实际上,如果浮点数Range迭代,这有可能出现:很小的一个范围,将产生非常庞大的迭代量。这对语言的实现有非常高的要求。况且,这样的功能,极少有用到。</p>
<h2 id="range-1">反向的Range</h2>
<p>我们讨论下限大于上限的Range,如:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">r</span> <span class="o">=</span> <span class="mi">6</span><span class="o">.</span><span class="n">.</span><span class="mi">3</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">begin</span> <span class="c1">#=> 6</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">end</span> <span class="c1">#=> 3</span>
<span class="n">flag</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">end_excluded?</span> <span class="c1">#=> false</span></code></pre></div>
<p>它确实是个合法的Range,但是,它包含的内容却并不是我们想像的那样:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">arr</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">to_a</span> <span class="c1">#=> []</span>
<span class="n">r</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span><span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="nb">p</span> <span class="n">x</span><span class="p">}</span> <span class="c1">#=> 无结果</span>
<span class="n">r</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1">#=> false</span></code></pre></div>
<p>那么说反向Range是没有什么用处的咯?那倒不是,我们可以在字符串和数组中使用反向Range:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">string</span> <span class="o">=</span> <span class="s2">"flowery"</span>
<span class="n">str1</span> <span class="o">=</span> <span class="n">string</span><span class="o">[</span><span class="mi">0</span><span class="o">.</span><span class="n">.</span><span class="o">-</span><span class="mi">2</span><span class="o">]</span> <span class="c1">#=> "flower"</span>
<span class="n">str2</span> <span class="o">=</span> <span class="n">string</span><span class="o">[</span><span class="mi">1</span><span class="o">.</span><span class="n">.</span><span class="o">-</span><span class="mi">2</span><span class="o">]</span> <span class="c1">#=> "lower"</span>
<span class="n">str3</span> <span class="o">=</span> <span class="n">string</span><span class="o">[-</span><span class="mi">5</span><span class="o">.</span><span class="n">.</span><span class="o">-</span><span class="mi">3</span><span class="o">]</span> <span class="c1">#=> "owe" (其实这是个正向的Range)</span></code></pre></div>
Become a Better Developer You Can
2012-11-25T14:46:00+00:00
/rubyonrails/2012/11/25/be-a-better-developer
<blockquote>
<p>很荣幸参加了RubyConfChina-2012大会,即第四次Ruby中国大会,第一次参加难免有点激动,
更何况还见到了很多国内外的大牛,这次讲师们的演讲也非常精彩,这篇Blog就来整理下Fred Wu的分享,
感觉对以后的编程道路有很好的帮助作用。</p>
</blockquote>
<h2 id="three-stages-of-programmer">Three Stages Of Programmer</h2>
<p>1.You discuss tools and editors.三流程序员谈工具</p>
<p>2.You discuss programming techniques.二流程序员谈技术</p>
<p>3.You discuss creative and thought processess.一流程序员谈思想</p>
<!--more-->
<h2 id="thoughts-on-software-development">Thoughts On Software Development</h2>
<ul>
<li>Second system syndrome - avoid total rewrites.</li>
</ul>
<p>第二系统综合症——避免重写</p>
<ul>
<li>Software engineering is like playing Warcraft - always check your mini-map.</li>
</ul>
<p>软件工程有如魔兽争霸——需经常关注小地图,以大局为重</p>
<ul>
<li>Experience is as important as talent, if not more important.</li>
</ul>
<p>经验与天赋同重要</p>
<ul>
<li>Technical debt is a debt, the longer you wait, the more interests you play.</li>
</ul>
<p>技术债务——拖欠越久,偿还越多</p>
<ul>
<li>Choose the application architecture wisely.</li>
</ul>
<p>谨慎选择软件架构</p>
<h2 id="four-simple-rules">Four Simple Rules</h2>
<p>Passed all the tests.</p>
<p>Contains no duplication.</p>
<p>Expresses the intent.</p>
<p>Minimises the number of classes and methods.</p>
<h2 id="contribution-to-open-source-projects">Contribution To Open Source Projects</h2>
<p>Reading code is just as important as writing code.</p>
<p>阅读代码与编写代码同重要</p>
<p>Learn from the masters.</p>
<p>向大师们学习</p>
<p>Take some, give some - sharing is awesome.</p>
<p>不断收获的同时,别忘了付出</p>
<p>Making things more useful for yourself therefore for others too.</p>
<p>对自己有用的东西,也许对别人也有用</p>
<h2 id="refactor-in-the-wild">Refactor In The Wild</h2>
<p>Improve code readability and maintainability</p>
<p>A Simple Example</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">search</span><span class="p">(</span><span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
<span class="n">scope</span> <span class="o">=</span> <span class="n">scoped</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">has_key?</span><span class="p">(</span><span class="ss">:employee_groups</span><span class="p">)</span>
<span class="n">employee_group_ids</span> <span class="o">=</span> <span class="nb">Array</span><span class="o">.</span><span class="n">wrap</span><span class="p">(</span><span class="n">options</span><span class="o">[</span><span class="ss">:employee_groups</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:id</span><span class="p">)</span>
<span class="n">scope</span> <span class="o">=</span> <span class="n">scope</span><span class="o">.</span><span class="n">includes</span><span class="p">(</span><span class="ss">:employee_group_dashboard_links</span><span class="p">)</span>
<span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">:employee_group_dashboard_links</span> <span class="o">=></span> <span class="p">{</span>
<span class="ss">:employee_group_id</span> <span class="o">=></span> <span class="n">employee_group_ids</span>
<span class="p">})</span>
<span class="k">end</span>
<span class="n">scope</span>
<span class="k">end</span></code></pre></div>
<p>After Refactor</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">search</span><span class="p">(</span><span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
<span class="n">result</span> <span class="o">=</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">has_key?</span><span class="p">(</span><span class="ss">:employee_groups</span><span class="p">)</span>
<span class="n">scoped</span><span class="o">.</span><span class="n">includes</span><span class="p">(</span><span class="ss">:employee_group_dashboard_links</span><span class="p">)</span>
<span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">:employee_group_dashboard_links</span> <span class="o">=></span> <span class="p">{</span>
<span class="ss">:employee_group_id</span> <span class="o">=></span> <span class="nb">Array</span><span class="o">.</span><span class="n">wrap</span><span class="p">(</span><span class="n">options</span><span class="o">[</span><span class="ss">:employee_groups</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:id</span><span class="p">)</span>
<span class="p">})</span>
<span class="k">end</span>
<span class="n">result</span> <span class="o">||</span> <span class="n">scoped</span>
<span class="k">end</span></code></pre></div>
<p>A Ruby-China Example</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># 检查用户是否看过</span>
<span class="c1"># result:</span>
<span class="c1"># 0 读过</span>
<span class="c1"># 1 未读</span>
<span class="c1"># 2 最后是用户的回复</span>
<span class="k">def</span> <span class="nf">user_readed?</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
<span class="n">uids</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="s2">"Topic:user_read:</span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">uids</span><span class="o">.</span><span class="n">blank?</span>
<span class="k">if</span> <span class="nb">self</span><span class="o">.</span><span class="n">last_reply_user_id</span> <span class="o">==</span> <span class="n">user_id</span> <span class="o">||</span> <span class="nb">self</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">user_id</span>
<span class="k">return</span> <span class="mi">2</span>
<span class="k">else</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">if</span> <span class="n">uids</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">else</span>
<span class="k">if</span> <span class="nb">self</span><span class="o">.</span><span class="n">last_reply_user_id</span> <span class="o">==</span> <span class="n">user_id</span> <span class="o">||</span> <span class="nb">self</span><span class="o">.</span><span class="n">user_id</span> <span class="o">==</span> <span class="n">user_id</span>
<span class="k">return</span> <span class="mi">2</span>
<span class="k">else</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
<p>After Refactor</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># 检查用户是否看过</span>
<span class="c1"># result:</span>
<span class="c1"># 0 读过</span>
<span class="c1"># 1 未读</span>
<span class="c1"># 2 最后是用户的回复</span>
<span class="k">def</span> <span class="nf">user_readed?</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>
<span class="n">user_ids</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="s2">"Topic:user_read:</span><span class="si">#{</span><span class="nb">self</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> <span class="o">||</span> <span class="o">[]</span>
<span class="k">if</span> <span class="n">user_id</span><span class="o">.</span><span class="n">in?</span><span class="p">(</span><span class="n">user_ids</span><span class="p">)</span>
<span class="mi">0</span>
<span class="k">elsif</span> <span class="n">user_id</span><span class="o">.</span><span class="n">in?</span><span class="p">(</span><span class="o">[</span><span class="nb">self</span><span class="o">.</span><span class="n">last_reply_user_id</span><span class="p">,</span> <span class="nb">self</span><span class="o">.</span><span class="n">user_id</span><span class="o">]</span><span class="p">)</span>
<span class="mi">2</span>
<span class="k">else</span>
<span class="mi">1</span>
<span class="k">end</span>
<span class="k">end</span></code></pre></div>
搭建Octopress
2012-11-21T00:00:00+00:00
/other/2012/11/21/use-octopress-to-write-blog
<p>准备开一个博客,但是一直犹豫在哪里开,是在CSDN,博客园,新浪,网易…纠结中,但是一直觉得以上平台要么不适合做技术博客,要么觉得不太高端,门槛太低。于某一天终于发现了Octopress,欣喜过望,这就是我想要的,有一定门槛,需要ruby,git等技术。终于可以像黑客一样写博客了,哈哈,很兴奋。下面记录下搭建的过程。</p>
<h2 id="octopress">1.安装Octopress</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="nb">clone</span> <span class="ss">git</span><span class="p">:</span><span class="sr">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">imathis</span><span class="o">/</span><span class="n">octopress</span><span class="o">.</span><span class="n">git</span> <span class="n">octopress</span>
<span class="n">cd</span> <span class="n">octopress</span>
<span class="n">bundle</span> <span class="n">update</span> <span class="c1"># 安装依赖的组件</span>
<span class="n">rake</span> <span class="n">install</span> <span class="c1"># 安装默认的Octopress主题</span></code></pre></div>
<h2 id="git">2.配置Git</h2>
<p>值得注意的是这里git的origin已经存在,并且指向octopress的master分支的,这里为了方便进行了更改:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">remote</span> <span class="n">rm</span> <span class="n">origin</span>
<span class="n">git</span> <span class="n">remote</span> <span class="n">add</span> <span class="n">origin</span> <span class="n">git</span><span class="vi">@github</span><span class="o">.</span><span class="n">com</span><span class="ss">:stormzhang</span><span class="o">/</span><span class="n">stormzhang</span><span class="o">.</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">.</span><span class="n">git</span>
<span class="n">git</span> <span class="n">remote</span> <span class="n">add</span> <span class="n">octopress</span> <span class="ss">git</span><span class="p">:</span><span class="sr">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">imathis</span><span class="o">/</span><span class="n">octopress</span><span class="o">.</span><span class="n">git</span> <span class="c1"># 为了octopress的升级而添加</span></code></pre></div>
<h2 id="github">3.配置github</h2>
<p>在github上创建一个仓库,注意仓库名称要以下这种格式yourname.github.com,这样代码发布后自动这个url就可以访问了(此处一定要注意哦,我刚开始没注意,死活没得到想要的效果)。 例如你的 GitHub 帐号是 jack 就将 Repository 命名为 jack.github.com, 完成后会得到一组 GitHub Pages URL http://yourname.github.com/ (注意不能用 https协议,必须用 http协议)。</p>
<p>设定 GitHub Pages</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">rake</span> <span class="n">setup_github_pages</span></code></pre></div>
<p>以上执行后会要求 read/write url for repository : </p>
<p>git@github.com:yourname/yourname.github.com.git </p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">rake</span> <span class="n">generate</span>
<span class="n">rake</span> <span class="n">deploy</span></code></pre></div>
<p>等待几分钟后,github上会收到一封信:“{yourname.github.com} Page build successful”,第一次发布后等比较久,之后每次都会直接更新。 当你发布之后,你就可以到 http://yourname.github.com 上看到你的博客了.</p>
<h2 id="source--git">4.将 source 也加入 git</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">add</span> <span class="o">.</span>
<span class="n">git</span> <span class="n">commit</span> <span class="o">-</span><span class="n">m</span> <span class="s1">'initial source commit'</span>
<span class="n">git</span> <span class="n">push</span> <span class="n">origin</span> <span class="n">source</span></code></pre></div>
<h2 id="octopress-1">5.更新 Octopress</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">git</span> <span class="n">remote</span> <span class="n">add</span> <span class="n">octopress</span> <span class="ss">git</span><span class="p">:</span><span class="sr">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">imathis</span><span class="o">/</span><span class="n">octopress</span><span class="o">.</span><span class="n">git</span>
<span class="n">git</span> <span class="n">pull</span> <span class="n">octopress</span> <span class="n">master</span> <span class="c1"># Get the latest Octopress</span>
<span class="n">bundle</span> <span class="n">install</span> <span class="c1"># Keep gems updated</span>
<span class="n">rake</span> <span class="n">update_source</span> <span class="c1"># update the template's source</span>
<span class="n">rake</span> <span class="n">update_style</span> <span class="c1"># update the template's style</span></code></pre></div>
<h2 id="section">6.发表新文章</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">rake</span> <span class="n">new_post</span><span class="o">[</span><span class="s2">"新文章名称"</span><span class="o">]</span>
<span class="n">rake</span> <span class="n">preview</span></code></pre></div>
<p>用浏览器打开 http://localhost:4000 就可以看到效果了。</p>
<h2 id="section-1">7.发布</h2>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">rake</span> <span class="n">gen_deploy</span>
<span class="n">rake</span> <span class="n">deploy</span> <span class="c1">#若发布后无效果可试试此命令</span></code></pre></div>