数码鹭岛论坛

标题: Android 技术专题系列 [打印本页]

作者: 翔子    时间: 2010-10-13 16:03
标题: Android 技术专题系列
来源: 乡下鱼的博客
http://blog.sina.com.cn/countryfish

Android 技术专题系列之一 -- Android 是什么


Android 是什么?
Android是一个完整的手机软件平台,包含底层的操作系统(Linux), 中间件以及一些关键应用。 Android 还提供了一个SDK 帮助开发者使用Java语言来开发。

特性

Android 架构
下图给出了Android平台的主要组成元素(参见
应用
Android平台预装了一些核心手机应用,包括电子邮件,短信,日历,地图,浏览器,地址本等等。这些应用都是使用Java语言编写。开发者也需要使用Java语言在Android平台上开发自己的应用程序。
应用框架
核心应用(电子邮件,短信等)所使用的任何API, Android 开发者都可以访问。Android架构设计的一个重要考虑是使组件(component)易于复用:任意一个应用都可以向系统发布功能,而任意一个其他应用都可以调用这些功能(在不违背系统安全策略前提下)。这个机制还使任意组件(component)都可被开发者替换。

在应用之下是一组服务 (service)和功能库, 包括:

Android平台包含一套C/C++库,供上层的组件使用。开发者不能直接访问这些库,但是可以通过Android应用框架来访问。下面列出了一些核心库:
Android runtime
Android 包含一套核心库,提供了Java核心库的大多数功能。
每个Android应用运行在自己的进程空间中, 运行在一个单独的Dalvik 虚拟机实例上。Dalvik的设计能够保证同一个设备上有效的运行多个虚拟机(实例)。Dalvik上运行的可执行文件格式是Dalvik Executable (.dex)。这个格式文件占用较小内存。VM是Register-based (?),运行的类由一个Java 编译器编译生成,然后由"dx"工具转化成.dex格式。
Dalvik虚拟机依赖Linux kernel 提供提供一些基础功能,如线程,初级内存管理等。
Linux Kernel
Android依赖Linux 内核2.6提供的一些核心功能,如安全机制,内存管理,进程管理,网络协议栈和驱动模型等。内核还为硬件提供一个抽象层。
作者: 翔子    时间: 2010-10-13 16:04
标题: Android 技术专题系列之二 -- telephony
Android 技术专题系列之二 -- telephony第一部分 c代码
Android源码中,hardware/ril目录中包含着Android的telephony底层源码。这个目录下包含着三个子目录,下面是对三个子目录的具体分析。
一、目录hardware/ril/include分析:                                               
只有一个头文件ril.h包含在此目录下。ril.h中定义了76个如下类型的宏:
RIL_REQUEST_XXX ,
这些宏代表着客户进程可以向Android telephony发送的命令,包括SIM卡相关的功能,打电话,发短信,网络信号查询等。好像没有操作地址本的功能?

                                                                                       
二、目录hardware/ril/libril分析。本目录下代码负责与客户进程进行交互。在接收客户进程命令后,调用相应函数进行处理,然后将命令响应结果传回客户进程。在收到来自网络端的事件后,也传给客户进程。
文件ril_commands.h:列出了telephony可以接收的命令;每个命令对应的处理函数;以及命令响应的处理函数。文件ril_unsol_commands.h:列出了telephony可以接收的事件类型;对每个事件的处理函数;以及WAKE Type???文件ril_event.h/cpp:处理与事件源(端口,modem等)相关的功能。ril_event_loop监视所有注册的事件源,当某事件源有数据到来时,相应事件源的回调函数被触发(firePending -> ev->func())
文件ril.cpp:RIL_register函数:打开监听端口,接收来自客户进程的命令请求 (s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);),当与某客户进程连接建立时,调用listenCallback函数;创建一单独线程监视并处理所有事件源 (通过ril_event_loop)

listenCallback函数:当与客户进程连接建立时,此函数被调用。此函数接着调用processCommandsCallback处理来自客户进程的命令请求

processCommandsCallback函数:具体处理来自客户进程的命令请求。对每一个命令,ril_commands.h中都规定了对应的命 令处理函数(dispatchXXX),processCommandsCallback会调用这个命令处理函数进行处理。

dispatch系列函数:此函数接收来自客户进程的命令己相应参数,并调用onRequest进行处理。

RIL_onUnsolicitedResponse函数:将来自网络端的事件封装(通过调用responseXXX)后传给客户进程。

RIL_onRequestComplete函数:将命令的最终响应结构封装(通过调用responseXXX)后传给客户进程。

response系列函数:对每一个命令,都规定了一个对应的response函数来处理命令的最终响应;对每一个网络端的事件,也规定了一个对应的 response函数来处理此事件。response函数可被onUnsolicitedResponse或者onRequestComplete调用。

三、目录hardware/ril/reference-ril分析。本目录下代码主要负责与modem进行交互。 文件reference-ril.c:此文件核心是两个函数:onRequest和onUnsolicitedonRequest 函数:在这个函数里,对每一个RIL_REQUEST_XXX请求,都转化成相应的AT command,发送给modem,然后睡眠等待。当收到此AT command的最终响应后,线程被唤醒,将响应传给客户进程(RIL_onRequestComplete -> sendResponse)。

onUnsolicited函数:这个函数处理modem从网络端收到的各种事件,如网络信号变化,拨入的电话,收到短信等。然后将时间传给客户进程 (RIL_onUnsolicitedResponse -> sendResponse)

文件atchannel.c:负责向modem读写数据。其中,写数据(主要是AT command)功能运行在主线程中,读数据功能运行在一个单独的读线程中。函数at_send_command_full_nolock:运行在主线程里面。将一个AT command命令写入modem后进入睡眠状态(使用 pthread_cond_wait或类似函数),直到modem读线程将其唤醒。唤醒后此函数获得了AT command的最终响应并返回。

函数readerLoop运行在一个单独的读线程里面,负责从modem中读取数据。读到的数据可分为三种类型:网络端传入的事件;modem对当前AT command的部分响应;modem对当前AT command的全部响应。对第三种类型的数据(AT command的全部响应),读线程唤醒(pthread_cond_signal)睡眠状态的主线程。

第二部分 Java代码
Android中,telephony相关的java代码主要在下列目录中:
1. frameworks/base/telephony/java/android/telephony
2. frameworks/base/telephony/java/com/android/internal/telephony
3. frameworks/base/services/java/com/android/server/TelephonyRegistry.java
4. packages/apps/Phone

其中,目录1中的代码提供Android telephony的公开接口,任何具有权限的第三方应用都可使用,如接口类TelephonyManager。
目录2/3中的代码提供一系列内部接口,目前第三方应用还不能使用。当前似乎只有packages/apps/Phone能够使用
目录4是一个特殊应用,或者理解为一个平台内部进程。其他应用通过intent方式调用这个进程的服务。

TelephonyManager主要使用两个服务来访问telephony功能:
1. ITelephony, 提供与telephony 进行操作,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java实现。
2. ITelephonyRegistry, 提供登记telephony事件的接口。由frameworks/base/services/java/com/android/server/TelephonyRegistry.java实现。

interface CommandsInterface 描述了对电话的所有操作接口,如命令, 查询状态,以及电话事件监听等
class BaseCommands是CommandsInterface的直接派生类,实现了电话事件的处理(发送message给对应的handler)。
而class RIL又派生自BaseCommands。RIL负责实际实现CommandsInterface中的接口方法。RIL通过Socket和rild守护进 程进行通讯。对于每一个命令接口方法,如acceptCall,或者状态查询,将它转换成对应的RIL_REQUEST_XXX,发送给rild。线程 RILReceiver监听socket,当有数据上报时,读取该数据并处理。读取的数据有两种,
1. 电话事件,RIL_UNSOL_xxx, RIL读取相应数据后,发送message给对应的handler (详见函数processUnsolicited)
2. 命令的异步响应。(详见函数processSolicited)

