[READING]Computer Systems: A Programmer's Perspective

Why Computer Science is so hard to learn?

People say

Most intelligent people research in Physics, the second most research in Math, and the third most (actually, the least) research in computer.

And in order to hide their inability, they attach science to computer, call it COMPUTER SCIENCE. We know it is just engineering.

In order to mask their intelligent disability, computer scientists have seperated the original simple thing into many complicated branches, and wrote many obscure books to describe their ideas. Good books are so scarce that every student find it hard to understand the very basic principles of computer science. Actually, computer science is simple, as simple as the book Computer Systems: A Programmer’s Perspective tells, but not less.

Computer Systems: A Programmer’s Perspective is such a good book that every one who wants to program should read. The two authors, Randal E. Bryant and David R. O’Hallaron are professors from Carnegie Mellon University. The language is straightforward and the authors’ explanations are incisive.
Anyway, this is a MUST-READ book for CS student.

Why read this book?

In fact, long long time ago, when I was a sophormore student studying software enginnering, I heard the book. But at that time, the book is too thick (over 1000 pages) for me, and English is a barrier. Until recently, when I read a book called Dark Time, I did not have the chance to recall the book. In Dark Time, the author mentions a concept KNOWLEDGE STRUCTURE. Put it simply, it means the best way to learn a field is to know the overall structure, but not many single isolated points. I highly agree with this idea. CSAPP is such a book that can help you understand the overall blueprint for computer science, or at least, programming.

Another reason I am planning to read this book is that it acts as a display stand for too long time on my colleagues desk, similar fate as “The Art of Computer Programming”. Then one day, I think I should change its fate. It should not only work as a stand, but enrich my understanding in computer programming.

In addition, in the last recent year, as I encountered bottleneck when solving framework or system level issues on Android, I get intense desire to totally understand how computer program works. So one of my expectations from reading the book is to improve my ability to handle system level design and development.

The last enchanting point is from the fame of the authors. They are from CMU, standing for the best computer programming education and research. So it should worth to read.

Knowledge Structure (GRAPH)

  • COMPUTER SCIENCE
    • Programming
      • Language
        • Use
        • Design
      • Design Pattern
      • Algorithms
      • Data Structures
      • Software Engineering
    • System Design
      • Parallel/Concurrency
      • Distributed System
      • Hardware
    • Security
    • Human Computer Interaction
      • UX
      • UI
    • Statistical Machine Learning
    • Data Mining
    • Artificial Intelligence

What to expect to learn from the book?

  • Computer science knowledge structure
  • hardware, software, OS, how program works, system design, etc.

What’s the prerequisite?

C programming, OS

Output

A series of blogs for reading notes.

Post Plan

  • Android ROM
  • System Architecture
  • Network
  • Programming Language
  • Distributed System

What does the book teach?

[READING]CS:APP, 1 A Tour of Computer Systems

This book trains you to a “Power Programmer”, not just a coder.

Classroom Tested Laboratory Exercises

  • Data Lab
  • Binary Bomb Lab
  • Buffer Overflow
  • Achitecture
  • Performance
  • Shell
  • Malloc
  • Proxy

1.1 Information is bits in context

1.2 Programs are translated by other program into different forms

1.3 It pays to understand how compilation systems work

1.4 Processors read and interpret instructions stored in memory

1.5 caches matter

1.6 Storage devices form a hierachy

1.7 the operating system manages the hard ware

1.8 Systems communicate with other systems using networks

1.9 important thems.

Android Design Prinicple

Android Design

Design

Design Principles

  • Enchant me
    • Delight me in surprising ways
    • Real objects are more fun than buttons and menus
    • Let me make it mine
    • Get to know me
  • Simplify my life
    • Keep it brief
    • Pictures are faster than words
    • Decide for me but let me have the final say
    • Only show what I need when I need it
    • I should always know where I am
    • Never lose my stuff
    • If it looks the same, it should act the same
    • Only interrupt me if it’s important
  • Make me amazing
    • Give me tricks that work everywhere
    • It’s not my fault
    • Sprinkle encouragement
    • Do the heavy lifting for me
    • Make important things fast

UI Overview

  • Home, All apps, and recents
  • System bars
    • Status Bar
    • Navigation Bar
    • Combined Bar
  • Notifications
  • Common App UI
    • Main Action Bar
    • View Control
    • Content Area
    • Split Action Bar

Styles

Patterns

Building Blocks

Downloads

Videos.

How to read

GRE 介绍一种英文阅读的方法: Structural reading。 简而言之,英文学术型文章都是八股文,写得很规范,可以总结为几个特定模式。 段落之间/意群之间: 整体结构 句间关系:并列结构,对比结构, 因果机制,让步转折结构 抓住keywords, 文章类型: physical sciences, biological sciences, social sciences, humanities

Article Types

现象解释/问题解决型

Beginning topic words:
phenomenon, fact; problem, difficulty, puzzle, question
Development
phen, prob, 1st explanation = kw1, 2nd interpretation = kw2 solutions, way
Author's Attitude
前负后正,多个解释
topic sentence
explain + 现象; way + 问题 (首段首末句子)
Conclusion sentence
少见

新老对比型

Begining topic words: has been, traditionally, until recently; however, reecntly, frequently, widely, many, however, now. most theories. popularity, common 发展:key words 老, however + 反驳 kw 新 评价 态度 attitude: 前负后正,多个解释 topic sentence: explain + 现象; way + 问题 (首段首末句子) conclusion sentence: 少见

论点说明型

Paragraph structure

并列

words: In addition, moreover, also, another, furthermore 分角度并列 时间并列 负评价并列

因果+ 机制

words: A determines B

Android Advanced

After more than 1 year experience on Android application development, it is time to dig deeper into more advanced topics.

Here I list some topics:

  1. Customize ROM, build from the source. Flash to Galaxy SIII.
  2. Android Security Survey
  3. Android Privacy Survey. Policy, Usablity and Technology.
  4. Go over Android documentation one again. Review new features.
  5. Read through the book: Android 内核剖析
  6. Survey Android Open source projects.
  7. JNI
  8. Game Development Survey.
  9. Scala on Android.

Android GUI 工作原理

Android View 工作原理 结构

Surface 对应内存中缓冲区。

SurfaceFlinger 服务进程 System Server 进程: SurfaceFlinger_client 客户端驱动: WindowManagerService APK 应用进程: WindowManger

Java 层 Native 层 重要的类及其关系 Sample

View 绘制原理

Reading Note: Giving the User Control over Android Permissions

paper link

Abstract

In this project, we investigate the possible options for users to restrict application permissions in a more ne-grained way and provide a proof of concept for a command-line tool that can remove permissions from applications before installation. We tested our tool with Android 2.2 (Froyo) on an emulated NexusS and a real Samsung Galaxy S GT-I9000.

Problem

Android OS provides zero-one solution, but users require more fine grained control over application permission:

  1. selectively accept permissions during installation process.
  2. revoke permission after application is installed.

Existing solution

  1. no perfect solution
  2. Google Play: permission management apps, like privacy blocker, PDroid Privacy Protection, etc.
  3. drawback: not very effective, crashes sometime, some blocking apps require root
  4. os level trust for blocking applicaiton

Our solution

modify the app before installation

  1. pros: no more trust than the app itself
  2. cons:
  3. approach: reverse engineering
    1. Unzip apk package 2. Remove permissions from AndroidManifest.xml 3. Modify application code to make sure it doesn’t crash because of permission issues 4. Zip modied apk package 5. Run on phone
  4. fads

#Implementation

  1. apktool -> smali, debugging is impractical, no enough material
  2. dex2jar -> java, recompiling is difficult.

implement a class which extends the class whose function requires the permission we are removing.

does not work with final classes, such as LocationManager.

replace all API call with a dummy static method call.

resources

  1. stowaway
  2. permission map: android-permissions.org, tells which permissions each API call requires.

Computational Advertising

Resources:

  1. 计算广告学 清华刘鹏公开课
  2. [Stanford open course]

什么是暗时间

读 刘未鹏的 《暗时间》所感

你或许有这样的体验, 你和你的朋友一起开始学习某项技能, 比如滑雪,或者打乒乓球, 你们之前的基础都为零。每次学习你们都是在一起的, 但是过了一段时间, 你会发现你朋友的技术比你好很多。他也并没有额外花更多的时间去练习,那是为什么? 他在这项技能上的天赋比你高么?或许是, 但是一个更为重要的原因是, 他的投入比你深,他在学习和练习时比你更专注。 如果用数学来描述的话, 投入=时间* 效率。 虽然你们投入的时间是一样的, 但是他的效率比你高,从而他投入的比你多, 自然学的比你好。 这也解释了为什么在高中班上很多学习好的同学其实并没有看起来比其他人勤奋多少, 相反,一些尖子生玩的更多。 那是什么影响了你的效率, 使得你的效率比别人要低呢?拿高中生的例子来说, 成绩好的学生多数会被夸脑子转的快,其实是他们勤于思考。 比如在课堂上老师讲了一道题,你能想到一种解法,然后就坐等老师讲解, 而他们想到一种解法之后,回去想第二种,第三种, 甚至更多。他们甚至会想如何将题目扩展成其他的形式。 所以看起来你们所花的时间是一样的,但是别人想的比你多很多,自然他能收获的就多很多。 换个角度来理解, 如果你能把自己的思考的效率提高到和他们一样, 你就相当于多出很多的时间, 这部分时间就可以成为暗时间。

学习运动技能是类似的。 我刚开始学习打台球的时候, 兴趣很浓, 所以打每一球都非常认真。 每打完一球之后脑海里面可以浮现出出杆时的感觉和球的运动路线, 对各球碰撞之后的运动轨迹也有大致了解。这样每一次出杆, 能知道哪里不对, 力度是大还是小,方向偏离多少,记住了下次就不会这样打。当然这比较费脑子, 但是技术提高确实很快。 过了初始阶段之后,兴趣逐渐衰退, 打球不会那么激动。 甚至打到后来, 瞎蒙,乱打一气。自然得不到进步。 再过一段时间, 大脑懒惰成惯性了, 更不去思考走位等问题,所以会越来越烂。 虽然某些比如瞄准等技能很难忘记。 说起走位, 一定要多思考。 也就是我之前常说的台球其实是一项智力运动。

我学习滑雪的经验告诉我, 任何时候都要非常认真的去学习。 因为很多时候机会只有那么少有的几次, 比如公众演讲。

当你能够培养出自己做任何事情都认真思考的习惯后, 你做任何事情都会做得非常好。 我们有时候会感慨, 某些知名人物在完全不相关的两个领域内都做得非常牛, 一直不明白为什么。比如憨豆先生, 演员是牛津大学物理学博士, 一个物理学博士去当喜剧演员, 完全扯不上关系嘛。 一个是非常严肃的学科, 一个是轻松的搞笑,没有共性。 但是我们仔细分析一下就知道, 憨豆先生的演员在私下里是一个非常严肃的人, 特别认真的人。他能够非常认真的去做物理, 也能非常认真的去多喜剧演员。而且本身在戏里他也是很严肃的。 另外一个我们都熟悉的喜剧演员周星驰先生在生活中也是非常严肃的。 不知道你们有没有注意到, 在周星驰所有的电影里面, 他的笑没有一次是发自心底的真笑。也就是说他的每一个笑其实都是他认真想过该笑成什么样子的。

所以如果某些人能够在一个领域里面做的非常好, 他们去做其他的领域也会非常出色的。 爱因斯坦的小提琴就拉的非常好。

要想更充分的利用暗时间, 就要提高效率。 这个效率和什么东西有关系呢? 答案是专注。 何为专注? 就是做一件事情的时候100% 的投入进去, 其他的什么都不要想。 实际上很难, 因为我们很难支配我们的大脑, 使得它一直处在某个任务上面。你在看这篇文章的时候,是不是同时也在想着中午吃什么? 要不要现在喝口水? 这些大脑潜意识里面的干扰会影响你, 让你分神,一旦你去切换到另外一个任务的时候, 需要花很多额外的时间切换回来。 所以要少做任务切换。

迅速进入专注状态,并且长时间保持专注状态, 是高效学习的两个重要条件

你是不是有这样的经历,上课听不懂, 想着课下多花时间自己去弄懂。或者某项技能, 比如和别人下棋,下不好, 想着自己私下里一个人多花时间琢磨琢磨。这源于一种心理恐惧和懒惰。等到了私下, 你并没有花时间去琢磨,或者你去琢磨了, 却不能浮现当时的场景和棋局。 所以如果你正在做一件事情, 就非常认真的去思考,不要把思考留到后面。

由《暗时间》所引起的读书计划

读 刘未鹏的 《暗时间》所感

一个偶然的机会, 看到同事桌上有一本叫《暗时间》(Dark Time)的书。 被书名和封面所吸引。 从高中开始对于天文以及天体物理的兴趣还好没有消失殆尽。看封面和书名以为是类似霍金《时间简史》一类的书。 充满期望的拿回来看了。 结果不是。 不过无心插柳, 反而容易柳成荫。虽说此书不是我期望的内容, 看了之后还是被深深吸引了。 尤其是其前言里面的文字深深的唬住了我。 四页的前言提到了几十本有关心理学,认知科学相关的书,给我非常大的压力。 看到作者介绍一些和人脑工作原理相关的书,我耐着性子看下来了。之前一直想写关于人脑和计算机类比分析的系类文章, 由于对人脑工作原理缺乏了解, 不能写下去。这次是个机会开始这项工作。 恰逢感冒在家休息, 花了3天时间认认真真从头到尾非常仔细的看完了。 看完之后才发现, 这居然是我第一本完整的认真的读下来的书。(写到这里有点碎碎念了:))

简单来说, 这是一本写给屌丝程序员看的夹杂着心理学,认知科学,成功学,玄学,计算机科学,数学,学习方法论以及作者本人的内心独白的科普读物。 貌似这个评价不高, 我还是很喜好这本书的,原因之一是作者年龄和我差不多大。 作者刘未鹏, 1984年生, 本科和硕士都在南京大学(学习机器学习的筒子们大概都知道, 就只周志华同学的那个学校)。在微软亚洲工程院工作,做机器学习相关工作。 从他的书来看, 虽然和我年纪差不多, 文笔也略显稚嫩,但是其个人能力和素质是我难以望其项背的。 从文字中可以看出来,他对很多现象是有比较多的思考的。

