Java: Reading a pdf file from URL into Byte array/ByteBuffer in an applet.

I’m trying to figure out why this particular snippet of code isn’t working for me. I’ve got an applet which is supposed to read a .pdf and display it with a pdf-renderer library, but for some reason when I read in the .pdf files which sit on my server, they end up as being corrupt. I’ve tested it by writing the files back out again.

I’ve tried viewing the applet in both IE and Firefox and the corrupt files occur. Funny thing is, when I trying viewing the applet in Safari (for Windows), the file is actually fine! I understand the JVM might be different, but I am still lost. I’ve compiled in Java 1.5. JVMs are 1.6. The snippet which reads the file is below.

public static ByteBuffer getAsByteArray(URL url) throws IOException {
        ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();

        URLConnection connection = url.openConnection();
        int contentLength = connection.getContentLength();
        InputStream in = url.openStream();
        byte[] buf = new byte[512];
        int len;
        while (true) {
            len = in.read(buf);
            if (len == -1) {
                break;
            }
            tmpOut.write(buf, 0, len);
        }
        tmpOut.close();
        ByteBuffer bb = ByteBuffer.wrap(tmpOut.toByteArray(), 0,
                                        tmpOut.size());
        //Lines below used to test if file is corrupt
        //FileOutputStream fos = new FileOutputStream("C:\\abc.pdf");
        //fos.write(tmpOut.toByteArray());
        return bb;
}

I must be missing something, and I’ve been banging my head trying to figure it out. Any help is greatly appreciated. Thanks.

 


 

Edit: To further clarify my situation, the difference in the file before I read then with the snippet and after, is that the ones I output after reading are significantly smaller than they originally are. When opening them, they are not recognized as .pdf files. There are no exceptions being thrown that I ignore, and I have tried flushing to no avail.

This snippet works in Safari, meaning the files are read in it’s entirety, with no difference in size, and can be opened with any .pdf reader. In IE and Firefox, the files always end up being corrupted, consistently the same smaller size.

I monitored the len variable (when reading a 59kb file), hoping to see how many bytes get read in at each loop. In IE and Firefox, at 18kb, the in.read(buf) returns a -1 as if the file has ended. Safari does not do this.

I’ll keep at it, and I appreciate all the suggestions so far.

 

come from : http://stackoverflow.com/questions/637100/java-reading-a-pdf-file-from-url-into-byte-array-bytebuffer-in-an-applet

Matcher: groupCount()

/*
Group 0: 1
Group 1: 1
Group 2: null
Group 3: null
Group 0: 2
Group 1: 2
Group 2: null
Group 3: null
Group 0: 3
Group 1: 3
Group 2: null
Group 3: null
Group 0: 4.5
Group 1: 4.5
Group 2: .5
Group 3: null
*/
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MainClass {

public static void main(String[] av) {
String regEx = “[+|-]?(\\d+(\\.\\d*)?)|(\\.\\d+)”;
String str = “a b c d e 1 2 3 4.5 “;
Pattern pattern = Pattern.compile(regEx);
Matcher m = pattern.matcher(str);
while(m.find()) {
for(int i = 0; i<=m.groupCount() ; i++) {
System.out.println(“Group ” + i + “: ” + m.group(i)); // Group i substring
}
}
}

}

LVS+Keepalived 介绍

LVS+Keepalived 介绍

LVS

LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一。目前有三种IP负载均衡技术(VS/NAT、VS/TUN和VS/DR);
十种调度算法(rrr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq)。
Keepalvied
Keepalived在这里主要用作RealServer的健康状态检查以及LoadBalance主机和BackUP主机之间failover的实现

Click here to open new window CTRL+Mouse wheel to zoom in/out