interface Phone描述了对电话的所有操作接口。 PhoneBase直接从Phone 派生而来。而另外两个类,CDMAPhone和GSMPhone,又从PhoneBase派生而来,分别代表对CDMA 和GSM的操作。
PhoneProxy也从Phone直接派生而来。当当前不需要区分具体是CDMA Phone还是GSM Phone时,可使用PhoneProxy。

抽象类Call代表一个call,有两个派生类CdmaCall和GsmCall。

interface PhoneNotifier定义电话事件的通知方法
DefaultPhoneNotifier从PhoneNotifier派生而来。在其方法实现中,通过调用service ITelephonyRegistry来发布电话事件。
service ITelephonyRegistey由frameworks/base/services/java/com/android/server /TelephonyRegistry.java实现。这个类通过广播intent,从而触发对应的broadcast receiver。

在PhoneApp创建时,
   sPhoneNotifier = new DefaultPhoneNotifier();
   ...
   sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
然后根据当前phone是cdma还是gsm,创建对应的phone,如
   sProxyPhone = new PhoneProxy(new GSMPhone(context,
                            sCommandsInterface, sPhoneNotifier));




下面我们来研究一个电话打出去的流程。
1. TwelveKeyDialer.java, onKeyUp()
2. TwelveKeyDialer.java, placeCall()
3. OutgoingCallBroadcaster.java, onCreate()
    sendOrderedBroadcast(broadcastIntent, PERMISSION,
                new OutgoingCallReceiver(), null, Activity.RESULT_OK, number, null);
4. OutgoingCallBroadcaster.java, OutgoingCallReceiver
    doReceive -> context.startActivity(newIntent);
5. InCallScreen.java, onCreate/onNewIntent
6. InCallScreen.java, placeCall
7. PhoneUtils.java, placeCall
8. GSMPhone.java, dial
9. GsmCallTracker.java, dial
10. RIL.java, dial
      RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
      ...
      send(rr);

下面来研究一个incoming call的流程:
1. 创建GsmPhone时,mCT = new GsmCallTracker(this);
2. 创建GsmCallTracker时:
    cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); -->   
    mCallStateRegistrants.add(r);
3. RIL中的RILReceiver线程首先读取从rild中传来的数据:processResponse -> processUnsolicited
4. 对应于incoming call,RIL收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED 消息,触发mCallStateRegistrants中的所有记录。
5. GsmCallTracker处理EVENT_CALL_STATE_CHANGE,调用pollCallsWhenSafe
6. 函数pllCallsWhenSafe 处理:
     lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
     cm.getCurrentCalls(lastRelevantPoll);
7. RIL::getCurrentCalls
    RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result);
    ...
    send(rr);
8. 接着RIL调用processSolicited处理RIL_REQUEST_GET_CURRENT_CALLS的返回结果
9. GsmCallTracker的handleMessage被触发,处理事件EVENT_POLL_CALLS_RESULT,调用函数  
    handlePollCalls
10. handlPollCalls 调用
    phone.notifyNewRingingConnection(newRinging);

11. PhoneApp中创建CallNotifier
12. CallNotifier注册:
    registerForNewRingingConnection -> mNewRingingConnectionRegistrants.addUnique(h, what, obj);
作者: 翔子    时间: 2010-10-13 16:19
标题: Android 技术专题系列之三 -- 编译(build) 流程
Android 技术专题系列之三 -- 编译(build) 流程Android 使用的build系统与普通Linux软件使用的build工具有很大不同,因而,可能使熟悉传统Linux开发工具的程序员稍感陌生。普通的软件项目一般使用autotools工具,包括autoconf, automake, libtool等,利用这些工具来自动生成Makefile。而Android创建了一套自己的系统来生成Makefile。下面对Android的 build系统作一初步探讨。Android源码中的build/core/build-system.html是我们研究Android build系统的基本文档。本文基于这一文档并进一步展开。

Android build系统定义了一套自己的变量,宏等,如LOCAL_MODULE_TAGS, LOCAL_PACKAGE_NAME等。如何代入这些变量,如何展开这些宏,都在build/core目录下的文件中定义。

开发人员可以使用这些变量编写Android build系统的Makefile -- Android.mk。每个模块,如一个应用,一个共享库等,都有一个自己的Android.mk。在编译的时候,Android.mk中的宏就会被展开成相应的Make规则,变量也会代入相关的规则。下面以packages/apps/AlarmClock/Android.mk举例介绍:
~~~~~~~~
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng development

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := AlarmClock

include $(BUILD_PACKAGE)
~~~~~~~~~~
关于这些变量的含义,在build/core/build-system.html中都有具体介绍。值得注意的是,如果是生成一个可执行程序,则引用 BUILD_PACKAGE,如果生成一个library,则引用BUILD_SHARED_LIBRARY。我们可以模仿现有的Android.mk去创建自己的Android.mk。

build/envsetup.sh中引入了几个方便的命令可用来帮助执行build命令。
m: 执行对整个Android源码的build
mm: 执行对当前目录下模块的build.
printconfig: 当前build的配置情况。

在整个Android源码编译完成后,生成的二进制文件以及image等都安装在out/目录下。通过如下命令序列可以启动emulator (假设Android源码安装在~/mydroid目录下):
export ANDROID_PRODUCT_OUT=~/mydroid/out/target/product/generic/
cd ~/mydroid/out/host/linux-x86/obj/EXECUTABLES/emulator_intermediates
./emulator
作者: 翔子    时间: 2010-10-13 16:21
标题: Android 技术专题系列之四 -- 网络资源及常见命令
Android 技术专题系列之四 -- 网络资源及常见命令
1.如何本地化一个Android应用:
http://groups.google.com/group/android-developers/web/localizing-android-apps-draft
2.如何安装.apk到模拟器
http://openhandsetmagazine.com/2008/01/tips-how-to-install-apk-files-on-android-emulator/
3.Android工具介绍
http://code.google.com/android/intro/tools.html
4.电源管理
http://letsgoustc.spaces.live.com/blog/cns!89AD27DFB5E249BA!526.entry
5.how to deal with INSTALL_FAILED_UPDATE_INCOMPATIBLE error
http://groups.google.com/group/android-developers/browse_thread/thread/3f084d78a15f6d3d/26c3520a041a279b?lnk=raot
6. 查看日志
./adb logcat
7. 查看当前所有安装包
./adb shell pm list packages
8.卸载包 (注意和安装包时的文件名不同)
./adb shell pm uninstall com.example.webview
9. camera  机制
http://bbs.androidin.com/viewthread.php?tid=2438&extra=page%3D2
10. 书籍 http://andbook.anddev.org/
11. adb shell dumpsys package 关于package的详细信息。
12. 为emulator使用不同分辨率的skin, e.g.,
./emulator -skindir xxx/android-1.6/skins -skin WVGA800
13. android编译需要javac 1.5版本,如果系统中有多个javac时,可用下列命令:
sudo update-java-alternatives -s java-1.5.0-sun
14. 从1.6开始,sdk提供了工具zipalign。它通过调整package中资源的对齐方式,提高了应用性能,节约了内存使用空间以及电池掂量。推荐对每个package都执行下列操作:

    * 执行对齐:
      zipalign -v 4 source.apk destination.apk

    * 校验是否成功
      zipalign -c -v 4 application.apk
作者: 翔子    时间: 2010-10-13 16:24
标题: Android 技术专题系列之五 -- 本地化
Android 技术专题系列之五 -- 本地化
本文内容主要基于http://groups.google.com/group/android-developers/web/localizing-android-apps-draft
展开。

Android已经设计好了国际化(I18N)和本地化(L10N)的框架,只要编写程序时符合这个框架要求,实现程序的本地化并不困难。

下面以HelloAndroid (http://code.google.com/android/intro/hello-android.html中Eclipse SDK自动生成)为例,讨论如何实现HelloAndroid的中文化。
1)文件src/com/android/hello/R.java中列出了hello 字符串的id:
public final class R {
...
public static final class string {
        public static final int app_name=0x7f040001;
        public static final int hello=0x7f040000;
    }
}

