乐者为王

Do one thing, and do it well.

我写的二分查找算法有bug!

看完 http://reprog.wordpress.com/2010/04/19/are-you-one-of-the-10-percent/ 觉得很不服气,写个二分查找算法还不简单吗?分分钟搞定的事情。

按照它的要求,用Java写出以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public int binarySearch(int[] array, int value) {
    int low = 0;
    int high = array.length - 1;
    int mid;
    while (low <= high) {
        mid = (low + high) / 2;
        if (array[mid] < value) {
            low = mid + 1;
        } else if (array[mid] > value) {
            high = mid - 1;
        } else {
            return mid;
        }
    }
    return -1;
}

把代码贴到评论区参加测试。

写代码和检查总共花费20分钟,75%的时间是用来在脑袋里模拟运算,防止数组索引溢出。然后编译2次,消除了两个语法错误(好久没写Java代码和使用Ruby的关系,竟然连个数组初始化都不会写了)。也想过数值溢出的问题,但是没想到点子上。结果恰恰倒在了这个地方。具体原因在 http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html 有描述。因为low + high的和会形成数值溢出,解决方法是采用:

1
int mid = low + ((high - low) / 2);

1
int mid = (low + high) >>> 1;

如何破解中兴ZXA10-F460 v3.0获取超级管理员密码

破解前的系统情况:

  • 硬件版本号 V3.0
  • 软件版本号 F460_IMS_V2.30.10P1T6_JS1111
  • BOOT版本号 V1.1.0P1T1
  • 版本日期 2012-02-29 16:29:07

中兴ZXA10-F460光猫(E8-C)在第一次安装的时候,超级管理员帐号是telecomadmin,密码为nE7jA%5m,这个是默认的,由安装人员设置好相关设置后,会自动下载配置,这时候这个超级管理员密码会被修改为telecomadminXXXXXXXX(8个X是8位数字,每个用户是不同的)这种格式。因为很多设置需要在超级管理员下才能设置,所以破解这个超级管理员密码是非常必要的。

其中V1.0和V2.0版本网上有破解方法,大家可以自己搜索。

在命令行下输入telnet 192.168.1.1,帐号和密码都是root。连接成功后,会出现“F460 login:”这样的提示。

注意:目前网上流传的查看/tmp目录下db_backup_cfg.xml文件内容的破解方法已经无效了。另外在/userconfig/cfg目录下有两个加密的配置文件db_backup_cfg.xml和db_user_cfg.xml,同目录下还有个db_default_cfg.xml是不加密的(该文件是出厂设置的配置文件),但是大家不要用这个文件去替换db_user_cfg.xml,否则会出现不可预知的后果。

实际上只要修改一下/home/httpd/login.gch这个文件,就能看到密码了。patch文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
--- /home/httpd/login.gch 2012-07-03 14:05:16.000000000 +0800
+++ login.gch 2012-07-03 14:03:37.000000000 +0800
@@ -152,6 +152,17 @@
set_language("langcn.conf");
langclass = "login_title_centeren";
}
+var CK_HANDLE = create_paralist();
+var login_name = "telecomadmin";
+set_para(CK_HANDLE, "Username", login_name);
+qeury_list_bycond("OBJ_USERINFO_ID", "IGD", CK_HANDLE);
+destroy_paralist(CK_HANDLE);
+CK_HANDLE = create_paralist();
+var CK_IDENTITY = query_identity(0);
+get_inst(CK_HANDLE, "OBJ_USERINFO_ID", CK_IDENTITY);
+var now_pwd = get_para(CK_HANDLE, "Password");
+now_pwd = delMoreSlash(now_pwd);
+destroy_paralist(CK_HANDLE);
%>
<head>
<META HTTP-EQUIV="pragma" CONTENT="no-cache>
@@ -239,4 +250,5 @@
</script>
</head>
<body onload="onFocus();">
+<div><%=now_pwd;%></div>
<div id="container" class="content">

效果是浏览器登录192.168.1.1的时候,超级管理员密码会显示在首页界面的左上角。

到这儿有人想了,怎么把上面的patch更新到/home/httpd/login.gch中呢?总结了三种方法:

第一种方法是telent到F460上后,执行下面的命令即可:

1
2
3
4
5
6
7
8
9
10
11
12
sed -i '155 s/^/var CK_HANDLE = create_paralist();\n/' /home/httpd/login.gch
sed -i '156 s/^/var login_name = "telecomadmin";\n/' /home/httpd/login.gch
sed -i '157 s/^/set_para(CK_HANDLE, "Username", login_name);\n/' /home/httpd/login.gch
sed -i '158 s/^/qeury_list_bycond("OBJ_USERINFO_ID", "IGD", CK_HANDLE);\n/' /home/httpd/login.gch
sed -i '159 s/^/destroy_paralist(CK_HANDLE);\n/' /home/httpd/login.gch
sed -i '160 s/^/CK_HANDLE = create_paralist();\n/' /home/httpd/login.gch
sed -i '161 s/^/var CK_IDENTITY = query_identity(0);\n/' /home/httpd/login.gch
sed -i '162 s/^/get_inst(CK_HANDLE, "OBJ_USERINFO_ID", CK_IDENTITY);\n/' /home/httpd/login.gch
sed -i '163 s/^/var now_pwd = get_para(CK_HANDLE, "Password");\n/' /home/httpd/login.gch
sed -i '164 s/^/now_pwd = delMoreSlash(now_pwd);\n/' /home/httpd/login.gch
sed -i '165 s/^/destroy_paralist(CK_HANDLE);\n/' /home/httpd/login.gch
sed -i '253 s/^/<div><%=now_pwd;%><\/div>\n/' /home/httpd/login.gch

第二种方法是telent到F460上后,执行cp /home/httpd/login.gch /home/httpd/login_gch.xml,通过浏览器把login_gch.xml保存到电脑上,然后再把patch更新进去。可以在电脑上架设ftp服务器,在F460上执行以下命令:

1
2
cd /home/httpd/
wget ftp://<YOUR_COMPUTER_IP>/login.gch  # 把<YOUR_COMPUTER_IP>替换成你自己的

第三种方法是telent到F460上后,执行df命令,查看是否有usb1_1或usb2_1,如果有就可以进行下一步操作。输入cp /home/httpd/login.gch /mnt/usb1_1/login.gch把文件拷贝到优盘上,修改后使用cp /mnt/usb1_1/login.gch /home/httpd/login.gch把文件更新到猫上就可以。

2013/6/16更新

其实还有种更简便的方法,中兴的光猫有个sendcmd命令,下面是该命令的简单用法:

  1. 读取全部表名 sendcmd 1 DB all;
  2. 读取指定表信息 sendcmd 1 DB p 表名。比如sendcmd 1 DB p UserInfo,查看路由器帐号和密码,超级管理员密码也在这里查看;
  3. 查看当前系统运行的服务状态 sendcmd -pc show;
  4. 修改某个表的某个字段的值 sendcmd 1 DB set 表名 行数 字段名 字段值。比如要修改超级管理员密码,可以使用sendcmd 1 DB set UserInfo 0 Password 123456;
  5. 保存对配置文件的修改 sendcmd 1 DB save。

当然还有其它的命令,但是就修改光猫配置来说,这几条命令足够了。

SQL连接总结

连接(join)可以在SELECT语句的FROM子句或WHERE子句中建立,在FROM子句中指出连接时有助于将连接操作与WHERE子句中的搜索条件区分开来。连接可以对同一个表操作,也可以对多个表操作,对同一个表操作的连接称为自连接(self join)。

连接类型(join type)分为三种:内连接、外连接和交叉连接。

  • 内连接(inner join):使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行。
  • 外连接(outer join):与内连接不同的是外连接不只列出与连接条件相匹配的行,而且列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合条件的数据行。
  • 交叉连接(cross join):返回连接表中所有数据行的笛卡尔积。

内/外链接可以分为等值连接和不等连接:

  • 等值连接(equijoin):在连接条件中使用等于(=)运算符比较被连接列的列值,其查询结果中列出被连接表中的所有列,包括其中的重复列。
  • 不等连接(non-equijoin):在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。

注意:自然连接(natural join)是一种特殊的等值连接。它使用选择列表指出查询结果中所包括的列,由此删除连接表中的重复列。

books数据表:

books-table

publishers数据表:

publishers-table

自连接查询:

1
SELECT * FROM books b1 [INNER] JOIN books b2 ON b1.price = b2.price

self-join

内连接(简称连接)查询:

1
SELECT * FROM books b [INNER] JOIN publishers p ON b.publisher_id = p.publisher_id

inner-join

自然连接查询:

1
SELECT b.*, p.name FROM books b [INNER] JOIN publishers p ON b.publisher_id = p.publisher_id

natural-join

左外连接(简称左连接)查询:

1
SELECT * FROM books b LEFT [OUTER] JOIN publishers p ON b.publisher_id = p.publisher_id