作者虽然喜欢写代码, 更喜欢码文字。 有一个专门的博客:[刘未鹏 MIND HACKS](http://mindhacks.cn/)。 上面有很多技术非技术的讨论。 同时在豆瓣上他也列出了很多他推荐的书。

提一下, 作者是C++的忠实用户, 是Top Language (Google Group) 的发起人。 翻译了很多C++方面的书籍。

我的读书列表。

看完《暗时间》,上面提到的很多书我都没有读过, 一口气把能买的都买了。

思考,认知科学

  1. [走出思维的误区:批判性思维指南(修订第9版)]
  2. [本能:为什么我们管不住自己?]
  3. [对”伪心理学”说不(第8版)]
  4. [找寻逝去的自我:大脑、心灵和往事的记忆]
  5. [数学与猜想(第1卷)]
  6. [数学与猜想(第2卷)]
  7. [怎样解题:数学思维的新方法]

关于脑科学的书, 看到曾今的笔记里还有两本,一并列在这里:

  1. HIERARCHICAL TEMPORAL MEMORY including HTM Cortical Learning Algorithms
  2. On Intelligence by Jeff Hawkins.

计算机科学

  1. [经典原版书库:深入理解计算机系统(英文版·第2版)]
  2. [你就是极客!软件开发人员生存指南]

下面3 本java 的书是之前买的, 没有来得及看, 一起列在这里作为这次阅读计划的一部分吧。

  1. Java解惑
  2. Java并发编程实战
  3. 编写高质量代码:改善Java程序的151个建议

还有一本流行书

  1. 创业者手册:教你如何构建伟大的企业(精益创业发起人全新力作,商业模式画布的绝佳应用)

《暗时间》

《暗时间》一书的内容从刘未鹏的豆列可以看出来:

主要包含三个方面: 认知科学, 心理学,决策。然后延伸到工作和学习当中如何做。 理论联系实际,写得比较大众科普。 可以这么说, 《暗时间》一书是作者对于上述几个方面的阅读总结, 同时也是我学习上述领域的引子。 作为引子,这本书是极好的,不在于它写的内容多么丰富,在于它勾起了我去学习相关领域的兴趣。从这个方面来说, 这本书于我是一本极好的书。

认知科学

对于没有阅读过任何正式的书籍和材料的我来说, 认知科学是一门研究大脑如何工作的学科。 这让我如此着迷。

我是如此聪明,但我却不知道我为何如此聪明, 也就是说我其实是不聪明的, 因为我连我为何聪明这么基本的问题都不知道。

大脑的主要工作包含两个方面: 思考和记忆。

思考

思考其实相当于在计算,在做逻辑推理。拿计算机做类比的话, 相当于CPU的功能。同CPU一样, 不同机器有配置不同的CPU, 运行速度不一样, 人脑也有思考快慢之分,比如我们经常跨某小孩脑子转得快。在面试工程师的时候,面试官也倾向于招那些脑子很灵活的员工。 另外一方面, 如同CPU的处理能力非常有限一样, 人脑的处理能力也非常有限。 在同一时间,基本是单线程处理。你或许不同意这个观点, 你可以试试同时速算两个稍微复杂的数学加减法,你行吗? 处理能力有限另外一层含义是指处理的问题规模不能太大。实际上人脑能同时处理的变化因素很少,同CPU一样, 只有很少的寄存器,大部分内容必须存储到另外的地方。你可以尝试一下,给你用7个以内的数排序,让你按顺序从大到小把数字念出来,你需要纸笔吗? 不需要。这是你一眼就可以完成的。 但是如果数字一多,比如100个, 你还可以吗? 不行了吧。得借助纸笔做些标记了。

思考的目的是为了更好的做决策。 要求我们有Critical Thinking. 记得在CMU 上学时, 我的老师Nicolas Christin 教会我们的就是如何从大师的paper中挑刺,写paper revieww。另外GRE的作文一部分就是critical analysis。 这一部分好好训练, 对于人的思考方式会是很大的改变。

记忆

记忆相当于计算机的memory。 HIERARCHICAL TEMPORAL MEMORY including HTM Cortical Learning Algorithms 这篇比较深入的从生物学角度解释人类记忆的机理。 人类记忆同计算机的存储设计有很多相似之处。 比如人的记忆有记忆曲线,过段时间会遗忘。 而计算机的缓存机制也是按照类似方式设计的, LRU算法对于长久不用的内容(记忆)会抛弃掉,相当于人的记忆如果长时间不回忆,就会遗忘。

学习

学习 = 记忆 + 思考 (= 程序)

学习的过程就是我们先去记忆一些知识, 在运用过程中加入自己的思考。《暗时间》提到学习的几个方面,包括:专注,效率,阅读,写作,知识结构等。

作者提到知识结构一词, 我非常认同。自认为从初中到高中数学非常容易,根本原因在于对于整体的数学知识结构把握的好。 这得益于初中遇到一个非常好的数学老师。 在他去启蒙下, 已经知道分析和综合方法是解决问题的两大基本方法。 数学中的基本知识点多多少少都忘的差不多了。就这最根本的方法没有忘记。 我估计这辈子也不会忘记了。 我敢说, 对于高中数学, 告诉我一个基本定理, 我能够把整个学科都建立起来,完全不需要记住这个定理,那个定理。

后来学习计算机科学就没有这种感觉, 太零散,学的是知识, 不是技能。从而走了很多弯路。 谴责一下坑人的大学教育。 读研的时候Nicolas 还是教了一些根本性的东西,那是非常好的。

总结

列了这么多书在这里, 压力很大啊。争取半年看完。

MIND MAP

Java Collections Framework

class- and interface hierarchy of java.util.Collection

class- and interface hierarchy of java.util.Map

Refences:

  1. Java collections framework from wikipedia.
  2. Java Collections Framework by John Zukowski. This short document(44 pages) gives a clear overview of Collection Framework. Also it provides some usage examples and exercises. Strongly recommeded.
  3. Magic with Merlin: Maintaining insertion order Working with the new linked HashSet and HashMap implementations. One of series by John Zukowski. Dig deep into Java Collection classes.

Opt out Android Permission

How to selectively accept permission list required by the applicaiton for user?

  1. Android Security architecture overview.
  2. Pros and cons analysis.

On Google groups for Android AOSP, there is a issue requesting for the feature. The major concern from Android Product team is the complexity of writing applicaitons. Bullshit.

Reference:

  1. Issue 3778: Feature request: Application permissions should be individually grantable by the user

Code Analysis

/dev/android/source/frameworks base/core/java/android/app/ApplicationPackageManager.java


    @Override
    public int[] getPackageGids(String packageName)
            throws NameNotFoundException {  
        try {
            int[] gids = mPM.getPackageGids(packageName);
            if (gids == null || gids.length > 0) {
                return gids;
            }
        } catch (RemoteException e) {   
            throw new RuntimeException("Package manager has died", e);
        }

        throw new NameNotFoundException(packageName);
    }

    ApplicationPackageManager(ContextImpl context,
                              IPackageManager pm) {
        mContext = context;
        mPM = pm;
    }

source/frameworks/base/core/java android/app/ContextImpl.java


    @Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {  
            return mPackageManager;         
        }

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }

        return null;
    }

services/java/com/android/server/pm/PackageManagerService.java

   public int[] getPackageGids(String packageName) {
        final boolean enforcedDefault = isPermissionEnforcedDefault(READ_EXTERNAL_STORAGE);
        // reader              
        synchronized (mPackages) {      
            PackageParser.Package p = mPackages.get(packageName); 
            if (DEBUG_PACKAGE_INFO)         
                Log.v(TAG, "getPackageGids" + packageName + ": " + p);
            if (p != null) {   
                final PackageSetting ps = (PackageSetting)p.mExtras; 
                final SharedUserSetting suid = ps.sharedUser;
                int[] gids = suid != null ? suid.gids : ps.gids;

                // include GIDs for any unenforced permissions
                if (!isPermissionEnforcedLocked(READ_EXTERNAL_STORAGE, enforcedDefault)) {
                    final BasePermission basePerm = mSettings.mPermissions.get(
                            READ_EXTERNAL_STORAGE);         
                    gids = appendInts(gids, basePerm.gids);
                }              

                return gids;   
            }                  
        }
        // stupid thing to indicate an error.
        return new int[0];     
    }

android.content.pm.PackageParser