2)文件res/values/strings.xml中列出了hello字符串的内容:
<string name="hello">Hello World, HelloAndroid</string>

3)创建一个新的目录和新的strings.xml文件,用来存储hello字符串的中文翻译:
res/values-zh/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">你好,Android</string>
    <string name="app_name">你好,Android</string>
</resources>

4) 修改文件 src/com/android/hello/HelloAndroid.java,


tv.setText("Hello, Android");改成 tv.setText(R.string.hello);

5)编译helloAndroid项目,设置虚拟机locale为zh-CN后重新启动emulator。在从eclipse中运行helloAndroid项目,这时,可见中文显示。
$./adb shell
#echo zh-CN > /data/locale;stop;sleep 5; start <sdk 1.0>

#setprop persist.sys.language zh;setprop persist.sys.country CN;stop;sleep 5;start <sdk 1.5>
作者: 翔子    时间: 2010-10-13 16:26
标题: Android 技术专题系列之六 --如何安装.apk到模拟
Android 技术专题系列之六 --如何安装.apk到模拟本文基于http://openhandsetmagazine.com/2008/01/tips-how-to-install-apk-files-on-android-emulator/。

在安装完SDK后,(假设SDK安装到/home/mydroid/sdk目录下)
1. 输出SDK_ROOT环境变量
export SDK_ROOT = /home/mydorid/sdk/tools
2. 启动emulator
cd /home/mydroid/sdk/tools
./emulator &
3. 安装.apk
./adb install your_app.apk

4. 如何在emulator和主机之间拷贝文件
adb push <local> <remote>  
  adb pull <remote> <local>
作者: 翔子    时间: 2010-10-13 16:30
标题: Android 技术专题系列之七 -- 输入法 框架
Android 技术专题系列之七 -- 输入法 框架


Android的输入法框架比价复杂。从进程的角度来讲,相关功能主要分布在下面三个位置:
  

客户端应用是一个包含有图形界面的应用,如地址本。图形界面上包含有能够接收输入的编辑框,如TextView。
输入法模块提供软键盘,将用户在软键盘上的按键输入根据某种算法(如Zi, T9, 国笔等)转换成单词,然后传递给客户端应用。目录development/samples/SoftKeyboard下提供了一个输入法模块实例。如果想要实现一个中文输入法,可参考这个实例。
平台部分实现一些管理功能,负责装载某个输入法模块,启动,终止该模块等。
相关代码主要位于下面几个位置。其中,位于3,5,6,7目录下的代码最值得关注。
1. frameworks/base/core/java/com/android/internal/view
这个目录下定义了几个重要的idl 接口。
IInputMethod.aidl 定义了IInputMethod idl 接口,用于客户端跨进程操作InputMethod接口。
IInputMethodSession.aidl 定义了IInputMethodSession接口,是IInputMethod的辅助接口。用于客户端跨进程操作InputMethodSession接口。

IInputMethodCallback.aidl定义了一个helper 接口,由客户端实现。IInputMethod.aidl和IInputMethodSession.aidl实例可以分别调用该接口中的不同方法

IInputMethodManager.aidl 定义了Input Method Manager的service接口。客户端通过InputMethodManager interface调用这个service。
InputMethodManagerService.java实现了IInputMethodManager.aidl接口

IInputMethodClient.aidl定义接口,标识一个Input Method Manager 的客户。这个service在客户端实现,提供给server端调用。

IInputContext.aidl定义了一个接口,由客户端提供InputMethod使用。InputMethod可以与客户端交互,调用客户端提供的callback。
IInputConnectionWrapper.java 实现了IInputContext接口。
IInputContextCallback.aidl定义了一个接口,定义了一组callback函数给IInputContext.aidl实例调用,从客户端返回信息给InputMethod。
InputConnectionWrapper.java实现了IInputContextCallback接口。

2. frameworks/base/services/java/com/android/server
InputMethodManagerService.java实现了IInputMethodManager.aidl接口

3. frameworks/base/core/java/android/view/inputmethod
这个目录下定义了几个重要的interface和类。
InputMethodManager.java实现了InputMethodManager 类。此类调用IInputMethodManager.aidl接口功能,而IInputMethodManager.aidl接口功能由InputMethodManagerService.java实现,并运行在不同于客户端进程的server进程中。

InputConnection.java定义了InputConnection interface。InputConnection 接口在输入法和客户端之间建立了一个连接,输入法可以使用该连接获取或发送信息给客户端。InputConnection实例由客户端创建之后传递给输入法使用。BaseInputConnection.java 实现了InputConnection接口的一个基类: BaseInputConnection。 EditableInputConnection.java实现了一个派生类

InputBinding.java 定义了类InputBinding,这个类实现了parcelable 接口。这个类的成员变量包含了客户端传向server的信息。

InputMethod.java定义了InputMethod interface。文件InputMethodService.java中类InputMethodImpl实现了这个接口。这个接口定义了一套操纵一个输入法的方法。如,createSession,startInput等。要编写一个具体输入法的话,就需要派生这个接口。

InputMethodSession.java定义了InputMethodSession接口。文件InputMethodService.java 中类InputMethodSessionImpl实现了这个接口。InputMethodSession是InputMethod的辅助接口,用于具体和某个输入法客户端交互。

CompletionInfo.java 类描述一个text completion.
EditorInfo.java类描述一个接收输入的view的属性,如内容属性(text, digit, etc)。
ExtractedText.java类描述从view中提取的传递给输入法的文本属性。


4. frameworks/base/core/java/com/android/internal/widget
EditableInputConnection.java实现了BaseInputConnection的一个派生类。

5. frameworks/base/core/java/android/inputmethodservice
这个目录下的代码提供了实现一个具体输入法的框架类。从这些类派生,就可以定制一个输入法。
SoftInputWindow.java中的SoftInputWindow类是一个Dialog子类。它代表一个输入法的顶级窗口(由窗口管理器管理),这个窗口由上到下,包含extractArea, candidatesArea, 和 inputArea。

Keyboard.java 中的Keyboard类装载并解析一个描述虚拟键盘(Soft Keyboard)的xml文件(如development/samples/SoftKeyboard/res/xml),并存储该键盘的属性,如该虚拟键盘包含多上行,每行有哪些键等。
KeyboardView.java 中的KeyboardView类是一个View子类。它根据Keyboard数据结构真正的在screen上画出一个虚拟键盘。这个虚拟键盘就是SoftInputWindow中的inputArea。

AbstractInputMethodService是Service的派生类,并实现了KeyEvent.Callback 接口。实现了InputMethod 和 InputMethodSession的基类。dispatchKeyEvent 函数将收到的key event传给相应的key 处理函数(在派生类中实现)。当这个service被客户端绑定时,其onBind()函数给客户端返回了一个IInputMethodWrapper实例,这个实例实现了IInputMethod idl接口。客户端可以使用该接口的相关功能。

IInputMethodWrapper.java 实现了IInputMethod idl 接口。这个类收到客户端的跨进程命令后,调用InputMethod完成相应功能。
IInputMethodSessionWrapper.java 实现了IInputMethodSession idl接口。这个类收到客户端的跨进程命令后,调用InputMethodSession完成相应功能。


6. frameworks/base/core/res/res/layout
这个目录下存放着一些系统资源。其中,
input_method.xml描述了一个输入法的窗口(即SoftInputWindow)布局,从上往下,依次排列extractArea, candidatesArea 和 inputArea。
input_method_extract_view.xml。

7. development/samples/SoftKeyboard
这个目录下代码实现了一个的输入法实例--软键盘英文/数字输入法。这里面实现的类大都是从frameworks/base/core/java/android/inputmethodservice 中的类派生而来。
AndroidManifest.xml:描述这个.apk提供的service以及关于这个输入法的一些信息。
res/xml/目录下存储着几个描述不同虚拟键盘的xml文件。
LatinKeyboard.java中的LatinKeyboard类是Keyboard的子类。
LatinKeyboardView.java中的LatinKeyboardView类是KeyboardView的子类。