left-outer-join

右外连接(简称右连接)查询:

1
SELECT * FROM books b RIGHT [OUTER] JOIN publishers p ON b.publisher_id = p.publisher_id

right-outer-join

全外连接(简称全连接)查询:

1
SELECT * FROM books b FULL [OUTER] JOIN publishers p ON b.publisher_id = p.publisher_id

full-outer-join

交叉连接查询:

1
SELECT * FROM books CROSS JOIN publishers

cross-join

下图是由Moffatt在2008年制作的连接集合表示:

sql-joins

在创业和生活中间你需要计划A、B和Z

英文原文:https://techcrunch.com/2012/02/14/in-startups-and-life-you-need-plan-a-b-and-z/

创业者总会从真正聪明的、有经验的人士那里得到很多矛盾的建议。例如,你可能被告知既要坚持又要灵活;要有一个你不懈地追求的清晰愿景,但是也要随着市场的变化而改变你的愿景。简单,对吧?

同样的矛盾在职业咨询当中盛行。有些人会告诉你,要想清楚十年后你希望在哪里,然后通过逆向推导,建立一个实现你的雄心的长期职业规划。其他人告诉你,坚定的计划就像一种桎梏:它们会让你错失意想不到的突破机会。他们说,最好是保持随机应变。

谁是对的呢?两者不仅都是对的,而且至关重要。创业者需要灵活性地坚持。我曾经共事过的优秀创业者忙于重要的规划与战略,但他们不会设置固定的计划。在我和Ben Casnocha的新书《The Start-Up of You: Adapt to the Future, Invest in Yourself, and Transform Your Career》中,我们展示了任何专业人士如何将创业技巧应用于他们的职业,即使你从未计划去求职于一家公司。

我参与过一些在它们初创时你不会认可的公司。尤其是作为很好例子的两个,因为它们都有着巨大的影响力:Flickr和PayPal。Flickr开始时是一个多玩家在线游戏,之后渐变为最广泛使用的照片托管和分享网站之一。PayPal开始时是移动电话的加密平台,然后成为Palm Pilot之间的转钱服务,最终成为领先的在线支付公司。两者都坚持它们最初的愿景,以求学习和成长:Flickr作为一种社交体验和PayPal是安全的现金。然而,正是因为有着极大的弹性和锲而不舍,两家公司都彻底地改变了产品的类型并最终获得用户的青睐。

在创业或职业生涯中,你是如何做到灵活地坚持的?这里是我使用的一个框架:ABZ计划。在事业和生活中,你应该有3个计划:A计划、B计划和Z计划。

A计划是你当前的计划,你目前的重点是怎样赢得市场。对于Flickr的联合创始人Caterina FakeStewart Butterfield而言,最初的A计划是Game Neverending,一个多玩家在线游戏。不同于当时的大多数游戏只能通过固定的体验在几个对手之间进行,他们希望他们的游戏能有数以百计的用户同时玩,并且永远地在游戏中创造新的事物。为了吸引住用户,他们构建了像群组、即时通讯这样的社交功能,最关键的功能是允许玩家彼此之间分享照片。

我最初的职业A计划是致力于学术界,因为我认为传播什么是造就一个良好社会的思想是对这个世界造成影响的最好方式。当我在牛津学习时,我学到很多关于人们如何聚在一起,在群组间互动和与社会产生关联。但是我也了解到,学术界的职业成功往往意味着写出只有50多人读过的专业著作。

B计划是当你意识到一个新机会比你所从事的事情拥有更多潜力时转向的计划。有时候你转变为B计划是因为A计划并不奏效,这是大多数人听到“B计划”时所想的。但有时候A计划还能正常工作,然而B计划似乎拥有更多潜力。不管转移的原因是什么,最好的B计划是虽有不同,但却和你现在正在做的事情有关。这样,你可以把你已经学到的经验和教训用于新计划。

对照到Flickr,出乎意料地,照片分享功能受欢迎的程度超越了游戏本身。Caterina和Stewart面临一个抉择:他们应该坚持他们的A计划还是暂时中止游戏(和2万狂热用户)以便完全专注于照片分享功能?最终他们转移到B计划。当然,Caterina和Stewart仍然遵循最初的想法来构建一个在线的社交空间——他们只是看到照片分享比游戏有更大的潜力。