365  public Package parsePackage(File sourceFile, String destCodePath,
366 DisplayMetrics metrics, int flags) {
367 = .;
369 = sourceFile.getPath();
370 if (!sourceFile.isFile()) {
371 Log.w(, "Skipping dir: " + );
372 = .;
373 return null;
374 }
375 if (!isPackageFilename(sourceFile.getName())
376 && (flags&) != 0) {
377 if ((flags&) == 0) {
378 // We expect to have non-.apk files in the system dir,
379 // so don't warn about them.
380 Log.w(, "Skipping non-package file: " + );
381 }
382 = .;
383 return null;
384 }
386 if ((flags&) != 0 && .) Log.d(
387 , "Scanning package: " + );
389 XmlResourceParser parser = null;
390 AssetManager assmgr = null;
391 boolean assetError = true;
392 try {
393 assmgr = new AssetManager();
394 int cookie = assmgr.addAssetPath();
395 if(cookie != 0) {
396 parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
397 assetError = false;
398 } else {
399 Log.w(, "Failed adding asset path:"+);
400 }

大用户时代

大用户时代下的互联网, 安全,稳定, 以及相关问题。

– 由github 被12306 拖垮说开去。

借助Mind Map来阅读和写作

Mind Map, 中文思维导图,相信很多人都听过大名。如果你还没有用过, 赶紧Google一下吧。

这个名字取得很好, 见词知意, 就是帮助你思考的图。简单来说就是通过把你脑海里的一坨搅在一起的东西帮你逐步捋顺了,因为你的脑子不够强大到在思考当前问题的某个细节时还能记住四五步之前的结论或者假设。 思维导图的目的就是帮你把你想到的东西写下来, 放在那里, 让你专注于目前的思考。 维基百科上面关于思维导图的解释是Mind Map:

A mind map is a diagram used to visually outline information. A mind map is often created around a single word or text, placed in the center, to which associated ideas, words and concepts are added. Major categories radiate from a central node, and lesser categories are sub-branches of larger branches.[1] Categories can represent words, ideas, tasks, or other items related to a central key word or idea. Mindmaps can be drawn by hand, either as “rough notes” during a lecture or meeting, for example, or as higher quality pictures when more time is available. An example of a rough mind map is illustrated. Other terms for this diagramming style are: “spider diagrams,” “spidergrams,” “spidergraphs,” “webs”, “mind webs”, or “webbing”, and “idea sun bursting”.[2][3] (A “spider diagram” used in mathematics and logic is different.)

所以看起来是一个网状的, 或者是树状的。特点是从一个点往外延伸发散出去。

下面是用来帮我写这篇文章的思维导图:

大概知道了什么是思维导图, 相信不用太多学习就知道怎么画了。 无非是选择你要思考或解决的问题作为中心点, 先把第一层相关的因素都列出来, 比如问题的前提条件, 假设, 结论,可能的解决方案等。 第二步对第一步每个因素进行分解,进入更细节的思考, 比如举例子, 列论据,pros and cons分析等等。 这个过程其实在中小学学习写作文的时候可能用过,当时的叫法是列提纲。 本质上类似, 都上帮助你思考的工具, 不必太拘泥于形式。 所以最直接的方式就是用纸笔。

当然我们已经进入电脑时代,讲究什么都需要无纸化,电子化。如果能够用计算机软件辅助我们来做这件事那最好不过了。 维基百科的这篇文章列出了很多受欢迎的软件以及在线服务:List of Mind Mapping Software. 对于我来说, 由于经常在家里和公司的电脑上进行切换, 在线服务明显是更为明智的选择。 15 Useful Online Mind Mapping and Brainstorming Tools 列出了15个受欢迎的在线服务。 起初我使用了mindmeister 的服务,他们家的服务很棒, 支持google 账号登陆, 所以也省去了注册的麻烦,而且mingmap文档是存在Google Drive 上的, 非常方便,界面漂亮。 使用起来很简单。 不幸的是没多久他们就要收费了, 而且比较贵, 专业版每个月需要$9.99 。 由于我认为mindmap不会是我使用率很高的软件,就转而寻找其他的替代方案了。

找到一个Wise Mapping 的在线服务, 虽然界面和mindmeister 稍逊,但操作也是非常简单。关键时我最看重的一点是它是在线的, 打开浏览器就能用, 还没有创建map 数量的限制。 同时它支持导入导出, 支持freemindmap 格式。 所以不用担心你被lockin到这个服务中去。 从操作界面上来看,可以用鼠标, 键盘操作, 还提供了丰富我菜单栏和各种图标供使用, 很友好。 下面是Wise Mapping 提供的一个学习指导, 在你创建帐号时系统自动帮你添加的思维导图:

想起那句谚语,

如果你手里有一把锤子, 所有东西看上去都像是钉子。

其实更应该是反过来,

如果你想钉一个钉子, 所有东西看上去都像是锤子。

当然这次我是拿着钉子找锤子的, 无意中找到mindmap这么个好锤子。 我的钉子是什么呢, 就是阅读和写作。 说是思考, 比较玄幻, 还是阅读和写作机会是天天会遇到的事情。 你读书, 总是怀着目的学习知识的。 当然这里的书不包括流行小说一类的纯娱乐读品。 要学习,总归要记笔记的。 怎么记, 学习最根本的是要了解一个东西的本质。 也就是知识结构。 知识结构用Mind map来勾勒非常合适的。 当然你也可以线性的把要点记录下来。 不过我认为mindmap更能帮助你看到东西的全貌。 关于如何用mindmap帮助我们写作, 我在前面有简单提到,就是如同我们中学作文写提纲一样。当你把提纲列出来之后, 你就会发现写作本身是件很轻松的事情了。 看, 我写这片介绍, 就是先用mindmap列出提纲。 在写的过程中基本没有本block在某个地方写不下去。 如果你有写作的欲望, 但是发现经常写着写着写不下去, 或者写跑题了, 就尝试使用mind map 列出你想写的。建议尽量列到细节,比如论证一个论点, 需要的论据1,2,3,说明某个现象,需要用到的例子,1,2,3等 都列出来。 写的时候就会非常顺畅了。

接下去如果有时间的话, 希望我能在博客里回顾一下GRE阅读分析的方法和技巧。 其实,在专业的论文阅读中,模式基本上是固定的,对于每种类型的文章, 大体的手法就那么几种, 通过练习,如果你能掌握这几种常见的模式, 读起书来就简单多了。相当于在你的脑子里很快形成一张关于文章或者书的Mind Map.

[未完待续]

Build android applications using maven

构建android项目的3种方式:

  1. Eclipse + ADT
  2. Android SDK tools + ant
  3. Maven + Maven android plugin.

其中1和2 是的文件结构相同。 第3种方式由于采用maven通用的文件结构和1.2 的方式有很大区别。 使用maven来构建android 工程有很多优势。

  1. 命令行下开发,对于真正的coder来说很方便
  2. 部署等自动化很方便,不需要额外写太多的ant脚本

实际上Android 源码的编译开始从ant转向gradle Using the new Build System

Tools

要使用Maven 来构建Android 项目, 需要按照下面3个开源工具。

  1. Maven 提供Maven 环境
  2. maven-android-plugin maven 针对Android 的插件,不用手动安装,由Maven托管。这也是Maven的nb之处。 不过需要手动设置一个环境变量,可参考链接。
  3. maven-android-sdk-deployer 如果已经安装过ADT等,这个项目帮助将原来下载的android.jar包copy到maven 的目录下。(~/.m2/repository) 随着 maven-android-plugin 项目的成熟, 第3个项目逐渐被deprecate。

  4. 选择安装 m2e-android, 如果你想使用Eclipse 来开发。

Reference Book Chapters:

Maven: The Complete Reference Chapter 14. Android Application Development with Maven

一些辅助项目。

  • BOOTSTRAP PROJECTS USING THE MAVEN-ANDROID-ARCHETYPES 快速创建Android项目模版,犹如Eclipse ADT 的wizard, 不过是命令行的,配置好参数, 一键创建Android 项目。这个项目是由STAND (akquinet Stack for Android Development)创建。还包含很多其他android项目: BOOTSTRAP INDEPENDENCY LOGGING TESTS INTEGRATION PLAY FEEDBACK ENTERPRISE.

解决Android 资源重用的问题, 使用Rindirect. 重新生成一份 orginal.package.R.java

The Mobile Web Developer's Tool Belt

The Mobile Web Developer’s Tool Belt

http://petelepage.com/presentations/2012/qcon/#2 ‘

Development Enviroment

  1. Sublime Text
  2. Codekit

Development Tools

Inspiration

  • www.mobile-patterns.com
  • pttrns.com

    Human Interface Guidelines*

  • Android’s Human Interface Guidelines
  • iOS Human Interface Guidelines
  • Developing Web Content for Safari
  • User Experience Guidelines for Windows Phone

Boiler Plates

  • jQuery Mobile
  • Bootstrap

Helpful Libraries: FT Fast Click

Open Device Lab

http://klick-ass.com/awesomeness/avoid-the-tamagotchis-a-list-of-open-device-labs/

Android 推送广告

OSGi

Implementations:

  • Filex
  • Equinox
  • Knoperfish
  • Concierge

Deployment

Bundle = class + metadata(imports/exports)

OSGi Specifications download: http://www.osgi.org/Download/Release5

OSGi for mobile

mBS Mobile

mBS Mobile is a carrier grade embedded OSGi solution integrated and optimized for a variety of smart phone platforms. It is built over ProSyst’s award winning and certified OSGi implementation mBedded Server Professional Edition. mBS Mobile provides highest availability, scalability and reliability and sets new standards for performance and memory efficiency through several unique technology innovations. Fully compliant to OSGi R4 Core, OSGi R4 Mobile and JSR 232 specifications Tightly integrated with target operating systems Contains fully featured remote management agent (based on OMA-DM) Adds platform support for Web Widgets (i.e. W3C, JIL, Opera, Bondi) applications Full security support with dynamic, manageable device policy Perfomance and footprint optimized For more information, see http://www.prosyst.com/index.php/de/html/content/46/Mobile-OSGi-Runtimes/. ‘

mBS Mobile for Android

http://www.prosyst.com/index.php/de/html/content/49/mBS-Mobile-for-Android/

Apache Felix

ezdroid

Android ADB

Components

  • server (pc)
  • client (pc)
  • daemon (Android devices or emulators)

serial number

The output for each instance is formatted like this

serialNumber state

Here is an example showing the devices command and its output:

$ adb devices
List of devices attached 
emulator-5554  device
emulator-5556  device
emulator-5558  device

Android UI Design

Google Official Design Guideline

pre-Honeycomb and post-Honeycomb comparision

3rd Party Design Reviews

Tools and references

Books

Open source projects to facilitate Android design and development

  • Android Bootstrap Bootstrap your next Android Application Android Bootstrap includes a full working implementation of Fragments, Fragment Pager, Account Manager, android-maven-plugin, RoboGuice 2, ActionBarSherlock 4, ViewPagerIndicator, http-request, GSON, Robotium for integration testing, API Consumption with an API on Parse.com and much more.

  • HoloEverywhere Bringing Holo Theme from Android 4.0 to 1.6 and above.

  • Android projects on Github is a community on Google+, which provides collections of nice Android open source projects.
  • gabrielemariotti/changeloglib Android Library to display your changelog

Nice App Collection

  • Google Currents
  • Google Play
  • Google Plus
  • Gmail
  • Google IO (open source)

Analytics App Collection

Libraries for developers by Desarrollo Droide

Chrome: 保护你的隐私, 骚年

用Chrome 的用户, 你们的使用数据, 浏览了什么网站, 点击了什么链接, 在搜索引擎里搜索了什么, 全都被Google, baidu, 等收集了。 醒醒吧,骚年。

Chrome 的插件

AdBlock

AdBlock for Chrome! Block all advertisements on all web pages, even Facebook, Youtube, and Hulu. It works automatically: just click “Add to Chrome,” then visit your favorite website and see the ads disappear!

Google Analytics Opt-out Browser Add-on

To provide website visitors more choice on how their data is collected by Google Analytics, we have developed the Google Analytics Opt-out Browser Add-on. The add-on communicates with the Google Analytics JavaScript to indicate that information about the website visit should not be sent to Google Analytics. If you want to opt-out, download and install the add-on for your current web browser. The Google Analytics Opt-out Browser Add-on is available for Microsoft Internet Explorer, Google Chrome, Mozilla Firefox, Apple Safari and Opera.

git not found after upgrading to mountain lion

After updating to Mountain Lion, many command line tools disappeared. The solution is described here: Git “Command Not Found” Error In Mountain Lion [Quickfix].

It may take a long time to download and install the huge XCode (1.6G, about 1hours for me)

Happy luck:)

THE COST OF FREE

Nowadays, may 2C software are using a free-ads mode. For end-user, the software costs no money at all. The usability and professioness of these software applications are as good as commercial products, or even better, for example, QQ, SOGOU Input Method,etc.

But is it really free to use these softwares?

拿输入法做例子, 国内现在有非常多家大公司在做输入法, 市场占有率很高的包括搜狗,QQ, Baidu, 谷歌, 微软的bing等。这些输入法无一不是免费的, 而且从我个人体验来说, 作为一个免费产品, 体验做的非常的棒。 大公司为什么做这个?他们当然不是大好人, 也不是公司的极客工程师们完美的英雄主义情节的产物。 究其根本原因, 作为商人, 无利不起早。 当然是有利可图他们才会花这么大的力气做这个产品。

作为输入法的普通用户可能要问, 输入法本身是免费的, 也很少看到输入法上有广告, 那他是怎么图利的呢?

输入法经济学原理是什么?

这是一个非常有意思的问题, 尤其是在这个大数据时代, 其实很多公司都做着类似于输入法这种为民牟利的事情。我们要警醒, 天下没有免费的午餐!

拿搜狗输入法来说, 它存在的最大目的是给搜狗浏览器带流量。 通过搜狗输入法下载搜狗浏览器的是不是大有人在? 我没干过, 不知道, 不过坊间传言是如此, 相信不是空穴来风。

另外一个目的, 手机用户数据。 其实很有可能, 你输入的每一个字符都上传到了搜狗的服务器上。其他的输入法也一样。 仔细想想, 这意味着什么?!

你可千万不要用这些输入法输入一些敏感关键词,小心被查水表。 另外, 小心你的银行卡号和密码, 还要小心给小三的留言。 古人说,隔墙有耳。现在, 你所敲打的每一个字都有可能成为呈堂证供, 小心,少年。

Java Global Try Catch

Java 为程序员提供了非常方便的错误处理。当有某段代码可能会发生错误, 比如读文件不存在, 所写的代码就不能正常工作。 这种情况下, 通常需要附加的逻辑来处理错误情况。 直接的方式是将可能会发生错误的代码段用try {}包起来。 在catch 中处理错误发生之后的逻辑, 使得整体程序不至于崩溃。

Java Exception Handling

Java 提供try catch 机制来捕获异常,方便程序员处理。

try {
  // code that may break
  //...
}
catch (Exception e){
  // to handle the exception here. 
  // Usaually, just print out the exception stacktrace. 
  e.printStackTrace();
}

一个简单的例子

下面的代码演示了try catch 的简单使用:

String filename = "/nosuchdir/myfilename";
try {
    // Create the file
    new File(filename).createNewFile();
} catch (IOException e) {
    // Print out the exception that occurred
    System.out.println("Unable to create "+filename+": "+e.getMessage());
}

Global try catch?

Java 程序员被要求在所有可能发生问题的代码段都要加上try catch. 幸好编译器有时会提醒你哪里需要添加。 对于一个不那么自信的程序员, 是不是需要在所有的代码块都加上try catch 呢? 天哪, 那样的代码还能用吗?能用的话, 即使程序不会崩溃, 也不能完成正常的功能吧。

在某些情况下, 你可能希望有一个全局的错误处理机制。 如果发生了某个你不能预料的错误,比如你的程序本来运行的好好的, 但是被某些杀毒软件强行夺取了某个文件的访问权限, 这时使得你的程序处于一个不稳定的状态,然后程序崩溃了。 这怎么办? 你会难道要责怪用户吗? 或许有更好的选择。

当这种不能预料的错误发生时, 你希望能有一个全局的默认的错误处理机制,让程序在错误发生时依然可以做些恢复工作, 至少可以提醒用户为什么出错了。

这就是Global Exeption Handler 的作用。

Java 对于线程提供了一个方法 setDefaultUncaughtExceptionHandler

下面是其说明

public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

Set the default handler invoked when a thread abruptly terminates due to an uncaught exception, and no other handler has been defined for that thread. Uncaught exception handling is controlled first by the thread, then by the thread’s ThreadGroup object and finally by the default uncaught exception handler. If the thread does not have an explicit uncaught exception handler set, and the thread’s thread group (including parent thread groups) does not specialize its uncaughtException method, then the default handler’s uncaughtException method will be invoked.

By setting the default uncaught exception handler, an application can change the way in which uncaught exceptions are handled (such as logging to a specific device, or file) for those threads that would already accept whatever “default” behavior the system provided.

Note that the default uncaught exception handler should not usually defer to the thread’s ThreadGroup object, as that could cause infinite recursion.

Parameters: eh - the object to use as the default uncaught exception handler. If null then there is no default handler.

Throws: SecurityException - if a security manager is present and it denies RuntimePermission (“setDefaultUncaughtExceptionHandler”) Since: 1.5

The following code illustrates an example:

//ExceptionHandlerExample.java
package com.air0day.machetejuggling;

public class ExceptionHandlerExample {
  public static void main(String[] args) throws Exception {

    Handler handler = new Handler();
    Thread.setDefaultUncaughtExceptionHandler(handler);

    Thread t = new Thread(new SomeThread(), "Some Thread");
    t.start();

    Thread.sleep(100);

    throw new RuntimeException("Thrown from Main");
  }

}

class Handler implements Thread.UncaughtExceptionHandler {
  public void uncaughtException(Thread t, Throwable e) {
    System.out.println("Throwable: " + e.getMessage());
    System.out.println(t.toString());
  }
}

class SomeThread implements Runnable {
  public void run() {
    throw new RuntimeException("Thrown From Thread");
  }
}

Migrate to Android

There are already many threads discussing the topic on StackOverflow:

The steps to take are quite similar to those of Java.

Something to take care:

When implementing ACRA, I have never been able to start a new Activity after receiving an uncaught exception. It looks like the whole process is switched by the system to a special state preventing him from allowing any new resource.

The only option I have found for the moment is to send a status bar notification which is kept by the system after the application being restarted. The notification then triggers an intent for a dialog activity when the user selects it.

Alexey Yakovlev studied in much more details this issue and came to the conclusion that there could be a chance of triggering a new activity when the crash occurs on a thread which is not the UI thread. Though we did not find a simple enough workaround to start directly an activity in all cases.

I got rid of the default force close dialog by killing the process myself without invoking the orginial default uncaught exception handler.

Trap, Be careful

References

Logging

Jetty Configuration

Jetty Configuration Files

There are a number of locations to configure settings for Jetty and web applciations running on it.

A number of configuration files can be found at ${jetty.home}/start.ini.

$java -jar start.jar --help

Available Configurations:
  By convention, configuration files are kept in $JETTY_HOME/etc.
  The known configuration files are:
  
    etc/jetty-ajp.xml
    etc/jetty-annotations.xml
    etc/jetty-bio-ssl.xml
    etc/jetty-bio.xml
    etc/jetty-contexts.xml
    etc/jetty-debug.xml
    etc/jetty-deploy.xml
    etc/jetty-fileserver.xml
    etc/jetty-ipaccess.xml
    etc/jetty-jmx.xml
    etc/jetty-logging.xml
    etc/jetty-monitor.xml
    etc/jetty-overlay.xml
    etc/jetty-plus.xml
    etc/jetty-policy.xml
    etc/jetty-proxy.xml
    etc/jetty-requestlog.xml
    etc/jetty-rewrite.xml
    etc/jetty-spdy-proxy.xml
    etc/jetty-spdy.xml
    etc/jetty-ssl.xml
    etc/jetty-stats.xml
    etc/jetty-testrealm.xml
    etc/jetty-webapps.xml
    etc/jetty-xinetd.xml
    etc/jetty.xml


Defaults:
  A start.ini file may be used to specify default arguments to start.jar,
  which are used if no command line arguments are provided and override 
  the defaults in the start.config file. If the directory jetty.home/start.d
  exists, then multiple *.ini files will be read from that directory in 
  alphabetical order. If --ini options are provided on  the command line,
  then start.ini and start.d will NOT be read. 
  
  The current start.ini arguments are:

    OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations
    etc/jetty.xml
    etc/jetty-annotations.xml
    etc/jetty-deploy.xml
    etc/jetty-webapps.xml
    etc/jetty-contexts.xml
    etc/jetty-testrealm.xml

Contexts

An important concept in Jetty is Context. A web application is a context. Thus each xml file configured under ./contexts directorty represents a web application.

  • ./contexts/test.xml The applicaiton configure file, using format of jetty. Jetty uses its own IOC mechanism to read and inject components to the server. The root element of the file is Server, representing a server.

  • web.xml
    [webapp_name]/WEB-INF/web.xml. This file is located under a web app WEB-INF directory. The directory name WEB-INF and file name web.xml is regulated by Serverlet web app standards.

  • jetty.xml

mac:jetty-distribution-8.1.5.v20120716 user1$ cat start.
cat: start.: No such file or directory
lucas-mac:jetty-distribution-8.1.5.v20120716 lucas$ cat start.ini 
#===========================================================
# Jetty start.jar arguments
# Each line of this file is prepended to the command line 
# arguments # of a call to:
#    java -jar start.jar [arg...]
#===========================================================



#===========================================================
# If the arguements in this file include JVM arguments 
# (eg -Xmx512m) or JVM System properties (eg com.sun.???),
# then these will not take affect unless the --exec 
# parameter is included or if the output from --dry-run
# is executed like:
#   eval $(java -jar start.jar --dry-run)
#
# Below are some recommended options for Sun's JRE
#-----------------------------------------------------------
# --exec
# -Dorg.apache.jasper.compiler.disablejsr199=true
# -Dcom.sun.management.jmxremote
# -Dorg.eclipse.jetty.util.log.IGNORED=true
# -Dorg.eclipse.jetty.LEVEL=DEBUG
# -Dorg.eclipse.jetty.util.log.stderr.SOURCE=true
# -Xmx2000m
# -Xmn512m
# -verbose:gc
# -XX:+PrintGCDateStamps
# -XX:+PrintGCTimeStamps
# -XX:+PrintGCDetails
# -XX:+PrintTenuringDistribution
# -XX:+PrintCommandLineFlags
# -XX:+DisableExplicitGC
# -XX:+UseConcMarkSweepGC
# -XX:ParallelCMSThreads=2
# -XX:+CMSClassUnloadingEnabled  
# -XX:+UseCMSCompactAtFullCollection
# -XX:CMSInitiatingOccupancyFraction=80
#-----------------------------------------------------------


#===========================================================
# Start classpath OPTIONS.
# These control what classes are on the classpath
# for a full listing do
#   java -jar start.jar --list-options
#-----------------------------------------------------------
OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations
#-----------------------------------------------------------


#===========================================================
# Configuration files.
# For a full list of available configuration files do
#   java -jar start.jar --help
#-----------------------------------------------------------
#etc/jetty-jmx.xml
etc/jetty.xml
etc/jetty-annotations.xml
# etc/jetty-ssl.xml
# etc/jetty-requestlog.xml
etc/jetty-deploy.xml
#etc/jetty-overlay.xml
etc/jetty-webapps.xml
etc/jetty-contexts.xml
etc/jetty-testrealm.xml
#===========================================================

Jetty Introduction

For one of my project, it took long time for me to choose which technology stack I would use for the server. Finally I did not choose popular frameworks like ruby, scala, etc. I chose Java/ JSP soly because the technology is mature and I was lucky to have some experience playing with it. Though not a experienced Java server developer, I think my knowledge of Java should be sufficent to kick off.

Surfing the internet, I prefer Jetty + nginx over tomcat as the previous compostion provides much better concurrency performance.

Things get complex when I start to write code.

First thing is to configure the dev and deploy enviroment. This was a hard time.

JConsole

JConsole is a Java monitoring tool,

Android Emulator Internet Access

Ocassionally we find our android emulators cannot connect to the internet. May it be working yesterday but not today. Damn it!

The explanation stated by this post reveils the underlying cause.

On some WI-FI network, the emulator could not get DNS configuration automatically. My experience shows this not only happenes to wifi netowrk, but also to cable network.

Solution

There are 2 solutions:

  1. Manually set DNS,
    • set DNS when emulator starts up.
 In Eclipse:

 Window>Preferences>Android>Launch

 Default emulator options: -dns-server 8.8.8.8,8.8.4.4 
  • set DNS after emulator starts up.
setprop dns.net1 8.8.8.8

The IP 8.8.8.8 and 8.8.4.4 are provided by Google freely.

  1. Toggle the Internet access to your emulator with F8 (on Windows) and Fn + F8 (on Mac OS X).

With this shortcut, you get the ACTION_BACKGROUND_DATA_SETTING_CHANGED dispatched.

Push Technology

Java Async IO

Java Async IO

AIO NIO

Background

Linux AIO poll select epoll

Windows event driven

Asynchronous NetworkProgramming

File Descriptors

Kernel

Framework

  • MINA
  • Jetty
  • Netty

Inversion of Control

Inversion of Control (IoC)

Hollywood Principle:

Don’t call us, we’ll call you.

Framework v.s. Library

One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user’s application code. The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.

– Ralph Johnson and Brian Foote

Example

JUnit Framework setUp and tearDown

Lightweight containers

J2EE

Dependency Injection

  • Constructor Injection
  • Setter Injection
  • Interface Injection
    private MutablePicoContainer configureContainer() {
        MutablePicoContainer pico = new DefaultPicoContainer();
        Parameter[] finderParams =  {new ConstantParameter("movies1.txt")};
        pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams);
        pico.registerComponentImplementation(MovieLister.class);
        return pico;
    }