8. frameworks/base/core/java/android/widget
在这里TextView.java是使用Input Method Framework (IMF)的客户端。TextView创建了一个InputMethodManager的实例并调用其restartInput 函数。
InputMethodManager::restartInput函数创建了一个InputConnection 实例并调用IInputMethodManager::startInput。
IInputMethodManager::startInput 函数使用mContext.bindService启动一个InputMethod service, 如 Sample Soft Keyboard。

9. frameworks/base/core/java/com/android/internal/widget
作者: 翔子    时间: 2010-10-13 16:39
标题: Android 技术专题系列之八 -- 浏览器及web widget
浏览器相关的代码主要位于以下三个位置:
1. external/webkit
该目录下存放开源的Webkit c/c++代码。
2. frameworks/base/core/java/android/webkit
external/webkit的java封装。提供了用于开发浏览器应用的java 类库。这些类库使用JNI访问external/webkit中相应的功能。
3. packages/apps/Browser/src/com/android/browser
浏览器应用。

web widget 目前种类比较多,比较流行的有Symbian, apple widget等。下图中的widget基于android示例HelloActivity,使用一个WebView去调用一个简单的widget。
值得注意的是,web widget中的javaScrip 可以调用java的函数,反之亦然。这将极大扩展web widget的功能。

Android <wbr>技术专题系列之八 <wbr>-- <wbr>浏览器及web <wbr>widget
~~~~
1. widget的index.html文件内容。此widget 利用google service API,实现语言翻译功能。将此文件拷贝到emulator上/data目录下。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
        <title>Google Translator</title>
        <meta name="generator" content="BBEdit 8.6" />
        <script src="main.js"></script>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">

    google.load("language", "1");

    function initialize() {
      var text = document.getElementByIdx("text").value;
      var tlang = document.getElementByIdx("tlang").value
      google.language.detect(text, function(result) {
        if (!result.error && result.language) {
          google.language.translate(text, result.language, tlang,
                                    function(result) {
            var translated = document.getElementByIdx("translation");
            if (result.translation) {
              translated.value = result.translation;
            }
          });
        }
      });
    }
    </script>
</head>
<body>
  <body>
        Auto detect source language and translate to:<br/>
        <select id="tlang">
          <option value="zh-TW">Chinese Trad</option>
          <option value="zh-CN">Chinese Simpl</option>
          <option value="en">English</option>
          <option value="fi">Finnish</option>
      <option value="fr">French</option>
<option value="de">German</option>
      <option value="it">Italian</option>
      <option value="ja">Japanese</option>
          <option value="ko">Korean</option>
          <option value="pt">Portuguese</option>
          <option value="es">Spanish</option>
        </select><br/>
        <textarea id="text" name="query" height="100px">Enter string here</textarea> <BUTTON TYPE=BUTTON onClick="initialize()">
        <I>GO</I></BUTTON><br/>
    <textarea id="translation" name="results" height="100px">Results</textarea><br/>
  Copy from the above text box and paste the text where ever. If there is an update, it will be here: <a href="http://www.waiseto.com/2009/01/simple-google-translator-widget-for-s60.html">link</a> Enjoy W. Seto

</body>
</html>
~~~~~~~~~~~~~~~~~~
2. 调用widget的android程序代码
package com.example.android.hellowebview;

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.view.*;

import com.example.android.hellowebview.R;


public class HelloWebView extends Activity {


   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Set the layout for this activity.  You can find it
        // in res/layout/hello_activity.xml
        setContentView(R.layout.hello_activity);
        WebView wv;
        wv = (WebView) findViewById(R.id.text);
        wv.getSettings().setJavaScriptEnabled(true);
        wv.loadUrl("file:///data/index.html");

    }
}
~~~~~~~~~
3. 在AndroidManifest.xml 中得加入如下行,否则,此程序不能访问Internet。
<uses-permission android:name="android.permission.INTERNET" />
作者: 翔子    时间: 2010-10-13 16:42
标题: Android 技术专题系列之九 -- 图形系统
本文试图讲述Android图形系统的底层实现。Android图形系统底层实现非常复杂,文档较少,没有使用比较流行的图形组建如X window, Cairo等。

Android中的图形系统采用Client/Server架构。Server (即SurfaceFlinger)主要由c++代码编写而成。Client端代码分为两部分,一部分是由Java提供的供应用使用的api,另一部分则是由c++写成的底层实现。下图概要介绍了android图形系统的架构以及使用到的主要组件。
Android <wbr>技术专题系列之九 <wbr>-- <wbr>图形系统
Android <wbr>技术专题系列之九 <wbr>-- <wbr>图形系统

Android图形系统中一个重要的概念和线索是surface。View及其子类(如TextView, Button)要画在surface上。每个surface创建一个Canvas对象(但属性时常改变),用来管理view在surface上的绘图操作,如画点画线。每个canvas对象对应一个bitmap,存储画在surface上的内容。

每个Surface通常对应两个buffer,一个front buffer, 一个back buffer。其中,back buffer就是canvas绘图时对应的bitmap (研究android_view_Surface.cpp::lockCanvas)。因此,绘画总是在back buffer上,需要更新时,则将back buffer和front buffer互换。

The window is tied to a Surface and the ViewRoot asks the Surface for a
Canvas that is then used by the Views to draw onto. After View draw its data to canvas, ViewRoot
will call surface.unlockCanvasAndPost(canvas) to schedule surfaceFlinger::composeSurfaces() which do the actually display to  display panel.  SurfaceFlinger handles to transfers drawn data in canvas to surface front buffer or backbuffer

Except for SurfaceViews, different views within the same ViewRoot share the same surface.

Layer的概念:

每个surface又对应一个layer, SurfaceFlinger负责将各个layer的front
buffer合成(composite)绘制到屏幕上。

A Layer is something that can be composited by SurfaceFlinger (should
have been called LayerFlinger). There are several types of Layers if
you look in the code,  in particular the regular ones (Layer.cpp) ,
they are backed by a Surface, and the LayerBuffer (very badly chosen
name) which don't have a backing store, but receive one from their
client. .
Note that the GGLSurface type, should have been called GGLBuffer



Multiple layers are just composited to the final buffer in  their Z order.