IP配置信息:

  • LVS-DR-Master          192.168.2.166
  • LVS-DR-BACKUP          192.168.2.167
  • LVS-DR-VIP             192.168.2.170
  • WEB1-Realserver        192.168.2.171
  • WEB2-Realserver        192.168.2.172
  • GateWay                192.168.2.253
  • 浏览器与服务器交互原理以及用java模拟浏览器操作

    * 1,在HTTP的WEB应用中, 应用客户端和服务器之间的状态是通过Session来维持的, 而Session的本质就是Cookie,
    * 简单的讲,当浏览器向服务器发送Http请求的时候, HTTP服务器会产生一个SessionID,这个SessionID就唯一的标识了一个客户端到服务器的请求会话过程.
    * 就如同一次会议开始时,主办方给每位到场的嘉宾一个临时的编号胸牌一样, 可以通过这个编号记录每个嘉宾(客户端)的活动(请求状态).
    * 为了保持这个状态, 当服务端向客户端回应的时候,会附带Cookie信息,当然,Cookie里面就包含了SessionID
    * 客户端在执行一系列操作时向服务端发送请求时,也会带上这个SessionID, 一般来说,Session也是一个URL QueryParameter ,就是说,session可以以Key-Value的形式通过URL传递
    * 比如,http://www.51etest.com/dede/login.php?PHPSESSIONID=7dg3dsf19SDf73wqc32fdsf
    * 一般而言,浏览器会自动把此Session信息放入Header报文体中进行传递.
    * 如果浏览器不支持Cookie,那么,浏览器会自动把SessionID附加到URL中去.
    *
    * 2,在这个例子中,以登陆这个功能点进行讲解.
    * 首先,我们登陆的页面是http://www.51etest.com/dede, 我们第一次访问这个页面后,可以从服务器过来的Http Response报文中的Header中找出服务器与浏览器向关联的数据 — Cookie,
    * 而且Session的值也在Cookie中. 于是,我们可以通过分析Set-Cookie这个Header中的参数的值,找到Seesion的Key-Value段.
    * 然后,我们再向服务器发送请求,请求URL为:post@@http://www.51etest.com/dede/login.php@@userid=admin&pwd=tidus2005&gotopage=/dede/&dopost=login
    * 服务器验证登陆成功了, 并且在此次会话变量中增加了我们登陆成功的标识.
    *
    * 3,增加一个广告定义
    * 增加一个广告定义其实就是一个添加数据的过程,无非是我们把我们要添加的数据通过参数的形式告诉指定url页面,页面获取后添加到数据库去而已.
    * 此url地址为:
    * post@@http://www.51etest.com/dede/ad_add.php@@dopost=save&tagname=test&typeid=0&adname=test&starttime=2008-05-29
    * 因为这个页面会先判断我是否登陆
    * 而判断的依据,前面讲了,就是根据我请求时的SessionID找到指定的Session数据区中是否存在我的登陆信息,
    * 所以我当然要把访问登陆页面时获取的SessionID原封不动的再发回去
    * 相当于对服务器说,这是我刚刚来时,你发我的临时身份证,我现在可以形势我的权利。
    *
    * 这就是整个Java后台登陆网站,然后添加数据的过程。

     
    /** 
     *  
     */ 
    package sky.dong.test; 
     
    import java.io.BufferedReader; 
    import java.io.InputStreamReader; 
     
    import org.apache.commons.httpclient.Cookie; 
    import org.apache.commons.httpclient.Header; 
    import org.apache.commons.httpclient.HttpClient; 
    import org.apache.commons.httpclient.NameValuePair; 
    import org.apache.commons.httpclient.cookie.CookiePolicy; 
    import org.apache.commons.httpclient.methods.PostMethod; 
    import org.apache.commons.httpclient.params.HttpMethodParams; 
     
    /** 
     * @author 核弹头 
     * Email:[email protected] 版权所有 盗版必究 
     * @since 2009-8-11 
     * @version 1.0 
     */ 
    public class HttpLoginTest { 
     
        public static void main(String[] args) { 
            String url = "http://discuzdemo.c88.53dns.com/logging.php?action=login&loginsubmit=yes&floatlogin=yes";//论坛的登陆页面 
            String url2="http://discuzdemo.c88.53dns.com/post.php?infloat=yes&action=newthread&fid=2&extra=&topicsubmit=yes&inajax=1";//论坛的发贴页面 
            HttpClient httpClient = new HttpClient(); 
            //httpClient.getHostConfiguration().setProxy("222.247.62.195", 8080); 
            httpClient.getParams().setCookiePolicy( 
                    CookiePolicy.BROWSER_COMPATIBILITY); 
            PostMethod postMethod = new PostMethod(url); 
            PostMethod postMethod2 = new PostMethod(url2); 
            NameValuePair[] data = { 
                    new NameValuePair("username", "123"), 
                    new NameValuePair("referer", 
                            "http://discuzdemo.c88.53dns.com/index.php"), 
                    new NameValuePair("password", "123"), 
                    new NameValuePair("loginfield", "username"), 
                    new NameValuePair("questionid", "0"), 
                    new NameValuePair("formhash", "fc922ca7") }; 
            postMethod.setRequestHeader("Referer", 
                    "http://discuzdemo.c88.53dns.com/index.php"); 
            postMethod.setRequestHeader("Host", "discuzdemo.c88.53dns.com"); 
            // postMethod.setRequestHeader("Connection", "keep-alive"); 
            // postMethod.setRequestHeader("Cookie", "jbu_oldtopics=D123D; 
            // jbu_fid2=1249912623; smile=1D1; jbu_onlineusernum=2; 
            // jbu_sid=amveZM"); 
            postMethod 
                    .setRequestHeader( 
                            "User-Agent", 
                            "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2"); 
            postMethod 
                    .setRequestHeader("Accept", 
                            "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); 
            // postMethod.setRequestHeader("Accept-Encoding", "gzip,deflate"); 
            // postMethod.setRequestHeader("Accept-Language", "zh-cn"); 
            // postMethod.setRequestHeader("Accept-Charset", 
            // "GB2312,utf-8;q=0.7,*;q=0.7"); 
            postMethod.setRequestBody(data); 
            try { 
                httpClient.executeMethod(postMethod); 
                StringBuffer response = new StringBuffer(); 
                BufferedReader reader = new BufferedReader(new InputStreamReader( 
                        postMethod.getResponseBodyAsStream(), "gb2312"));//以gb2312编码方式打印从服务器端返回的请求 
                String line; 
                while ((line = reader.readLine()) != null) { 
                    response.append(line).append( 
                            System.getProperty("line.separator")); 
                } 
                reader.close(); 
                Header header = postMethod.getResponseHeader("Set-Cookie"); 
                Cookie[] cookies=httpClient.getState().getCookies();//取出登陆成功后,服务器返回的cookies信息,里面保存了服务器端给的“临时证” 
                String tmpcookies=""; 
                for(Cookie c:cookies){ 
                    tmpcookies=tmpcookies+c.toString()+";"; 
                    System.out.println(c); 
                } 
                System.out.println(tmpcookies); 
    //            System.out.println(header.getValue()); 
                System.out.println(response); 
                NameValuePair[] data2 = { 
                        new NameValuePair("subject", "测试自动发贴"), 
                        new NameValuePair("message", 
                                "能否发贴成功呢?测试一下就知道了"), 
                        new NameValuePair("updateswfattach", "0"), 
                        new NameValuePair("wysiwyg", "0"), 
                        new NameValuePair("checkbox", "0"), 
                        new NameValuePair("handlekey", "newthread"), 
                        new NameValuePair("formhash", "885493ec") }; 
                postMethod2.setRequestHeader("cookie",tmpcookies);//将“临时证明”放入下一次的发贴请求操作中 
                postMethod2.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "gbk");//因为发贴时候有中文,设置一下请求编码 
                postMethod2.setRequestHeader("Referer", 
                        "http://discuzdemo.c88.53dns.com/forumdisplay.php?fid=4"); 
                postMethod2.setRequestHeader("Host", "discuzdemo.c88.53dns.com"); 
                // postMethod.setRequestHeader("Connection", "keep-alive"); 
                // postMethod.setRequestHeader("Cookie", "jbu_oldtopics=D123D; 
                // jbu_fid2=1249912623; smile=1D1; jbu_onlineusernum=2; 
                // jbu_sid=amveZM"); 
                postMethod2 
                        .setRequestHeader( 
                                "User-Agent", 
                                "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2"); 
                postMethod2 
                        .setRequestHeader("Accept", 
                                "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");//以上操作是模拟浏览器的操作,使用服务器混淆 
                 
                postMethod2.setRequestBody(data2); 
                httpClient.executeMethod(postMethod2); 
                StringBuffer response1 = new StringBuffer(); 
                BufferedReader reader1 = new BufferedReader(new InputStreamReader( 
                        postMethod2.getResponseBodyAsStream(), "gb2312")); 
                String line1; 
                while ((line1 = reader1.readLine()) != null) { 
                    response1.append(line1).append( 
                            System.getProperty("line.separator")); 
                } 
                reader1.close(); 
                System.out.println(response1); 
            } catch (Exception e) { 
                System.out.println(e.getMessage()); 
                // TODO: handle exception 
            } finally { 
                postMethod.releaseConnection(); 
                postMethod2.releaseConnection(); 
            } 
     
        } 
     
    } 
     
    

    以上代码完成一个登陆论坛后在指定的版块自动发贴的功能

     

    Bash 小技巧:给目录加上书签,快速切换目录

    原文地址:http://www.zhuoqun.net/html/y2011/1635.html

    当我们在命令行下面做开发的时候,很大一部分时间都浪费在了目录切换上面,相信不少人每天敲 “cd” 都敲得想吐。如果目录层次多一点,Tab 键也会饱受摧残。虽然 Bash 有内置的 “cd -”, “pushd” 和 “popd” 命令,但用起来都不是很顺手。

    昨天在 Twitter 上看到了 Huy Nguyen 的一篇文章:Quick Bash Tip : Directory Bookmarks,用几行简单的 Bash 脚本巧妙地给目录加上了书签,这样你就可以给最常用的那几个工作目录加上书签,不需要每次都敲 cd 了。

    昨天 Huy Nguyen 的这篇文章被发布到了 Hacker News 上,然后引来了很多人评论,其中不少评论都是非常有价值的,甚至还有人直接在评论里对 Huy Nguyen 的脚本做了改进。Huy Nguyen 看到评论之后也修改了自己最初写的脚本,并放到了 github 上(https://github.com/huyng/bashmarks),你可以用 git clone 命令把那个脚本下载下来,或者直接把源码复制到你的 ~/.bashrc 中。

    这个脚本只有三个命令:

    s – 给当前目录加上书签,书签名为 bookmark_name
    g – 跳到名为 bookmark_name 的书签
    l – 列出所有的书签

    如果想要删除已经保存的书签,请直接编辑 $HOME/.sdirs 文件删除相应的行。

    评论中也有人推荐了另外一个功能更为强大的名为 z 的脚本,这个脚本也在 github 上(https://github.com/rupa/z),有兴趣的可以试用一下。

    BTW, 经常在国外很多技术文章下面看到很多精彩评论,有些甚至比文章本身更加精彩,所以我很多时候都把每个评论仔细看完。评论里没有人说“沙发”,没有人说“傻 逼,这都不懂”,没有人说“菜鸟,早就有人写出同样的东西了!连z都没听说过,我只用它!”,大家都很礼貌,即使是有反对意见也都是逻辑清晰地列出自己的 理由。这让我非常惊诧和羡慕。

    作者发布一篇文章,读者提供反馈,然后作者改进原文,文章的改动历史和评论都成为文章不可分割的一部分,作者和读者都可以不断思考和获益。反过来 说,也正是因为这样的氛围才使得很多人敢于分享自己的看法,敢于写出自己的观点,敢于否认自己最初的看法并不断修正。真希望国内在别人文章下面评论的人也 可以有那样的耐心和礼貌,以及与作者对话的逻辑和才能。至少,要把别人的文章看完再发言。

     

    开发与研发(下)

    原文地址:http://www.zhuoqun.net/html/y2011/1590.html

    接上篇

    研发

    相对于开发来说,我个人更喜欢研发一点。研发和开发的一个不同之处就是研发有更多的“研究”成分在里面,也就是说研发的时候会有更多“光明正大”的 学习时间,这对于那些对技术本身有追求的工程师来说是很有吸引力的。有一些人做工程师是为了可以创造出好的产品,然后挣大钱或者改变世界;也有一些人做工 程师是因为对技术本身有兴趣,想要好好研究。可以凭借技术名利双收变身成功人士固然很有吸引力,但不关心世事钻研一些自己喜欢的东西也自有它的乐趣在。

    如果说开发产品是“输出”,那么学习思考就是“输入”,只有输出没有输入整个人就会废掉,完全沦为一颗螺丝钉。在很多公司尤其是那种经常加班赶项目 的公司,你每天都会处于很忙碌的状态,脑子里想的都是赶紧把指定的任务完成上线。因为时间紧,所以你在开发过程中遇到什么问题都是只求解决,没有心思和时 间去搞明白为什么会出现那种问题,在这样的工作状态下完全没有办法积累工作经验,看上去好像工作了五年,其实是工作了一年,然后重复了四年。

    做研发一般不会直接为产品贡献代码,更多做的是一些基础架构或者实验性的产品,所以它有几个很明显的好处。首先,很少开会。其次,没有产品经理。第 三,一般都会把质量放在第一位,时间不会特别紧。这是三个非常巨大的优势,这意味着你绝大部分时间都可以安心学习、思考、设计、编程,幸福指数会飙升。如 果你是做基础架构,那么代码质量就会有硬性要求,你不得不写得健壮、易用、松耦合并且易于调试,要花心思和时间细细打磨,对个人的能力提高、习惯养成和经 验积累都非常有帮助;如果你是做实验性的产品,那么你就有大量的机会和时间去调研最新的技术,而且最棒的是你可以在产品当中使用它们——这对于开发线上产 品的工程师来说是不太可能的,因为不成熟的新技术存在太多未知的风险。

    此外,做研发对工程师的素质要求很高,需要很好的技术基础、学习能力和研究能力——我把它看作是一个优点。从个人角度来说,我宁愿一家公司招聘非常严格需 要竭尽全力才可以进去,因为严格的招聘可以保证团队所有成员的质量,不用担心进去之后会“和臭棋篓子下棋”。既然选择去做研发,那么基本可以说明你是一个 对技术有追求的人,也肯定希望周围是一群和你一样的人,而不是连基础知识都不够熟悉的家伙。只有这样一群“互相看得起”的人在一块研究、学习、思考、切磋 才会其乐无穷,才能够产生更多创意,做出好玩的东西。

    当然,做研发也有不好的地方。只有大公司才有研发部门,这些公司一般都已经上市或者员工已经很多,你不太可能有机会一夜暴富。当你埋头做了几年研发 之后,某一天去参加同学会,发现大学时候那个数据结构不及格总是求你让他拷贝编程作业的张三衣着光鲜四处敬酒。他所在的公司刚刚上市,因为进去得早,现在 他变成了百万富翁而且荣升高层。于是你忽然开始怀疑自己当初的选择,连学习和编程的乐趣都变得很不真实。所以,如果你渴望建功立业,那么就不要选择做研 发,或者做几年研发之后就出来闯荡。成功需要的条件很多,而编程只是你的优势之一,只有这一个优势你需要太多的运气才可以得到你想要的。

    不过,我们也可以换个角度看。“乱世放不下一张安静的书桌”,现在到处都无比浮躁,有个地方可以让你安安心心做一些自己喜欢的事情已经非常难得,多 少人拼命挣钱就是为了可以和你一样做自己喜欢的事情。尽管那么多人在叫嚷“搞原子弹的不如卖茶叶蛋的”,但总有一些人愿意去追求人类最高财富——知识和艺术家般的技艺

    本来做研发成就感会少一点,作为一个 Twitter 的开发工程师看到那么多人在用 Twitter 肯定会特别开心,相比之下某个在 Google 做基础研究的工程师的成就感可能没那么强烈。不过在国内环境比较神奇,开发工程师非但成就感不多,反而会不少挨骂,还经常会有负罪感,相信做过邮件推广和 广告弹窗的工程师都深有体会。这样一来,研发工程师的“清苦”反而变成了一个优点,可以远离很多“不得不做”的违背良心的事情。

    相信很多工程师在入行之前是喜欢技术的,但是工作之后发现完全不是自己当初想象的那个样子,然后就变得失望麻木,不再对技术有热情。其实你可以把热 情延续下去,只不过要去做研发,而不是做开发。大部分由于兴趣而不是生计学习编程的人,内心真正渴望的都是去做研发,只不过没有人告诉他们开发和研发的巨 大差别。现在不少大公司都有自己的研发部门,有一些还成立了自己的研究院,想要一直做技术的同学不妨尝试一下。

    如何选择

    很多人在大学里之所以会选择计算机为自己的专业,并不是因为自己对计算机和编程有兴趣,而是因为计算机是“热门专业”,在毕业之后也浑浑噩噩地找了 一份工作进入了这个行业,做着自己并不喜欢的事情;还有一些人则是毕业之后找不到工作,然后看到一些培训机构的广告就去报名学习编程,希望广告上描绘的 “月薪过万”不只是一场梦。于是就有了越来越多的“代码民工”,在形形色色的大小公司做着又脏又累的工作,只为了“混口饭吃”。

    我并不想批评这些人,毕竟在这个大环境下有着太多无奈,逼得我们无从选择。对于这样一些只想找一份好工作的人,是被骗到这个行业中来的。仔细回忆一 下,这些年来我们看到的业界新闻,了解到的互联网公司文化,大部分都是有关诸如 Google, Facebook 等国外公司的;我们平时学习和使用的技术,几乎都是国外发明的。这让我们深信互联网就是那样美好,那些激动人心的东西触手可及,但请你关上电脑出门好好看 一下周围:这是在中国。互联网没有国界,但互联网公司有。Google 和 Facebook 这样的公司看上去离我们很近,我们每天也使用它们的产品,但国内的互联网公司可能要几百年之后才会有那样的气质和文化。所以如果你不幸误入了这个行业,还 是及早打算改行或者转型做管理比较好,这样就不需要再学习自己并不喜欢的“枯燥”技术了。

    对于那些“真的”对技术有兴趣的人,要么去做一个同时具备软件设计能力的开发人员,也就是富有创造力的 Hacker;要么去做一个自得其乐的研发工程师。虽然环境恶劣,但是任何东西都挡不住真正的热爱。在这个几乎人人都把金钱作为衡量标准的社会里,你真是得到了上天的眷顾,不仅能够以自己喜欢的事情谋生,而且收入还过得去。

    Hacker 是适合创业的,因为他拥有创造一个产品的全部能力。电影《社交网络》让很多以写代码为生的人产生了幻觉,Facebook 创始人传奇般的经历好像在向全世界宣布:世界是程序员的。很多人只是激动地看到扎克伯格的技术能力,但是却忽视了他的软件设计能力和对产品细节的重视程 度,好像只要埋头编程就可以做出 Facebook。除了优秀的技术能力之外,扎克伯格的思考能力和创造力同样出类拔萃,可以感受得到他眼里的世界是不一样的。我们的工程师又有多少人对生 活中的事物有独特而深刻的理解呢?独立思考也应该是 Hacker 的必备技能。

    很多工程师都觉得自己会编程,只是缺少一个“好的 idea”;很多非技术人员则觉得自己有一个“好的 idea”,但是缺少编程能力来实现。要做一个产品,好的 idea 和实现它的能力缺一不可。然而,我们可以看到最后成功的往往是那些非技术人员,因为他们可以清楚地看到编程是一件可以学习的事情;而工程师们则往往天真地 认为好的 idea 靠的是“灵机一动”,不会有意识地培养自己的观察能力和想象力。很多好的 idea 都是来自于平日对生活的敏锐观察和思考,然后这些点在某个时候忽然连成了一条线,把它简单地归结为“天才”是懒惰的做法。

    “成为一个 Hacker”和“做研发”,很难说二者哪一个更困难。Hacker 在技术上可以不是一流,但他运用技术创造产品的综合能力肯定是一流的;而研发更注重技术上的造诣和理解程度,关注的是深度而不是广度。如果想要做研发,那 么就要好好把基础知识研究透彻,比如数据结构、算法和网络协议等,不然很容易就会遇到瓶颈。我遇到过的每一位研发工程师都是技术上的大牛,在很多技术问题 上都有非常深刻的见解;他们会从本质上分析问题,而不只是纠结于语言细节。

    如果你想要通过自己的作品改变世界,那么就好好提高一下编程之外的能力,做一个好的 Hacker;如果只想埋头技术,就应该选择去做研发。不过,无论是想要做一个 Hacker 还是一个研发工程师,都需要长年累月地不断学习和思考。听上去好像非常辛苦,不过每一个热爱技术的人应该都会把学习和思考当作一种乐趣,而不是一种苦役。 如果你无法享受学习和思考的乐趣,那么还是不要在技术这条路上走下去了,你会活得特别累,并且毫无幸福可言。

    在这个充斥着“代码民工”并且缺乏“技术文化”的国度,我们只是关心怎么样可以活得更舒服,似乎忘记了编程本身所具有的迷人色彩。Joel Spolsky 说过,许许多多的人选择编程,首要的原因就是,他们宁愿将自己的时间花在一个公平有序的地方,一个严格的能者上庸者下的地方,一个只要你是对的就能赢得任 何争论的地方。此外,我觉得选择编程还可以获得最大限度的自由和独立。因为找工作的时候只需要凭借自己的编程能力,所以不需要见人说人话见鬼说鬼话,不需 要去结交权贵达人,不需要去为了所谓人脉去混圈子,也不需要看到邮件列表里有领导的邮件就去“顶”。平日里写写代码,其它时间喝酒吃肉,只交性情相投的朋 友,武侠小说里的畅快适意也不过如此。这种独立和自由是极为宝贵的,你可知道有多少人在醉酒之后哭喊“安能摧眉折腰事权贵,使我不得开心颜”?

    所以说,编程这件事情关乎公平,关乎自由,关乎美。而作为一个拥有编程能力的人,你可以亲手创造美。只有艺术家才可以创造美。希望有越来越多的人可以真正领会到编程的魅力所在,喜欢上这种艺术。正如 Raymond 所说,软件设计和实现应该是一门充满快乐的艺术,一种高水平的游戏。你需要用心。你需要去游戏。你需要乐于探索。

    黑客事业之未来, 全依赖我们今日之创造。

    最后推荐一些文章和书,这些文章和书大部分都与技术细节无关,它们讨论的是基于编程的令人心醉的文化,也适合非技术人员阅读。

    1. 如何成为一名黑客。所有学习编程的都应该多看几遍这篇文章,至少把 Hacker 和 Cracker 的区别弄清楚。

    2. 大教堂和市集。这是一篇关于 Linux 的经典文章。这里需要声明一下,我对那些 Windows 程序员没有偏见,只是我觉得作为一个以编程为职业的人,如果不参观一下 Linux/Unix 的深邃世界,未免太过狭隘。

    3. UNIX编程艺术。 这本书虽然名字叫做“编程艺术”,但里面并不讲授如何编程,而是全面展示了迷人的 Unix 哲学和文化。看完之后你会发现,那些看上去不修边幅、整日对着电脑屏幕编写代码的邋遢程序员,对于美竟然会有那么高的追求。“美在计算机科学中的地位,要 比在其他任何技术中的地位都重要,因为软件太复杂了。美是抵御复杂的终极武器。” 这本书的作者 Raymond 同样是《如何成为一名黑客》和 《大教堂和市集》的作者。

    4. 黑客与画家。这篇文章是 Paul Graham 写的,文中详细描述了黑客与画家的相似之处。这里所说的“黑客”和《如何成为一名黑客》中所说的“黑客”略有不同,但你可以看到他们很多共同点。本文也已 经被收录到 《Hackers and Painters》一书,该书的中文版《黑客和画家——Paul Graham文集》由阮一峰翻译,应该很快就会面世,我十分期待。

    5.创造者的品味。作者同样是 Paul Graham,文章观点独到,见解深刻,每读一次都有新的收获。

    6. 软件随想录:程序员部落酋长Joel谈软件。这本书是 Joel Spolsky 的精华文章结集,作者写文章写得非常有趣,擅长讲故事,前几天我翻译的那篇《程序员阿士顿的故事》就是他的手笔。本书由阮一峰翻译,翻译质量非常高,有兴趣的可以先去试读几篇

    7. About Face3交互设计精髓。本书是交互设计领域的经典著作,作者之一 Alan Cooper 原来也是知名程序员,被称为 “Visual Basic 之父”,所以这本书里面对程序员的批评还是很中肯的。另外,书中“设计体贴的软件”的核心思想非常棒,值得程序员好好阅读和思考。

     

    开发与研发(上)

    原文地址:http://www.zhuoqun.net/html/y2011/1573.html

    按:这几天我一直在写这篇东西,本来是胸有成竹,没想到后来越写越发现自己在这个题目下有太多话想说,而以我现在的能力又不能很好地概括总结,以至 于越写越长,文章结构也变得混乱,到后来修改的时候每次都要考虑好久才能下笔,所以决定拆成两部分来发,以便阅读。这篇写得我心力交瘁,质量不算好,凑合 着看吧。

    同样是写程序,不同的岗位工作内容不一样,对程序质量以及工程师的要求也不一样。程序开发大概可以划分成两类:开发和研发,相应也就有开发工程师和 研发工程师。很多人觉得做开发和做研发没什么区别,“都是一样对着电脑写程序啊”,但其实这两者是完全不一样的,下面我想抛开公司对员工的期望、社会对工 程师的需求等其它因素,单纯从国内互联网行业“工程师个人发展”的角度来说一下我个人对这两类工作的看法。

    开发

    开发一般是指产品开发,开发工程师直接为产品贡献代码。每个公司都有自己的产品线,拿 Google 来说吧,它有 Gmail, Chrome 等产品,每个产品都有很多开发工程师在后面支持,这些产品的开发、维护以及升级都是由相应的开发工程师负责的。由于开发工程师的工作直接关系到产品的质量 和在线情况,所以开发工程师的责任是很重的,他可能经常为了下个版本的发布而加班,为了产品的故障不得不在休假的时候打开电脑工作,甚至在过年的时候都会 接到领导的电话。所以你看到那些总抱怨加班太多,总是说自己是“IT民工”的,大部分都是开发工程师。在工程师当中,大部分人都是做产品开发的,毕竟公司 都是要靠产品盈利,招聘的大部分人也要直接为产品服务。

    做开发是很辛苦,但也有好处,因为需要对产品线负责,所以会是公司的核心,裁员对你威胁不大,如果你负责的产品恰好又是盈利产品的话,那么加薪、奖 金、集体出游等福利都不会少。如果你足够幸运地加入了一家快速发展的创业公司,说不定一下子就发家了。还有很重要的一点是,作为产品的开发人员可以看到自 己做的东西被那么多人使用,那是一种莫大的鼓励和肯定。

    苦闷的开发工程师

    尽管我很尊重开发工程师,但是我不得不承认,在国内大部分的公司,做开发工程师是没有前途的。首先,从微博到开心,有多少国内的产品不是山寨的?这 也罢了,最恶心的是有一些产品经理连产品设计图都懒得自己画,直接去截取别人产品的图片,假如我是一个人人网的开发工程师,每天看到产品经理把 Facebook 新上线功能的截图拿过来让我做,你让我如何对产品有荣誉感和认同感?而如果一个开发工程师对自己做的东西没有荣誉感和认同感,那么他坚守自己的岗位要么是 因为公司给的钱多,要么是因为他还没有找到下家。我个人认为,做开发最大的一个好处就是可以亲手实现一个“自己的作品”,就算平时很累,但最后完成它的时 候也还是会无比满足,这点被剥夺了之后,和饭店打工的服务员有什么两样?不一样是为了糊口吗?

    我不知道别人怎样,但我自参加工作以来就一直纠结于此——甚至开发的大部分产品都不好意思写上自己的名字;直到前不久有机会去做一个公司内部使用的 平台,才终于有个作品让自己觉得满意。相信很多开发工程师参加工作之前都对互联网上很多诸如Gmail, Facebook 等优秀的产品耳熟能详,自己也常梦想做出那样的产品,但万万没有想到的是,工作之后要学习的第一课就是“不要对自己做的东西有感情”——有了感情你就不愿 意做广告弹窗,不愿意看到它下线,不愿意为了短期利益伤害用户。与此同时,你还要继续听产品经理和老大们满怀激情地说“我们一定要让用户喜欢我们的产 品”。一个连开发工程师本人都觉得无聊的产品如何让用户真正喜欢呢?拿搜索巨人来说吧,Google 把社交网站看作是某种形式的娱乐而不是有用的工具,所以它会在社交领域失败,再牛的技术也无法遮盖情感上的空白。不过话说回来,这好像对于国内大部分的公司都不是问题,因为它们做一款产品只是想从用户那里拿到钱,如果以后用户流失了就下线,然后再开发一个新的。他们要的不是用户的长期感情,而是一夜情,开发工程师就是一夜情的工具。

    其次,国内几乎所有公司的技术流程和技术积累都做得很烂,大部分都只是片面地追求开发速度。我们在大学里受到的教育是“文档和注释很重要”,工作之 后才发现文档和注释是很稀有的东西,只有特别负责任的工程师才会挤时间去写。有一个很有意思的现象是,国内很多产品发布之后会特别自豪地说“XX 是我们开发团队在时间紧迫的情况下,封闭开发了X 天就完成的!只有最牛的工程师才能创造这样的奇迹!!多少个凌晨,XX写字楼上只有我们办公室的灯还亮着……”,然后你会觉得“好感动啊”,但冷静下来想 一想,这种拼命赶工做出来的东西质量会过硬吗?抛开产品质量不谈,没有时间写文档、没有时间写注释、没有时间做 code review, 没有时间做阶段总结……没有了这些,作为一个开发工程师你通过这个项目可以提升多少呢?所以好多开发工程师一开始是“代码民工”,过了几年还是“代码民 工”,而一个人年富力强的时间又有几年呢?怪不得那么多人说工程师和妓女一样,都是吃青春饭的。

    发展方向

    我个人认为,国内的开发工程师大概有三个发展方向:1.做管理。 2. 去做架构等与产品关系不那么紧密的研发。3. 提升其它方面的能力,做 “A+ Player”,然后自己创业。我对管理没有研究,也没有兴趣,这里就不说了。研发我会在下篇中细说,这里主要说一下第三条。

    为什么要关注代码之外的事情

    如果你只会埋头写代码,那么代码写得再好也可能不会是一个好的开发工程师。做开发不是做学术研究,你的任务不是去钻研技术,而是利用自己的技术把产 品做出来。尽管技术能力是基础,但如果无法把能力很好地应用到开发当中,那么你在团队中就没什么价值。举个例子,如果你不能很好地理解产品需求,那么就会 根据自己的理解去做技术方面的架构和编码,等到后来发现了再去修改就特别麻烦,这个时候技术能力强反而成了坏事,南辕北辙的故事我想大家都听说过。

    很多开发工程师属于那种“很本分”的人,从来不会提出意见,不关心产品形态和细节,只是去做产品经理提出的需求。我觉得别人把工程师叫做“代码民 工”也就算了,但是工程师对自己做的东西完全没有看法,那就是甘心沦落为民工了。这也有文化的原因,国内的公司都喜欢那些不爱抱怨的员工,因为他们听话而 且符合中国传统的价值观,但我更喜欢那些爱抱怨并且抱怨得有道理的人,因为国内(不只是互联网上面)粗制滥造的东西实在太他妈的多了,不抱怨才不正常,有 不满才会去思考如何做得更好。

    曾经听到有人谈论如何管理技术人员的时候说:“管理技术人员很简单,找一个比他们都牛的人就行了。” 这个人很了解工程师的脾气。工程师去判断其他工程师的时候,往往只看他的技术能力,觉得谁的技术好谁就最牛,其它的都无所谓。没错,技术牛的工程师写的代 码质量很高,但这只是一个方面而已,判断一个人在团队中是不是“很牛”要看他对团队对产品的整体贡献,而不是他的个人能力。他能很好地理解产品需求吗?能 很好地理解设计师的意图吗?和团队其他成员沟通顺利吗?写出的代码方便测试吗?会对产品提出好的建议吗?……这些都是判断一个开发工程师的标准,整体素质 越高在团队中的价值也就越大。

    所以要想做一个好的开发工程师,就要在写好代码的同时努力提高其它方面的能力。我知道大部分的工程师都喜欢和机器而不是和人打交道,所以遇到和产品 经理、设计师以及 QA 等部门协调沟通的时候就皱眉头。协调沟通确实是一件闹心的事情,但从另一方面来说,这是开发工程师的一个得天独厚的优势:你可以深入接触产品生产线上的所 有环节。需求评审的时候,你可以了解产品设计;开发界面的时候,你可以了解到视觉和交互设计;测试的时候,你可以了解到产品测试的细节;上线的时候,你也 可以多观察 Ops 同事的操作。如果你可以在协调沟通的时候学会换位思考,多从对方的角度看问题,多想一下“他为什么要这么做”,那么不知不觉就会对各个领域有一些了解,进 而发现原来每个领域都大有学问,就不会因为周围那些学艺不精的人而轻视他们所在的领域。

    学习设计

    对于工程师来说,测试和上线都是技术性的工作,和开发有很多相通的地方,而产品设计、交互设计和视觉设计等设计领域则比较陌生。对于自己不了解的东 西,我们的看法往往会趋于两个极端:要么是看得高深莫测,要么是看得一文不值。其实对于大部分的东西,只要不笨并且愿意下功夫学习,总是可以学会的。尽管 达到大师的水平可能需要传说中的“天赋”,但做到中等水平并不是特别困难。关于设计领域我一直在断断续续地在学习,到现在可能连略窥门径也算不上,这里只 是说一下我个人对设计的理解和心得,供大家参考。

    产品设计

    产品设计看上去比较简单,因为只要清楚自己想要做什么,那么自然可以慢慢勾勒出产品的形态和功能。要做好产品设计,就需要平时多下一些功夫,多研究 一下互联网上那些已有的产品,另外还需要多看一些诸如社会学、历史等“闲书”,举个例子,假如你想开发一款针对台湾用户的产品,那么了解一下台湾的文化肯 定是有必要的。总之,学习产品设计是慢功夫,没有什么速成的捷径,只有一点一滴地不断积累才能培养出敏锐的产品意识和深刻的洞察力。

    工程师学习产品设计有一个优势,那就是设计出来的产品是自己亲手实现的,你可以在实现的过程中不断重新反思原来的设计,然后加以修改和完善。这就好 像写文章一样,很多时候你写东西的时候并不清楚自己具体要写什么,但只要是下笔开始写,写着写着就会发现新的想法,写作的过程同时也是思考的过程。写作和 写代码很像,它们不仅可以表达想法,还可以创造想法。

    视觉设计

    很多工程师听到视觉设计会立刻退避三舍,觉得自己“不会画画”、“不懂配色”是不可能学习视觉设计的。诚然,视觉设计是需要更多艺术方面的基本功, 要完全掌握需要长期的训练,但我们还是可以从简单的学起,慢慢培养对设计的感觉。我个人在这方面所知非常有限,但是对视觉设计中的完美主义印象深刻。

    编程的时候,如果你的某行代码多了一个空行可能不会有什么问题,但在视觉设计中差了 1 个像素或者 10% 的透明度就是不可容忍的,很多设计师要求的都是 “Pixel-Perfect”——像素级别的完美。如果你不苛刻地追求完美,几个这样的“小瑕疵”就可以把整个作品毁掉。在我没有接触过视觉设计的时候 很难理解这一点,切页面的时候并不会特别仔细地去看设计图,而且为了降低技术难度会想当然地篡改设计师的意图,比如把一些微小的渐变用纯色代替,这是很无 知的做法。所以当设计师要求你做一个 1px 的修改的时候,即使会花掉你几个小时的时间也要听他的——只有这样才可以把界面做到百分之一百的完美。当然,设计师自己做不到完美另当别论。

    此外,作为一个页面设计师,从职位名称上来看他的最终作品应该是页面,而不只是视觉效果图。所以我觉得页面设计师应该精通 CSS,只有自己才可以精确实现自己的设计意图。对于那些没有受过设计训练的工程师来说,很难注意到页面上色彩、字体和渐变的细节,让他们精确实现一个设 计师的意图几乎是不可能的。精通 CSS 对于页面设计师来说并不算一个过分的要求,很多国外的设计师甚至可以自己用 PHP 写出产品原型,相比之下,国内的页面设计师进化得实在太慢了。

    交互设计

    交互设计是有关行为的设计,它更关注如何让产品更好用。举个例子,网页中一般都有很多超链接,当你把鼠标移动到超链接上的时候,鼠标形状会变成手型,暗示它是可以点击的,而且访问过的超链接和普通超链接的颜色是不同的,这样就很好地引导了用户行为。

    之前我一直把设计和“视觉设计”等同起来,但在深入了解了之后发现,对于互联网产品来说,交互设计要比视觉设计重要得多,而且交互设计相对于视觉设 计也更加有迹可循,对“感觉”要求没那么高,工程师完全可以把重点放在交互设计上。如果交互设计做得好,视觉设计遵循一些标准,那么完全可以做出一款“不 难看并且好用”的产品。没有人特别夸赞 Google 的产品“好看”,但它们都特别好用,Google 注重的是易用、快速,用户体验是很棒的。

    互联网行业的大部分页面设计师(Web Designer)都是学习平面设计出身的,但我觉得网页和软件设计更像是“显示器里面的工业设计”。很多平面设计师设计出的页面很好看,好像海报一样, 非常适合打印出来,但往往对交互方面重视不够。不太好看影响不会很大,但不好用就没有办法留住用户,而且有时候太注重外观的视觉效果反而会分散用户的注意 力进而影响产品的使用,这种 “eye candy” 是糟糕的设计。现在专门培养交互设计师的机构不多,我很希望对互联网有兴趣的工业设计师们到这个行业中来。

    关于设计我就说这么多,以后有机会再另外撰文专门探讨这些主题。值得一提的是,没有人可以真正把设计和开发全部精通,如果深入到细节,无论设计和开 发都会占用你大量的时间和脑力。单从设计来说,需要掌握的就有颜色、字体排印(Typography)、排版(Layout)、交互设计等,其中每一种技 能又涵盖无数细节,真的是要皓首穷经才可以在其中的某个领域成为大师。不过,即使你对这些知识只是有一个大致的了解,以后在看一款产品的时候也可以从功 能、交互、排版、页面代码、整体性能以及URL语义化等各个方面进行全面而细致的分析,明白它哪里做得好,哪里做得不好,而不是在那里想当然地说“真酷” 或者“狗屎”。真正了解什么是好的什么是差的,自己做东西的时候才会心中有数。

    一专多能的好处

    很多人可能会说:“一个人要是可以把所有事情都搞定,那还要其他人干嘛?我更相信团队的力量。” 没错,一个人就算从设计到开发都精通,如果只有他一个人做东西,开发效率也不会高。但是若你真的花心思去了解那些“与代码无关的事情”,你就会在写代码的 时候更多考虑到产品经理/设计师的想法,对产品经理/设计师疏忽的地方也可以及时提醒,让自己真正地融入整个团队。目标并不一定要实现,它是用来指明方向 的。开发工程师提高自己的产品意识和设计能力绝对不会是白费心血,不然的话你就只是一个实现产品的工具。你只会回答别人提出的问题,而好的问题要比好的答 案有价值得多。

    当你各方面能力提高得差不多的时候,应该就可以出来创业了(注意,我说的是创业,不是去创业公司打工)。因为对各个领域都有一定的了解,平时也经常 接触到各个领域的人,那么在创业的时候你就很清楚自己需要什么样的产品经理/设计师,知道具有什么样能力的产品经理/设计师才是最好的,这样就可以从一开 始就保证团队的质量和气质。很多互联网的业界前辈都说过“要招聘最好的人”,但问题是你如何判断一个人是不是该领域最好的呢?如果一个人对程序和设计一窍 不通,满脑子都是商业运作,你觉得他有可能找出最好的工程师和设计师吗?有一次和一个创业公司的CEO聊天,他和我讲他们“只招聘 Geek”,后来我才发现他其实根本不知道什么是 Geek,只是不知道从那里听到 Geek 这个词,他真正想要的应该是那种只知道写代码愿意没日没夜任劳任怨给他当牛做马的人。国内大部分的创业公司就是这样,老大们喊着技术密集型的口号,实际上 做着劳动密集型的事情,金玉其外,败絮其中。你可以和他们不一样。

    我自己并没有创业的经历,也没有创业的打算,所以对创业的理解可能很片面而且天真。但是我相信,找到最好的人永远都是关键,不然即便后来成功了,也 不过是多了一家靠人数取胜的血汗工厂。假如你选择成为移动互联网的独立开发者,对一个产品各个环节的全局把握也是有必要的。如果一个团队的每个人都能独当 一面并且可以很好地理解其他人的意图和专业技能,就算最后在商业上失败了,那也会是一个幸福的团队,比那些除了盈利之外找不到任何亮点的团队好太多。

    对产品经理的偏见

    在“开发”这个小节的最后,我想多说一点自己对产品经理这个角色的看法。在国内绝大多数公司,开发工程师的作用就是把产品经理的想法以代码的方式写 出来,“代码民工”这个称呼倒是很恰当。我对互联网行业的产品经理们一直感到很奇怪:他们没有能力把自己的想法实现出来,但是却几乎总是认为自己比其他人 更理解产品;当工程师对产品提出自己的意见的时候,他们往往会心中不屑但尽量保持礼貌挤出微笑说一句:“呵呵,工程师不是普通用户”。一个产品本来就是需 要很多人齐心协力一起完成的,产品经理和工程师的地位也是平等的,但是由于产品经理在工作流的上游,所以情况往往演变成工程师在为产品经理工作。如果产品 经理真的对产品负责也就罢了,可惜的是大公司的产品经理大部分是对KPI负责,小公司的产品经理大部分是对老板的个人好恶负责,结果就是工程师跟在产品经 理屁股后面做一些莫名其妙的事情。我接触到的几乎所有开发工程师都对他们的产品经理头疼不已,据他们说,好的产品经理就像真正的爱情,是极为稀有和可遇不 可求的。

    按照现在大部分公司的分工方式,产品经理是产品的总负责人。根据我个人的理解,产品经理之于产品,应该相当于导演之于电影,建筑师之于建筑。一个导 演如果对拍摄一窍不通,那么就很难控制镜头的表现力;一个建筑师如果对建筑材料和结构一无所知,就不可能把握建筑整体的感觉。那为什么那么多人会觉得产品 经理可以不懂技术不懂视觉设计,只需要写好文档画个框图然后交给别人去做就可以做出好的产品呢?本来是一个需要对各个领域融会贯通最难做得好的角色,现在 反而被很多人视为清闲的差事,不爱干活的人纷纷想要转去做产品经理,实在是可悲至极。

    我一直坚信好的工程师是不需要产品经理的。如果一个产品非要有一个什么产品经理的话,Google 的很多产品都不会出现,DropBox 这种只招聘工程师的公司也早就完蛋了。很多伟大的产品都是几个工程师想到一个点子然后慢慢做出来的,比如 Paypal 和 Google. 但需要说明的是,我讨厌产品经理并不是说我推崇“技术导向”——无论怎样产品都应该是让用户使用的,而不是用来炫耀技术的,只不过工程师不需要产品经理也 可以设计好一个产品并且实现它。产品设计不是产品经理的专利。

    想知道懂得设计的工程师没有产品经理的时候可以做出什么东西吗?去看一下 Livid 做的 V2EX 就知道了。在国内,设计和代码都有品味的网站可不多,我觉得 Livid 同学真是开发工程师的典范。

    接下来我们说一下“研发”。

    下半部分请看这里

    android 如何保存簡單的配置信息(SharedPreferences、File和Properties)

    我們知道在android的開發中,保存項目私有數據的存儲方式我們可以使用:SharedPreferences,File,SQLite,Network.四種方式,而要用到應
    用程序之間數據的共享要使用ContentProvider 。那今天我們只敘述一下僅僅保存一些我們登錄等的一些配置信息的數據,也就是說用到的數
    據量都不是很大,那麼我們就可以選擇SharedPreferences和File的方式。這裡只針對性的結合File和Properties進行敘述。
    一。SharedPreferences

    1. 它可以保存上一次用戶所做的修改或者自定義參數的設定,當再次啟動程序後依然可以保持原有的設置。這裡只說明一下使用方式。比如下
    面的代碼在OnCreate中使用:

    SharedPreferences mSharedPreferences = getSharedPreferences(“list”,MODE_PRIVATE);

    String mTempString = mSharedPreferences.getString(“config”,”default”);

    其中”list”是SharedPreferences的文件的名字,SharedPreferences是以鍵值映射的關係存放數據。不過多解釋,你也可以這樣用:

    SharedPreferences mSharedPreferences = getPreferences(MODE_PRIVATE);

    這樣默認的文件名是activity的名字。

    2. 退出activity的時候保存數據,在OnPause中使用:

    SharedPreferences mSharedPreferences = getSharedPreferences(“list”,    MODE_PRIVATE);

    mSharedPreferences.edit().putString(“config”,”data” ).commit();

    3. SharedPreferences 是以xml文件的方式自動保存的,在DDMS中的FileExplorer中展開/data/data/包名/shared-prefs下面就是SharedPreferences文件。

    4. SharedPreferences文件只可以用來存放基本的數據類型。
    二。結合File和Properties進行保存。

    A Properties object is a Hashtable where the keys and values must be Strings. Each property can have a default Properties list which specifies the default values to be used when a given key is not found in this Properties instance.

    1.所以,Properties對象也是一個哈希表,也是一個鍵值對應的關係,因此和上面的操作相似。下面看具體的程序。

    public class File_ByProperties extends Activity {

    private boolean mStatus;

    private TextView mShowStatus;
    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    mShowStatus = (TextView) findViewById(R.id.show);

    load();

    }
    private void load() {

    // TODO Auto-generated method stub

    Properties mProperties = new Properties();

    try {

    FileInputStream mInputStream = openFileInput(“configuration”);

    mProperties.load(mInputStream);

    mStatus = Boolean.valueOf(mProperties.get(“status”).toString());

    mShowStatus.setText(“the status is : ” + mStatus);

    } catch (FileNotFoundException e) {

    // TODO Auto-generated catch block

    System.out.println(e.toString());

    } catch (IOException e) {

    System.out.println(e.toString());

    }

    }
    @Override

    public boolean onKeyDown(int keyCode, KeyEvent event) {

    // TODO Auto-generated method stub

    if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {

    mStatus = !mStatus;

    mShowStatus.setText(“the status is : ” + mStatus);

    }

    return super.onKeyDown(keyCode, event);

    }
    @Override

    protected void onPause() {

    // TODO Auto-generated method stub

    super.onPause();

    Properties mProperties = new Properties();

    if (mProperties.containsKey(“status”)) {

    mProperties.remove(“status”);

    }

    mProperties.put(“status”, String.valueOf(mStatus));

    try {

    FileOutputStream mOutputStream = openFileOutput(“configuration”,     MODE_WORLD_WRITEABLE);

    mProperties.store(mOutputStream, null);

    } catch (FileNotFoundException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }
    }

    2. 在DDMS中的FileExplorer中展開/data/data/包名/files,可以查看到該文件。
    三。你還可以將一個靜態的文件放到res/raw/下面,然後通過getResources().openRawResource(R.raw.文件);來得到一個InputStream對象,然後讀取文件的內容。

    开发 Eclipse 插件

    简介: 在本文中,David Gallardo 向您展示了如何使用 Plug-in Development Environment 的代码生成向导来创建 Eclipse 插件。您将学到如何在运行时工作台中运行和调试插件,并且在 Eclipse 中安装完成的插件。David 还研究了与打包插件相关的问题 ― 包括维护版本信息、以插件片段的形式更新功能,以及组合插件来创建完整的功能部件。

     

    基于插件的体系结构

    Eclipse 平台是 IBM 向开发源码社区捐赠的开发框架,它之所以出名并不是因为 IBM 宣称投入开发的资金总数 ― 4 千万美元 ― 而是因为如此巨大的投入所带来的成果:一个成熟的、精心设计的以及可扩展的体系结构。Eclipse 的价值是它为创建可扩展的集成开发环境提供了一个开放源码平台。这个平台允许任何人构建与环境和其它工具无缝集成的工具。

    工具与 Eclipse 无缝集成的关键是插件。除了小型的运行时内核之外,Eclipse 中的所有东西都是插件。从这个角度来讲,所有功能部件都是以同等的方式创建的。从这个角度来讲,所有功能部件都是以同等的方式创建的。

    但是,某些插件比其它插件更重要些。Workbench 和 Workspace 是 Eclipse 平台的两个必备的插件 ― 它们提供了大多数插件使用的扩展点,如图 1 所示。插件需要扩展点才可以插入,这样它才能运行。
    图 1. Eclipse Workbench 和 Workspace:必备的插件支持
    Eclipse Workbench

    Workbench 组件包含了一些扩展点,例如,允许您的插件扩展 Eclipse 用户界面,使这些用户界面带有菜单选择和工具栏按钮;请求不同类型事件的通知;以及创建新视图。Workspace 组件包含了可以让您与资源(包括项目和文件)交互的扩展点。

    当然,其它插件可以扩展的 Eclipse 组件并非只有 Workbench 和 Workspace。此外,还有一个 Debug 组件可以让您的插件启动程序、与正在运行的程序交互,以及处理错误 ― 这是构建调试器所必需的。虽然 Debug 组件对于某些类型的应用程序是必需的,但大多数应用程序并不需要它。

    还有一个 Team 组件允许 Eclipse 资源与版本控制系统(VCS)交互,但除非您正在构建 VCS 的 Eclipse 客户机,否则 Team 组件,就象 Debug 组件一样,不会扩展或增强它的功能。

    最后,还有一个 Help 组件可以让您提供应用程序的联机文档和与上下文敏感的帮助。没有人会否认帮助文档是专业应用程序必备的部分,但它并不是插件功能的必要部分。

    上述每个组件提供的扩展点都记录在 Eclipse Platform Help 中,该帮助在 Platform Plug-in Developer 指南的参考部分中。乍一看,尤其是 API 参考大全的 Workbench 部分,一开始会令人望而却步。我们不会深入了解众多可用扩展点的详细信息,而只是粗略地看一个简单插件及其组件。

    插件简介

    创建插件最简单的方法是使用 Plug-in Development Environment(PDE)。PDE 和 Java Development Tooling(JDT)IDE 是 Eclipse 的标准扩展。PDE 提供了一些向导以帮助创建插件,包括我们将在这里研究的“Hello, world”示例。

    从 Eclipse 菜单,选择 File=>New=>Other(或按 Ctrl-N),然后选择 Select 对话框左边的 Plug-in Development 向导。在 Select 对话框的右边,选择 Plug-in Project。按 Next。在下一屏上,输入项目名称;我使用了 com.example.hello。再次按 Next。在下一屏上,请注意,插件标识就与项目名称相同。使用项目名称作为插件标识可以将该插件与另一个插件的名称发生冲突的机会减到最小。再按一次 Next。下一屏让您选择是手工创建初始插件代码,还是运行代码生成向导。保留代码生成向导的缺省选项,选择“Hello, World”,然后按 Next,如图 2 所示。
    图 2. 选择“Hello, World”代码生成向导
    新插件

    下一屏要求一些附加信息。请注意这一屏上的信息:它包含了插件名称、版本号、提供者名称和类名。这些是关于插件的重要信息,我们将在稍后研究。可以接受向导提供的缺省值。按 Next。在下一屏幕上,接受包名、类名和消息文本的缺省值。选择“Add the action set to the resource perspective”复选框。按 Finish。

    如果接到通知:向导需要启用某些其它插件才能完成,那么按 OK。

    过一会儿,向导将完成,而在您的工作区中将会有一个新的项目,名为 com.example.hello,如图 3 所示。
    图 3. PDE 透视图:Welcome to Hello Plug-in
    PDE 透视图

    在 Package Explorer 中,工作台的左边是向导创建的一些东西的概述。大多数项都不引人关注:包括项目类路径中的许多 .jar 文件(这些包括插件和 Java 运行时所需的 Eclipse 类)、一个图标文件夹(包含了工具栏按钮的图形),以及 build.properties 文件(包含自动构建脚本所使用的变量)。

    这里最有意思的东西是 src 文件夹,它包含了插件和 plugin.xml 文件的源代码 ― plug-in.xml 是插件的清单文件。我们将先查看 plugin.xml。

    插件清单文件

    插件清单文件 plugin.xml 包含了 Eclipse 将插件集成到框架所使用的描述信息。缺省情况下,当第一次创建插件时,会在清单编辑器区域中打开 plugin.xml。编辑器底部的选项卡让您可以选择关于插件的不同信息集合。Welcome 选项卡显示了消息“Welcome to Hello Plug-In”,并且简要讨论了所使用的模板和关于使用 Eclipse 实现插件的提示。选择“Source”选项卡可以让您查看 plugin.xml 文件的完整源代码。

    让我们看看插件清单文件的各个部分。首先是关于插件的常规信息,包括它的名称、版本号、实现它的类文件的名称和 .jar文件名。
    清单 1. 插件清单文件 ― 常规信息

    <?xmlversion="1.0" encoding="UTF-8"?>
    <plugin
       id="com.example.hello"
       name="Hello Plug-in"
       version="1.0.0"
       provider-name="EXAMPLE"
      >
    
       <runtime>
          <library name="hello.jar"/>
       </runtime>

     

    接着,列出了我们的插件所需的插件:
    清单 2. 插件清单文件 ― 必需的插件

       <requires>
          <import plugin="org.eclipse.core.resources"/>
          <import plugin="org.eclipse.ui"/>
       </requires>

     

    列出的第一个插件 org.eclipse.core.resources 是工作区插件,但实际上我们的插件并不需要它。第二个插件 org.eclipse.ui 是工作台。我们需要工作台插件,因为我们将扩展它的两个扩展点,正如后面的 extension 标记所指出的。

    第一个 extension 标记拥有点属性 org.eclipse.ui.actionSets。操作集合是插件添加到工作台用户界面的一组基值 ― 即,菜单、菜单项和工具栏。操作集合分组了基值,这样用户可以更方便地管理它们。例如,我们的 Hello 插件的菜单和工具栏项将出现在 Resource 透视图中,因为当在运行代码生成向导时,我们做了这样的选择。如果用户要更改它,可以使用 Window=>Customize Perspective 菜单选项从要在 Resource 透视图中显示的项中除去“Sample Action Set”。
    图 4. 定制 Resource 透视图
    Resource 透视图

    操作集合包含了两个标记:menu 标记(描述菜单项应该出现在工作台菜单的什么位置,以及如何出现)和action 标记(描述它应该做什么)― 尤其是 action 标记标识了执行操作的类。注:这个类不是上面列出的插件类。
    清单 3. 操作集合

       <extension
             point="org.eclipse.ui.actionSets">
          <actionSet
                label="Sample Action Set"
                visible="true"
                id="com.example.hello.actionSet">
             <menu
                   label="Sample &Menu"
                   id="sampleMenu">
                <separator
                      name="sampleGroup">
                </separator>
             </menu>
             <action
                   label="&Sample Action"
                   icon="icons/sample.gif"
    
                   tooltip="Hello, Eclipse world"
                   menubarPath="sampleMenu/sampleGroup"
                   toolbarPath="sampleGroup"
                   id="com.example.hello.actions.SampleAction">
             </action>
          </actionSet>
       </extension>

     

    许多菜单和操作属性的目的相当明显 ― 例如,提供工具提示文本和标识工具栏项的图形。但还要注意 action 标记中的menubarPath:这个属性标识了 menu 标记中定义的哪个菜单项调用 action 标记中定义的操作。有关这个和其它工作台扩展点的详细信息,请参考 Platform Plug-in Developer Guide,尤其是“Plugging into the workbench”章节(可以从 Eclipse 的帮助菜单中获取该指南)。

    由于我们选择了将插件添加到 Resource 透视图,于是生成了第二个 extension 标记。这个标记会导致当 Eclipse 第一次启动并装入我们的插件时,将插件添加到 Resource 透视图。
    清单 4. extension 标记

       <extension
             point="org.eclipse.ui.perspectiveExtensions">
          <perspectiveExtension
                targetID="org.eclipse.ui.resourcePerspective">
             <actionSet
                   id="com.example.hello.actionSet">
             </actionSet>
          </perspectiveExtension>
       </extension>
    </plugin>

     

    如果忽略这最后一个 extension,用户就需要使用 Window=>Customize Perspective 将插件添加到 Resource(或其它)透视图。

    插件源代码

    代码生成向导生成了两个 Java 源文件,打开 PDE Package Explorer 中的 src 文件夹就可以看到它们。第一个文件HelloPlugin.java 是插件类,它继承了 AbstractUIPlugin 抽象类。HelloPlugin 负责管理插件的生命周期,在更为扩展的应用程序中,它负责维护诸如对话框设置和用户首选项等内容。HelloPlugin 要做的事就这么多:
    清单 5. HelloPlugin

    packagecom.example.hello.actions;
    
    import org.eclipse.ui.plugin.*;
    import org.eclipse.core.runtime.*;
    import org.eclipse.core.resources.*;
    import java.util.*;
    
    /**
     * The main plugin class to be used in the desktop.
     */
    public class HelloPlugin extends AbstractUIPlugin {
          //The shared instance.
          private static HelloPlugin plugin;
          //Resource bundle.
          private ResourceBundle resourceBundle;
    
          /**
           * The constructor.
           */
          public HelloPlugin(IPluginDescriptor descriptor) {
                super(descriptor);
                plugin = this;
                try {
                      resourceBundle= ResourceBundle.getBundle(
                           "com.example.hello.HelloPluginResources");
                } catch (MissingResourceException x) {
                      resourceBundle = null;
                }
          }
    
          /**
           * Returns the shared instance.
           */
          public static HelloPlugin getDefault() {
                return plugin;
          }
    
          /**
           * Returns the workspace instance.
           */
          public static IWorkspace getWorkspace() {
                return ResourcesPlugin.getWorkspace();
          }
    
          /**
           * Returns the string from the plugin's resource bundle,
           * or 'key' if not found.
           */
          public static String getResourceString(String key) {
                ResourceBundle bundle= HelloPlugin.getDefault().getResourceBundle();
                try {
                      return bundle.getString(key);
                } catch (MissingResourceException e) {
                      return key;
                }
          }
    
          /**
          * Returns the plugin's resource bundle,
          */
          public ResourceBundle getResourceBundle() {
              return resourceBundle;
          }
    }

     

    第二个源文件 SampleAction.java 包含的类将执行在清单文件的操作集合中指定的操作。SampleAction 实现了IWorkbenchWindowActionDelegate 接口,它允许 Eclipse 使用插件的代理,这样不是在万不得已的情况下,Eclipse 就无需装入插件(这项优化工作使在装入插件时发生内存和性能方面的问题降到最低)。IWorkbenchWindowActionDelegate 接口方法使插件可以与代理进行交互:
    清单 6. IWorkbenchWindowActionDelegate 接口方法

    package com.example.hello.actions;
    
    import org.eclipse.jface.action.IAction;
    import org.eclipse.jface.viewers.ISelection;
    import org.eclipse.ui.IWorkbenchWindow;
    import org.eclipse.ui.IWorkbenchWindowActionDelegate;
    import org.eclipse.jface.dialogs.MessageDialog;
    
    /**
     * Our sample action implements workbench action delegate.
     * The action proxy will be created by the workbench and
     * shown in the UI. When the user tries to use the action,
     * this delegate will be created and execution will be
     * delegated to it.
     * @see IWorkbenchWindowActionDelegate
     */
    public class SampleAction implements IWorkbenchWindowActionDelegate {
          private IWorkbenchWindow window;
          /**
           * The constructor.
           */
          public SampleAction() {
          }
    
          /**
           * The action has been activated. The argument of the
           * method represents the 'real' action sitting
           * in the workbench UI.
           * @see IWorkbenchWindowActionDelegate#run
           */
          public void run(IAction action) {
                MessageDialog.openInformation(
                      window.getShell(),
                      "Hello Plug-in",
                      "Hello, Eclipse world");
          }
    
          /**
           * Selection in the workbench has been changed. We
           * can change the state of the 'real' action here
           * if we want, but this can only happen after
           * the delegate has been created.
           * @see IWorkbenchWindowActionDelegate#selectionChanged
           */
          public void selectionChanged(IAction action, ISelection selection) {
          }
    
          /**
           * We can use this method to dispose of any system
           * resources we previously allocated.
           * @see IWorkbenchWindowActionDelegate#dispose
           */
          public void dispose() {
          }
    
          /**
           * We will cache window object in order to
           * be able to provide parent shell for the message dialog.
           * @see IWorkbenchWindowActionDelegate#init
           */
          public void init(IWorkbenchWindow window) {
                this.window = window;
          }
    }

     

    运行和调试插件

    当开发 Eclipse 的插件时,必须停止 Eclipse 并用新的插件重新启动它以便进行测试和调试,这很笨拙。幸好,Eclipse PDE 提供了一个自托管(self-hosted)的开发环境,它让您无需将插件安装在工作台的单独实例中即可运行。

    要运行 Hello 插件,选择 Run=>Run As=>Run-time Workbench 来启动另一个 Workbench 实例,而该实例添加了插件的菜单选项和工具栏,如图 5 所示。
    图 5. 在运行时工作台中运行的 Hello 插件
    Hello 插件

    我们可以通过单击工具栏按钮或从“Sample Menu”菜单激活插件。任何一种方法都会生成一个框,其标题是“Hello Plug-in”,内容是“Hello, Eclipse world”,以及一个 OK 按钮,按该按钮可以关闭这个框。

    通过选择 Run=>Debug As=>Run-time Workbench,按类似的方法调试插件。这次,当插件在第二个工作台实例中运行时,我们可以在最初的工作台中单步执行源代码,以及检查变量等。

    一旦插件经过测试并准备发布,我们就需要将它适当打包,以便在 Eclipse 中安装。

    打包插件

    Eclipse 在启动时会查看其插件目录来确定要装入哪些插件。要安装插件,我们需要在插件目录中创建一个子目录,并将程序文件和清单文件复制到那里。建议目录名称能表示插件的标识,并且后面跟下划线和版本号,但是这种做法不是必需的。假设 Eclipse 安装在 C:\eclipse 中;我们要创建一个目录:

    C:\eclipse\plugins\com.example.hello_1.0.0.

    按照 Java 程序的标准,我们的程序文件需要归档到 .jar文件中 — 我们的插件清单文件,您也许记得它包含这个项:

       <runtime>
          <library name="hello.jar"/>
       </runtime>

     

    要创建 hello.jar 文件,我们可以通过突出显示项目名称,并从 Eclipse 菜单选择 File=>Export,以导出插件文件。选择 JAR 文件作为导出方式,按 Next,然后浏览到我们为它创建的目录。下一步,我们还需要将 plugin.xml 文件复制到这个目录。也可以使用File=>Export 菜单选项(但请要记住选择 File System 作为导出目的地)。

    这就是安装插件所需的全部操作,但您将需要停止并重新启动 Eclipse,以便能识别这个新的插件。从帮助菜单中选择“About Eclipse Platform”,可以找到关于已安装插件的信息,包括版本号。在出现的屏幕上有一个按钮是 Plug-in Details;向下滚动列表来寻找 Hello 插件及其版本号。

    更新插件版本

    在目录名称中包含版本号的目的是允许在同一台机器上共存某个插件的多个版本(每次只装入一个版本)。我们可以通过创建一个 Hello 插件的已更新版本来看看这是如何工作的:例如,将 plugin.xml 文件中的版本号更改成“1.0.1”,然后将 SampleAction.java 中的文本更改成“New and improved Hello, Eclipse world”。从 Eclipse 菜单中选择 Project=> Rebuild All。下一步,将项目文件以 JAR 形式导出到新的插件目录,例如,com.example.hello_1.0.1。将修订过的 plugin.xml 文件复制到同一个目录中。当停止并重新启动 Eclipse 时,只会装入已更新的插件。

    插件片段和功能部件

    Eclipse 由插件组成,但在开发 Eclipse 的插件时,还要慎重考虑另外两个级别的组件 ― 插件片段和功能部件。

    插件片段(如名称所暗示的)是完整插件的组成部分 ― 目标插件。片段提供的功能与目标插件的功能合并。片段可以用于将插件本地化成各种语言;在无需形成一个全新发行版的情况下,以增量形式将功能部件添加到现有插件,或者提供特定于平台的功能。在许多方面,片段与插件一样。主要的区别就是片段没有插件类 ― 片段的生命周期由其目标插件管理。此外,片段的清单文件叫作 fragment.xml,它列出了目标插件的标识和版本号,以及片段的标识和版本号。

    另一方面,插件功能部件根本不包含编码。在 Eclipse 体系结构术语中,功能部件是将一组相关插件打包到完整的产品中。例如,JDT 是包含了象 Java 编辑器、调试器和控制台这样的插件的功能部件。名为 feature.xml 的清单文件描述了一个功能部件归档文件。在其中,该清单文件包含了对该功能部件所包含的插件和其它资源的引用、关于如何更新该功能部件的信息、版权信息和许可证信息。

    在 Eclipse 中,主功能部件设置了 Eclipse 平台的外观。主功能部件旨在确定诸如给予 Eclipse 其身份的闪屏和其它特征之类的东西。Eclipse 只允许一个主功能部件。用这种方式,通过创建一组插件,将它们打包到功能部件中,并且使这个功能部件成为主功能部件,就可以重新创建 Eclipse 的品牌,并将它用于创建全新且不同的产品。如果从 Eclipse.org 下载,缺省主功能部件是eclipse.org.platform

    后续步骤

    在插件的介绍里我们只是稍微了解一些插件的必要用法。学习插件的更多知识的最佳参考资料是 Plug-in Developer’s Guide,可以从 Eclipse 中的帮助菜单中获得该指南。该文档包含了编程指南、Eclipse API 和插件扩展点的参考大全、Eclipse.org 上可用的编程示例的指南,以及常见问题列表。另一个优秀参考资料是 Eclipse 本身的源代码。根据您的兴趣,您也许想要查找一些示例,以了解不同工作台功能部件(如视图和编辑器)是如何扩展的,或者如何使用 SWT(Eclipse 图形 API)。此外,下面的参考资料可以帮助您学到更多知识。

     

    参考资料

    关于作者

    David Gallardo 是一名独立软件顾问和作家,他的专长是软件国际化、Java Web 应用程序和数据库开发。他成为专业软件工程师已经有十五年了,他拥有许多操作系统、编程语言和网络协议的经验。他最近在一家企业对企业电子商业公司 TradeAccess, Inc 从事先进的数据库和国际化开发。在这之前,他是 Lotus Development Corporation 的 International Product Development 组中的高级工程师,负责开发为 Lotus 产品(包括 Domino)提供 Unicode 和国际语言支持的跨平台库。可以通过 [email protected]与 David 联系。

     

    原文地址:http://www.ibm.com/developerworks/cn/java/os-ecplug/

    说说Stack Overflow和Quora

    今天看到一个新闻,Quora的中国克隆“知乎”得到了创新工场的投资。我之前从创新工场的投资经理张亮那里要到了一个知乎邀请码,最近一直泡知乎,觉得Quora类的产品有很多创新的亮点,所以比较感兴趣这类产品,忍不住就谈谈。

    Stack Overflow(以下简称SO)和Quora虽然都是知识问答类的网站,但是他们有共同的成功基因,也有本质的差别。

    先说SO和Quora共同成功基因,那就是:用户身份的真实性和唯一性。

    我看过不少关于SO和Quora如何成功的很多讨论,各种说法都有:如说SO的SEO很好,SO的积分激励机制如何如何,Quora的种子用户运营如何如何等等。其实这些原因都对,但不是最根本的原因。真正根本的原因在于:用户身份的真实性和唯一性。

    我们国内做社区网站,特别是知识型社区网站,核心的竞争力就是高质量的内容。如何获取高质量内容的手段自然多种多样,但是当社区成长起来以后,特 别是用户量急剧膨胀以后,很难避免社区的水化,大量低水平用户,特别是不负责任的用户随意的发帖、发言和评论,会破坏整个社区的内容质量,造成劣币驱逐良 币的现象,最终毁掉这个社区。

    这个现象是社区特别是BBS型社区的发展宿命,JavaEye也面临这个问题,早先几年采取的运营手段可以称之为“堵”,惩罚低水平用户的发帖, 恐吓不负责任的行为。但是随着社区规模不断的扩大,注册用户数量越来越多(已经超过70万注册用户,每天UV有25万以上),堵的手段成本越来越高,有效 性越来越低,因此必须改变运营方式。我想到的一个办法就是BBS产品创新,而且是大胆的产品创新。不过这个话题不是本文的主题,我以后会另外撰文。总之这 是一个社区需要解决的大问题。

    而SO和Quora很巧妙的解决了这个问题,办法就是:不提供网站账号注册功能,必须通过你在互联网的得到公认的唯一身份标识来登录网站,例如必 须使用你的Google帐户,Facebook帐户,或者Twitter帐户登录。这样的做法就保证了用户使用真实的身份,而且身份是唯一的。

    真实而且唯一的身份保证了你必须对自己的发言负责任,保证了你不可能养很多马甲,保证了你必须深思熟虑的行为,确保你自己的网络身份和信誉。特别 是通过Facebook帐户登录以后,可以连带你的好友关系一起导入进来,让你在这个网站的种种行为在你的众多好友的注视之下,更加不可能胡作非为,而且 有了好友关系以后,天然的可以增加网站的用户关系和黏性。

    很多人可能以为外国人就是网络素质高,不会像中国人那样网络随地大小便。其实不然,我去过很多国外的小论坛,用phpbb搭建的BBS里面,包括 TSS,照样是Help! Urgent!满天飞,和国内的论坛标题党绝对有的一比。但是一旦用你的真实和唯一身份登录,老外也马上规规矩矩起来,特别是Facebook,外国很多 人就是拿Facebook当作个人的网络通讯录来用的,这个东西有点像国内的MSN。你敢在你的MSN上面对着好友说那些很不着调的话吗? 所以那些SO和Quora上面的人确实就不会乱来。这就是真实而且唯一身份带来的威力。

    不过可惜的是,明知这一点,但国内的网站是绝对抄不到手的,因为国内没有一个具有像Facebook这种占据统治地位的真实身份和用户关系,并且 彻底开放的SNS好友关系平台的存在。这也是国内互联网行业的悲哀之处,国外的互联网基础设施实在太好了,AWS,PAAS,连SNS平台的海量用户都现 成,你只要有想法,会写代码,一堆基础设施拿来就用,三两下,一个创新型应用平地而起,这也是为什么硅谷创业公司这么流行Ruby on rails的原因(呀,跑题了)。

    总之,你没有这种海量的真实唯一用户导入,就算你的运营水平和产品做很好,也长不成SO和Quora。说到这里,Facebook现在真的成长为 下一代互联网应用的基础设施了,以后做网站真没有必要搞自己的帐户系统了,直接Facebook账号登录搞定,所以Facebook估值到800亿美刀不 算夸张,什么叫基础设施啊朋友,baidu这种烂货都400亿刀了。(又跑题了)

    所以,我并不太看好知乎这个产品的前景,目前严格的邀请准入制迟早要改变的,到时候用什么手段来解决这个问题呢? 我认为根本无解,或者说如果有解的话,产品形态必须做出重大的改变,那样的话,从一开始就不应该照着Quora的样子去长。

    然后说说SO和Quora的差异在于:知识的组织方式:以内容组织结构为基础,还是以用户关系为基础

    其实SO和Quora的差异还是很大的,从目前Google Adplanner来看,SO的UV和PV大致是Quora的5倍,考虑到SO只是一个纯技术网站,这个差距不可谓不小。但是这个不重要,重要的你怎么理解“知识”的组织方式:

    SO是一个标准的内容型社区,不需要登录就可以访问,用tag和search良好的组织整个网站的知识体系,在这个基础上添加用户关系和用户身 份。而Quora是一个标准的Social型的社区,不登录对不起,什么都看不到,登录以后你没有用户关系,还是对不起,什么都看不到,你获取信息要依赖 于你的用户关系之上,用户关系决定了你获取信息的效率。

    我们说问答这种知识沉淀的方式包含了两种需求: 沉淀知识需求和快需求:

    什么是快需求? 我就是来找答案的,别TMD的让我自己搜索半天,然后自己摸索,我就是等着你喂我,我不想费任何力气自己去找答案。其实绝大多数中国的论坛充斥的都是这种 快需求的帖子,而满足这种快需求最好的产品形态就是百度知道,没有唯二的了。我不知道如何形容我对百度知道的仰慕之情,以致于我抄袭了百度知道,做了一个 JavaEye问答频道,而且强迫性的不允许JavaEye用户在论坛发提问贴,必须给我到问答频道提问,而且我还老搞问答大赛,刺激答题者的热情,重点 不在于问题是否重复的问,而在于你答题是否及时有效,这就是旗帜鲜明的满足快需求而去的。

    SO也好,Quora也罢,可以满足一部分的快需求,但是从产品形态来说,他们主要不是为了满足快需求而量身打造的产品。快需求有一个很大的特 点,就是并不需要回答的内容是高质量的,反而对实效性要求可能稍微高一些,因此这种产品真的不需要特别的追求回答的质量多高,只要能够快速满足提问者的需 求就OK了。

    那么沉淀型需求呢? 沉淀型需求做的最好的产品其实是wikipedia,基本上你可以找到大多数各个领域的知识。当然wikipedia不能覆盖所有的知识领域,一些知识需 要通过问答的形态来组织,因此SO也做的不错。沉淀型需求对内容的质量有要求,越是高质量的内容,越能够加强社区的竞争力和黏性。所以你会看到不管是 SO,Quora还是很多知识型社区的一个基本诉求就是:内容的质量。

    这里,我们要更加深入的思考一个问题: 什么叫做高质量的内容? 你如何评判一个内容的质量高低与否?

    一篇学术论文,只有几个教授评审,然后永远的束之高阁,它是高质量内容否?
    一个李宇春发的微博,被几万粉丝转发,它是高质量内容否?

    你肯定本能的说,被束之高阁的学术论文是高质量内容,明星的脑残微博是低质量内容。错!我要告诉你的是在这两个例子中,被几万粉丝转发的明星脑残微博才是高质量内容。为什么?

    互联网内容的质量高低并不是由字数多寡来判断的,而是由它的传播范围和影响范围来决定的! 传播的越广,传播的受众越精确,影响的群体越多,内容的质量就越高,这是由互联网的媒体特性来决定的。

    从这一点来衡量,SO的内容组织架构显然更加有利于内容的传播,所以它的内容质量就更高,而Quora的Social组织架构对于内容的传播并不是特别有利,即便对于传播受众的精确性也未必能够做到比SO更高,因此我不认为Quora是一个比SO更好的产品形态。

    总之我个人不是很看好Quora这类产品的前景,当然我个人的看法也很可能出错,Quora的产品创新还是很多的,给了我很多的BBS产品创新的启发。

     

    原文地址:http://robbin.javaeye.com/blog/978077