在我的职业生涯中,我对学术界的认识导致我转向B计划,找到具有更广泛影响力的职业路径。我的B计划是创建新的软件。软件行业的成功也意味着“影响力”——但是比学术界有更广泛的规模。在某些情况下,它意味着构建一种每天改善数百万人生活的产品。为追求这条替代路线,我首先通过在苹果和富士通的在线部门工作来专注于建立相关技能和关系。其次,我与可以共同创建一家我自己的公司的人联系。然后,当我开始我的第一家公司时,我招募了许多聪明的顾问和参与者,以便快速地学习和调整。而且,就公司规划方面来说,由于我的第一家公司(SocialNet)不成功,PayPal和LinkedIn都是我自己转向的新的B计划。

请记住,你应该很少写下一个明确的B计划,但是你应该总是在你执行你的A计划时注意你的动态参数。你应该考虑“临近可能性”。你的可转换的技能。在视野内的其它机会。

Z计划有两个关键的部分。首先,确认如何衡量你正在朝着最坏的情况发展,其次,它是告诉你最坏的情况发生时应该做什么。也许当你的信用卡债务膨胀到一定数量时,你会提前支取你的401k养老金,或者在星巴克找份工作。Z计划最后担保的确定性就是让你在职业生涯中承担各种不确定性和风险。当我开始我的第一家公司SocialNet时,我的父母在他们的住宅为我提供了一间屋子,以防事情没有成功。住在那里然后找其它的工作是我的Z计划。它让我有信心全身心地投入到事业中,因为我知道就算一切都没有了,我也不至于沦落街头。为了可以重新开始,你需要在失败中存活。

TechCrunch读者每天都会看到一些成功公司的适应性路径。然而,很少有人会将这种适应性手册应用于自己的生活。像ABZ规划这样的框架可以帮助你掌控你的职业生涯。我们应该记住的是,无论我们开始创业,在初创公司工作,或者在大公司工作:最终的创业者都是你。

如何避免点击链接下载文件时浏览器自动打开文件

HTTP Response Header有个Content-Disposition参数,该参数的目的是为了在客户端另存文件时提供一个建议的文件名。将它的值设置为attachment后,浏览器就会老老实实地显示另存为对话框,如果这个值设成inline,则无论怎样浏览器都会自动尝试用已知关联的程序打开文件。

注意:在Firefox中需要把filename用双引号包起来才能得到想要的文件名,不然如果含有空格就会丢掉空格后面的部分,而IE会把空格转为下划线。

Java设置header代码:

1
res.addHeader("Content-Disposition", "attachment; filename=" + res.encodeURL(filename));

Ruby设置header代码:

1
headers['Content-Disposition'] = "attachment; filename=#{filename}"

如何避免项目总是完成90%

程序员经常会说“功能已经基本完成,接下来只需要花点时间进行扫尾工作即可,但这些基本都是些小事情,我已经完成了90%。”但实际情况是项目进度总是延迟。这里最最根本的问题是什么?这个程序员并没有一个清单,把自己要做的所有事情都列出来。这就意味着,即使他坚信自己已经完成90%的工作,他还是不知道这个项目什么时候可以做完,他的时间表是没有真实依据的。

一个好的软件项目经理的任务就是,鼓励并且强制要求程序员创建一张他们所要做的全部事情的列表。然后再为其中的每一项列出子项,并且尽可能把所有的子项都加进来。一旦你拥有了这么一个包含所有事项的列表,你就可以开始估算这个任务需要花费多少时间了。

以下是避免“总是完成90%”问题的发生的几点建议:

  1. 把项目中所有要做的事情全部罗列出来做成列表。
  2. 估算这个列表中每一项所需要花费的时间。这种最初的估算可以帮助你知道整个项目大致要花费多少时间。
  3. 如果列表中有一项的时间花费超过1天,则把这项拆分成若干小项。这种将大任务拆分成小任务的方式是解决“总是完成90%”问题的关键步骤。
  4. 使用一种呈现任务状态的方式,以便那些感兴趣的人可以了解项目进度。
  5. 既然你已经拥有很多小的任务,并且这些任务所消耗的时间都在1天以内,你就可以每天追踪自己的任务完成情况。

改写Rails 2.x generator到Rails 3.x时的一些记录

因为有个基于Rails 2.x的项目用到了feedback插件,在把项目迁移到Rails 3.x版本后,需要让feedback也支持Rails 3.x。但是原作者早已不再更新该插件,所以只能自己动手。花了两天时间查找资料,修改代码,终于完成。

Rails 3.x中生成generator可用下面的命令:

1
2
3
4
5
rails g generator feedback_form
      create  lib/generators/feedback_form
      create  lib/generators/feedback_form/feedback_form_generator.rb
      create  lib/generators/feedback_form/USAGE
      create  lib/generators/feedback_form/templates
1
2
3
class InitializerGenerator < Rails::Generators::NamedBase
  source_root File.expand_path("../templates", __FILE__)
end

生成基类也从Rails::Generators::Base变成了Rails::Generators::NamedBase。它们的区别是Rails::Generators::NamedBase期望至少一个参数。

USAGE文件里的内容是输入-h选项时显示的文档:

1
2
3
rails generate feedback_form --help
Usage:
  rails generate feedback_form NAME [options]

Rails 2.x中的manifest可以删除了,因为在3.x的generator中每个public方法在生成过程中会被自动调用。

用Rails 3.2打造简单记账应用

Rails已经到3.2版本了,和以前的2.3版本有了很大的不同,决定把原来老版本的记账应用重新写一遍,更新部分插件。

首先是创建项目:

1
2
3
4
rails new qianbao
cd qianbao
rails g scaffold entry amount:decimal tags:string comment:text effective_date:date
rails g controller home index

删除public/index.html,在config/routes.rb中添加根路由:

1
root :to => 'home#index'

现在要给应用加上一个认证系统(注册、激活、登录、登出),这次使用devise插件实现。

在Gemfile中添加:

1
gem 'devise'

然后执行下列命令:

1
2
3
4
bundle install
rails g devise:install
rails g devise user
rails g devise:views

创建一个迁移任务,把User和Entry模型关联起来:

1
2
3
4
5
6
7
class AddUserIdToEntries < ActiveRecord::Migration
  def change
    add_column :entries, :user_id, :integer

    add_index :entries, :user_id
  end
end

修改Entry和User模型:

1
2
3
4
5
6
7
8
9
class Entry < ActiveRecord::Base
  validates :effective_date, :presence => true
  validates :amount,         :presence => true,
                             :numericality => { :greater_than => 0.0 },
                             :format => { :with => /^\d+??(?:\.\d{0,2})?$/ }
  validates :tags,           :presence => true,
                             :length => { :within => 1..255 }

  belongs_to :user
1
2
class User < ActiveRecord::Base
  has_many :entries

修改app/views/layouts/application.html.erb文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
  <% if user_signed_in? %>
    <p>
      <strong><%= link_to current_user.email, edit_user_registration_path %></strong>
      <%= link_to 'Logout', destroy_user_session_path, :method => :delete %>
    </p>
    <%= link_to "All Entries", entries_path %>
  <% else %>
    <p>
      <strong>You are currently not logged in.</strong>
      <%= link_to 'Sign in', new_user_session_path %> or
      <%= link_to 'Sign up', new_user_registration_path %>
    </p>
  <% end %>

  <%= yield %>
</body>