有几个对象与Surface概念紧密相关:
1. Java Surface (frameworks/base/core/java/android/view/Surface.java)。该对象被应用间接调用(通过SurfaceView, ViewRoot等), 应用需要创建surface,(并同时创建canvas), 将图形绘制到这个对象上并最终投递到屏幕上。
2. C++ Surface (frameworks/base/libs/ui/Surface.cpp。 这个对象被Java Surface通过Jni 调用,实现Java Surface 的功能
3. ISurface (以及其派生类BnSurface)。这个对象是应用和server之间的接口。C++ Surface创建这个ISurface (BnSurface)并发送命令,如更新surface内容到屏幕上。Server端接受这个命令并执行相应操作。

研究一个surface如何创建的关键路径如下:
1. frameworks/base/core/java/android/view/Surface.java -- Surface::Surface ()
2. frameworks/base/core/jni/android_view_Surface.cpp -- Surface_init ()。在这个函数中SurfaceComposerClient 对象被创建。
3. frameworks/base/libs/ui/SurfaceComposerClient.cpp -- SurfaceComposerClient::SurfaceComposerClient (). 这个函数非常重要,在这里建立了client和server之间的桥梁。通过函数_get_surface_manager()获得了一个指向 server的IBinder 对象(具有ISurfaceComposer接口),之后通过这个IBinder就可以跨进程访问Server的功能。接着调用 ISurfaceComposer::createConnection()创建并返回了一个ISurfaceFlingerClient的 IBinder。
4. frameworks/base/libs/ui/SurfaceComposerClient.cpp -- SurfaceComposerClient::createSurface().这个函数中,利用前面获得的ISurfaceFlingerClient的IBinder,调用其createSurface接口。
5.frameworks/base/libs/surfaceflinger/SurfaceFlinger.cpp -- BClient::createSurface ()。BClient由ISurfaceFlingerClient派生而来。
6. frameworks/base/libs/surfaceflinger/SurfaceFlinger.cpp -- SurfaceFlinger:: createSurface()。这个函数为Surface创建一个对应的Layer。

上述关键路径中,1,2,3,4运行于client进程中,而5,6运行与server进程中。server作为一个service提供给client访问。

与图形相关的代码主要位于下列目录:
1、frameworks/base/graphics/java/android/graphics
2、frameworks/base/core/java/android/view
3、frameworks/base/core/java/android/widget
4、frameworks/base/opengl/
5、frameworks/base/libs/ui
6、frameworks/base/libs/surfaceflinger
7、frameworks/base/core/jni/android/graphics
8、frameworks/base/core/jni/android/opengl
9、frameworks/base/core/jni/android/android_view_*.cpp
10、external/skia


一、下列目录中的部分代码:
1、frameworks/base/graphics/java/android/graphics
2、frameworks/base/core/java/android/view
3、frameworks/base/core/java/android/widget

android.graphics, android.view和android.widget功能和其他类似的图形库如Qt/Gtk+差不多,分别提供基本的图形原语(如画点画线,设置图形上下文等),事件机制,以及开发图形用户界面的控件等。canvas 用于开发2D图形, Surface 代表一个可供图形系统绘制的surface。可在其上绘制2D活3D图形。


二. frameworks/base/opengl/
这个目录包含opengel的接口以及软件实现。在http://developer.android.com/guide/topics/graphics/opengl.html有详细介绍如何使用android.opengl开发3d graphics。

三.external/skia,台湾的Jserv先生有一篇比较好的介绍,感兴趣的读者可以参考他的博文(http://blog.linux.org.tw/~jserv/archives/002095.html)。简而言之,skia与cairo功能相当,封装底层的图形硬件,为上面的图形库提供最基础的操作图形硬件的原语。

四. frameworks/base/libs/ui 和 frameworks/base/libs/surfaceflinger
ISurface 定义了基础的Surface接口,供图形系统客户端(应用)和server端(即surfaceflinger)交互。
BpSurface是ISurface的派生类,提供接口供server 调用客户端功能;
BnSurface是ISurface的另一个派生类,提供接口供客户端调用server功能。当 server 收到来自客户端(通过BnSurace)的调用请求后,如registerBuffers, postBuffer等,BnSurface::onTransact被触发。
Surface (LayerBaseClient的私有类)是BnSurface的派生类。
SurfaceBuffer (SurfaceBuffer的私有类)是Surface的派生类。

ISurfaceComposer 定义了基础的接口,供客户端和server端交互。
BpSurfaceComposer是一个派生类,提供接口供server调用客户端功能;
BnSurfaceComposer是另一派生类,提供接口供客户端调用server功能。类 SurfaceFlinger 由BnSurfaceComposer派生而来。

SurfaceComposerClient直接供客户端使用,调用ISurface (BnSurface)和 ISurfaceComposer (BnSurfaceComposer)以及 ISurfaceFlingerClient 接口,与server交互。

BClient 派生自ISurfaceFlingerClient (BnSurfaceFlingerClient),调用server的createSurface,真正创建一个surface。每个surface对应一个layer.

egl_native_window_t 定义了一个本地window类 。这个类提供了对本地window的所有描述以及用于egl (opengl 与本地图形系统的接口)操作本地windwo的所有方法。
EGLNativeSurface是egl_native_window_t的一个派生类。
EGLDisplaySurface是EGLNativeSurface的派生类。 EGLDisplaySurface 是一个非常重要的类,在这个类里,真正打开framebuffer设备(/dev/graphics/fb0 或者/dev/fb0),并将这个设备封装成EGLDisplaySurface的形式供server使用。函数mapFrameBuffer打开framebuffer, 创建两个缓冲区,(一个是on screen front 缓冲区, 另一个back buffer, 可能位于offscreen framebuffer,也可能位于系统内存)。 函数swapBuffers将back buffer内容拷贝到front buffer中。

DisplayHardware 类中初始化了egl系统,并为本地窗口对象EGLDisplaySurface 创建了对应的EGLSurface 对象。surfaceflinger 使用DisplayHardware去和本地窗口打交道。



五、下列目录中的部分代码
7、frameworks/base/core/jni/android/graphics
8、frameworks/base/core/jni/android/opengl
9、frameworks/base/core/jni/android/android_view_*.cpp

这些目录下的代码在Java层的graphics 组件和native (c++)组件之间衔接,将java层的功能调用转换到对应的本地调用。

hardware/libhardware实现了HAL(Hardware Abstraction Layer)层,copybit device是其中一个模块。
作者: 翔子    时间: 2010-10-13 16:42
标题: Android 技术专题系列之十 -- Audio manager
Android的Audio Manager (即AudioFlinger)相对比较简单,代码主要集中在目录
frameworks/base/libs/audioflinger,  frameworks/base/media 和 hardware/libhardware_legacy/include/hardware_legacy下面。 Audio Manager的主要功能如下
1. 接收来自各个track的PCM data, 如普通的audio playback, ringtone, voice call等,
2. 管理多个输入输出设备,如mic,handset, speaker, bluetooth等
3. 将一路track上的数据输出到某个输出设备上
4.将多路track上的数据混音(mix)后再输出到某个设备上。
5. 录音。
作者: 翔子    时间: 2010-10-13 16:43
标题: Android 技术专题系列之十一 -- DRM
简而言之,DRM系统提供一套机制对用户使用手机上的媒体内容(如ringtong, mp3等)进行限制,如限制拷贝给第三方,限制使用次数或时限等,从而保护内容提供商的权利。建议读者可以阅读OMA DRM 的规范,以便更好的了解代码。

相关代码主要位于下列目录:
frameworks/base/media/java/android/drm/mobile1
frameworks/base/media/libdrm/moblile1
frameworks/base/media/libdrm/mobile2
packages/apps/Mms/src/com/android/mms/drm: drm
packages/providers/DrmProvider/src/com/android/providers/drm

下面先作简要分析:
frameworks/base/media/libdrm/moblile1应该是提供OMA DMA1.0的本地/c++ 实现
frameworks/base/media/libdrm/moblile2应该是提供OMA DAM2.0的本地/c++ 实现。

frameworks/base/media/java/android/drm/mobile1 对应用提供OMA DRM 1.0 (即frameworks/base/media/libdrm/moblile1)的java接口。目前尚没有OMA DRM 2.0的java接口,也就意味着OMA DRM 2.0在Android中尚未应用。

packages/apps/Mms/src/com/android/mms/drm 实现在MMS中如何使用DRM。
packages/providers/DrmProvider/src/com/android/providers/drm ?

下面再作具体介绍:
一 目录 frameworks/base/media/java/android/drm/mobile1
这个目录是OMA DRM 1.0的java 接口。
OMA DRM 1.0 定义了几个重要概念:
DRM消息(DRM message):用户下载的一条DRM内容。根据下面介绍的DRM内容传输方式的不同,DRM消息中包含的内容亦有所不同,可能只包含一个未经加密的媒体对象;也可能即包含未加密的媒体对象,也包含一个权利对象;也可能只包含一个经过加密的媒体对象(.dcf)。
媒体对象 (media object):包含媒体资源,如一个mp3, ringtone等。
权利对象(Right object): 限制用户如何使用媒体对象。


OMA DRM 1.0 定义了DRM内容的四种传输方式:
1. Combined delivery: 这种传输方式中,媒体对象以未经加密的方式(plain) 与Right object打成一个包,一块传输。这个包下载到手机设备上后,不允许转发给其他设备。
2. Forward lock: 这种传输方式是combined delivery的一个特例。媒体对象没有对应的Rigth object。媒体对象不允许转发给其他设备
3. Separate delivery: 媒体对象和权利对象分别传输。媒体对象采用对称密钥加密,文件以.dcf为后缀。权利对象中则包含对应的密钥。
4. Superdistribution:类似separate delivery, 但媒体内容允许转发给其他设备。

相应地,本目录的代码中:

DrmConstraintInfo对象描述了(对媒体内容的)一组限制属性,如开始/结束使用日期,使用次数等;

DrmRights对象代表了一个OMA 权利对象。

DrmRightsManager管理设备上的Rigth Objects。所有下载的权利对象,不论是由separate delivery 还是 combined delivery,权利对象都要首先安装到设备上。

DrmRawContent代表一条DRM内容,分为两类:DRM_MIMETYPE_MESSAGE_STRING --DRM内容是经combined delivery 或 forward lock传输的(也即媒体对象没有加密,没有或者有一个权利对象); DRM_MIMETYPE_CONTENT_STRING -- DRM内容是经 separate delivery传输的(也即媒体对象是经过加密的dcf格式,不包含权利对象)。

DrmInputStream:这个对象从一个DrmRawContent对象中读出经过权利对象验证并解密后的(需要的话)媒体对象内容。这个内容之后就可以传给相关应用输出给用户。


二 目录frameworks/base/media/libdrm/moblile1
本目录真正实现DRM DRM 1.0。
drm1_jni.c提供对DrmRawContent中方法的本地实现;
objmng目录下是具体实现。frameworks/base/media/libdrm/mobile1/include/objmng/svc_drm.h中有关于每个方法的详细解释,基本思路是每打开一个Drm 内容,就创建一个session,然后提供一系列方法,以此session为参数,对Drm进行各种操作,如获取属性,读取解密后内容等。这里不再赘述。
作者: 翔子    时间: 2010-10-13 16:44
标题: Android 技术专题系列之十二 -- Alarm manager
Alarm manager 主要管理硬件时钟。一些与时间相关的应用,如日历,闹钟等需要使用Alarm Manager的服务。Alarm manager功能相对比较简单,相关代码位于
frameworks/base/core/jni/server/com_android_server_AlarmManagerService.cpp
frameworks/base/services/java/com/android/server/AlarmManagerService.java

一. frameworks/base/core/jni/server/com_android_server_AlarmManagerService.cpp
这部分代码直接管理硬件时钟,设备名为/dev/alarm。包括打开设备,关闭设备,设置时区,设置触发时间(timeout),以及等待时钟触发。
二. frameworks/base/services/java/com/android/server/AlarmManagerService.java
这部分封装目录一中的代码,向上提供java接口,同时与客户端(如calendar)交互,接收来自客户端的时钟设置请求,并在时钟触发时通知客户端。
作者: 翔子    时间: 2010-10-13 16:44
标题: Android 技术专题系列之十三 -- 与PC同步
为了更好的了解Android 的Sync架构,建议读者可以参考OMA DS (Syncml)规范。 Android的Sync架构符合这一规范。相关代码主要位于frameworks/base/core/java/android/content。

网页http://mylifewithandroid.blogspot.com/2008/02/synchronization-in-android.html给出了目前来说比较深入的一篇关于Android同步的分析 (但是应该没有更新最近的Android cupcake的情况)。

值得注意的是,Android中的mail (GMail), Contact, Calendar等还可以与Google相应的服务器同步,而服务器又可以和PC同步。
作者: 翔子    时间: 2010-10-13 16:45
标题: Android 技术专题系列之十四 -- 组件模型及组件间通
PPT
作者: 翔子    时间: 2010-10-13 16:46
标题: Android 技术专题系列之十五 -- 更新开发手机到1.5
参照http://www.htc.com/www/support/android/adp.html,下面是在Android 开发手机上更新1.5 image的步骤 (主机是ubuntu Hardy):
1. 下载必要的image。
Radio image: ota-radio-2_22_19_26I.zip
Recovery image: signed-dream_devphone-ota-147201.zip

2. 在主机上创建文件/etc/udev/rules.d/50-android.rules,内容:
SUBSYSTEM=="usb", SYSFS{idVendor}=="0bb4", MODE="0666"
并修改文件属性:
chmod a+rx /etc/udev/rules.d/50-android.rules
然后重启机器

3. 将主机和Android开发手机通过usb 相连,运行命令 (./adb 命令来自sdk的tools)
./adb devices

应当看到一个device被列出。

4.参见http://www.htc.com/www/support/android/adp.html的 Update the Device Radio Firmware一节 ,更新firmware

5.参见http://www.htc.com/www/support/android/adp.html的 Copy the Recovery Image Package to the device一节 ,更新系统。

6. 切换到中文环境
./adb shell
$ su -
# setprop persist.sys.language zh;setprop persist.sys.country CN;stop;sleep 5;start

运行步骤6后,能看到有些应用的图标是中文,如"google 地图“。但大部分图标/环境仍然是英文。这应该是由于应用没有带中文语言包的缘故。读者可以试着安装一些带中文语言报的测试程序验证是否能够显示中文。
作者: 翔子    时间: 2010-10-13 16:46
标题: Android 技术专题系列之十六 -- 在模拟器上安装 sd
本文基于http://blog.jayway.com/2009/04/22/working-with-sd-cards-in-the-android-emulator/。特此申明。

今天想玩玩Android模拟上的music应用,可总是报告没有sd卡。网上搜了一下,有一哥们有一完整的解决方案(见上述连接)。这儿我将要点列在下面:
1.创建sd card image:
./mksdcard 1024M sdcard.img

2. 拷贝媒体文件, 如xxx.mp3, 到sd card image上。我考了一个我最喜欢的《滚滚长江东逝水》,呵呵。
sudo mount -o loop sdcard.img /mnt
cp xxx.mp3 /mnt
sudo umount /mnt

3. 创建一个AVD (Android Virtual Device)
./android create avd --name myAVD --target 2 --sdcard sdcard.img

这个命令会有很多选项让你确认,我只管一路回车。

4. 启动emulator
./emulator -avd myAVD

现在就可以打开music应用,欣赏音乐了。
作者: 翔子    时间: 2010-10-13 16:47
标题: Android 技术专题系列之十七 -- volume 服务
Android 的volume服务主要是用来管理usb/sd卡等外部存储设备。平台可以对外部存储设备进行操作和轮询状态,当外部存储设备状态发生变化时,volume 服务也会即时报告平台。

相关代码主要位于:
system/core/vold
frameworks/base/services/java/com/android/server/MountListener.java
frameworks/base/services/java/com/android/server/MountService.java
frameworks/base/core/java/android/os/IMountService.aidl
hardware/libhardware_legacy/mount/IMountService.cpp
hardware/libhardware_legacy/include/hardware_legacy/IMountService.h

下图概述了这些组件之间的关系。但是没有搞清楚IMountService.h/cpp是干什么用的。

还有一点值得注意,MountService被一个Android内部类ServiceManager注册(SystemServer.java):
ServiceManager.addService("mount", new MountService(context))
以这种方式注册有名服务(named service),所注册的服务是一个实现了aidl产生的stub类的派生类,并不需要实现一个Service的派生类。ServiceManager仅供Android平台内部使用。
作者: 翔子    时间: 2010-10-13 16:47
标题: Android 技术专题系列之十八-- 媒体文件管理
本文引用了http://letsgoustc.spaces.live.com/Blog/cns!89AD27DFB5E249BA!473.entry部分内容,特此申明。

Android平台上的媒体文件管理和桌面系统不同。在桌面系统上,不同目录下的媒体文件呈树状结构显示给用户,用户需要进入不同目录寻找该目录下的文件。而在Android平台上,不同目录下的媒体文件则以一层列表方式显示给用户,用户不需进入子目录就可以列出(某种类型的)所有媒体文件。

在Android上,为了实现这种模式的媒体文件管理,对所有管理的媒体文件抽取其元数据(mp3文件包含的元数据可参考http://en.wikipedia.org/wiki/ID3),存储在数据库中,并作为一个content provider提供给其他应用使用。用户的每一次显示媒体文件的操作,就是对这个数据库的一次查询操作。

实现这一功能的代码位于
frameworks/base/core/java/android/provider/MediaStore.java
packages/providers/MediaProvider/src/com/android/providers/media.
frameworks/base/media/java/android/media/MediaScanner*
frameworks/base/media/jni/android_media_*
external/opencore/android/mediascanner.cpp

下面略作分析:

external/opencore/android/mediascanner.cpp负责从媒体文件(mp3, mp4, wma等)中提取元数据。
作者: 翔子    时间: 2010-10-13 16:48
标题: Android 技术专题系列之十九-- Appwidget
要了解AppWidget,得先了解Homescreen。
HomeScreen是一个手机的脸面,是一个门户网站的首页,是手机上最为重要的应用。有一个好的homescreen,可以说这个手机已经成功了一半。

开发者当然可以在homescreen上开发很多功能和界面,比如一个时钟,一个天气预报,一个显示当前电池电量,未读短信等状态的状态栏等等。实际上,传统的homescreen也是这样做的。但这样做的弊端有两个:1. homescreen代码过于庞杂,和系统各个子系统都有交互,容易出错,不易维护;2. homescreen不能定制。最终用户可能有不同需求,而预设的功能则限制了用户选择的权力。

AppWidget 的出现主要就解决了这些问题。AppWidget是一个特殊的view,由一个应用(假设应用A)创建和维护,而绘制却在另一个应用上(通常即 homescreen)。即应用A负责决定画什么,如一个时钟;而homescreen却负责具体画,即怎么画。如果用一个比喻来形容的话,homescreen和应用A之间的关系就像是一个Browser/Server架构, homesreen 就是一个负责显示的browser,而应用A就是后台的Server。应用A传递RemoteView给homescreen,而homescreen解析并绘制这个RemoteView。这样,不用对homescreen应用做任何改动,却可以定制并丰富了homescreen的显示内容,如一个时钟,一个日历等。

如何创建一个AppWidget, 如何深入理解AppWidget, 等等,可见下列链接:
1. http://www.google.com/url?sa=t&source=web&ct=res&cd=1&ved=0CAgQFjAA&url=http%3A%2F%2Fdeveloper.android.com%2Fguide%2Ftopics%2Fappwidgets%2F&ei=Ct6iS93OMc-GkAWp0ODFCA&usg=AFQjCNGPwi7jyY4KL4MVAQzOZr_ALxZfwQ&sig2=iLmH_iOVfFHZz5p7gpGENA
2. http://www.google.com/url?sa=t&source=web&ct=res&cd=3&ved=0CBAQFjAC&url=http%3A%2F%2Fandroid-developers.blogspot.com%2F2009%2F04%2Fintroducing-home-screen-widgets-and.html&ei=Ct6iS93OMc-GkAWp0ODFCA&usg=AFQjCNF-SNy2OXJjVbirZVZi5zroa-wHbg&sig2=-2PREps5wYMGqtLOqoghsA
3. http://mylifewithandroid.blogspot.com/2010/01/remoteviews-as-hypertext-document.html
4. sample code located in development/samples/ApiDemos/src/com/example/android/apis/appwidget/

使用service, 也可实现两个应用之间的交互,大家可以探讨一下二者的区别。
作者: 翔子    时间: 2010-10-13 16:50
标题: Android系列之二十-- 在eclipse中浏览Android源码
Pls refer to http://stuffthathappens.com/blog/2008/11/01/browsing-android-source-in-eclipse/

如果能够在eclipse中浏览Android源代码,这对提高阅读源码的效率非常有帮助。根据上面连接,下面是一个简要步骤描述:
1. 安装eclipse 3.4或3.5 (本人使用的是从http://www.eclipse.org/downloads/上下载的3.4.1。3.5版本下面有些步骤没有成功)
2. 安装Android plugin (参见sdk 文档docs/sdk/eclipse-adt.html)
3. 从源码中创建一个项目, (create a project from existing sources),如为packages/apps/calendar
4. 安装Android源码到这个项目中。
4.1 在eclipse的Window->Preference->Android中,指定sdk location,例如xxx/android-linux-sdk_86
4.2 假设使用的sdk是1.6,则在目录xxx/android-linux-sdk_86/platforms/android-1.6/下创建sources目录
4.3 符号连接 你的源码 ln -s frameworks/base/core/java/android xxx/android-linux-sdk_86/platforms/android-1.6/sources/android
4.4 符号连接其他源码到sources目录下。注意:源码文件所放路径应该与源码文件所属Java包的名字匹配。例如,Bitmap.java属于android.graphics包,则应放于目录sources/android/graphics目录下。
4.5 如果你仍然不能浏览源码的话,可通过重新创建一个project进行刷新。这可能是我这个版本的eclipse的一个bug。
4.6,下面的script给出了如何抽取所有的java源码文件(http://blog.michael-forster.de/2008/12/view-android-source-code-in-eclipse.html):

from __future__ import with_statement  # for Python < 2.6



import os


import re


import zipfile


# open a zip file


DST_FILE = 'sources.zip'


  if os.path.exists(DST_FILE):


  print DST_FILE, "already exists"
  

exit(1)


zip = zipfile.ZipFile(DST_FILE, 'w', zipfile.ZIP_DEFLATED)




# some files are duplicated, copy them only once
written = {}


# iterate over all Java files




for dir, subdirs, files in os.walk('.'):


   for file in files:
   

    if file.endswith('.java'):
      

      # search package name


      path = os.path.join(dir, file)


      with open(path) as f:
        

        for line in f:
         

          match = re.match(r'\s*package\s+([a-zA-Z0-9\._]+);', line)
         

          if match:


            # copy source into the zip file using the package as path
            

            zippath = match.group(1).replace('.', '/') + '/' + file
            

            if zippath not in written:
              

               written[zippath] = 1
              

               zip.write(path, zippath)
            

            break;


         
zip.close()
作者: 翔子    时间: 2010-10-13 16:52
标题: Android系列之二十一 -- track memory allocations
下文来自http://developer.android.com/resources/articles/track-mem.html但需要越墙访问,特摘录在此。
Despite the impressive hardware of the first Android phones (T-Mobile G1 and ADP1) writing efficient mobile applications is not always straightforward. Android applications rely on automatic memory management handled by Dalvik's garbage collector which can sometimes cause performance issues if you are not careful with memory allocations.
In a performance sensitive code path, like the layout or drawing method of a view or the logic code of a game, any allocation comes at a price. After too many allocations, the garbage collector will kick in and stop your application to let it free some memory. Most of the time, garbage collections happen fast enough for you not to notice. However, if a collection happens while you are scrolling through a list of items or while you are trying to defeat a foe in a game, you may suddenly see a drop in performance/responsiveness of the application. It's not unusual for a garbage collection to take 100 to 200 ms. For comparison, a smooth animation needs to draw each frame in 16 to 33 ms. If the animation is suddenly interrupted for 10 frames, you can be certain that your users will notice.
Most of the time, garbage collection occurs because of tons of small, short-lived objects and some garbage collectors, like generational garbage collectors, can optimize the collection of these objects so that the application does not get interrupted too often. The Android garbage collector is unfortunately not able to perform such optimizations and the creation of short-lived objects in performance critical code paths is thus very costly for your application.
To help you avoid frequent garbage collections, the Android SDK ships with a very useful tool called allocation tracker. This tool is part of DDMS, which you must have already used for debugging purposes. To start using the allocation tracker, you must first launch the standalone version of DDMS, which can be found in the tools/ directory of the SDK. The version of DDMS included in the Eclipse plugin does not offer you ability to use the allocation tracker yet.
Once DDMS is running, simply select your application process and then click the Allocation Tracker tab. In the new view, click Start Tracking and then use your application to make it execute the code paths you want to analyze. When you are ready, click Get Allocations. A list of allocated objects will be shown in the first table. By clicking on a line you can see, in the second table, the stack trace that led to the allocation. Not only you will know what type of object was allocated, but also in which thread, in which class, in which file and at which line. The following screenshot shows the allocations performed by [url=http://code.google.com/p/shelves]Shelves while scrolling a ListView.
Even though it is not necessary, or sometimes not possible, to remove all allocations for your performance critical code paths. the allocation tracker will help you identify important issues in your code. For instance, a common mistake I have seen in many applications is to create a new Paint object on every draw. Moving the paint into an instance field is a simple fix that helps performance a lot. I highly encourage you to peruse the [url=http://source.android.com/]Android source code to see how we reduce allocations in performance critical code paths. You will also thus discover the APIs Android provide to help you reuse objects.
作者: 翔子    时间: 2010-10-13 16:53
标题: Android系列之二十二 -- 图形系统
http://www.moandroid.com/?p=937中介绍了一些关于Android图形系统的知识。

1 android.view.animation.Interpolator:管理动画过程的速度变化,比如整个动画过程是线性的,加速的,或者减速的,等等。
2 android.widget.Scroller:滚动管理。computeScrollOffset函数计算出当前位置以及是否滚动到边界。
3 android.widget.Gallery:水平方向管理一组child,并可以通过手势来滚动这组child。private class FlingRunnable implements Runnable 具体负责滚动这组child。run()计算当前的位置,并调用trackMotionScroll(delta)更新视图以及设置当前具有焦点的child。
4. Gradient
5. Android的view/widget有两种不同的监听和处理事件的方式。
第一种是对任意一个widget/view, 你都可以通过调用下列函数,setOnClickListener, setOnLongClickListener, setOnFocusChangeListener, setOnKeyListener, setOnTouchListener, setOnCreateContextMenuListener,为其设置一个分别处理click, longClick, focusChange, key, touch, createContextMenu事件的callback函数。

第二种是当派生一个新的widget时,可以重载其父类的事件处理函数,如onKeyDown, onTouchEvent, onFocusChanged等等。

第一种方法好处是无需派生一个新类而增加了widget处理任意事件的能力。


《未完待续》
作者: 翔子    时间: 2010-10-13 16:54
标题: Android系列之二十三 -- message queue
这两天进一步了解了一下Android的message queue, thread, hander,runnable之间的关系。

每个message queue都会有一个对应的handler。handler会向message queue通过两种方法发送消息:send或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过send发送的是一个message对象,会被handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。

每个带图形界面的应用启动后,都会创建一个主线程,可称之为GUI线程。这个线程自动就会创建一个message queue,来自于系统的消息都会投放到这个message queue里面,并按先进先出的顺序处理。

GUI线程图形界面中的view可通过post方法向GUI线程的message queue投递一个runnable。

对于除GUI线程以外的其他线程,创建时缺省并没有message queue,下面的示例演示了如何创建一个带有message queue 和handler的线程:

class LooperThread extends Thread
   {
      public Handler mHandler;

     public void run() {
         Looper.prepare();
         mHandler = new Handler() {

            public void handleMessage(Message msg) {
               // process incoming messages here
            }

         };

         Looper.loop();

      }

    }

而对于GUI线程,则可以直接(比如在onCreate)创建一个handler并重载handleMessage,省去创建message queue的过程。
作者: 翔子    时间: 2010-10-13 16:56
标题: Android系列之二十四 -- home screen
Home screen可以说是一个手机的最重要应用,就像一个门户网站的首页,直接决定了用户的第一印象。下面对home screen做一简要分析。

home screen的代码位于packages/apps/Launcher目录。从文件launcher.xml,workspace_screen.xml可获知home screen的UI结构如下图所示:
[url=http://photo.blog.sina.com.cn/showpic.html#blogid=5e71b14f0100hx38&url=http://static8.photo.sina.com.cn/orignal/5e71b14ft84517031c237]<IMG title="Android系列之二十四 -- home screen" name=image_operate_22611271376309450 alt="Android系列之二十四 -- home screen" src="http://simg.sinajs.cn/blog7style/images/common/sg_trans.gif" width=690 height=394 real_src="http://static8.photo.sina.com.cn/middle/5e71b14ft84517031c237&690">

整个homescreen是一个包含三个child view的FrameLayout(com.android.launcher.DragLayer)。

第一个child就是桌面com.android.launcher.Workspace。这个桌面又包含三个child。每个child就对应一个桌面。这就是你在Android上看到的三个桌面。每个桌面上可以放置下列对象:应用快捷方式,appwidget和folder。

第二个child是一个SlidingDrawer控件,这个控件由两个子控件组成。一个是 com.android.launcher.HandleView,就是Android桌面下方的把手,当点击这个把手时,另一个子控件,com.android.launcher.AllAppsGridView就会弹出,这个子控件列出系统中当前安装的所有类型为 category.launcher的Activity。

第三个child是com.android.launcher.DeleteZone。当用户在桌面上长按一个widget时,把手位置就会出现一个垃圾桶形状的控件,就是这个控件。

在虚拟桌面上可以摆放四种类型的对象:
1. ITEM_SHORTCUT,应用快捷方式
2. ITEM_APPWIDGET,app widget
3. ITEM_LIVE_FOLDER,文件夹
4. ITEM_WALLPAPER,墙纸。

类Favorites(LauncherSettings.java)和类LauncherProvider定义了一个content provider,用来存储桌面上可以放置的几个对象,包括shortcut, search和clock等。

类AddAdapter(AddAdapter.java)列出了这四个类型对象。当用户在桌面空白处长按时,下列函数序列被执行:
Launcher::onLongClick -->
Launcher::showAddDialog -->
Launcher::showDialog(DIALOG_CREATE_SHORTCUT); -->
Launcher::onCreateDialog -->
Launcher::CreateShortcut::createDialog:这个函数创建一个弹出式对话框,询问用户是要添加什么(快捷方式,appwidget, 文件夹和墙纸)其内容就来自AddAdapter。

类Favorites(LauncherSettings.java)和类LauncherProvider定义了一个content provider,用来存储桌面上可以放置的几个对象,包括shortcut, search和clock等。

类DesktopItemsLoader负责将桌面上所有的对象从content provider中提取。

线程private ApplicationsLoader mApplicationsLoader负责从包管理器中获取系统中安装的应用列表。(之后显示在AllAppsGridView上)。ApplicationsLoader::run实现:
1)通过包管理器列出系统中所有类型为Launcher,action为MAIN的activity;
2)对每一个Activity,
      a) 将Activity相关元数据信息,如title, icon, intent等缓存到appInfoCache;
      b) 填充到ApplicationsAdapter 中。填充过程中用到了一些小技巧,每填充4(UI_NOTIFICATION_RATE)个activity更新一下相应view。



在Launcher::onCreate中,函数startLoaders被调用。而该函数接着调用loadApplications和loadUserItems,分别获取系统的应用列表,以及显示在桌面上的对象列表(快捷方式,appwidget,folder等)。

Launcher上排列的所有应用图标由AllAppsGridView对象呈现。这个对象是一个GridView。其对应的Adapter是 ApplicationsAdapter,对应的model则是ApplicationInfo数组。数组内容是由ApplicationsLoader 装载的。
private class ApplicationsLoader implements Runnable。
作者: 翔子    时间: 2010-10-13 16:57
标题: Android系列之二十五 -- aapt的一个bug
工具aapt用来生成apk。然而1.6版本中的aapt有下面一个bug。
假设res/layout目录下,有两个文件:
aaa.xml
bbb.xml

在aaa.xml中可以定义一个widget,假设id = aaa_widget。而在bbb.xml中,可以引用这个widget,不会出现任何错误。

然而,如够颠倒引用顺序,假设bbb.xml中定义了一个widget,id= bbb_widget,然后在aaa.xml中引用这个widget,就会报错:No resource found that matches the given name

其原因就在于aapt在处理xml中的widget resource引用时,竟然不会处理向后索引。第一种情况下,因为aaa.xml排名先于bbb.xml (按字母顺序排序),因此aaa_widget已经被定义,所以不出错。而第二种情况下,因为bbb_widget定义在后面的一个文件bbb.xml,因此在先处理aaa.xml时,这个widget还没有定义,就会报引用出错的错误。

呵呵。




欢迎光临 数码鹭岛论坛 (http://www.clore.net/forum/) Powered by Discuz! X3.2