PicoContainer NanoContainer: XML configure mapping

PicoContainer

Spring

Avalon

Interface Injection

References

  1. Inversion of Control Containers and the Dependency Injection pattern, Martin Fowler

Android Dependency Injection

Andoid ioc https://github.com/socialize/android-ioc/

RoboGuice

Tiny Spring

  • otto
  • dagger

http://www.slideshare.net/InfoQ/dagger-a-fast-dependency-injector-for-android-and-java

Android dependency injection

Android 依赖注入

reflection v.s code generation

Videos

SIM card for dummy

Android开发者在开发应用是总会遇到很多的电话相关的名词, 如IMEI, IMSI等,百度文库的这篇文章比较清晰的解释了这些名词。

SIM卡号码 详解

</embed>

Android 手机API获取手机号码

之前在网上经常听到开发者传言Android平台中可以通过API接口获取SIM卡的ISDN(MSISDN)号码,即大众所受的手机号码。实际上在Android系统设置里面用户去查看这一项的时候是空白。 说明两个问题:

  • OS提供API获取这个号码
  • OSAPI无法从SIM卡中获取到这个号码

也就是说, 这个API的获取结果和SIM卡相关, 即和运营商相关。

如果使用API可以获取这个号码的话, 对用户隐私将是一个非常大的威胁。

我用下面的代码在移动的动感地带和联通SIM卡上都没有得到手机号码。 松一口气。

说明暂时在中国移动和中国联通SIM卡上是无法获取这个号码的。


import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;

public class AndroidNumberActivity extends Activity {
	private static final String TAG = "AndroidNumberActivity";

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		// 创建电话管理
		TelephonyManager tm = (TelephonyManager)
		// 与手机建立连接
		getSystemService(Context.TELEPHONY_SERVICE);
		// 获取手机号码
		String phoneNo = tm.getLine1Number();
		Log.d(TAG, "phnone NO: " + phoneNo);

	}
}

注意, 要在AndroidManifest.xml中添加权限android.permission.READ_PHONE_STATE. 如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.number"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15"/>

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".AndroidNumberActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

How does an Android developer fix a bug?

Day and day ago, I wrote a DownloadingService for Android application. The service is provided in an Android SDK, which ill be utilized by other android apps. Some app developers wrote their code in such as way that when user press BACK button on Android, the app will kill itself in such a way:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
    if ((keyCode == KeyEvent.KEYCODE_BACK))
    {
       android.os.Process.killProcess(android.os.Process.myPid()) ;
       return true;
    }
    return super.onKeyDown(keyCode, event);
}

As an advanced Android developer, you may think how this could be stupid and violate the design principle of Android Framework, i.e., “Activity” should follow it’s own lifecycle.

But as the old saying said, customer is our God. SDK users, some could be novice android develoeprs, they refuse to change their existing strucutre and insist to kill the process when back button is pressed. As a result, the “DownloadingService” is also killed as it resides in the app’s main proces.

Though the phenomenon is expected, the novice developer does not think so. They want the DownloadingService to continue download files from the Internet even though they killed the main app process. To make a concession, I changed the “DownloadingSercie” so it runs in a separate process. This is fair easy. Developer only has to mark the service as “exported” in “AndroidManifest.xml”.

<service
    android:name="com.example.net.DownloadingService"
    android:exported="true"
    android:process=":DownloadingService" >
</service>

Everything goes well until one day.

One developer reported under the scenario he created, the DownloadingService could not correctly download the file:

  1. Start the app, click download
  2. Exit the app using “BACK” button, which kills app’s main process.
  3. Restart the app, click download (the same url as the Step 1). He observes that even though the url is the same as Step 1, there will be another notification bar item. The first download task is still excuting (from the notification bar).

This is absolutely unexpected, as I added code for this kind of duplicate check. If the url is in downloaing task list, it will not be downloaded again. DownloadingService generates a file name based on url (MD5 hash of the url) along with the file extension, plus suffix “.tmp”, then saves it to SD card. After the file is complete downloaded, the file will be renamed to remove suffix “.tmp”. Here the duplicate check code did not work!

Here it is:

	/**
	 * Check if the download request is already processed and is in downloading.
	 * 
	 * @param item
	 * @return
	 */
	private static boolean isInDownloadList(DownloadItem item) {
		if (mClients == null)
			return false;
		for (DownloadItem d : mClients.keySet()) {
			if (d.mUrl.equals(item.mUrl)) {
				return true;
			}
		}
		return false;
	}

mClients is declared as DownloadingService’s class member:

	// The clients connected to the service. Messenger references the client,
	// where DownloadItem specifies the information required by this service.
	private Map<DownloadAgent.DownloadItem, Messenger> mClients = new HashMap<DownloadAgent.DownloadItem, Messenger>();
	

mClient as a Map records all downloading url. If a url is in downloading, it can’t be downloaded again. So no reason that a duplicate url can be added to mClients. It is so strange.

Each single line of code looks fine to me.

Then I launch the debug mode, for both the main process and DownloadingService process.


	@Override
	public void onCreate() {
		super.onCreate();
		Log.d(LOG_TAG, "onCreate ");
		// uncomment the following line to enable inter-process debug.
		android.os.Debug.waitForDebugger();
		// ...
	}

The trace tells that in Step 3, mClients is empty: no elements is added before. This is so abnormal. What happened?

Obviously the Process is still living there, otherwise, the first task cannot continue executing. Recalling from Android API description, the service runs in the main thread of its process. I doubt if the service was destroyed and re-created. After adding log information in it’s onCreate and onDestroy method, yes! The service was destroyed and re-created again. But the process id and thread id are the same.

	private static boolean isInDownloadList(DownloadItem item) {
		Log.d(TAG, "pid = " + android.os.Process.myPid());
		Log.d(TAG, "threadid = " + Thread.currentThread().getId());
		if (mClients == null)
			return false;
		for (DownloadItem d : mClients.keySet()) {
			if (d.mUrl.equals(item.mUrl)) {
				return true;
			}
		}
		return false;
	}

threadid shows ‘1’ for both Step 1 and Step 3.

I was really confused. It seems Step 1 and Step 3 were in the same thread. But if that’s the case, why data stored in ‘mClients’ is losed? Hinted by the number ‘1’, I guess the thread might be killed and re-created, but the thread number was reused as OS find the number can be re-allocated the second time as it realize that no other thread is using this id.

So to make the intestigation a further step, I added a member

class DownloadingService extends Service{
	// generate an random id each time a class instance is created. In this case, it means a new thread is created.  
	int thread_random_id = new Random().nextInt(10000);
	
	// ....
}

A-ha, thread_random_id is different for the 2 cases!

DownloadingService is killed and re-created! The first task is still downloading because the task was launched in a different thread other than the Service thread itself.

To resolve the issue, simply make mClient static.

	private static Map<DownloadAgent.DownloadItem, Messenger> mClients = new HashMap<DownloadAgent.DownloadItem, Messenger>();

To be continued: why Service thread is killed and re-created?

Android Test For UI Thread Listeners

The other day I wrote an Android downloading service running in a separate process. The main application process will send command/message to the downloading process asking it to download item. The downloading process is supposed to post back the downloading process and status to the main applicaiton process by sending messages. Everything works fine: the application process can send command and downloading process can receive the command and then start downloading the item. Also the application process can also receives the process update messages from the donwloading process.

For the sake of recursive testing, namely, I wrote the following test case, following the direction specified by Android SDK documentation. Then run in Eclipse, everything worked fine, and I didnot put much effort on it, it got into the source depot.