修改app/controller/entries_controller.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class EntriesController < ApplicationController
  before_filter :authenticate_user!

  # GET /entries
  # GET /entries.json
  def index
    @entries = current_user.entries

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @entries }
    end
  end

  # GET /entries/1
  # GET /entries/1.json
  def show
    @entry = current_user.entries.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @entry }
    end
  end

  # GET /entries/new
  # GET /entries/new.json
  def new
    @entry = Entry.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @entry }
    end
  end

  # GET /entries/1/edit
  def edit
    @entry = current_user.entries.find(params[:id])
  end

  # POST /entries
  # POST /entries.json
  def create
    @entry = Entry.new(params[:entry])
    @entry.user = current_user

    respond_to do |format|
      if @entry.save
        format.html { redirect_to @entry, notice: 'Entry was successfully created.' }
        format.json { render json: @entry, status: :created, location: @entry }
      else
        format.html { render action: "new" }
        format.json { render json: @entry.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /entries/1
  # PUT /entries/1.json
  def update
    @entry = current_user.entries.find(params[:id])

    respond_to do |format|
      if @entry.update_attributes(params[:entry])
        format.html { redirect_to @entry, notice: 'Entry was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @entry.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /entries/1
  # DELETE /entries/1.json
  def destroy
    @entry = current_user.entries.find(params[:id])
    @entry.destroy

    respond_to do |format|
      format.html { redirect_to entries_url }
      format.json { head :no_content }
    end
  end
end

执行以下命令启动应用:

1
2
rake db:migrate
rails s

以前的版本用的CSS框架是Blueprint,这次使用比它更方便快捷的Bootstrap。Bootstrap建立在Less上,有个less-rails-bootstrap,不过在安装libv8时竟然要提示缺少Python,拜托!我用的是Ruby,要我装Python,啥意思啊!还是找个Sass版的Bootstrap吧。

下载sass-twitter-bootstrap

1
git clone https://github.com/jlong/sass-twitter-bootstrap.git

将sass-twitter-bootstrap/lib中的文件拷贝到app/assets/stylesheets/twitter目录下。然后将app/assets/stylesheets/application.css中的

1
*= require_tree .

修改为

1
*= require twitter/bootstrap

再在文件末尾添加:

1
body { padding-top: 60px; }

现在就可以使用Bootstrap来布局美化应用了。

使用showDialog()创建的ProgressDialog再次打开时进度条不变化

先来看段代码:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Copy Contacts"
        android:onClick="copyContacts" />
</LinearLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;

public class DialogActivity extends Activity {
    private static final int COPY_CONTACTS = 0;

    private ProgressDialog mProgressDialog;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    public void copyContacts(View target) {
        showDialog(COPY_CONTACTS);
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case COPY_CONTACTS:
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setTitle("Copy contacts");
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.setMax(100);
            new AsyncCopyContactsTask().execute();
            break;
        }
        return mProgressDialog;
    }

    private class AsyncCopyContactsTask extends AsyncTask<Void, Void, Integer> {
        @Override
        protected Integer doInBackground(Void... params) {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
                publishProgress();
            }
            return 0;
        }

        protected void onProgressUpdate(Void... progress) {
            mProgressDialog.incrementProgressBy(1);
        }

        protected void onPostExecute(Integer result) {
            mProgressDialog.dismiss();
        }
    }
}

第一次点击按钮会显示一个进度条,上面的数字处于变动当中,到达最大值后进度条会消失不见;再次点击按钮后,就会发现进度条上的数值和消失前相同,并且不再变化,而且进度条也不会消失。这是为什么呢?

Activity中有三个和对话框显示有关的方法:showDialog(),dismissDialog()和removeDialog()。其中showDialog()用于显示一个对话框;dismissDialog()使对话框消失,但仍然处于内存中,只是不显示而已,如果再次调用showDialog()方法,则缓存在内存中的对话框会重新显示,而不需要重新创建。removeDialog()使对话框消失,并从内存中将对话框清除,如果再次调用showDialog()来显示它,则在显示之前需要重新创建对话框。

在一个对话框的声明周期内,onCreateDialog()仅被调用一次,但onPrepareDialog()方法会每次被调用。比如,第一次调用 showDialog()方法显示某个对话框时,会先触发onCreateDialog()的执行,然后再触发onPrepareDialog()方法;如果将某个对话框 removeDialog()后再showDialog(),也会先触发onCreateDialog(),接着触发onPrepareDialog()方法;如果某个对话框是被dismiss()掉的,再调用 showDialog()方法来显示它时就不会触发onCreateDialog()方法的执行,只会触发onPrepareDialog()方法。

这是因为第一次调用showDialog()时,Android从你的Activity中调用onCreateDialog()方法,得到返回的Dialog对象。把当前Activity设置为该对话框的所有者,从而把对话框挂靠到Activity上,让Activity自动管理该对话框的状态。这样,下次调用showDialog()时就不用重新创建Dialog对象,而是复用旧的。

所以,解决再次打开ProgressDialog时进度条不变化的最好方法就是将mProgressDialog.dismiss()改成removeDialog(COPY_CONTACTS)。

注意:如果你决定在onCreateDialog()方法之外创建一个对话框,它将不会被附着到活动上。不过,你可以通过setOwnerActivity()把它附着到一个Activity上。

史蒂夫·乔布斯的辞职信

英文原文:http://www.apple.com/pr/library/2011/08/24Letter-from-Steve-Jobs.html

致苹果董事会和苹果社区:

我一直说,如果有一天我不能履行作为苹果CEO的职责和期望,我会第一个让你们知道。遗憾的是,这一天已经来临。

我在此辞去苹果CEO的职务。如果董事会同意,我将愿意担任董事长、董事或者苹果员工。

就我的继任者而言,我强烈建议执行我们的接班计划,任命Tim Cook为苹果的CEO。

我相信,苹果最辉煌和最具创新的日子就在前方。我期望在新的岗位看到它的成功,并为此做出自己的贡献。

在苹果,我结交了一些我一生中最好的朋友。谢谢你们大家,多年来能和你们一起工作。

史蒂夫