Android 技术专题系列
来源: 乡下鱼的博客http://blog.sina.com.cn/countryfish
Android 技术专题系列之一 -- Android 是什么
Android 是什么?
Android是一个完整的手机软件平台,包含底层的操作系统(Linux), 中间件以及一些关键应用。 Android 还提供了一个SDK 帮助开发者使用Java语言来开发。
特性
[*]应用框架:提供机制使组件(component)能够被复用和替换[*]Dalvik 虚拟机:为移动设备专门进行优化[*]浏览器:基于Webkit引擎[*]优化的图形库:基于一个定制的2D图形库; 3D图形库基于OpenGL ES 1.0草案(硬件加速功能可选)[*]SQLite[*]媒体支持:包括常见的音频,视频和静态图像格式(MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF)[*]GSM 库(硬件相关)[*]Bluetooth, EDGE, 3G, 和 WIFi (硬件相关)[*]Camera, GPS, compass 和accelerometer (硬件相关)[*]完善的开发环境, 包括一个模拟器,调试工具,内存和性能分析工具以及Eclipse IDE插件Android 架构
下图给出了Android平台的主要组成元素(参见http://code.google.com/android/images/system-architecture.jpg)
应用
Android平台预装了一些核心手机应用,包括电子邮件,短信,日历,地图,浏览器,地址本等等。这些应用都是使用Java语言编写。开发者也需要使用Java语言在Android平台上开发自己的应用程序。
应用框架
核心应用(电子邮件,短信等)所使用的任何API, Android 开发者都可以访问。Android架构设计的一个重要考虑是使组件(component)易于复用:任意一个应用都可以向系统发布功能,而任意一个其他应用都可以调用这些功能(在不违背系统安全策略前提下)。这个机制还使任意组件(component)都可被开发者替换。
在应用之下是一组服务 (service)和功能库, 包括:
[*]一组丰富和易于扩展的View(控件),用于构建应用的图形用户界面,包括list, grid, 文本框, 按钮,甚至一个强大的web浏览器控件[*]Content provider (内容提供者),使一个应用能够访问另外一个应用的数据(如地址本),或者共享自己的数据给其他应用使用。[*]资源管理器(Resource Manager), 提供接口访问应用的non-code (非代码)资源,如本地化的字符串,图片文件,布局文件等。[*]Notification Manager (通知管理器):所有应用都可以通过Notification Manager在状态栏显示定制化的Alert (通知)或状态信息[*]Activity Manager: 管理应用的生命周期以及activity 窗口的后退机制。库
Android平台包含一套C/C++库,供上层的组件使用。开发者不能直接访问这些库,但是可以通过Android应用框架来访问。下面列出了一些核心库:
[*]系统C语言库 - 标准C系统库(libc)的一个BSD-派生实现,并为嵌入式设备进行了优化[*]媒体库 - 急于PacketVideo的OpenCORE。这个库支持许多音視频格式以及静态图像的播放和录音/像, 如MPEG4, H。264, MP3,AAC,AMR, JPG和PNG。[*]Surface Manager - 管理对显示子系统的访问,并无缝的合成多个应用的2D和3D图层[*]LibWebCore - 先进的web浏览器引擎。 Android浏览器应用以及web view (控件,可嵌入其他应用)都是基于这个库。[*]SGL - 底层的2D图形引擎[*]3D 库 - 基于OpenGL ES 1.0 API的一个实现库。如果硬件提供3D加速功能,这个库会利用硬件这个功能,否则使用一个优化的3D软件库[*]Freetype - 位图和矢量字体引擎[*]SQLite - 轻量级的关系数据库引擎。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提供的一些核心功能,如安全机制,内存管理,进程管理,网络协议栈和驱动模型等。内核还为硬件提供一个抽象层。
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);
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
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
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>
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>
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
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" />
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 todisplay 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 intheir 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是其中一个模块。
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. 录音。