public class DownloadAgentTest extends
		ActivityInstrumentationTestCase2<UtActivity> {
	private static final String LOG_TAG = DownloadAgentTest.class.getName();
	public DownloadAgentTest() {
		super("com.example", com.ut.UtActivity.class);
	}

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		mActivity = getActivity();
		mContext = mActivity.getBaseContext();
	}

	private UtActivity mActivity;
	private Context mContext;

	@Test
	public void testDownloadAgent() {
		final CountDownLatch signal = new CountDownLatch(1);

		IDownloadListener listener = new IDownloadListener() {
			@Override
			public void onStart() {
				Log.d(LOG_TAG, "onStart");
			}

			@Override
			public void onProgressUpdate(int progress) {
				Log.d(LOG_TAG, "onProgressUpdate("+progress +")");
			}

			@Override
			public void onEnd(int result, String file) {
				Log.d(LOG_TAG, "onEnd("+result +", "+ file +")");
				Assert.assertTrue(result ==1);
				Assert.assertTrue(file.contains("/sdcard/"));
				signal.countDown();
			}

		};
		DownloadAgent agent = new DownloadAgent(
				mContext,
				"xp",
				"App_name",
				"http://www.google.com/some.apk",
				listener);
		agent.start();
		try {
			signal.await(100, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

Days later, one of my collegues read that code, run it, but asked: “How do you know functions onProgressUpdate and onEnd are called?”. Inspired by his question, I updated the code as:


public class DownloadAgentTest extends
		ActivityInstrumentationTestCase2<UtActivity> {
	private static final String LOG_TAG = DownloadAgentTest.class.getName();
	
	boolean onStartTriggered = false;
	boolean onProgressUpdateTriggered = false;
	boolean onEndTriggered = false;

	public DownloadAgentTest() {
		super("com.example", com.ut.UtActivity.class);
	}

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		mActivity = getActivity();
		mContext = mActivity.getBaseContext();
	}

	private UtActivity mActivity;
	private Context mContext;

	@Test
	public void testDownloadAgent() {
		final CountDownLatch signal = new CountDownLatch(1);
	
		onStartTriggered = false;
		onProgressUpdateTriggered = false;
		onEndTriggered = false;
		
		IDownloadListener listener = new IDownloadListener() {
	        			@Override
	        			public void onStart() {
	        				Log.d(LOG_TAG, "onStart");
	        				onStartTriggered = true;
	        			}

	        			@Override
	        			public void onProgressUpdate(int progress) {
	        				Log.d(LOG_TAG, "onProgressUpdate("+progress +")");
	        				onProgressUpdateTriggered =true;
	        			}

	        			@Override
	        			public void onEnd(int result, String file) {
	        				onEndTriggered =true;
	        				Log.d(LOG_TAG, "onEnd("+result +", "+ file +")");
	        				Assert.assertTrue(result ==1);
	        				Assert.assertTrue(file.contains("/sdcard/"));
	        				signal.countDown();
	        			}
	        };	
		
		DownloadAgent agent = new DownloadAgent(
				mContext,
				"xp",
				"App_name",
				"http://www.google.com/some.apk",
				listener);
		agent.start();
		try {
			signal.await(100, TimeUnit.SECONDS);
			Assert.assertTrue(onStartTriggered);
			Assert.assertTrue(onProgressUpdateTriggered);
			Assert.assertTrue(onEndTriggered);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

Here I add 3 class variables onStartTriggered, onProgressUpdateTriggered, onEndTriggered to track whether each method is successfully called. And in try catch block, use Assert.assertTrue to make sure these variables were accessed by correspondent methods after signal.await call. But unfortunately, this time I am not lucky enough. The test case failed. After digging into the code, I found these 3 methods were not called at all. That was tricky, they can be successfuly called in the applicaiton code, but not in the test! With a few Google search, I found this diagram on StackOverflow. Thread: Intent resolved to different process when running Unit Test in Android

Instrumentation runs all of your application components in the same process.

The diagram explained the phenomenon to some extent. The test runner is running in a sparate thread than the UI thread. What this hint is that the test runner thread has no looper, which is required for Android message communication mechanism. See andorid.os.Looper.

As it infers, the runner thread has no looper, thus IDownloadListener listener, which was implemented using the Messager in android to receive messages from downloading process, cannot receives message. As a result, in the test code, the above 3 methods were not called.

But then how can we test that code? The solution is simple, move the listener code to UI thread. In fact, Android Test framework does support this. Testing on the UI thread.

As illustrated in the following code snippet:


public class DownloadAgentTest extends
		ActivityInstrumentationTestCase2<UtActivity> {
	private static final String LOG_TAG = DownloadAgentTest.class.getName();

	boolean onStartTriggered = false;
	boolean onProgressUpdateTriggered = false;
	boolean onEndTriggered = false;

	public DownloadAgentTest() {
		super("com.example", com.ut.UtActivity.class);
	}

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		mActivity = getActivity();
		mContext = mActivity.getBaseContext();
	}

	private UtActivity mActivity;
	private Context mContext;

	@Test
	public void testDownloadAgent() {
		final CountDownLatch signal = new CountDownLatch(1);

		onStartTriggered = false;
		onProgressUpdateTriggered = false;
		onEndTriggered = false;

	  	mActivity.runOnUiThread(new Runnable() {
	          public void run() {
	        		IDownloadListener listener = new IDownloadListener() {
	        			@Override
	        			public void onStart() {
	        				Log.d(LOG_TAG, "onStart");
	        				onStartTriggered = true;
	        			}

	        			@Override
	        			public void onProgressUpdate(int progress) {
	        				Log.d(LOG_TAG, "onProgressUpdate("+progress +")");
	        				onProgressUpdateTriggered =true;
	        			}

	        			@Override
	        			public void onEnd(int result, String file) {
	        				onEndTriggered =true;
	        				Log.d(LOG_TAG, "onEnd("+result +", "+ file +")");
	        				Assert.assertTrue(result ==1);
	        				Assert.assertTrue(file.contains("/sdcard/"));
	        				signal.countDown();
	        			}
	        		};

	      		DownloadAgent agent = new DownloadAgent(
	    				mContext,
	    				"xp",
	    				"App_name",
	    				"http://www.google.com/some.apk",
	    				listener);
	        	agent.start();
	          }
	      });

		try {
			signal.await(300, TimeUnit.SECONDS);
			Assert.assertTrue(onStartTriggered);
			Assert.assertTrue(onProgressUpdateTriggered);
			Assert.assertTrue(onEndTriggered);

		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

This time it works fine.

Caution: Although Testing on the UI thread tells that you can use both @UIThreadTest annotation and mActivity.runOnUiThread(new Runnable() to run some code on UI thread, in the case I mentioned above, do not use the first solution. Our code use

                        signal.await(300, TimeUnit.SECONDS);

to synchronize. So if we put @UIThreadTest annotation, listener will not be executed, as await will block the main UI thread. The Looper is blocked, it cannot receive messages!.

Android Account Manager

Android系统本身提供了多用户账户的支持。 这里的多用户账户不是指操作系统中的用户,而是不同网络服务的账户, 如Google账户, Facebook账户, Twitter账户等。

对于android操作系统来讲,本身就是Linux系统,是一个支持多用户的系统。 每一个应用对应于一个process,运行时会有一个独立的UID, 也就对应于Linux中的用户。 开发者可以在adb下使用 ps 命令来查看。 如下,左边第一列就是运行对应应用的用户名. 可以看到, 对于安装的应用而言,每个应用都有自己独立的UID, 如com.baidu.input的UID为app_67

root      117   2     0      0     c01839a4 00000000 S ext4-dio-unwrit
system    133   1     17020  4296  c052d258 400307b0 S /system/bin/servicemanager
root      134   1     6040   996   ffffffff ffff0520 S /system/bin/vold
system    138   1     64424  11408 ffffffff 400c17b0 S /system/bin/surfaceflinger
root      139   1     445988 40160 ffffffff 4008d8d4 S zygote
drm       140   1     18912  4340  ffffffff 400507b0 S /system/bin/drmserver
media     141   1     49032  8836  ffffffff 400207b0 S /system/bin/mediaserver
bluetooth 142   1     1368   820   c01fad68 ffff0520 S /system/bin/dbus-daemon
root      143   1     5692   992   ffffffff 400ac578 S /system/bin/installd
keystore  144   1     1764   564   c05797b8 4004f0b8 S /system/bin/keystore
system    146   1     18832  4248  ffffffff 4007b7b0 S /system/bin/fmradioserver
root      147   1     10428  1312  ffffffff 4006f6d4 S /system/bin/thermald
nobody    164   1     7000   340   ffffffff 400be7b0 S /system/bin/rmt_storage
system    165   1     8360   1292  ffffffff 4005f0b8 S /system/bin/time_daemon
root      166   1     4504   216   ffffffff 0000829c S /sbin/adbd
radio     174   1     13668  3444  ffffffff ffff0520 S /system/bin/rild
radio     176   1     10604  652   ffffffff 400ee8d4 S /system/bin/qmuxd
radio     178   1     5688   932   ffffffff 400cb6d4 S /system/bin/netmgrd
root      249   2     0      0     c01839a4 00000000 S rpcrotuer_smd_x
root      250   2     0      0     c0125c10 00000000 S krpcserversd
root      251   2     0      0     c012462c 00000000 D krmt_storagecln
root      252   2     0      0     c01273c0 00000000 D krmt_storagecln
root      266   2     0      0     c0210144 00000000 S flush-179:0
system    305   139   578160 86556 ffffffff 4008d7b0 S system_server
system    427   139   487128 57384 ffffffff 4008e4c4 S com.android.systemui
app_67    496   139   463356 44732 ffffffff 4008e4c4 S com.baidu.input
radio     511   139   511076 68580 ffffffff 4008e4c4 S com.android.phone
app_22    520   139   469480 47564 ffffffff 4008e4c4 S com.lbe.security.miui
app_29    536   139   480532 82768 ffffffff 4008e4c4 S com.miui.home
app_67    568   496   1320   924   c01f1f28 40090594 S logcat
app_47    593   139   453256 32464 ffffffff 4008e4c4 S com.android.smspush
app_0     605   139   508268 62564 ffffffff 4008e4c4 S android.process.acore
app_10    622   139   515376 49132 ffffffff 4008e4c4 S com.google.process.gapps
root      732   1     5252   516   ffffffff 400f56d4 S /system/bin/mpdecision
app_82    1272  1     1156   764   c01fad68 400758d4 S logcat
app_72    1552  139   469184 33464 ffffffff 4008e4c4 S com.oasistudio.tk
app_82    6993  139   473684 37396 ffffffff 4008e4c4 S cn.lookoo.tuangou
app_82    7006  6993  1024   632   c01fad68 400ef8d4 S logcat
root      18719 2     0      0     c0121ccc 00000000 D kworker/u:3
root      19261 2     0      0     c018dd14 00000000 S iscan_sysioc
root      19262 2     0      0     c018dd14 00000000 S dhcp_sysioc
root      19263 2     0      0     c018dd14 00000000 S dhd_watchdog
root      19264 2     0      0     c018dd14 00000000 S dhd_dpc
root      19265 2     0      0     c018dd14 00000000 S dhd_sysioc
log       19266 1     716    292   c03a6b1c 4002b578 S /system/bin/logwrapper
wifi      19268 19266 2468   1252  c01fad68 400f28d4 S /system/bin/wpa_supplicant
app_87    19394 139   461244 38592 ffffffff 4008e4c4 S com.box.brian.activity
root      21861 2     0      0     c0181c44 00000000 S kworker/u:1
app_99    22996 139   494164 35896 ffffffff 4008e4c4 S com.renren.mobile.chat:sixinpush
root      23578 2     0      0     c0210144 00000000 S flush-179:96
app_96    23724 139   454356 33508 ffffffff 4008e4c4 S com.mbook.itaoshu
app_88    23830 139   477360 52496 ffffffff 4008e4c4 S com.wandoujia.phoenix2
dhcp      25092 1     952    468   c01fad68 ffff0520 S /system/bin/dhcpcd
root      25559 1     9648   1412  ffffffff ffff0520 S /system/bin/netd
root      26702 1     728    292   c05797b8 4008c0b8 S /system/bin/debuggerd
app_26    27385 139   470492 40448 ffffffff 4008e4c4 S com.google.android.apps.maps:NetworkLocationService
app_43    28373 139   476148 41240 ffffffff 4008e4c4 S com.android.vending
app_91    28402 139   463464 36712 ffffffff 4008e4c4 S com.renren.mobile.android
app_0     28696 139   456256 33980 ffffffff 4008e4c4 S com.android.contacts
app_18    28726 139   500412 61412 ffffffff 4008e4c4 S com.google.android.gm
app_93    28801 139   453896 31740 ffffffff 4008e4c4 S com.fractalist.MobileAcceleration
app_38    28814 139   454696 32664 ffffffff 4008e4c4 S com.android.quicksearchbox
app_10    28829 139   458112 35592 ffffffff 4008e4c4 S com.google.android.gsf.login
root      28885 2     0      0     c0181c44 00000000 S kworker/0:1
app_49    28886 139   470840 41624 ffffffff 4008e4c4 S com.xiaomi.channel
root      28931 2     0      0     c0181c44 00000000 S kworker/u:0
root      28939 2     0      0     c0181c44 00000000 S kworker/0:0
app_48    28987 139   453272 31420 ffffffff 4008e4c4 S com.qualcomm.wiper
app_60    28999 139   454104 33892 ffffffff 4008e4c4 S com.glorymob.joymax.view
root      29103 2     0      0     c0181c44 00000000 S kworker/u:4
app_30    29142 139   459356 40064 ffffffff 4008e4c4 S com.android.mms
system    29168 139   477500 49812 ffffffff 4008e4c4 S com.android.settings
app_15    29212 139   477548 46216 ffffffff 4008e4c4 S com.android.email
app_16    29232 139   460760 32804 ffffffff 4008e4c4 S com.android.exchange
root      29323 2     0      0     c0181c44 00000000 S kworker/0:2
root      29324 2     0      0     c0181c44 00000000 S kworker/0:3
app_26    29345 139   460068 36716 ffffffff 4008e4c4 S com.google.android.apps.maps
app_14    29386 139   454912 34484 ffffffff 4008e4c4 S android.process.media
root      29439 166   804    432   c01090a8 400d6f94 S /system/bin/sh
root      29444 29439 980    364   00000000 400ed578 R ps

由于Android系统是为互联网而设计的,自然需要对各种网络服务有很好的支持。 Android的解决方案是提供AccountManager来管理不同服务的账户。 AccountManager是由系统提供的服务([AccountManagerService]), 从而很好的在系统层面解决不同应用共享服务账户的问题。

比如, 用户注册了Google账户,使用这个账户可以登陆Google+,Google Play, Gmail, 也可以同步联系人甚至手机上的其他设置如WI-FI密码和浏览器书签到Google的服务器上。 由于这些服务是由不同的应用提供的,如果账户由每个应用自己单独处理,将是一件很繁琐的事情。 甚至处理不好, 会带来很大的安全隐患。 现在好了,Android在系统级别提供了账户管理功能, 用户只要去账户管理中心登陆账户, 不同应用就可以想账户管理中心请求账户访问权限,而不需要应用本身去维护和管理这些账户,对应用开发来说既简单又安全了。

Android Account 管理类库介绍

Android中和账户相关的API都在android.accounts包下。

重要的接口和类有:

Interfaces

  • AccountManagerCallback<V> 包含回调函数。 类似于我们经常写的Listener
  • AccountManagerFuture<V> 异步调用AccountManager的结果。
  • OnAccountsUpdateListener AccountMonitor用到的回调接口。

Classes

  • AbstractAccountAuthenticator 如果开发者需要实现自己的认证方式, 可以通过继承这个类来实现
  • Account 表示我们的账户
  • AccountAuthenticatorActivity Base class for implementing an Activity that is used to help implement an AbstractAccountAuthenticator.
  • AccountAuthenticatorResponse Object used to communicate responses back to the AccountManager
  • AccountManager 账户管理的核心类,是访问账户的入口。
  • AuthenticatorDescription A Parcelable value type that contains information about an account authenticator.

使用[AccountManager]访问已经支持的账户

Android 系统默认支持Google账户, Microsoft Exchange账户,和普通邮件账户。

现在,假如你开发了一个应用,会使用到用户的Google账户, 比如, 类似于SMS Backup这种应用, 可以把手机端的短信同步到GMail邮箱当中。 当然你不希望已经在自己手机上登陆过Gmail账户的用户再次在你的应用中输入用户名和密码,用户也很害怕把密码告诉你。这种情况下, 你可以请求用户授予你访问他的账户的权利, 如同下图所示: ,用户也很害怕把密码告诉你。这种情况下, 你可以请求用户授予你访问他的账户的权利, 如同下图所示: Request Account Access from SMS Backup

将如何实现呢?

Google 提供了一个很好的例子, 在Google Task API 下。 简单的使用如下:

	mAccountManager = AccountManager.get(this);

    	Account[] accounts = mAccountManager.getAccounts();
    	for(Account account : accounts){
    		Log.i(TAG, String.format("account.name={0}, type={1}, content={2}",account.name, account.type, account.describeContents()));
    	}

扩展[AccountManager]支持自定义的账户

既然系统的AccountManager提供了这么多的便利, 你开始考虑把自己的在线服务加到Android系统中去了, 比如你有类似人人或者新浪微博, 想要把账户管理加到系统中去, 该怎么做呢?

Android Account Manager

Android系统本身提供了多用户账户的支持。 这里的多用户账户不是指操作系统中的用户,而是不同网络服务的账户, 如Google账户, Facebook账户, Twitter账户等。

对于android操作系统来讲,本身就是Linux系统,是一个支持多用户的系统。 每一个应用对应于一个process,运行时会有一个独立的UID, 也就对应于Linux中的用户。 开发者可以在adb下使用 ps 命令来查看。 如下,左边第一列就是运行对应应用的用户名. 可以看到, 对于安装的应用而言,每个应用都有自己独立的UID, 如com.baidu.input的UID为app_67

root      117   2     0      0     c01839a4 00000000 S ext4-dio-unwrit
system    133   1     17020  4296  c052d258 400307b0 S /system/bin/servicemanager
root      134   1     6040   996   ffffffff ffff0520 S /system/bin/vold
system    138   1     64424  11408 ffffffff 400c17b0 S /system/bin/surfaceflinger
root      139   1     445988 40160 ffffffff 4008d8d4 S zygote
drm       140   1     18912  4340  ffffffff 400507b0 S /system/bin/drmserver
media     141   1     49032  8836  ffffffff 400207b0 S /system/bin/mediaserver
bluetooth 142   1     1368   820   c01fad68 ffff0520 S /system/bin/dbus-daemon
root      143   1     5692   992   ffffffff 400ac578 S /system/bin/installd
keystore  144   1     1764   564   c05797b8 4004f0b8 S /system/bin/keystore
system    146   1     18832  4248  ffffffff 4007b7b0 S /system/bin/fmradioserver
root      147   1     10428  1312  ffffffff 4006f6d4 S /system/bin/thermald
nobody    164   1     7000   340   ffffffff 400be7b0 S /system/bin/rmt_storage
system    165   1     8360   1292  ffffffff 4005f0b8 S /system/bin/time_daemon
root      166   1     4504   216   ffffffff 0000829c S /sbin/adbd
radio     174   1     13668  3444  ffffffff ffff0520 S /system/bin/rild
radio     176   1     10604  652   ffffffff 400ee8d4 S /system/bin/qmuxd
radio     178   1     5688   932   ffffffff 400cb6d4 S /system/bin/netmgrd
root      249   2     0      0     c01839a4 00000000 S rpcrotuer_smd_x
root      250   2     0      0     c0125c10 00000000 S krpcserversd
root      251   2     0      0     c012462c 00000000 D krmt_storagecln
root      252   2     0      0     c01273c0 00000000 D krmt_storagecln
root      266   2     0      0     c0210144 00000000 S flush-179:0
system    305   139   578160 86556 ffffffff 4008d7b0 S system_server
system    427   139   487128 57384 ffffffff 4008e4c4 S com.android.systemui
app_67    496   139   463356 44732 ffffffff 4008e4c4 S com.baidu.input
radio     511   139   511076 68580 ffffffff 4008e4c4 S com.android.phone
app_22    520   139   469480 47564 ffffffff 4008e4c4 S com.lbe.security.miui
app_29    536   139   480532 82768 ffffffff 4008e4c4 S com.miui.home
app_67    568   496   1320   924   c01f1f28 40090594 S logcat
app_47    593   139   453256 32464 ffffffff 4008e4c4 S com.android.smspush
app_0     605   139   508268 62564 ffffffff 4008e4c4 S android.process.acore
app_10    622   139   515376 49132 ffffffff 4008e4c4 S com.google.process.gapps
root      732   1     5252   516   ffffffff 400f56d4 S /system/bin/mpdecision
app_82    1272  1     1156   764   c01fad68 400758d4 S logcat
app_72    1552  139   469184 33464 ffffffff 4008e4c4 S com.oasistudio.tk
app_82    6993  139   473684 37396 ffffffff 4008e4c4 S cn.lookoo.tuangou
app_82    7006  6993  1024   632   c01fad68 400ef8d4 S logcat
root      18719 2     0      0     c0121ccc 00000000 D kworker/u:3
root      19261 2     0      0     c018dd14 00000000 S iscan_sysioc
root      19262 2     0      0     c018dd14 00000000 S dhcp_sysioc
root      19263 2     0      0     c018dd14 00000000 S dhd_watchdog
root      19264 2     0      0     c018dd14 00000000 S dhd_dpc
root      19265 2     0      0     c018dd14 00000000 S dhd_sysioc
log       19266 1     716    292   c03a6b1c 4002b578 S /system/bin/logwrapper
wifi      19268 19266 2468   1252  c01fad68 400f28d4 S /system/bin/wpa_supplicant
app_87    19394 139   461244 38592 ffffffff 4008e4c4 S com.box.brian.activity
root      21861 2     0      0     c0181c44 00000000 S kworker/u:1
app_99    22996 139   494164 35896 ffffffff 4008e4c4 S com.renren.mobile.chat:sixinpush
root      23578 2     0      0     c0210144 00000000 S flush-179:96
app_96    23724 139   454356 33508 ffffffff 4008e4c4 S com.mbook.itaoshu
app_88    23830 139   477360 52496 ffffffff 4008e4c4 S com.wandoujia.phoenix2
dhcp      25092 1     952    468   c01fad68 ffff0520 S /system/bin/dhcpcd
root      25559 1     9648   1412  ffffffff ffff0520 S /system/bin/netd
root      26702 1     728    292   c05797b8 4008c0b8 S /system/bin/debuggerd
app_26    27385 139   470492 40448 ffffffff 4008e4c4 S com.google.android.apps.maps:NetworkLocationService
app_43    28373 139   476148 41240 ffffffff 4008e4c4 S com.android.vending
app_91    28402 139   463464 36712 ffffffff 4008e4c4 S com.renren.mobile.android
app_0     28696 139   456256 33980 ffffffff 4008e4c4 S com.android.contacts
app_18    28726 139   500412 61412 ffffffff 4008e4c4 S com.google.android.gm
app_93    28801 139   453896 31740 ffffffff 4008e4c4 S com.fractalist.MobileAcceleration
app_38    28814 139   454696 32664 ffffffff 4008e4c4 S com.android.quicksearchbox
app_10    28829 139   458112 35592 ffffffff 4008e4c4 S com.google.android.gsf.login
root      28885 2     0      0     c0181c44 00000000 S kworker/0:1
app_49    28886 139   470840 41624 ffffffff 4008e4c4 S com.xiaomi.channel
root      28931 2     0      0     c0181c44 00000000 S kworker/u:0
root      28939 2     0      0     c0181c44 00000000 S kworker/0:0
app_48    28987 139   453272 31420 ffffffff 4008e4c4 S com.qualcomm.wiper
app_60    28999 139   454104 33892 ffffffff 4008e4c4 S com.glorymob.joymax.view
root      29103 2     0      0     c0181c44 00000000 S kworker/u:4
app_30    29142 139   459356 40064 ffffffff 4008e4c4 S com.android.mms
system    29168 139   477500 49812 ffffffff 4008e4c4 S com.android.settings
app_15    29212 139   477548 46216 ffffffff 4008e4c4 S com.android.email
app_16    29232 139   460760 32804 ffffffff 4008e4c4 S com.android.exchange
root      29323 2     0      0     c0181c44 00000000 S kworker/0:2
root      29324 2     0      0     c0181c44 00000000 S kworker/0:3
app_26    29345 139   460068 36716 ffffffff 4008e4c4 S com.google.android.apps.maps
app_14    29386 139   454912 34484 ffffffff 4008e4c4 S android.process.media
root      29439 166   804    432   c01090a8 400d6f94 S /system/bin/sh
root      29444 29439 980    364   00000000 400ed578 R ps

由于Android系统是为互联网而设计的,自然需要对各种网络服务有很好的支持。 Android的解决方案是提供AccountManager来管理不同服务的账户。 AccountManager是由系统提供的服务([AccountManagerService]), 从而很好的在系统层面解决不同应用共享服务账户的问题。

比如, 用户注册了Google账户,使用这个账户可以登陆Google+,Google Play, Gmail, 也可以同步联系人甚至手机上的其他设置如WI-FI密码和浏览器书签到Google的服务器上。 由于这些服务是由不同的应用提供的,如果账户由每个应用自己单独处理,将是一件很繁琐的事情。 甚至处理不好, 会带来很大的安全隐患。 现在好了,Android在系统级别提供了账户管理功能, 用户只要去账户管理中心登陆账户, 不同应用就可以想账户管理中心请求账户访问权限,而不需要应用本身去维护和管理这些账户,对应用开发来说既简单又安全了。

Android Account 管理类库介绍

Android中和账户相关的API都在android.accounts包下。

重要的接口和类有:

Interfaces

  • AccountManagerCallback<V> 包含回调函数。 类似于我们经常写的Listener
  • AccountManagerFuture<V> 异步调用AccountManager的结果。
  • OnAccountsUpdateListener AccountMonitor用到的回调接口。

Classes

  • AbstractAccountAuthenticator 如果开发者需要实现自己的认证方式, 可以通过继承这个类来实现
  • Account 表示我们的账户
  • AccountAuthenticatorActivity Base class for implementing an Activity that is used to help implement an AbstractAccountAuthenticator.
  • AccountAuthenticatorResponse Object used to communicate responses back to the AccountManager
  • AccountManager 账户管理的核心类,是访问账户的入口。
  • AuthenticatorDescription A Parcelable value type that contains information about an account authenticator.

使用[AccountManager]访问已经支持的账户

Android 系统默认支持Google账户, Microsoft Exchange账户,和普通邮件账户。

现在,假如你开发了一个应用,会使用到用户的Google账户, 比如, 类似于SMS Backup这种应用, 可以把手机端的短信同步到GMail邮箱当中。 当然你不希望已经在自己手机上登陆过Gmail账户的用户再次在你的应用中输入用户名和密码,用户也很害怕把密码告诉你。这种情况下, 你可以请求用户授予你访问他的账户的权利, 如同下图所示: ,用户也很害怕把密码告诉你。这种情况下, 你可以请求用户授予你访问他的账户的权利, 如同下图所示: Request Account Access from SMS Backup

将如何实现呢?

Google 提供了一个很好的例子, 在Google Task API 下。 简单的使用如下:

	mAccountManager = AccountManager.get(this);

    	Account[] accounts = mAccountManager.getAccounts();
    	for(Account account : accounts){
    		Log.i(TAG, String.format("account.name={0}, type={1}, content={2}",account.name, account.type, account.describeContents()));
    	}

扩展[AccountManager]支持自定义的账户

既然系统的AccountManager提供了这么多的便利, 你开始考虑把自己的在线服务加到Android系统中去了, 比如你有类似人人或者新浪微博, 想要把账户管理加到系统中去, 该怎么做呢?

VIM QUICK REFERENCE CARD

VIM Quick Reference Card origin:http://tnerual.eriogerg.free.fr/vimqrc.html

VIM QUICK REFERENCE CARD


Basic movement
h l k jcharacter left, right; line up, down
b wword/token left, right
ge eend of word/token left, right
{  }beginning of previous, next paragraph
( )beginning of previous, next sentence
0 gmbeginning, middle of line
^  $first, last character of line
nG nggline n, default the last, first
n%percentage n of the file (n must be provided)
n|column n of current line
%match of next brace, bracket, comment, #define
nH nLline n from start, bottom of window
Mmiddle line of window

Insertion & replace insert mode
i ainsert before, after cursor
I Ainsert at beginning, end of line
gIinsert text in first column
o Oopen a new line below, above the current line
rcreplace character under cursor with c
grclike r, but without affecting layout
Rreplace characters starting at the cursor
gRlike R, but without affecting layout
cmchange text of movement command m
cc or Schange current line
Cchange to the end of line
schange one character and insert
~switch case and advance cursor
g~mswitch case of movement command m
gum gUmlowercase, uppercase text of movement m
<m >mshift left, right text of movement m
n<< n>>shift n lines left, right

Deletion
x Xdelete character under, before cursor
dmdelete text of movement command m
dd Ddelete current line, to the end of line
J gJjoin current line with next, without space
:rddelete range r lines
:rdxdelete range r lines into register x

Insert mode
^Vc ^Vninsert char c literally, decimal value n
^Ainsert previously inserted text
^@same as ^A and stop insert command mode
^Rx ^R^Rxinsert content of register x, literally
^N ^Ptext completion before, after cursor
^Wdelete word before cursor
^Udelete all inserted character in current line
^D ^Tshift left, right one shift width
^Kc1c2 or c1c2enter digraph \c1,c2\
^Ocexecute c in temporary command mode
^X^E ^X^Yscroll up, down
<esc> or ^[abandon edition command mode

Copying
"xuse register x for next delete, yank, put
:regshow the content of all registers
:reg xshow the content of registers x
ymyank the text of movement command m
yy or Yyank current line into register
p Pput register after, before cursor position
]p [plike p, P with indent adjusted
gp gPlike p, P leaving cursor after new text

Advanced insertion
g?mperform rot13 encoding on movement m
n^A n^X+n, -n to number under cursor
gqmformat lines of movement m to fixed width
:rce wcenter lines in range r to width w
:rle ileft align lines in range r with indent i
:rri wright align lines in range r to width w
!mcfilter lines of movement m through command c
n!!cfilter n lines through command c
:r!cfilter range r lines through command c

Visual mode
v V ^Vstart/stop highlighting characters, lines, block
oexchange cursor position with start of highlighting
gvstart highlighting on previous visual area
aw as apselect a word, a sentence, a paragraph
ab aBselect a block ( ), a block { }

Undoing, repeating & registers
u Uundo last command, restore last changed line
.  ^Rrepeat last changes, redo last undo
nrepeat last changes with count replaced by n
qc qCrecord, append typed characters in register c
qstop recording
@cexecute the content of register c
@@repeat previous @ command
:@cexecute register c as an Ex command
:rg/p/cexecute Ex command c on range r
where pattern p matches

Complex movement
- +line up, down on first non-blank character
B Wspace-separated word left, right
gE Eend of space-separated word left, right
n_down n-1 line on first non-blank character
g0beginning of screen line
g^  g$first, last character of screen line
gk gj screen line up, down
fc Fcnext, previous occurence of character c
tc Tcbefore next, previous occurence of c
; ,repeat last fFtT, in opposite direction
[[ ]]start of section backward, forward
[] ][end of section backward, forward
[( ])unclosed (, ) backward, forward
[{  ]}unclosed {, } backward, forward
[m ]mstart of backward, forward Java method
[# ]#unclosed #if, #else, #endif backward, forward
[* ]*start, end of /* */ backward, forward

Search & substitution
/s  ?ssearch forward, backward for s
/s/o  ?s?osearch fwd, bwd for s with offset o
or /repeat forward last search
or ?repeat backward last search
# *search backward, forward for word under cursor
g# g*same, but also find partial matches
gd gDlocal, global definition of symbol under cursor
:rs/f/t/xsubstitute f by t in range r
x: g-all occurrences, c-confirm changes
:rs xrepeat substitution with new r & x

Special characters in search patterns
.   ^  $any single character, start, end of line
\< \>start, end of word
[c1-c2]a single character in range c1..c2
[^c1-c2]a single character not in range
\i \k \I \Kan identifier, keyword; excl. digits
\f \p \F \Pa file name, printable char.; excl. digits
\s \Sa white space, a non-white space
\e \t \r \b<esc>, <tab>, <>, <>
\= * \+match 0..1, 0.., 1.. of preceding atoms
\|separate two branches (  or)
\( \)group patterns into an atom
\& \nthe whole matched pattern, nth () group
\u \lnext character made upper, lowercase
\c \Cignore, match case on next pattern

Offsets in search commands
n or +nn line downward in column 1
-nn line upward in column 1
e+n e-nn characters right, left to end of match
s+n s-nn characters right, left to start of match
;scexecute search command sc next

Marks and motions
mcmark current position with mark [a..Z]
`c `Cgo to mark c in current, C in any file
`0..9go to last exit position
`` `"go to position before jump, at last edit
`[ `]go to start, end of previously operated text
:marksprint the active marks list
:jumpsprint the jump list
n^Ogo to nth older position in jump list
n^Igo to nth newer position in jump list

Key mapping & abbreviations
:map c emap e in normal & visual mode
:map!  c emap e in insert & cmd-line mode
:unmap c  :unmap!  cremove mapping c
:mk fwrite current mappings, settings... to file f
:ab c eadd abbreviation for e
:ab cshow abbreviations starting with c
:una cremove abbreviation c

Tags
:ta tjump to tag t
:ntajump to nth newer tag in list
^] ^Tjump to the tag under cursor, return from tag
:ts tlist matching tags and select one for jump
:tj tjump to tag or select one if multiple matches
:tagsprint tag list
:npo  :n^Tjump back from, to nth older tag
:tljump to last matching tag
^W}  :pt tpreview tag under cursor, tag t
^W]split window and show tag under cursor
^Wz or :pcclose tag preview window

Scrolling & multi-windowing
^E ^Yscroll line up, down
^D ^Uscroll half a page up, down
^F ^Bscroll page up, down
zt or zset current line at top of window
zz or z.  set current line at center of window
zb or z-set current line at bottom of window
zh zlscroll one character to the right, left
zH zLscroll half a screen to the right, left
^Ws or :splitsplit window in two
^Wn or :newcreate new empty window
^Wo or :onmake current window one on screen
^Wj ^Wkmove to window below, above
^Ww ^W^Wmove to window below, above (wrap)

Ex commands ()
:e fedit file f, unless changes have been made
:e!  fedit file f always (by default reload current)
:wn :wNwrite file and edit next, previous one
:n :Nedit next, previous file in list
:rwwrite range r to current file
:rw fwrite range r to file f
:rw>>fappend range r to file f
:q :q!quit and confirm, quit and discard changes
:wq or :x or ZZwrite to current file and exit
<up> <down>recall commands starting with current
:r finsert content of file f below cursor
:r!  cinsert output of command c below cursor
:argsdisplay the argument list
:rco  a :racopy, move range r below line a

Ex ranges
, ;  separates two lines numbers, set to first line
nan absolute line number n
.   $the current line, the last line in file
% *entire file, visual area
'tposition of mark t
/p/ ?p?the next, previous line where p matches
+n -n+n, -n to the preceding line number

Folding
zfmcreate fold of movement m
:rfocreate fold for range r
zd zEdelete fold at cursor, all in window
zo zc zO zCopen, close one fold; recursively
[z ]zmove to start, end of current open fold
zj zkmove down, up to start, end of next fold

Miscellaneous
:sh  :!cstart shell, execute command c in shell
Klookup keyword under cursor with man
:makestart make, read errors and jump to first
:cn  :cpdisplay the next, previous error
:cl  :cflist all errors, read errors from file
^L ^Gredraw screen, show filename and position
g^Gshow cursor column, line, and character position
gashow ASCII value of character under cursor
gfopen file which filename is under cursor
:redir>fredirect output to file f
:mkview [f]save view configuration [to file f]
:loadview [f]load view configuration [from file f]
^@ ^K ^_  \  Fn ^Fnunmapped keys


Copyright (C) 2005 by Laurent GRÉGOIRE (laurent.gregoire@icam.fr)
Converted from vimqrc.tex using a Python script.
You can find the latest revision at http://tnerual.eriogerg.free.fr

Videos on REST, Android, and Google API

Intro to REST

Google I/O 2010 - Android REST client applications

Google I/O 2011: Best Practices for Accessing Google APIs on Android

github视通的jekyll真的很不给力

一直以来想找一个简单的可以写技术博客的站点,要求很简单, 不需要什么酷的界面,也不需要什么炫的皮肤,只需要能够不错的展示代码和图片就可以了。

github falvored markdown 对code block 很不错, 所谓的”fancy code block”。 看似简单的东西, 在实际的系统里面没有几个做得好的。 Github提供pages服务, 可以供写技术博客用。 背后使用的引擎是Jekyll, 这个东西是ruby写的, 以非常简单的方式来管理博客。 虽然Github使用的是Jekyll, Jekyll也主要是用markdown格式来写博客的, 要命的是, Jekyll不支持GFM, 使用什么所谓的maruku引擎渲染, 而不是github 所用的redcarpet. Ridiculous!估计这就是开源所带来的后果吧。 这就造成了使用GFM写的markdown post不能被Jekyll在Github pages上很好的展示出来。

Android Contacts Provider In Deep

Android contact API uses Content Provider to provide data for application. The service is provided by com.android.providers.contacts. All the contact data are stored in SQLite database. You can find all the data under /data/data/com.android.providers.contacts/databases, as following:

# adb shell 
root@android:/data/data/com.android.providers.contacts/databases # ls
contacts2.db
contacts2.db-journal
contacts2.db-mj6E07C4A4
profile.db
profile.db-journal

The most important and the central database here is contacts2.db. To view what’s in this database, you can utilize the tool called sqlite3 provided by Google. This is the link to the official description of the tool. This tool is under /system/bin/ by default in Android. But in case you cannot find it on you device, you can use Eclipse SQLite Plugin to visualize the table structure and data. See this article Browse an Android Emulator SQLite Database in Eclipsefor help.

To view the db under command line, execute:

# sqlite3 contacts2.db 

sqlite> .tables
_sync_state               phone_lookup              view_data              
_sync_state_metadata      photo_files               view_data_usage_stat   
accounts                  properties                view_entities          
activities                raw_contacts              view_groups            
agg_exceptions            search_index              view_raw_contacts      
android_metadata          search_index_content      view_raw_entities      
calls                     search_index_docsize      view_stream_items      
contacts                  search_index_segdir       view_v1_contact_methods
data                      search_index_segments     view_v1_extensions     
data_usage_stat           search_index_stat         view_v1_group_membership
default_directory         settings                  view_v1_groups         
directories               status_updates            view_v1_organizations  
groups                    stream_item_photos        view_v1_people         
mimetypes                 stream_items              view_v1_phones         
name_lookup               t9_lookup                 view_v1_photos         
nickname_lookup           v1_settings               visible_contacts       
packages                  view_contacts             voicemail_status  

3 most important tables are contacts, raw_contacts and data.

For a explaination of these 3 tables, see Contact Provider

Let’s take a look at the schema of the tables:

sqlite> .schema contacts
CREATE TABLE contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT,name_raw_contact_id INTEGER REFERENCES raw_contacts(_id),photo_id INTEGER REFERENCES data(_id),photo_file_id INTEGER REFERENCES photo_files(_id),custom_ringtone TEXT,send_to_voicemail INTEGER NOT NULL DEFAULT 0,times_contacted INTEGER NOT NULL DEFAULT 0,last_time_contacted INTEGER,starred INTEGER NOT NULL DEFAULT 0,has_phone_number INTEGER NOT NULL DEFAULT 0,lookup TEXT,company TEXT,nickname TEXT,contact_account_type TEXT,status_update_id INTEGER REFERENCES data(_id));
CREATE INDEX contacts_has_phone_index ON contacts (has_phone_number);
CREATE INDEX contacts_name_raw_contact_id_index ON contacts (name_raw_contact_id);
sqlite> .schema raw_contacts
CREATE TABLE raw_contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT,account_name STRING DEFAULT NULL, account_type STRING DEFAULT NULL, data_set STRING DEFAULT NULL, sourceid TEXT,raw_contact_is_read_only INTEGER NOT NULL DEFAULT 0,version INTEGER NOT NULL DEFAULT 1,dirty INTEGER NOT NULL DEFAULT 0,deleted INTEGER NOT NULL DEFAULT 0,contact_id INTEGER REFERENCES contacts(_id),aggregation_mode INTEGER NOT NULL DEFAULT 0,aggregation_needed INTEGER NOT NULL DEFAULT 1,custom_ringtone TEXT,send_to_voicemail INTEGER NOT NULL DEFAULT 0,times_contacted INTEGER NOT NULL DEFAULT 0,last_time_contacted INTEGER,starred INTEGER NOT NULL DEFAULT 0,display_name TEXT,display_name_alt TEXT,display_name_source INTEGER NOT NULL DEFAULT 0,phonetic_name TEXT,phonetic_name_style TEXT,sort_key TEXT COLLATE PHONEBOOK,sort_key_alt TEXT COLLATE PHONEBOOK,sort_key_custom TEXT COLLATE PHONEBOOK,name_verified INTEGER NOT NULL DEFAULT 0,sync1 TEXT, sync2 TEXT, sync3 TEXT, sync4 TEXT );
CREATE INDEX raw_contact_sort_key1_index ON raw_contacts (sort_key);
CREATE INDEX raw_contact_sort_key2_index ON raw_contacts (sort_key_alt);
CREATE INDEX raw_contact_sort_key_custom_index ON raw_contacts (sort_key_custom);
CREATE INDEX raw_contacts_contact_id_index ON raw_contacts (contact_id);
CREATE INDEX raw_contacts_source_id_data_set_index ON raw_contacts (sourceid, account_type, account_name, data_set);
CREATE INDEX raw_contacts_source_id_index ON raw_contacts (sourceid, account_type, account_name);
CREATE TRIGGER raw_contacts_deleted    BEFORE DELETE ON raw_contacts BEGIN    DELETE FROM data     WHERE raw_contact_id=OLD._id;   DELETE FROM agg_exceptions     WHERE raw_contact_id1=OLD._id        OR raw_contact_id2=OLD._id;   DELETE FROM visible_contacts     WHERE _id=OLD.contact_id       AND (SELECT COUNT(*) FROM raw_contacts            WHERE contact_id=OLD.contact_id           )=1;   DELETE FROM default_directory     WHERE _id=OLD.contact_id       AND (SELECT COUNT(*) FROM raw_contacts            WHERE contact_id=OLD.contact_id           )=1;   DELETE FROM contacts     WHERE _id=OLD.contact_id       AND (SELECT COUNT(*) FROM raw_contacts            WHERE contact_id=OLD.contact_id           )=1; END;
CREATE TRIGGER raw_contacts_marked_deleted    AFTER UPDATE ON raw_contacts BEGIN    UPDATE raw_contacts     SET version=OLD.version+1      WHERE _id=OLD._id       AND NEW.deleted!= OLD.deleted; END;
sqlite> .schema data
CREATE TABLE data (_id INTEGER PRIMARY KEY AUTOINCREMENT,package_id INTEGER REFERENCES package(_id),mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL,raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL,is_read_only INTEGER NOT NULL DEFAULT 0,is_primary INTEGER NOT NULL DEFAULT 0,is_super_primary INTEGER NOT NULL DEFAULT 0,data_version INTEGER NOT NULL DEFAULT 0,data1 TEXT,data2 TEXT,data3 TEXT,data4 TEXT,data5 TEXT,data6 TEXT,data7 TEXT,data8 TEXT,data9 TEXT,data10 TEXT,data11 TEXT,data12 TEXT,data13 TEXT,data14 TEXT,data15 TEXT,data_sync1 TEXT, data_sync2 TEXT, data_sync3 TEXT, data_sync4 TEXT );
CREATE INDEX data_mimetype_data1_index ON data (mimetype_id,data1);
CREATE INDEX data_raw_contact_id ON data (raw_contact_id);
CREATE TRIGGER data_deleted BEFORE DELETE ON data BEGIN    UPDATE raw_contacts     SET version=version+1      WHERE _id=OLD.raw_contact_id;   DELETE FROM phone_lookup     WHERE data_id=OLD._id;   DELETE FROM status_updates     WHERE status_update_data_id=OLD._id;   DELETE FROM name_lookup     WHERE data_id=OLD._id; END;
CREATE TRIGGER data_updated AFTER UPDATE ON data BEGIN    UPDATE data     SET data_version=OLD.data_version+1      WHERE _id=OLD._id;   UPDATE raw_contacts     SET version=version+1      WHERE _id=OLD.raw_contact_id; END;

To visiualize the table, here is the screen short from Eclipse SQlite Plugin:

contacts table

raw_contacts table

data table

一道很考验java功底的题目

Lucas Xu

问题 Challenge: can you write code that invokes foo()?

1
2
3
4
5
6
7
8
class Impossible {
	 public Impossible() {
		 throw new AssertionError("you cannot instantiate me"); 
	 }
	 public void foo() { 
		System.out.println("The impossible is possible!"); 
	}
}

这是来自Android team 的几位工程师的博客。 众志成城, 他们想出了很多解法。大家发挥一下想象力,看是不是可以找到解法。

Android Account Manager

Android系统本身提供了多用户账户的支持。 这里的多用户账户不是指操作系统中的用户,而是不同网络服务的账户, 如Google账户, Facebook账户, Twitter账户等。

对于android操作系统来讲,本身就是Linux系统,是一个支持多用户的系统。 每一个应用对应于一个process,运行时会有一个独立的UID, 也就对应于Linux中的用户。 开发者可以在adb下使用 ps 命令来查看。 如下,左边第一列就是运行对应应用的用户名. 可以看到, 对于安装的应用而言,每个应用都有自己独立的UID, 如com.baidu.input的UID为app_67

root      117   2     0      0     c01839a4 00000000 S ext4-dio-unwrit
system    133   1     17020  4296  c052d258 400307b0 S /system/bin/servicemanager
root      134   1     6040   996   ffffffff ffff0520 S /system/bin/vold
system    138   1     64424  11408 ffffffff 400c17b0 S /system/bin/surfaceflinger
root      139   1     445988 40160 ffffffff 4008d8d4 S zygote
drm       140   1     18912  4340  ffffffff 400507b0 S /system/bin/drmserver
media     141   1     49032  8836  ffffffff 400207b0 S /system/bin/mediaserver
bluetooth 142   1     1368   820   c01fad68 ffff0520 S /system/bin/dbus-daemon
root      143   1     5692   992   ffffffff 400ac578 S /system/bin/installd
keystore  144   1     1764   564   c05797b8 4004f0b8 S /system/bin/keystore
system    146   1     18832  4248  ffffffff 4007b7b0 S /system/bin/fmradioserver
root      147   1     10428  1312  ffffffff 4006f6d4 S /system/bin/thermald
nobody    164   1     7000   340   ffffffff 400be7b0 S /system/bin/rmt_storage
system    165   1     8360   1292  ffffffff 4005f0b8 S /system/bin/time_daemon
root      166   1     4504   216   ffffffff 0000829c S /sbin/adbd
radio     174   1     13668  3444  ffffffff ffff0520 S /system/bin/rild
radio     176   1     10604  652   ffffffff 400ee8d4 S /system/bin/qmuxd
radio     178   1     5688   932   ffffffff 400cb6d4 S /system/bin/netmgrd
root      249   2     0      0     c01839a4 00000000 S rpcrotuer_smd_x
root      250   2     0      0     c0125c10 00000000 S krpcserversd
root      251   2     0      0     c012462c 00000000 D krmt_storagecln
root      252   2     0      0     c01273c0 00000000 D krmt_storagecln
root      266   2     0      0     c0210144 00000000 S flush-179:0
system    305   139   578160 86556 ffffffff 4008d7b0 S system_server
system    427   139   487128 57384 ffffffff 4008e4c4 S com.android.systemui
app_67    496   139   463356 44732 ffffffff 4008e4c4 S com.baidu.input
radio     511   139   511076 68580 ffffffff 4008e4c4 S com.android.phone
app_22    520   139   469480 47564 ffffffff 4008e4c4 S com.lbe.security.miui
app_29    536   139   480532 82768 ffffffff 4008e4c4 S com.miui.home
app_67    568   496   1320   924   c01f1f28 40090594 S logcat
app_47    593   139   453256 32464 ffffffff 4008e4c4 S com.android.smspush
app_0     605   139   508268 62564 ffffffff 4008e4c4 S android.process.acore
app_10    622   139   515376 49132 ffffffff 4008e4c4 S com.google.process.gapps
root      732   1     5252   516   ffffffff 400f56d4 S /system/bin/mpdecision
app_82    1272  1     1156   764   c01fad68 400758d4 S logcat
app_72    1552  139   469184 33464 ffffffff 4008e4c4 S com.oasistudio.tk
app_82    6993  139   473684 37396 ffffffff 4008e4c4 S cn.lookoo.tuangou
app_82    7006  6993  1024   632   c01fad68 400ef8d4 S logcat
root      18719 2     0      0     c0121ccc 00000000 D kworker/u:3
root      19261 2     0      0     c018dd14 00000000 S iscan_sysioc
root      19262 2     0      0     c018dd14 00000000 S dhcp_sysioc
root      19263 2     0      0     c018dd14 00000000 S dhd_watchdog
root      19264 2     0      0     c018dd14 00000000 S dhd_dpc
root      19265 2     0      0     c018dd14 00000000 S dhd_sysioc
log       19266 1     716    292   c03a6b1c 4002b578 S /system/bin/logwrapper
wifi      19268 19266 2468   1252  c01fad68 400f28d4 S /system/bin/wpa_supplicant
app_87    19394 139   461244 38592 ffffffff 4008e4c4 S com.box.brian.activity
root      21861 2     0      0     c0181c44 00000000 S kworker/u:1
app_99    22996 139   494164 35896 ffffffff 4008e4c4 S com.renren.mobile.chat:sixinpush
root      23578 2     0      0     c0210144 00000000 S flush-179:96
app_96    23724 139   454356 33508 ffffffff 4008e4c4 S com.mbook.itaoshu
app_88    23830 139   477360 52496 ffffffff 4008e4c4 S com.wandoujia.phoenix2
dhcp      25092 1     952    468   c01fad68 ffff0520 S /system/bin/dhcpcd
root      25559 1     9648   1412  ffffffff ffff0520 S /system/bin/netd
root      26702 1     728    292   c05797b8 4008c0b8 S /system/bin/debuggerd
app_26    27385 139   470492 40448 ffffffff 4008e4c4 S com.google.android.apps.maps:NetworkLocationService
app_43    28373 139   476148 41240 ffffffff 4008e4c4 S com.android.vending
app_91    28402 139   463464 36712 ffffffff 4008e4c4 S com.renren.mobile.android
app_0     28696 139   456256 33980 ffffffff 4008e4c4 S com.android.contacts
app_18    28726 139   500412 61412 ffffffff 4008e4c4 S com.google.android.gm
app_93    28801 139   453896 31740 ffffffff 4008e4c4 S com.fractalist.MobileAcceleration
app_38    28814 139   454696 32664 ffffffff 4008e4c4 S com.android.quicksearchbox
app_10    28829 139   458112 35592 ffffffff 4008e4c4 S com.google.android.gsf.login
root      28885 2     0      0     c0181c44 00000000 S kworker/0:1
app_49    28886 139   470840 41624 ffffffff 4008e4c4 S com.xiaomi.channel
root      28931 2     0      0     c0181c44 00000000 S kworker/u:0
root      28939 2     0      0     c0181c44 00000000 S kworker/0:0
app_48    28987 139   453272 31420 ffffffff 4008e4c4 S com.qualcomm.wiper
app_60    28999 139   454104 33892 ffffffff 4008e4c4 S com.glorymob.joymax.view
root      29103 2     0      0     c0181c44 00000000 S kworker/u:4
app_30    29142 139   459356 40064 ffffffff 4008e4c4 S com.android.mms
system    29168 139   477500 49812 ffffffff 4008e4c4 S com.android.settings
app_15    29212 139   477548 46216 ffffffff 4008e4c4 S com.android.email
app_16    29232 139   460760 32804 ffffffff 4008e4c4 S com.android.exchange
root      29323 2     0      0     c0181c44 00000000 S kworker/0:2
root      29324 2     0      0     c0181c44 00000000 S kworker/0:3
app_26    29345 139   460068 36716 ffffffff 4008e4c4 S com.google.android.apps.maps
app_14    29386 139   454912 34484 ffffffff 4008e4c4 S android.process.media
root      29439 166   804    432   c01090a8 400d6f94 S /system/bin/sh
root      29444 29439 980    364   00000000 400ed578 R ps

由于Android系统是为互联网而设计的,自然需要对各种网络服务有很好的支持。 Android的解决方案是提供AccountManager来管理不同服务的账户。 AccountManager是由系统提供的服务([AccountManagerService]), 从而很好的在系统层面解决不同应用共享服务账户的问题。

比如, 用户注册了Google账户,使用这个账户可以登陆Google+,Google Play, Gmail, 也可以同步联系人甚至手机上的其他设置如WI-FI密码和浏览器书签到Google的服务器上。 由于这些服务是由不同的应用提供的,如果账户由每个应用自己单独处理,将是一件很繁琐的事情。 甚至处理不好, 会带来很大的安全隐患。 现在好了,Android在系统级别提供了账户管理功能, 用户只要去账户管理中心登陆账户, 不同应用就可以想账户管理中心请求账户访问权限,而不需要应用本身去维护和管理这些账户,对应用开发来说既简单又安全了。

Android Account 管理类库介绍

Android中和账户相关的API都在android.accounts包下。

重要的接口和类有:

Interfaces

  • AccountManagerCallback<V> 包含回调函数。 类似于我们经常写的Listener
  • AccountManagerFuture<V> 异步调用AccountManager的结果。
  • OnAccountsUpdateListener AccountMonitor用到的回调接口。

Classes

  • AbstractAccountAuthenticator 如果开发者需要实现自己的认证方式, 可以通过继承这个类来实现
  • Account 表示我们的账户
  • AccountAuthenticatorActivity Base class for implementing an Activity that is used to help implement an AbstractAccountAuthenticator.
  • AccountAuthenticatorResponse Object used to communicate responses back to the AccountManager
  • AccountManager 账户管理的核心类,是访问账户的入口。
  • AuthenticatorDescription A Parcelable value type that contains information about an account authenticator.

使用[AccountManager]访问已经支持的账户

Android 系统默认支持Google账户, Microsoft Exchange账户,和普通邮件账户。

现在,假如你开发了一个应用,会使用到用户的Google账户, 比如, 类似于SMS Backup这种应用, 可以把手机端的短信同步到GMail邮箱当中。 当然你不希望已经在自己手机上登陆过Gmail账户的用户再次在你的应用中输入用户名和密码,用户也很害怕把密码告诉你。这种情况下, 你可以请求用户授予你访问他的账户的权利, 如同下图所示: ,用户也很害怕把密码告诉你。这种情况下, 你可以请求用户授予你访问他的账户的权利, 如同下图所示: Request Account Access from SMS Backup

将如何实现呢?

Google 提供了一个很好的例子, 在Google Task API 下。 简单的使用如下:

	mAccountManager = AccountManager.get(this);

    	Account[] accounts = mAccountManager.getAccounts();
    	for(Account account : accounts){
    		Log.i(TAG, String.format("account.name={0}, type={1}, content={2}",account.name, account.type, account.describeContents()));
    	}

扩展[AccountManager]支持自定义的账户

既然系统的AccountManager提供了这么多的便利, 你开始考虑把自己的在线服务加到Android系统中去了, 比如你有类似人人或者新浪微博, 想要把账户管理加到系统中去, 该怎么做呢?

Android Content Provider

Lucas Xu

ContactProvider

Table ContactsContract.RawContacts

| Column Name | example |description | |:———–|:———–|:————:| |long _ID ||Row ID. Sync adapters should try to preserve row IDs during updates. In other words, it is much better for a sync adapter to update a raw contact rather than to delete and re-insert it. |CONTACT_ID || read-only The ID of the row in the ContactsContract.Contacts table that this raw contact belongs to. Raw contacts are linked to contacts by the aggregation process, which can be controlled by the AGGREGATION_MODE field and ContactsContract.AggregationExceptions. int AGGREGATION_MODE read/write A mechanism that allows programmatic control of the aggregation process. The allowed values are AGGREGATION_MODE_DEFAULT, AGGREGATION_MODE_DISABLED and AGGREGATION_MODE_SUSPENDED. See also ContactsContract.AggregationExceptions. int DELETED read/write The “deleted” flag: “0” by default, “1” if the row has been marked for deletion. When delete(Uri, String, String[]) is called on a raw contact, it is marked for deletion and removed from its aggregate contact. The sync adaptor deletes the raw contact on the server and then calls ContactResolver.delete once more, this time passing the CALLER_IS_SYNCADAPTER query parameter to finalize the data removal. int TIMES_CONTACTED read/write The number of times the contact has been contacted. To have an effect on the corresponding value of the aggregate contact, this field should be set at the time the raw contact is inserted. After that, this value is typically updated via markAsContacted(ContentResolver, long). long LAST_TIME_CONTACTED read/write The timestamp of the last time the contact was contacted. To have an effect on the corresponding value of the aggregate contact, this field should be set at the time the raw contact is inserted. After that, this value is typically updated via markAsContacted(ContentResolver, long). int STARRED read/write An indicator for favorite contacts: ‘1’ if favorite, ‘0’ otherwise. Changing this field immediately affects the corresponding aggregate contact: if any raw contacts in that aggregate contact are starred, then the contact itself is marked as starred. String CUSTOM_RINGTONE read/write A custom ringtone associated with a raw contact. Typically this is the URI returned by an activity launched with the ACTION_RINGTONE_PICKER intent. To have an effect on the corresponding value of the aggregate contact, this field should be set at the time the raw contact is inserted. To set a custom ringtone on a contact, use the field Contacts.CUSTOM_RINGTONE instead. int SEND_TO_VOICEMAIL read/write An indicator of whether calls from this raw contact should be forwarded directly to voice mail (‘1’) or not (‘0’). To have an effect on the corresponding value of the aggregate contact, this field should be set at the time the raw contact is inserted. String ACCOUNT_NAME read/write-once The name of the account instance to which this row belongs, which when paired with ACCOUNT_TYPE identifies a specific account. For example, this will be the Gmail address if it is a Google account. It should be set at the time the raw contact is inserted and never changed afterwards. String ACCOUNT_TYPE read/write-once The type of account to which this row belongs, which when paired with ACCOUNT_NAME identifies a specific account. It should be set at the time the raw contact is inserted and never changed afterwards.

To ensure uniqueness, new account types should be chosen according to the Java package naming convention. Thus a Google account is of type “com.google”.

String DATA_SET read/write-once The data set within the account that this row belongs to. This allows multiple sync adapters for the same account type to distinguish between each others’ data. The combination of ACCOUNT_TYPE, ACCOUNT_NAME, and DATA_SET identifies a set of data that is associated with a single sync adapter.

This is empty by default, and is completely optional. It only needs to be populated if multiple sync adapters are entering distinct data for the same account type and account name.

It should be set at the time the raw contact is inserted and never changed afterwards.

String SOURCE_ID read/write String that uniquely identifies this row to its source account. Typically it is set at the time the raw contact is inserted and never changed afterwards. The one notable exception is a new raw contact: it will have an account name and type (and possibly a data set), but no source id. This indicates to the sync adapter that a new contact needs to be created server-side and its ID stored in the corresponding SOURCE_ID field on the phone. int VERSION read-only Version number that is updated whenever this row or its related data changes. This field can be used for optimistic locking of a raw contact. int DIRTY read/write Flag indicating that VERSION has changed, and this row needs to be synchronized by its owning account. The value is set to “1” automatically whenever the raw contact changes, unless the URI has the CALLER_IS_SYNCADAPTER query parameter specified. The sync adapter should always supply this query parameter to prevent unnecessary synchronization: user changes some data on the server, the sync adapter updates the contact on the phone (without the CALLER_IS_SYNCADAPTER flag) flag, which sets the DIRTY flag, which triggers a sync to bring the changes to the server. String SYNC1 read/write Generic column provided for arbitrary use by sync adapters. The content provider stores this information on behalf of the sync adapter but does not interpret it in any way. String SYNC2 read/write Generic column for use by sync adapters. String SYNC3 read/write Generic column for use by sync adapters. String SYNC4 read/write Generic column for use by sync adapters.

OAuth 认证

Lucas Xu

我们开发各种应用和网站是给用户用的, 用户登陆之后就会积攒各种各样的数据。 但是现在的世界被几个用户大户所统治了, 如google, facebook, tencent, 人人等 大平台。用户在不同平台上注册不同账户, 进行不同的登录。小网站或者应用也需要有自己的用户系统。 这给用户带来了极大的麻烦。同时不同用户平台之间共享数据也非常不方便。 记得原来开心网为了进行病毒营销找你要邮箱账户密码的痛苦吗?其实世界不需要这么混乱的。

假如如果有你只需要在一家网站注册,在其他网站都可用, 那怎么样,是不是感觉世界很美好? 比如现实世界中的身份证, 只需要公安机关给你发一张, 你可以拿着去住宾馆, 也可以那个买菜刀和住院, 是不是方便很多。 这里, 公安机关是一个权威机构, 他出具的文件其他机构都会承认。 如果你的身份证丢了, 去公安机关开个证明,盖个戳也好使。

网络情况下, 事情变得混乱了,因为没有公安机关。 不过人人也都可以充当公安机关, 你只要在我的系统中注册为我的用户, 我就可以帮你在别的地方认证。 比如作为google的用户, Google可以帮你告诉Facebook说, 这个人是我的用户, 这是他的信息, 给你。 这样Facebook就可以从Google那里拿到你的信息。 这个过程中间有一些授权问题了。

首先, google不能在你不知情的情况下把你的信息告诉Facebook, (谁知道私下里会怎么样, 不过表面上还是需要文明一些的). 所以当Facebook想从google那里知道你的信息的时候, 一种方式是找你要在google那里的用户名和密码, 然后假冒你登陆google的系统,从而得到这些信息。 另外一种方式就是让你写个条子,“我授予Facebook访问我信息的权利”,然后签上你的大名,盖上你的戳,把条子交给Facebook, Facebook拿着这个条子去找Google, 说这是刘文彩同学给我的条子, 你看, 他授权我从你那儿访问他的数据了。 Google验证过签名之后发现是真的, 于是就让facebook把你的信息拿走了。

在网络世界里,你当然不可能真的去写张条子交给facebook, 那该怎么办呢?

Understanding the JVM Advanced Features and Best Practices

Lucas Xu

读《深入理解Java 虚拟机 - JVM 高级特性与最佳实践》by 周志明

Java 作为技术体系包含多个部分:

  • Java 语言
  • Java 虚拟机
  • JDK, API 等

##第一章 走进Java 关于OpenJDK 和JDK的关系请参考: http://en.wikipedia.org/wiki/OpenJDK

###自己编译JDK

  1. 下载源码  地址: http://download.java.net/openjdk/jdk7/
  2. 准备编译环境
  3. 编译

Android项目的依赖关系

3月9日下午02:35,2012年 ,来自Xavier Ducrohet
原文:Dealing with dependencies in Android projects by Xavier Ducrohet
翻译: Lucas Xu

在Android SDK Tools和Eclipse ADT 插件的第17版本 (revision 17)中,我们对Android项目的依赖关系管理做了很多改变。

我们所做的第一个改变是调整基于 Ant 的编译系统和 Eclipse ADT 插件,使他们具有相同的行为。

Android 项目包含源代码文件夹,以及对库项目 (android library projects) 和 jar 文件的依赖。 不需要其他多余的设置,只需要在project.properties中添加对于库项目的依赖,Android项目就会在动在 classpath 中添加以下依赖项:

  • libs/*.jar 的内容
  • 库项目(library project)的输出。
  • 库项目的 libs/*.jar 的内容。

这些依赖项,加上项目自身源代码编译的结果,被一起交给DEX工具,然后被转化成字节码从而打包到最终的APK当中。

因为一个项目可能依赖于好几个使用相同的 jar 文件的库项目,Android 编译系统会检查所有所需的 jar 文件,检测出来自不同的库项目的重复 jar 文件,并删除这些重复的jar引用。这会防止可怕的”already added” DX错误。

下面介绍了系统如何发现重复引用。

重要的变化 我们已经改变了库项目生成和打包R类的方式:

  • R类不再被打包到库项目的输出 jar 包中。
  • 库项目不再为其所依赖的库项目生成R类。 只有主应用项目才会在生成本身R类的同时为所依赖的库项目生成R类。

这意味着库项目不能导入(import)另一个库项目的R类。 其实这是没有必要的,因为它们自身的R类包含所有必要的资源。

请注意,应用程序的项目仍然可以导入所引用的库项目的R类,但是重申一遍,其实没有必要这么做, 因为他们本身的R类包括所有的资源。

Eclipse的具体变化

由于不光包含库项目,被称为”Library Projects”的动态class path容器 (dynamic class path container) 已更名为“Android Dependancies”。

现在被库项目引用的纯 Java 项目也可以填充classpath容器。 如果这些Java项目也引用其他 Java 项目或 jar 文件,它们将被自动添加(现在也支持通过 user libraries引用的jar文件)。

重要: 仅当被引用的项被标记为”exported”时这才起作用。 请注意,默认情况下,当一个项目或jar文件被添加到项目的build path 中时是不会被标记为”exported”。

库项目( 以及它们所引用的lib/*.jar文件)总是会被标记为”exported”。

重要: 如果你还在手动引用jar类库,而不是把他们放在libs目录下, 注意以下几点:

  • 如果该项目是一个 library project,应用项目默认情况下看不见这些jar 类库。 你必须把这些类库挪到”libs”子目录下。
  • 如果该项目是一个应用程序项目,你可以这样做,但你必须确保把引用的jar文件标记为”exported”。

下图介绍了如何将Java项目和jar类库标记为”exported”(Android Dependencies 容器不必被标为”exported”,反正它总是会被导出):

Mark references as exported in Eclipse

再次强调一下, 重复的引用(包括引用的项目和 jar 文件)会被自动发现并且删除掉。

依赖解析

当一个项目引用了两个库项目,都需要相同的jar文件,编译系统需要检测和解决这种重复。

一个完整的依赖系统会将每个jar文件关联到一个完全限定名 (fully qualified name) 和一个版本号,以决定使用哪个版本。

不幸的是,Android编译系统暂时还没有一个完整的依赖解析系统。 在此期间,我们按照下列规则实现了一个简单的系统:

** 严格按照文件名来识别 jar 文件 **

这意味着 mylib.jar 和 mylib-v2.jar 是不同的两个文件,虽然它们实际上是不同版本的同一个类库, 他们都将被打包,从而可能导致 dx “already added” 错误。

对于具有相同文件名的 jar 文件,“相同版本”是指完全相同的文件。

目前,我们的检测是非常基本的,只检查这些文件是不是有相同的大小和sha1 值。

如果两个库都包含在他们的libs文件夹名为mylib.jar文件,但是这两个文件是不同的,编译系统将不能指出这种依赖错误。

解决的办法: 如果它们是相同的类库,确保两个jar文件实际上是同一个。 如果是不同的两个类库,把他们重新命名为不同的jar包。

特殊情况: android-support-v4.jar 和 android-support-v13.jar

我们把这两个库当作特殊情况来处理, 因为-V13里面包含一个完整-V4的版本。 如果发现两者同时存在,只有V13被使用。

请注意,我们不能保证-V13中使用的-V4版本和其他老的类库使用到的-V4版本是相同的。 我们建议,当您更新您的项目与新版本的support library,你在同一时间更新您的所有项目,无论他们使用-V4还是-V13。