Android platform是一个用于开发移动程序的软件包,它包括了操作系统、中间件及一些关键应用。开发者能使用android SDK为Android platform开发应用,这些应用使用JAVA语言书写,运行在虚拟机Dalvik(一个专为手机程序开发的基于linux内核的JAVA虚拟机)。
1 什么是Android
1.1 Android的特性
ü         应用框架,让一些基础设施得以重用
ü         Dalvik虚拟机,专为开发移动程序优化
ü         集成的浏览器,(基于WebKit引擎)
ü         优化的图形库,(2D图形库以及基于 OpenGL ES 1.0 规范的 3D 图形库)
ü         SQLite ,用于结构化数据的存储,是一个数据库
ü         多媒体支持,支持多种音频,视频格式( MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF
ü         GSM 技术(依赖具体硬件)
ü         Bluetooth, EDGE, 3G, WiFi (依赖具体硬件)
ü         Camera, GPS, compass, accelerometer (依赖具体硬件)
ü         丰富的开发环境(DEBUG工具,内存及性能工具,Eclipse的插件等)
1.2 Android的架构
   Android操作系统的模块如下:
Ø         应用:Android已集成了一些应用,如邮件客户端,SMS程序,日历,地图,浏览器等
Ø         应用框架:
Ø         程序库:
Ø         例行程序
Ø         Linux内核
2 Andvoid起步
2.1 开发环境配置
   使用Eclipse + Android Development Tools (ADT) 插件。在 Help > Software Updates > Find and Install.... 中键入更新地址:
2.2 运行第一个Andvoid程序
使用 Eclipse 插件
   1、建立的工程类型为:Andvoid Project
   2、建立Launch Configuration. Run > Open Run Dialog... or Run > Open Debug Dialog
命令行运行程序
   1 、创建工程activityCreator your.package.name.ActivityName
 2、编译。在build.xml所在的目录ant一下。
 3、启动模拟器. 运行命令:emulator
 4、在模拟器中,切换到主屏幕。
 5、在命令行输入: adb install myproject/bin/<appname>.apk将其上载至模拟器。
 6、在模拟器中选择程序并启动。
 
Android需要专门的编译工具来正确的编译资源文件和Android程序的其他部分。基于此,你需要为你的程序准备一个专门的编译环境。
Andorid的编译过程通常包括编译XML和其他资源文件、创建正确的输入格式。经过编译的Android程序是一个.apk文件,.apk文件是一个压缩文件,它其中包含了.dex文件、资源文件、raw data文件和其他文件。
Andoriod暂时还不支持用本地代码(C/C++)开发第三方程序。
 
移除 Andorid 程序
要移除你安装在模拟器上的程序,你需要通过run adb并删除相应的.apk文件。通过adb shell命令在模拟器上打开一个UNIX shell,进入目录data/app/,通过命令rm 你程序的名称.apk来移除文件。
2.3 调试程序
 Andvoid用于调试的手段有:
 DDMS,DDMS是一个图形化的程序,支持端口转发(因此你可以在程序中设置断点),支持模拟器上的截屏,支持线程和堆栈信息和其他的一些特性。
 Logcat,Dump一份系统消息的日志。这些消息包括模拟器抛出错误时的堆栈跟踪。
 Android Log, 打印日志的类,用来将消息写入模拟器上的日志文件中。如Log.v()用于打印verbose级别的日志
 Traceview,Android可以保存一个日志用来记录被调用的方法以及该方法被调用的次数,通过Traceview你可以在一个图形化的界面中查看这个日志文件。
 可接解设置emulator的设置以方便调试,
 
模拟器上调试和测试的设置
   Android 提供了众多的设置使你可以更容易的调试和测试程序。要进入开发设置页面,在模拟器中转到Dev Tools > Development Settings。在该设置页面有以下选项:
  • Debug app:选择要调试的程序。你不需要设定其关联至调试器,但是设定这个值有两个效果:
    • 在调试的时候,如果你在一个断点处暂停了过长的时间,这个设定会防止Android抛出一个错误
    • 这个设定使你可以选择“等待调试器”选项,使程序只有在调试器关联上之后才启动
  • Wait for Debugger:阻塞所选的程序的加载直到有调试器关联上,这样你就可以在onCreate()中设置断点,这对于调试一个Activity的启动进程是非常重要的。当你对该选项进行了更改,任何正在运行的程序的实例都会被终止。你只有在上面的选项中选择了一个调试程序才能够选中该选项。你一也可以在代码中添加waitForDebugger()来实现同样的功能。
  • Immediately destroy activities:告诉系统一旦一个activity停止了就销毁该activity(例如当Android释放内存的时候)。这对于测试代码onFreeze(Bundle)/onCreate(android.os.Bundle)是非常有用的,否则会比较困难。如果你的程序没有保存状态,那么选择这个选项很可能会引发很多问题。
  • Show screen updates:对于任何正在被重绘的screen sections都会在其上闪现一个粉红色的矩形。这对于发现不必要的screen绘制是很有必要的。
  • Show CPU usage:在屏幕上方显示CPU信息,显示有多少CPU资源正在被使用。上方红色条显示总的CPU使用率,它下方绿色的条显示CPU用在compositing the screen上的时间。注意:在没有重启模拟器之前,一旦你开启了该功能就不能关闭。
  • Show screen FPS:显示当前的帧率。这对于查看游戏达到的总的帧率是非常有用的。注意:在没有重启模拟器之前,一旦你开启了该功能就不能关闭。
  • Show background:当没有activity screens可见时,显示一个背景模式。一般是不会出现的,仅仅在Debug的时候会出现。
设定的选项在模拟器重启之后仍然有效,如果要取消设定的选项,在取消设定以后还要重启模拟器,才能生效。
2.4 andvoid中的概念
   一个andvoid应用包括四个部分:
n         Activity活动       (个人认为 :类似于JSP, 也相当于SWT中的Shell, View则相当于wegiet)
n         Intent Receiver (个人认为 :类似于Struts action)
n         Service        (个人认为 :类似于Servlet)
n         Content Provider (个人认为 :用于持久化)
用上面哪些组件,要在 AndroidManifest.xml 文件中声明。
1、 Activity. 一个 activity 是应用中的一个单一的屏幕,它继承自 Activity 类,它将显示由 Views 组成的 UI 以及响应事件。(个人理解,相当于 JSP
2、 Intent Intent Filters. Intent 用于从一个屏幕跳到别一个屏幕,描述一个应用想做什么,它的数据结构包括 action data 两部分,它是一个消息操作对象,一个被动的数据结构。 action MAIN VIEW PICK EDIT 等等 , data 被表达成一个 URI IntentFilter 相当于告诉跳到哪个 activity, IntentReceiver 用于响应事件,虽然它不显示 UI ,但它可以用 NotificationManager 去通知用户。它需要在 AndroidManifest.xml 文件中注册,或者是用 Context.registerReceiver() 硬编码。
3、 Service 是一段有生命周期的无 UI 的代码。
4、 Content Provider, 持久化,例如存储在文件系统中,或者存储在 SQLite 数据库中。
2.5 例子
     例如:Notepadv1工程是问题工程,Notepadv1Solution则是相应的解决工程。
   2,3个例子的函义如下:
通过练习 1>
1.        数据库对象的例子程序
2.        如何在一个Activity初始化的时候创建数据库对象
3.        如何为一个Activity创建一个pop菜单
4.        如何得到一个用户在POP菜单中选择的ITEM 的ID
5.        如何向一个ListView中写入数据
通过练习 2>
1.        如何取得数据集中被选择数据行的ID, 注意rows是java的一个listarray对象,所以它有它的getSelection()的方法
2.        如何调用一个SubActivity以及在调用一个SubActivity之前应该做些事情。
3.        要实现一个方法, 当SubActivity返回后,应该做些什么
4.        关于layout。由于Android采用MVC的模式, 所以屏幕的布局采用了XML进行定义。一个好的布局会让程序显的更漂亮。可以参考 http://code.google.com/android/reference/view-gallery.html, 有很多的布局模式可供利用。
5.        如何创建一个开始的时候代码中不含onCreate()的类,因为到目前为止还没有创建过自己的类,以前看到的类和修改的方法都是已经写好的了。不过这个类也是一个从android.app.Activity继承的。创建后还是要通过Source Override一个onCreate的
6.        从一个SubActivity返回时,在返回之前应该做哪些事情
7.        如何在AndroidManifest.xml中声明一个新的Activity
8.        如何创建一个内部隐含的onClickLister及实现其应有的功能。
通过练习 3>
1. 除了onCreate()还有哪些和lifecycle有关的方法
a.onFreeze():
b.onPause()
c.onResume()
等等…
2. 大部分程序都有必要考虑lifecycle的问题,应该把下面的这个图记住:
   3. 为了不丢失需要保存的信息,要明确的理解Activity的各个状态,并相应的给出在各个状态的行为。
4.开始的时候对于onFreeze()和onPause()有些理解上的困难,要多读几遍DOC来会理解。 通过额外练习 >
进一步的理解lifecycle和通过手机按键进行操作时的时间和程序状态关系
另外,SDK目录下也有一些例子。
练习目标:
1.
使用 ListActivities ,并使用菜单
2.
学习使用操作 SQLite 数据库
3.
使用 ArrayAdapter 绑定数据到 ListView
4.
掌握一些基本的操作,如菜单的显示,菜单命令的处理,增加数据项等。
第一步
在SDK中下载获得Notepadv1的代码,并导入到Eclipse中。导入步骤:
 a. 在Package Explorer中,右键选择Import.../General/Existing Projects into Workspace
 b. 点Browse按钮,选择Notepadv1 的目录,并点OK
 c. 你将会看到Notepadv1被列在项目区中,默认会被打勾,如果没有打勾,请手动勾上。
 d. 点Finish
 e. Notepadv1将被列在Package Explorer中
 f. 如果有提示关于AndroidManifest.xml的错误,请选中此项目,并右键选择Android   Tools->Fix Project,他将会自动帮你修复错误。
第二步
看一下数据库操作类: DBHelper ,还是比较简单的,自己看去 :)
第三步
打开 res/layout/notepad_list.xml 这个文件,快速的看下就可以了:
 a.<?xml version="1.0" encoding="utf-8"?>
XML 文件的固定头
b. 一个 Layout 的定义,这里是 LinearLayout ,但不一定是这个,可以是其他的 Layout
第四步
在上面的那个文件中加入:
  <ListView id="@id/android:list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
  <TextView id="@id/android:empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/no_notes"/>
 a. ListView TextView 不会同是显示,如果没有数据,则默认显示 TextView (这个 View 里会显示一个字符串)。如果有数据,则会显示 ListView
b.@
是默认的关键字, XML 解析器将会自动替换这个符号后面的 ID
c.android:list
android:empty android 平台预定义好的 ID ,如果你想显示空的 TextView ,可以调用 setEmptyView().
第五步:
建立一个新文件 res/layout/notes_row.xml ,文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<TextView id="@+id/text1"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
然后保存, R.java 下将会自动刷新。
第六步
改变继承类
public class Notepadv1 extends ListActivity
第七步:
看一下这三个事件:
onCreate()
:界面初始化的时候调用
onCreateOptionsMenu()
:按了 Menu 按钮的时候调用
onOptionsItemSelected()
:选择了一个菜单项的时候调用
第八步 :
改写 OnCreate 函数:
 
    private DBHelper dbHelper;
    @Override
    public void onCreate(Bundle icicle)
    {
        super.onCreate(icicle);
        setContentView(R.layout.notepad_list);
        dbHelper = new DBHelper(this);
        fillData();
    }

第九步:
strings.xml 中增加: <string name="menu_insert">Add Item</string>
并在 Notepadv1 类中加入: public static final int INSERT_ID = Menu.FIRST;
改写 onCreateOptionsMenu()
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        boolean result = super.onCreateOptionsMenu(menu);
        menu.add(0, INSERT_ID, R.string.menu_insert);
        return result;
    }

第十步:
改写 onOptionsItemSelected()
@Override
    public boolean onOptionsItemSelected(Item item) {
        switch (item.getId()) {
        case INSERT_ID:
            createNote();
            break;
        }
      
        return super.onOptionsItemSelected(item);
    }
第十一步:
实现两个函数:
private void createNote() {
        String noteName = "Note " + noteNumber++;
        dbHelper.createRow(noteName, "");
        fillData();
    }
private void fillData() {
        // We need a list of strings for the list items
        List<String> items = new ArrayList<String>();
        // Get all of the rows from the database and create the item list
        List<Row> rows = dbHelper.fetchAllRows();
        for (Row row : rows) {
            items.add(row.title);
        }
       
        // Now create an array adapter and set it to display using our row
        ArrayAdapter<String> notes =
            new ArrayAdapter<String>(this, R.layout.notes_row, items);
// 指定notes_row视图,作为数据容器。
        setListAdapter(notes);
       
    }
第十二步:
运行: Run As -> Android Application
2.6 开发工具
2.6.1仿真器
   仿真器是在计算机中运行的一个虚拟的移动装置,用它来设计、调试应用。
2.6.2 ADT
ADT是一个用于开发andvoid应用的确良eclipse插件,它方便了我们的开发。例如,它让我们方便从eclipse内部访问DDMS工具(可用于截屏,管理port-forwarding,设断点,查看线程和进程信息)
2.6.3 DDMS
DDMS(Dalvik Debug Monitor Service)和Dalvik虚拟机集成,将在IDE和模拟器起到一个转发服务。用它能管理仿填器或者设备的进程,并且辅助调试。你可用它杀死进程,选择某一进程去DEBUG,产生TRACE数据,查看堆和线程信息等等。
2.6.4 ADB
ADB(Andvoid Debug Bridge)。在命令行操作。它能安装.apk文件到仿真器上等等。用于将文件发送到仿真器。adb(Android Debug Bridge)是Android提供的一个通用的调试工具,借助这个工具,我们可以管理设备或手机模拟器的状态。还可以进行以下的操作:
1 、快速更新设备或手机模拟器中的代码,如应用或Android系统升级;
2 、在设备上运行shell命令;
3 、管理设备或手机模拟器上的预定端口;
4 、在设备或手机模拟器上复制或粘贴文件;
以下为一些常用的操作:
1 、安装应用到模拟器:
adb install
比较郁闷的是,Android并没有提供一个卸载应用的命令,只能自己手动删除:
adb shell
cd /data/app
rm app.apk
2 、进入设备或模拟器的shell:
adb shell
通过上面的命令,就可以进入设备或模拟器的shell环境中,在这个Linux Shell中,你可以执行各种Linux的命令,另外如果只想执行一条shell命令,可以采用以下的方式:
adb shell [command]
如:adb shell dmesg会打印出内核的调试信息。
3 、发布端口:
你可以设置任意的端口号,做为主机向模拟器或设备的请求端口。如:
adb forward tcp:5555 tcp:8000
4 、复制文件:
你可向一个设备或从一个设备中复制文件,
复制一个文件或目录到设备或模拟器上:
adb push
如:adb push test.txt /tmp/test.txt
从设备或模拟器上复制一个文件或目录:
adb pull
如:adb pull /addroid/lib/libwebcore.so .
5 、搜索模拟器/设备的实例:
取得当前运行的模拟器/设备的实例的列表及每个实例的状态:
adb devices
6 、查看bug报告:
adb bugreport
7 、记录无线通讯日志:
一般来说,无线通讯的日志非常多,在运行时没必要去记录,但我们还是可以通过命令,设置记录:
adb shell
logcat -b radio
8 、获取设备的ID和序列号:
adb get-product
adb get-serialno
9 、访问数据库SQLite3
adb shell
sqlite3
2.6.5 aapt
   Aapt(Andvoid Asset Packaging Tool),用于创建.apk文件。
2.6.6 aidl
Aidl(Andvoid Interface Description Language)用于产生代码。
2.6.7 sqlite3
用于访问SQLite数据文件。
2.6.8 Traceview
   查看LOG信息
2.6.9 mksdcard
   Helps you create a disk image that you can use with the emulator, to simulate the presence of an external storage card (such as an SD card
2.6.10 dx
 Dx工具用于重写.class字节码到Andvoid字节码
2.6.11 activityCreator
   用于产生ant文件的脚本。当然,若用eclipse插件就不需要它了。
2.7 Andvoid应用的生命周期
在大部份情况下,每个Android应用都将运行在自己的Linux进程当中。当这个应用的某些代码需要执行时,进程就会被创建,并且将保持运行,直到该进程不再需要,而系统需要释放它所占用的内存,为其他应用所用时,才停止。
    Android一个重要并且特殊的特性就是,一个应用的进程的生命周期不是由应用自身直接控制的,而是由系统,根据运行中的应用的一些特征来决定的,包括:这些应用对用户的重要性、系统的全部可用内存。
     对于应用开发者来说,理解不同的应用组件(特别是Activity、Service、Intent Receiver)对应用进程的生命周期的影响,这是非常重要的。如果没有正确地使用这些组件,将会导致当应用正在处理重要的工作时,进程却被系统消毁的后果。
     对于进程生命周期,一个普遍的错误就是:当一个Intent Receiver在它的onReceiveIntent()方法中,接收到一个intent后,就会从这个方法中返回。而一旦从这个方法返回后,系统将会认为这个Intent Receiver不再处于活动状态了,也就会认为它的宿主进程不需要了(除非宿主进程中还存在其它的应用组件)。从而,系统随时都会消毁这个进程,收回内存,并中止其中还在运行的子线程。问题的解决办法就是,在IntentReceiver中,启动一个Service,这样系统就会知道在这个进程中,还有活动的工作正在执行。
     为了决定在内存不足情况下消毁哪个进程,Android会根据这些进程内运行的组件及这些组件的状态,把这些进程划分出一个“重要性层次”。这个层次按顺序如下:
     1、前端进程是拥有一个显示在屏幕最前端并与使用者做交互的Activity(它的onResume已被调用)的进程,也可能是一个拥有正在运行的IntentReceiver(它的onReceiveIntent()方法正在运行)的进程。在系统中,这种进程是很少的,只有当内存低到不足于支持这些进程的继续运行,才会将这些进程消毁。通常这时候,设备已经达到了需要进行内存整理的状态,为了保障用户界面不停止响应,只能消毁这些进程;
    2、可视进程是拥有一个用户在屏幕上可见的,但并没有在前端显示的Activity(它的onPause已被调用)的进程。例如:一个以对话框显示的前端activity在屏幕上显示,而它后面的上一级activity仍然是可见的。这样的进程是非常重要的,一般不会被消毁,除非为了保障所有的前端进程正常运行,才会被消毁。
     3、服务进程是拥有一个由startService()方法启动的Service的进程。尽管这些进程对于使用者是不可见的,但他们做的通常是使用者所关注的事情(如后台MP3播放器或后台上传下载数据的网络服务)。因此,除非为了保障前端进程和可视进程的正常运行,系统才会消毁这种进程。
    4、后台进程是拥有一个用户不可见的Activity(onStop()方法已经被调用)的进程。这些进程不直接影响用户的体验。如果这些进程正确地完成了自己的生命周期(详细参考Activity类),系统会为了以上三种类型进程,而随时消毁这种进程以释放内存。通常会有很多这样的进程在运行着,因些这些进程会被保存在一个LRU列表中,以保证在内存不足时,用户最后看到的进程将在最后才被消毁。
    5、空进程是那些不拥有任何活动的应用组件的进程。保留这些进程的唯一理由是,做为一个缓存,在它所属的应用的组件下一次需要时,缩短启动的时间。同样的,为了在这些缓存的空进程和底层的核心缓存之间平衡系统资源,系统会经常消毁这些空进程。
    当要对一个进程进行分类时,系统会选择在这个进程中所有活动的组件中重要等级最高的那个做为依据。可以参考Activity、Service、IntentReceiver文档,了解这些组件如何影响进程整个生命周期的更多细节。这些类的文档都对他们如何影响他们所属的应用的整个生命周期,做了详细的描述。
 
2 开发应用
2.1 前端UI
2.1.1 .屏幕元素的层次
1. Views
     一个View是android.view.View基础类的一个对象,它是一个有屏幕上特定的一个矩形内布局和内容属性的数据结构。一个View对象处理测量和布局,绘图,焦点变换,滚动条,还有屏幕区域自己表现的按键和手势。
    View类作为一个基类为widget(窗体部件)服务,widget--是一组用于绘制交互屏幕元素的完全实现子类。Widget处理它们自己的测距和绘图,所以你可以更快速地用它们去构建你的UI。可用到的widget包括 Text EditText InputMethod Button RadioButton Checkbox ,和 ScrollView
2. Viewgroups
一个ViewGroup是一个android.view.Viewgroup类的对象。一个viewgroup是一个特殊的view对象,它的功能是去装载和管理一组下层的view和其他viewgroup,Viewgroup让你可以为你的UI增加结构并且将复杂的屏幕元素构建成一个独立的实体。
    Viewgroup类作为一个基类为layout(布局)服务,layout--是一组提供屏幕界面通用类型的完全实现子类。layout让你可以为一组view构建一个结构。
3. A Tree-Structured UI
     在Android平台上,你用view树和viewgroup节点来定义一个Activity的UI,就如同下面图表一样。这个树可以如你需要那样简单或者复杂,并且你可以使用Android的预定义widget和layout或者你自定义的view类型来构建它。
     要将屏幕绑定一个树以便于渲染,你的Activity调用它的setContentView()方法并且传递一个参数给根节点对象。一旦Android系统获得了根节点的参数,它就可以直接通过节点来无效化,测距和绘制树。 当你的 Activity 被激活并且获得焦点时,系统会通知你的 activity 并且请求根节点去测距并绘制树,根节点就会请求它的子节点去绘制它们自己。每个树上的viewgroup节点都为它的子节点的绘制负责。
     正如之前提到的,每个view group都有测量它的有效空间,布局它的子对象,并且调用每个子对象的Draw()方法去绘制它们自己。子对象可能会请求获得一个它们在父对象中的大小和位置,但是父对象对于每个子对象的大小和位置有最终的决定权。
4. LayoutParams :一个子对象如何指定它的位置和大小
     每个viewgroup类都会使用一个继承于Viewgroup.LayoutParams的嵌套类。这个子类包含了包含了定义一个子对象位置和大小的属性类型,并且需适用于view group类。
      要注意的是,每个LayoutParams子类都有它自己赋值的语法。每个子元素必须定义适用于它们父对象的LayoutParams,尽管父对象可能会为子元素定义不同的LayoutParams。
      所有的viewgroup都包括宽和高。很多还包括边界的定义(margin和border)。你可以非常精确地描述宽和高,尽管你并不想经常这么做。更多时候你希望你的view自行调整到适应内容大小,或者适应容器大小。
2.1.2.通用布局对象(最普遍的view groups)
1. FrameLayout ( 上下压着的那种 )
      FrameLayout是最简单的一个布局对象。它被定制为你屏幕上的一个空白备用区域,之后你可以在其中填充一个单一对象 — 比如,一张你要发布的图片。所有的子元素将会固定在屏幕的左上角;你不能为FrameLayout中的一个子元素指定一个位置。后一个子元素将会直接在前一个子元素之上进行 覆盖填充,把它们部份或全部挡住(除非后一个子元素是透明的)。
2. LinearLayout
      LinearLayout以你为它设 置的垂直或水平的属性值,来排列所有的子元素。所有的子元素都被堆放在其它元素之后,因此一个垂直列表的每一行只会有一个元素,而不管他们有多宽,而一个水平列表将会只有一个行高(高度为最高子元素的高度加上边框高度)。LinearLayout保持子元素之间的间隔以及互相对齐(相对一个元素的右对齐、中间对齐或者左对齐)。
      LinearLayout还支持为单独的子元素指定weight。好处就是允许子元素可以填充屏幕上的剩余空间。这也避免了在一个大屏幕中,一串小对象挤成一堆的情况,而是允许他们放大填充空白。子元素指定一个weight值,剩余的空间就会按这些子元素指定的weight比例分配给这些子元素。默认的weight值为0。例如,如果有三个文本框,其中两个指定了weight值为1,那么,这两个文本框将等比例地放大,并填满剩余的空间,而第三个文本框不会放大。
      Tip:为了在屏幕上创建一个按比例安排大小的layout,需要根据这个屏幕上每个元素将按什么比例显示,创建一个指定fill_parent,子元素的height或width为0,且为每一个子元素分配weight值的容器对象。
      下面的两个窗体采用LinearLayout,包含一组的元素:一个按钮,几个标签,几个文本框。两个窗体都为布局做了一番修饰。文本框的width被设置为FILL_PARENT;其它元素的width被设置为WRAP_CONTENT。默认的对齐方式为左对齐。左边的窗体没有设置weight(默认为0);右边的窗体的comments文本框weight被设置为1。如果Name文本框也被设置为1,那么Name和Comments这两个文本框将会有同样的高度。
      在一个水平排列的LinearLayout中,各项按他们的文本基线进行排列(第一列第一行的元素,即最上或最左,被设定为参考基线)。因此,人们在一个窗体中检索元素时,就不需要七上八下地读元素的文本了。我们可以在layout的XML中设置android:baselineAligned="false",来关闭这个设置。
3. TableLayout
      TableLayout 将子元素的位置分配到行或列中。一个TableLayout由许多的TableRow组成,每个TableRow都会定义一个row(事实上,你可以定义其它的子对象,这在下面会解释到)。TableLayout容器不会显示row、cloumns或cell的边框线。每个row拥有0个或多个的cell;每个cell拥有一个View对象。表格由列和行组成许多的单元格。表格允许单元格为空。单元格不能跨列,这与HTML中的不一样。下图显示了一个TableLayout,图中的虚线代表不可视的单元格边框。
       列可以被隐藏,也可以被设置为伸展的从而填充可利用的屏幕空间,也可以被设置为强制列收缩直到表格匹配屏幕大小。对于更详细信息,可以查看这个类的参考文档。
4. AbsoluteLayout
       AbsoluteLayout可以让子元素指定准确的x/y坐标值,并显示在屏幕上。(0, 0)为左上角,当向下或向右移动时,坐标值将变大。AbsoluteLayout没有页边框,允许元素之间互相重叠(尽管不推荐)。 我们通常不推荐使用 AbsoluteLayout ,除非你有正当理由要使用它,因为它使界面代码太过刚性,以至于在不同的设备上可能不能很好地工作
5. RelativeLayout
      RelativeLayout允许子元素指定他们相对于其它元素或父元素的位置(通过ID指定)。因此,你可以以右对齐,或上下,或置于屏幕中央的形式来排列两个元素。元素按顺序排列,因此如果第一个元素在屏幕的中央,那么相对于这个元素的其它元素将以屏幕中央的相对位置来排列。如果使用XML来指定这个layout,在你定义它之前,被关联的元素必须定义。
     这是一个RelativeLayout例子,其中有可视的和不可视的元素。基础的屏幕layout对象是一个RelativeLayout对象。
      这个视图显示了屏幕元素的类名称,下面是每个元素的属性列表。这些属性一部份是由元素直接提供,另一部份是由容器的LayoutParams成员(RelativeLayout的子类)提供。RelativeLayout参数有width,height,below,alignTop,toLeft,padding和marginLeft。注意,这些参数中的一部份,其值是相对于其它子元素而言的,所以才RelativeLayout。这些参数包括toLeft,alignTop和below,用来指定相对于其它元素的左,上和下的位置。
6. Summary of Important View Groups
      重要View Group摘要,这些对象拥有UI子元素。一些提供可视的UI,另一些只处理子元素的布局。
2.1.3数据绑定
     这部分会提及UI有的一些View groups,些组成对象是经典AdapterView类的子类.例如包括图像,数层结构表现.这些对象有2个通用的任务: 数据层的填充与用户操作选择
1. 数据层填充
    This is typically done by binding the class to an Adapter that gets its data from somewhere — either a list that the code supplies, or query results from the device's database.
// Get a Spinner and bind it to an ArrayAdapter that
// references a String array.
private String[] fruit = {"apples", "oranges", "lemons"}
Spinner s1 = (Spinner)findViewById(R.id.fruitlist);
s1.setAdapter(new ArrayAdapter<String>(this, mStrings));
 
// Load a Spinner and bind it to a data query.
private String[] cols={android.provider.Contacts.PeopleColumns.NAME};
private Cursor cur = managedQuery(android.provider.Contacts.People.CONTENT_URL, cols, null, null);
s2.setAdapter(new CursorAdapter(cur, this));
2. 用户操作选择
    设置类的AdapterView.OnItemClickListener 方法监听和捕捉用户的操作事件.
// Create a message handling object as an anonymous class.
private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
     public void onItemClick(AdapterView parent, View v, int position, long id)
     {
         // Display a messagebox.
         showAlert("You've got an event", "Clicked me!", "ok", false);
     }
};

// Now hook into our object and set its onItemClickListener member
// to our class handler object.
mHistoryView = (ListView)findViewById(R.id.accept_button);
mHistoryView.setOnItemClickListener(mMessageClickedHandler);
2.1.4使用XML设计你的屏幕显示
1. Android定义了大量的自定义元素,各自代表了特定的Android显示子类。
2. 你可以象创建HTML文档一样,通过保存在应用res/layout/目录下的XML文件中一系列的嵌套标签来设计你的屏幕显示。
3. 每个文档描述一个android.view.View这个元素既可以是
一个简单的显示元素,也可以是一个在子节点中包含了一个集合的版面设计的元素,当Android编译你的应用时,他将每个文件都编译进android系统。你可以在代码Activity.onCreate()实现中通过调用setContentView(R.layout.layout_file_name)方法加载显示资源。
2.1.5在屏幕元素中设置句柄
1.您可以使用Activity.findViewById来取得屏幕上的元素的句柄. 使用该句柄您可以设置或获取任何该对象外露的值.
TextView msgTextView = (TextView)findViewById(R.id.msg);
   msgTextView.setText(R.string.push_me);
2.2 构建组成模块
   Android应用是由各种各样的组件来构成. 这些组件大部分都是松散联接,你可以精确的描述它们的联接程度,所以组建的联合比单个程序更有结合力. 显然,所有的组件运行在同一个系统进程里面.在这个进程里面创建多线程是可以允许的,并且是常见的.如果你需要,也可以对刚才那个系统进程创建相互独立的子进程.即使会有很多实例运行,但是他们之间互不干扰,这个是很难得的,因为Android可以确保代码是进程间透明的. 以下部分是很重要的Android APIs;
AndroidManifest.xml 是控制文件,告诉所有由高级组件构成的系统可以做什么.这些高级的组件包括( 特殊 activities, 服务 , 接收器 , 第三方提供商).控制文件用来告诉系统如何使用你所创建的组件.
Activity 是一个有生命周期的对象. 一个Activity做一些工作需要相当大的数量的代码;如必要的话,这部分工作还可能包括对用户UI界面的显示,也可能是没有UI界面.代表性地解释Activity就是,你必须标明你应用程序的入口点.
视图(Views)可以将其自身绘制到屏幕(screen)上。Android的接口都是由一组以树的形式出现的视图组成的。开发者可以通过创建一个新的视图的方法来使用自定义的图形处理技术(比如开发游戏,或者是使用了不常用的用户图形(UI)窗口界面(widget))。
Intents 是一个消息操作对象 .如果一个应用程序想要显示一个网页,那么它表示为Intent,我们可以通过Intent实例创建一个URI视图并且可以手工断开系统.系统设置一些代码(例如浏览器),可以让我们知道如果去操作Intent并且运行它.Intents 也可以被用于广播系统范围内的有效事件(例如播发一则通知).
服务是运行在后台的一段代码.它可以运行在它自己的进程,也可以运行在其他应用程序的进程里面,这要取决于自身的需要. 其他组件绑定到这个服务上面,并且可以请求远程方法调用.例如媒体播放器的服务,甚至当用户退出媒体用户向导界面,音乐依然可以持续播放.甚至当用户界面关闭,音乐播放依然继续.
通知将以小图标的形式呈现在状态栏里.收到消息以后,用户可以与图标进行交互式操作.大部分熟知的通知是 以短信息 , 通话记录 , 语音邮件的形式创建出来.通知是提请用户注意的重要机制.
ContentProvider是访问数据设备的提供者.典型的例子是访问用户联系列表.你的应用程序需要访问的数据可以由ContentProvider来支持.并且你也可以定义自己专用数据的ContentProviders.
 
2.2.1 AndroidManifest.xml 文件
AndroidManifest.xml 是每一个应用都需要的文件. 位于应用根目录下, 描述了程序包的一个全局变量, 包括暴露的应用组件(activities, services等等)和为每个组件的实现类, 什么样的数据可以操作, 以及在什么地方运行.
这个文件的一个重要方面(概念)是其中的intent过滤器. 这个过滤器描述了何时何种情况下让activity 启动. 当一个activity(或是操作系统)想要执行一个动作, 例如打开一个Web页或是打开一个联系人选取屏幕, 会创建一个Intent对象. 该对象包含了很多的描述信息, 描述了你想做什么操作, 你想处理什么数据, 数据的类型, 以及一些其他的重要信息. Android拿这个Intent的信息与所有应用暴露的intent过滤器比较, 找到一个最能恰当处理请求者要求的数据和action的activity. intents的更多信息在Intent页.
另外还要声明您的应用的Activities, Content Providers, Services, 和 Intent Receivers, 你也可以在AndroidManifest.xml文件中指定权限和instrumentation(安全控制和测试). 请查看AndroidManifest, 了解这个标签和他们的属性.
一个AndroidManifest.xml文件的例子:
   <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.my_domain.app.helloactivity">
          <application android:label="@string/app_name">
                  <activity class=".HelloActivity">
               <intent-filter>
                   <action android:value="android.intent.action.MAIN"/>
                   <category android:value="android.intent.category.LAUNCHER"/>
               </intent-filter>
           </activity>
                 </application>
          </manifest>
下面列出了AndroidManifest.xml 这个文件详细的结构大纲, 描述了所有可用标签.
<manifest>
The root node of the file, describing the complete contents of the package. Under it you can place:
文件根节点, 描述了程序包的所有内容. 在其节点下面内可以放置:
<uses-permission>
Requests a security permission that your package must be granted in order for it to operate correctly. See the Security Model document for more information on permissions. A manifest can contain zero or more of these elements.
请求一个安全授权, 必须被授予该权限, 您的程序包才能正确的操作. 查看安全模块文档, 了解有关授权的更多信息. 一个manifest可以包含零个或多个这样的节点.
<permission>
Declares a security permission that can be used to restrict which applications can access components or features in your (or another) package. See the Security Model document for more information on permissions. A manifest can contain zero or more of these elements.
声明一个安全授权, 用来限制哪些应用可以访问您的程序包内的组件和特有机制. 查看安全模块文档, 了解有关授权的更多信息. 一个manifest可以包含零个或多个这样的节点.
<instrumentation>
Declares the code of an instrumentation component that is available to test the functionality of this or another package. See Instrumentation for more details. A manifest can contain zero or more of these elements.
TODO
<application>
Root element containing declarations of the application-level components contained in the package. This element can also include global and/or default attributes for the application, such as a label, icon, theme, required permission, etc. A manifest can contain zero or one of these elements (more than one application tag is not allowed). Under it you can place zero or more of each of the following component declarations:
描述程序包内应用级别组件的根节点. 该节点能够描述应用程序的全局(和/或)默认属性, 例如标签, 图标, 主题, 需要的授权, 等等. 一个manifest可以包含零个或一个这样的节点(多个application 节点是不允许的). 在该节点下, 可以包含零个或多个以下每个组件的声明:
<activity>
An Activity is the primary facility for an application to interact with the user. The initial screen the user sees when launching an application is an activity, and most other screens they use will be implemented as separate activities declared with additional activity tags.
Activity 是应用于用户交互的最主要机制. 当一个应用运行的时候, 用户看到的第一个屏幕就是activity, 并且, 用户所使用的其他绝大多数屏幕(界面)也会是
Note: Every Activity must have an <activity> tag in the manifest whether it is exposed to the world or intended for use only within its own package. If an Activity has no matching tag in the manifest, you won't be able to launch it.
Optionally, to support late runtime lookup of your activity, you can include one or more <intent-filter> elements to describe the actions the activity supports:
<intent-filter>
Declares a specific set of Intent values that a component supports, in the form of an IntentFilter. In addition to the various kinds of values that can be specified under this element, attributes can be given here to supply a unique label, icon, and other information for the action being described.
<action>
An Intent action that the component supports.
<category>
An Intent category that the component supports.
<type>
An Intent data MIME type that the component supports.
<scheme>
An Intent data URI scheme that the component supports.
<authority>
An Intent data URI authority that the component supports.
<path>
An Intent data URI path that the component supports.
<receiver>
An IntentReceiver allows an application to be told about changes to data or actions that happen, even if it is not currently running. As with the activity tag, you can optionally include one or more <intent-filter> elements that the receiver supports; see the activity's <intent-filter> description for more information.
一个IntentReceiver 可以让应用接收到一次数据变化和一次行为发生的通知, 甚至这个应用没有在运行也可以. 同activity 标签一样, 你可以选择包含一个或多个<intent-filter>元素; 查看activity的<intent-filter>标签描述了解更多信息.
<service>
A Service is a component that can run in the background for an arbitrary amount of time. As with the activity tag, you can optionally include one or more <intent-filter> elements that the receiver supports; see the activity's <intent-filter> description for more information.
Service 是一个在后台任意时刻都可以运行的组件. 同activity 标签一样, 你可以选择包含一个或多个<intent-filter>元素; 查看activity的<intent-filter>标签描述了解更多信息.
<provider>
A ContentProvider is a component that manages persistent data and publishes it for access by other applications.
ContentProvider组件是用来管理数据持久化及数据发布的, 发布的数据可以被其他的应用访问.
2.2.2 Activity
2.2.3 View
android.view
公有类
android.view.View
java.lang.Object
android.view.View Drawable.Callback KeyEvent.Callback
视图 (View)类代表了一种基本的用户界面组成模块。一个视图占据了屏幕上的一个矩形区域,并响应绘制图形和事件处理。视图类是窗体类(Widget)的基类,而窗体类用来生成可交互的用户图形接口(interactive GUI)。
视图类的使用窗口中所有的视图构成一个树形结构。要想增加视图,既可以用直接添加代码的方法,也可以在一个或者多个 XML文件中声明新视图构成的树。在视图类的子类中,有的可以用来控制,有的具有显示文字、图片或者其他内容的功能。
当视图树被创建后,以下这若干种通用操作将可以被使用: 1.设置属性(properties):比如,可以设置TextView类的一个实例的文本内容。不同的子类可以用来设置的属性与方法不同。注意:只有编译时能够检测到的属性才可以在XML布局管理(layout)文件中设置。
2.设置输入焦点 (focus):为了响应用户输入,整个框架将处理移动的焦点。如果想把焦点强制指向某一个特定的视图,必须调用requestFocus()方法。
3.设置监听器 (listener):在视图中,允许设置监听器来捕获用户感兴趣的某些事件。比如说,在所有的视图中,无论视图是获得焦点还是失去焦点,都可以通过设置监听器来捕获。可以通过调用setOnFocusChangeListener(View.OnFocusChangeListener)来注册一个监听器。在其他视图子类中,提供了一些更加特殊的监听器。比如,一个按键(Button)可以触发按键被按下的事件。
4.设置是否可视 (visibility):可以通过调用setVisibility(int)来显示或者隐藏视图。
2.2.4 Intent
Intent 介绍
Intent是对被执行操作的抽象描述。调用 startActivity(Intent),可以启动 Activity;调用 broadcastIntent(Intent),可以把 Intent 发送给任何相关的 IntentReceiver 组件;调用 startService(Intent, Bundle) 以及 bindService(Intent, String, ServiceConnection, int) 可以让应用和后台服务进行通信。
Intent 提供了一个在不同应用的代码之间进行晚绑定 (late runtime binding) 的机制。它主要被用来启动 Activities,因此可以被看作是 Activities 之间的粘合剂。 Intent 大体上是一个被动数据结构,该数据结构包括被执行动作的抽象描述。Intent 中的主要内容有:
ü         action -- 需要被执行的动作。比如 VIEW_ACTION, EDIT_ACTION, MAIN_ACTION 等。
ü         data -- 执行动作要操作的数据,在 Intent 里用指向数据记录的URI (ContentURI) 表示。比如联系人数据库中的一个联系人记录。
译注:被动数据结构:只能由外部线程或者进程改变的数据结构。与能够通过相关的线程或者进程执行内部操作从而产生外部行为的主动数据结构相对应。
下面是一些 action/data 对的例子:
ü         VIEW_ACTION content://contacts/1 -- 显示标识符为"1"的联系人的信息。
ü         EDIT_ACTION content://contacts/1 -- 编辑标识符为"1"的联系人的信息。
ü         VIEW_ACTION content://contacts/ -- 显示可遍历的联系人列表。这是用来进入联系人应用主界面(顶级入口,top-level entry)的典型方法。在这个界面中察看某个联系人会产生一个新的 Intent:{VIEW_ACTION content://contacts/N},用来启动新的Activity,显示该联系人的详细信息。
ü         PICK_ACTION content://contacts/ -- 先是可遍历的联系人列表,并且允许用户在列表中选择一个联系人,然后把这个联系人返回给"上级活动"(parent activity)。例如:电子邮件客户端可以使用这个 Intent,要求用户在联系人列表中选择一个联系人。
除了 action, data 两个主要属性,Intent 还具有一些其它属性,这些属性也可以被用在Intent 里:
category -- 类别,被执行动作的附加信息。 例如 LAUNCHER_CATEGORY 表示 Intent 的接受者应该在 Launcher 中作为顶级应用出现;而 ALTERNATIVE_CATEGORY 表示当前的 Intent 是一系列的可选动作中的一个,这些动作可以在同一块数据上执行。
type -- 数据类型,显式指定 Intent 的数据类型 (MIME)。一般上 Intent 的数据类型能够根据数据本身进行判定,但是通过设置这个属性,可以强制采用显式指定的类型而不再进行推导。
component -- 组件,为使用 Intent 的组件类指定名称。通常会根据 Intent 中包含的其它信息 —— 比如 action, data/type, categories —— 进行查找,最终找到一个与之匹配的组件。如果这个属性存在的话,将直接使用它指定的组件,不再执行上述查找过程。指定了这个属性以后,Intent 的其它所有属性都是可选的。
extras -- 额外的附加信息,是其它所有附加信息的集合。使用 extras 可以为组件提供扩展信息,比如,如果要发送电子邮件,也就是要执行“发送电子邮件”的动作,可以将电子邮件的标题、正文等保存在 extras 里。
在 Intent 类里定义了多种标准 action 和 category 常量(字符串),同时应用也可以根据自己的需要进行定义。这些字符串使用 JAVA 风格的 scoping,从而保证它们的唯一性。比如标准 VIEW_ACTION 的定义是 “android.app.action.VIEW”。
概括而言,“动作”、“数据类型”、“类别”(译注:Intent的action类型)和“附加数据”一起形成了一种语言。这种语言使得系统能够理解诸如“打john的手机”之类的短语。随着应用不断的加入到系统中,它们可以添加新的“动作”、“数据类型”、“类别”来扩展这种语言。应用也可以提供自己的 activities 来处理已经存在的“短语”,从而改变这些“短语”的行为。
 
Intent 解析
Intent 有两种主要形式:
ü         显式意图(直接意图?)。 显式意图是指定了 component 属性的 intents 调用 setComponent(ComponentName) 或者 setClass(Context, Class) 可以为 intents 设定 component 属性 —— 指定具体的组件类。这些 intents 一般不包括包括其它任何信息,它们通常只是用来通知应用启动内部的 activities 作为该应用的(当前)用户界面。
ü         隐式意图(含蓄意图?)。隐式意图是没有指明 comonent 的 intents。这些 intents 必须包括足够的信息,这样系统才能确定在所有的可用组件中,对一个 intent 来说运行哪一个组件才是最合适的。
在使用 implicit intents 的时候,对于一个任意的 intent,我们需要知道用它来做什么。“Intent 解析过程”用来处理这个问题。“Intent 解析过程”将 intent 映射到可以处理它的 activity, IntentReceiver 或者 service。
Intent 解析机制主要是将已安装应用程序包里的 Intent-Filter 描述和 Intent 进行匹配。如果使用广播发送 Intent,还要在已经注册的 IntentReceiver 中尽心匹配。更多的相关描述可以在 IntentFilter 中找到。
在解析 Intent 的过程中要用到 Intent 的三个属性:动作、数据类型和类别。使用这些属性,就可以 PackageManager 上查询能够处理当前 intent 的合适组件。组件是否合适由 AndroidManifest.xml 文件中提供的 intent 信息决定。判断的方法如下:
1、如果 intent 指明了要执行的 action,组件 action 列表中就必须包含着个 action,否则不能匹配;
2、如果 Intent 没有提供数据类型 (type),系统从数据 (data) 中得到数据类型。和 action 一样,组件的数据类型列表中必须包含 intent 的数据类型,否则不能匹配。
3、如果 Intent 中的数据不是 content: 类型的 URL,而且 Intent 也没有明确指定它的数据类型,将根据 Intent 中数据的 scheme (比如 http: or mailto:) 进行匹配。同上,Intent 的 scheme 必须出现在组件的 scheme 列表中。
4、如果 Intent 指定了一个或多个类别,这些类别必须全部出现在组建的类别列表中。比如 intent 中包含了两个类别:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的组件必须至少包含这两个类别。
以一个应用实例作为例子,这个应用可以让用户浏览便笺列表、查看每一个便笺的详细信息:
<manifest xmlns:android=" http://schemas.android.com/apk/res/android"
      package="com.google.android.notepad">
    <application android:icon="@drawable/app_notes"
            android:label="@string/app_name">
        <provider class="NotePadProvider"
                android:authorities="com.google.provider.NotePad" />
               
        <activity class=".NotesList" android:label="@string/title_notes_list">
            <intent-filter>
                <action android:value="android.intent.action.MAIN" />
                <category android:value="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:value="android.intent.action.VIEW" />
                <action android:value="android.intent.action.EDIT" />
                <action android:value="android.intent.action.PICK" />
                <category android:value="android.intent.category.DEFAULT" />
                <type android:value="vnd.android.cursor.dir/vnd.google.note" />
            </intent-filter>
            <intent-filter>
                <action android:value="android.intent.action.GET_CONTENT" />
                <category android:value="android.intent.category.DEFAULT" />
                <type android:value="vnd.android.cursor.item/vnd.google.note" />
            </intent-filter>
        </activity>
        <activity class=".NoteEditor" android:label="@string/title_note">
            <intent-filter android:label="@string/resolve_edit">
                 <action android:value="android.intent.action.VIEW" />
                <action android:value="android.intent.action.EDIT" />
                <category android:value="android.intent.category.DEFAULT" />
                <type android:value="vnd.android.cursor.item/vnd.google.note" />
            </intent-filter>
            <intent-filter>
                <action android:value="android.intent.action.INSERT" />
                <category android:value="android.intent.category.DEFAULT" />
                <type android:value="vnd.android.cursor.dir/vnd.google.note" />
            </intent-filter>
        </activity>
        <activity class=".TitleEditor" android:label="@string/title_edit_title"
                android:theme="@android:style/Theme.Dialog">
            <intent-filter android:label="@string/resolve_title">
                <action android:value="com.google.android.notepad.action.EDIT_TITLE" />
                <category android:value="android.intent.category.DEFAULT" />
                <category android:value="android.intent.category.ALTERNATIVE" />
                <category android:value="android.intent.category.SELECTED_ALTERNATIVE" />
                <type android:value="vnd.android.cursor.item/vnd.google.note" />
            </intent-filter>
        </activity>
    </application>
</manifest>
 
例子中的第一个 activity 是 com.google.android.notepad.NotesList。它是进入应用的主入口(main entry),具有三种功能,分别由三个 intent 模板进行描述。
1、第一个功能是进入便笺应用的顶级入口。它的类型是 android.app.category.LAUNCHER,说明这个应用应该在 Launcher 中被列出。
2、第二个功能用来浏览可用的便笺,或者让用户选择一个特定的便笺并且把这个便笺返回给调用者。当数据类型是 vnd.android.cursor.dir/vnd.google.note (便笺记录的目录) 的时候,执行动作 android.app.action.VIEW 可以浏览可用的便笺;执行动作 android.app.action.PICK 可以让用户选择便笺。
3、第三个功能返回给调用者一个用户选择的便笺。当数据类型是 vnd.android.cursor.dir/vnd.google.note 的时候,执行动作 android.app.action.GET_COUTENT 调用者不需要知道
有了这些功能,就能够将下列 intents 匹配到 NotesList 的 activity:
{ action=android.app.action.MAIN }. 如果 activities 能够被用作进入应用的顶级入口,就可以和这个 intent 进行匹配。
{ action=android.app.action.MAIN, category=android.app.category.LAUNCHER }. 这是目前 Launcher 实际使用的 intent,构成了它的顶级列表。
问题:怎么构成??
{ action=android.app.action.VIEW data=content://com.google.provider.NotePad/notes }. 显示 "content://com.google.provider.NotePad/notes" 下所有便笺的列表,用户可以遍历这个列表,并且察看便笺的详情。
{ action=android.app.action.PICK data=content://com.google.provider.NotePad/notes }. 让用户在 "content://com.google.provider.NotePad/notes" 之下的便笺列表中选择一个,然后将这个便笺的 URL 返回给调用者。
{ action=android.app.action.GET_CONTENT type=vnd.android.cursor.item/vnd.google.note }. 这个 intent 和上面的 pick 动作类似,不同的是这个 intent 允许调用者(仅仅)指定它们需要的数据类型(,而不需要了解数据存放的详细位置,即数据的 URI)。系统根据这个数据类型选择恰当的 activity,然后让用户选择某些数据。
 
第二个 activity 是 com.google.android.notepad.NoteEditor,它为用户显示一个单独的便笺,并且允许用户对这个便笺进行修改。 它具有两个 intent 模板,所以具有两个功能。第一个操作是主要的操作,允许用户察看和编辑一个便签(执行 android.app.action.VIEW 和 android.app.action.EDIT 动作,数据类型是 vnd.android.cursor.item/vnd.google.note)。第二个模板可以让调用者显示创建新便笺的用户界面,并且将新便笺插入到便笺列表中(执行 android.app.action.INSERT 动作,数据类型是 vnd.android.cursor.dir/vnd.google.note)。
有了这两个功能,下列 intents 就能够匹配到 NotesList 的 activity:
{ action=android.app.action.VIEW data=content://com.google.provider.NotePad/notes/{ID} } 向用户显示标识为 ID 的便笺。将标识为 ID 的便笺缩写为 note{ID},下同。
{ action=android.app.action.EDIT data=content://com.google.provider.NotePad/notes/{ID} } 让用户能够编辑 notes{ID}。
{ action=android.app.action.INSERT data=content://com.google.provider.NotePad/notes } 创建一个新的便笺,新便笺被创建在“content://com.google.provider.NotePad/notes”所表示的便笺列表中。用户可以编辑这个便签。当用户保存这个便笺后,这个新便笺的 URI 将会返回给调用者。
 
最后一个 activity 是 com.google.android.notepad.TitleEditor,它可以让用户编辑便笺的标题。它可以被实现为一个类,在 intent 中明确设定 component 属性后,应用可以直接调用这个类;不过在这里我们展示的是如何在已有数据上发布可选操作。这个 activity 只有一个单独的 intent 模板,它具有一个私有 action: com.google.android.notepad.action.EDIT_TITLE,允许用户编辑便笺的标题。和前面的 view 和 edit 动作一样,调用这个 intent 的时候,也必须指定具体的便笺。不一样的是,这里显示和编辑的只是便笺数据中的标题。
除了支持确省类别 (default category, android.intent.category.DEFAULT,原文是 android.intent.category.VIEW,有误),标题编辑器还支持另外两个标准类别:android.intent.category.ALTERNATIVE 和 android.intent.category.SELECTED_ALTERNATIVE。实现了这两个类别之后,其它 activities 可以调用函数 queryIntentActivityOptions(ComponentName, Intent[], Intent, int) 查询这个 activity 支持的 actions,而不需要了解它的具体实现;或者调用 addIntentOptions(int, int, ComponentName, Intent[], Intent, int, Menu.Item[]) 建立动态菜单。需要说明的是, 这个 intent 模板有一个明确的名称(通过 android:label="@string/resolve_title" 指定)。在用户浏览数据的时候, 如果这个 activity 是数据的一个可选操作,指定明确的名称可以为用户提供一个更好控制界面。 有了这个功能,下列 intents 就能够匹配到 NotesList activity    {action=com.google.android.notepad.action.EDIT_TITLE data=content://com.google.provider.NotePad/notes/{ID} } 显示并且允许用户编辑 note{ID} 的标题。
 
Activity 的标准动作 (Actions)
下面是 Intent 为启动 activities 定义的标准动作,一般使用 startActivity(Intent) 启动 activities。其中最重要也是最经常使用的是 MAIN_ACTION 和 EDIT_ACTION.
ü         MAIN_ACTION
ü         VIEW_ACTION
ü         EDIT_ACTION
ü         PICK_ACTION
ü         GET_CONTENT_ACTION
ü         DIAL_ACTION
ü         CALL_ACTION
ü         SENDTO_ACTION
ü         ANSWER_ACTION
ü         INSERT_ACTION
ü         DELETE_ACTION
ü         RUN_ACTION
ü         LOGIN_ACTION
ü         CLEAR_CREDENTIALS_ACTION
ü         SYNC_ACTION
ü         PICK_ACTIVITY_ACTION
ü         WEB_SEARCH_ACTION
 
标准的广播动作 (broadcase actions)
下面是 Intent 为接收广播而定义的动作。可以通过 registerReceiver(IntentReceiver, IntentFilter),或者在 manifest 中增加 receiver 标记来注册。
ü         TIME_TICK_ACTION
ü         TIME_CHANGED_ACTION
ü         TIMEZONE_CHANGED_ACTION
ü         BOOT_COMPLETED_ACTION
ü         PACKAGE_ADDED_ACTION
ü         PACKAGE_REMOVED_ACTION
ü         BATTERY_CHANGED_ACTION
 
标准类别
下面是已定义的标准类别。通过 addCategory(String) 可以为 Intent 设置类别。
ü         DEFAULT_CATEGORY
ü         BROWSABLE_CATEGORY
ü         TAB_CATEGORY
ü         ALTERNATIVE_CATEGORY
ü         SELECTED_ALTERNATIVE_CATEGORY
ü         LAUNCHER_CATEGORY
ü         HOME_CATEGORY
ü         PREFERENCE_CATEGORY
ü         GADGET_CATEGORY
ü         TEST_CATEGORY
 
标准附加数据
下面是已定义的标准字段,用来在 putExtra(String, Object) 中为 Intent 设置附加数据。
ü         TEMPLATE_EXTRA
ü         INTENT_EXTRA
 
启动标记 (launch flags)
下面是 Intent 中可能用到的启动标记,通过 setLaunchFlags(int) 和 addLaunchFlags(int) 使用这些标记。
ü         NO_HISTORY_LAUNCH
ü         SINGLE_TOP_LAUNCH
ü         NEW_TASK_LAUNCH
ü         MULTIPLE_TASK_LAUNCH
ü         FORWARD_RESULT_LAUNCH
 
嵌套类
Intent.FilterComparison 持有一个 Intent 对象,并且为过滤实现了 Intent 的比较操作。
 
常量
Values
String
ADD_SHORTCUT_ACTION
动作:在系统中添加一个快捷方式。.
"android.intent.action.ADD_SHORTCUT"
String
ALL_APPS_ACTION
动作:列举所有可用的应用。
输入:无。
"android.intent.action.ALL_APPS"
String
ALTERNATIVE_CATEGORY
类别:说明 activity 是用户正在浏览的数据的一个可选操作。
"android.intent.category.ALTERNATIVE"
String
ANSWER_ACTION
动作:处理拨入的电话。
"android.intent.action.ANSWER"
String
BATTERY_CHANGED_ACTION
广播:充电状态,或者电池的电量发生变化。
"android.intent.action.BATTERY_CHANGED"
String
BOOT_COMPLETED_ACTION
广播:在系统启动后,这个动作被广播一次(只有一次)。
"android.intent.action.BOOT_COMPLETED"
String
BROWSABLE_CATEGORY
类别:能够被浏览器安全使用的 activities 必须支持这个类别。
"android.intent.category.BROWSABLE"
String
BUG_REPORT_ACTION
动作:显示 activity 报告错误。
"android.intent.action.BUG_REPORT"
String
CALL_ACTION
动作:拨打电话,被呼叫的联系人在数据中指定。
"android.intent.action.CALL"
String
CALL_FORWARDING_STATE_CHANGED_ACTION
广播:语音电话的呼叫转移状态已经改变。
"android.intent.action.CFF"
String
CLEAR_CREDENTIALS_ACTION
动作:清除登陆凭证 (credential)。
"android.intent.action.CLEAR_CREDENTIALS"
String
CONFIGURATION_CHANGED_ACTION
广播:设备的配置信息已经改变,参见 Resources.Configuration.
"android.intent.action.CONFIGURATION_CHANGED"
Creator
CREATOR
String
DATA_ACTIVITY_STATE_CHANGED_ACTION
广播:电话的数据活动(data activity)状态(即收发数据的状态)已经改变。
"android.intent.action.DATA_ACTIVITY"
String
DATA_CONNECTION_STATE_CHANGED_ACTION
广播:电话的数据连接状态已经改变。
"android.intent.action.DATA_STATE"
String
DATE_CHANGED_ACTION
广播:日期被改变。
"android.intent.action.DATE_CHANGED"
String
DEFAULT_ACTION
动作:和 VIEW_ACTION 相同,是在数据上执行的标准动作。
"android.intent.action.VIEW"
String
DEFAULT_CATEGORY
类别:如果 activity 是对数据执行确省动作(点击, center press)的一个选项,需要设置这个类别。
"android.intent.category.DEFAULT"
String
DELETE_ACTION
动作:从容器中删除给定的数据。
"android.intent.action.DELETE"
String
DEVELOPMENT_PREFERENCE_CATEGORY
类别:说明 activity 是一个设置面板 (development preference panel).
"android.intent.category.DEVELOPMENT_PREFERENCE"
String
DIAL_ACTION
动作:拨打数据中指定的电话号码。
"android.intent.action.DIAL"
String
EDIT_ACTION
动作:为制定的数据显示可编辑界面。
"android.intent.action.EDIT"
String
EMBED_CATEGORY
类别:能够在上级(父)activity 中运行。
"android.intent.category.EMBED"
String
EMERGENCY_DIAL_ACTION
动作:拨打紧急电话号码。
"android.intent.action.EMERGENCY_DIAL"
int
FORWARD_RESULT_LAUNCH
启动标记:如果这个标记被设置,而且被一个已经存在的 activity 用来启动新的 activity,已有 activity 的回复目标 (reply target) 会被转移给新的 activity。
16 0x00000010
String
FOTA_CANCEL_ACTION
广播:取消所有被挂起的 (pending) 更新下载。
"android.server.checkin.FOTA_CANCEL"
String
FOTA_INSTALL_ACTION
广播:更新已经被确认,马上就要开始安装。
"android.server.checkin.FOTA_INSTALL"
String
FOTA_READY_ACTION
广播:更新已经被下载,可以开始安装。
"android.server.checkin.FOTA_READY"
String
FOTA_RESTART_ACTION
广播:恢复已经停止的更新下载。
"android.server.checkin.FOTA_RESTART"
String
FOTA_UPDATE_ACTION
广播:通过 OTA 下载并安装操作系统更新。
"android.server.checkin.FOTA_UPDATE"
String
FRAMEWORK_INSTRUMENTATION_TEST_CATEGORY
类别:To be used as code under test for framework instrumentation tests.
"android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"
String
GADGET_CATEGORY
类别:这个 activity 可以被嵌入宿主 activity (activity that is hosting gadgets)。
"android.intent.category.GADGET"
String
GET_CONTENT_ACTION
动作:让用户选择数据并返回。
"android.intent.action.GET_CONTENT"
String
HOME_CATEGORY
类别:主屏幕 (activity),设备启动后显示的第一个 activity。
"android.intent.category.HOME"
String
INSERT_ACTION
动作:在容器中插入一个空项 (item)。
"android.intent.action.INSERT"
String
INTENT_EXTRA
附加数据:和 PICK_ACTIVITY_ACTION 一起使用时,说明用户选择的用来显示的 activity;和 ADD_SHORTCUT_ACTION 一起使用的时候,描述要添加的快捷方式。
"android.intent.extra.INTENT"
String
LABEL_EXTRA
附加数据:大写字母开头的字符标签,和 ADD_SHORTCUT_ACTION 一起使用。
"android.intent.extra.LABEL"
String
LAUNCHER_CATEGORY
类别:Activity 应该被显示在顶级的 launcher 中。
"android.intent.category.LAUNCHER"
String
LOGIN_ACTION
动作:获取登录凭证。
"android.intent.action.LOGIN"
String
MAIN_ACTION
动作:作为主入口点启动,不需要数据。
"android.intent.action.MAIN"
String
MEDIABUTTON_ACTION
广播:用户按下了“Media Button”。
"android.intent.action.MEDIABUTTON"
String
MEDIA_BAD_REMOVAL_ACTION
广播:扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)。
"android.intent.action.MEDIA_BAD_REMOVAL"
String
MEDIA_EJECT_ACTION
广播:用户想要移除扩展介质(拔掉扩展卡)。
"android.intent.action.MEDIA_EJECT"
String
MEDIA_MOUNTED_ACTION
广播:扩展介质被插入,而且已经被挂载。
"android.intent.action.MEDIA_MOUNTED"
String
MEDIA_REMOVED_ACTION
广播:扩展介质被移除。
"android.intent.action.MEDIA_REMOVED"
String
MEDIA_SCANNER_FINISHED_ACTION
广播:已经扫描完介质的一个目录。
"android.intent.action.MEDIA_SCANNER_FINISHED"
String
MEDIA_SCANNER_STARTED_ACTION
广播:开始扫描介质的一个目录。
"android.intent.action.MEDIA_SCANNER_STARTED"
String
MEDIA_SHARED_ACTION
广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。
"android.intent.action.MEDIA_SHARED"
String
MEDIA_UNMOUNTED_ACTION
广播:扩展介质存在,但是还没有被挂载 (mount)。
"android.intent.action.MEDIA_UNMOUNTED"
String
MESSAGE_WAITING_STATE_CHANGED_ACTION
广播:电话的消息等待(语音邮件)状态已经改变。
"android.intent.action.MWI"
int
MULTIPLE_TASK_LAUNCH
启动标记:和 NEW_TASK_LAUNCH 联合使用,禁止将已有的任务改变为前景任务 (foreground)。
8 0x00000008
String
NETWORK_TICKLE_RECEIVED_ACTION
广播:设备收到了新的网络 "tickle" 通知。
"android.intent.action.NETWORK_TICKLE_RECEIVED"
int
NEW_TASK_LAUNCH
启动标记:设置以后,activity 将成为历史堆栈中的第一个新任务(栈顶)。
4 0x00000004
int
NO_HISTORY_LAUNCH
启动标记:设置以后,新的 activity 不会被保存在历史堆栈中。
1 0x00000001
String
PACKAGE_ADDED_ACTION
广播:设备上新安装了一个应用程序包。
"android.intent.action.PACKAGE_ADDED"
String
PACKAGE_REMOVED_ACTION
广播:设备上删除了一个应用程序包。
"android.intent.action.PACKAGE_REMOVED"
String
PHONE_STATE_CHANGED_ACTION
广播:电话状态已经改变。
"android.intent.action.PHONE_STATE"
String
PICK_ACTION
动作:从数据中选择一个项目 (item),将被选中的项目返回。
"android.intent.action.PICK"
String
PICK_ACTIVITY_ACTION
动作:选择一个 activity,返回被选择的 activity 的类(名)。
"android.intent.action.PICK_ACTIVITY"
String
PREFERENCE_CATEGORY
类别:activity是一个设置面板 (preference panel)。
"android.intent.category.PREFERENCE"
String
PROVIDER_CHANGED_ACTION
广播:更新将要(真正)被安装。
"android.intent.action.PROVIDER_CHANGED"
String
PROVISIONING_CHECK_ACTION
广播:要求 polling of provisioning service 下载最新的设置。
"android.intent.action.PROVISIONING_CHECK"
String
RUN_ACTION
动作:运行数据(指定的应用),无论它(应用)是什么。
"android.intent.action.RUN"
String
SAMPLE_CODE_CATEGORY
类别:To be used as an sample code example (not part of the normal user experience).
"android.intent.category.SAMPLE_CODE"
String
SCREEN_OFF_ACTION
广播:屏幕被关闭。
"android.intent.action.SCREEN_OFF"
String
SCREEN_ON_ACTION
广播:屏幕已经被打开。
"android.intent.action.SCREEN_ON"
String
SELECTED_ALTERNATIVE_CATEGORY
类别:对于被用户选中的数据,activity 是它的一个可选操作。
"android.intent.category.SELECTED_ALTERNATIVE"
String
SENDTO_ACTION
动作:向 data 指定的接收者发送一个消息。
"android.intent.action.SENDTO"
String
SERVICE_STATE_CHANGED_ACTION
广播:电话服务的状态已经改变。
"android.intent.action.SERVICE_STATE"
String
SETTINGS_ACTION
动作:显示系统设置。输入:无。
"android.intent.action.SETTINGS"
String
SIGNAL_STRENGTH_CHANGED_ACTION
广播:电话的信号强度已经改变。
"android.intent.action.SIG_STR"
int
SINGLE_TOP_LAUNCH
启动标记:设置以后,如果 activity 已经启动,而且位于历史堆栈的顶端,将不再启动(不重新启动) activity。
2 0x00000002
String
STATISTICS_REPORT_ACTION
广播:要求 receivers 报告自己的统计信息。
"android.intent.action.STATISTICS_REPORT"
String
STATISTICS_STATE_CHANGED_ACTION
广播:统计信息服务的状态已经改变。
"android.intent.action.STATISTICS_STATE_CHANGED"
String
SYNC_ACTION
动作:执行数据同步。
"android.intent.action.SYNC"
String
TAB_CATEGORY
类别:这个 activity 应该在 TabActivity 中作为一个 tab 使用。
"android.intent.category.TAB"
String
TEMPLATE_EXTRA
附加数据:新记录的初始化模板。
"android.intent.extra.TEMPLATE"
String
TEST_CATEGORY
类别:作为测试目的使用,不是正常的用户体验的一部分。
"android.intent.category.TEST"
String
TIMEZONE_CHANGED_ACTION
广播:时区已经改变。
"android.intent.action.TIMEZONE_CHANGED"
String
TIME_CHANGED_ACTION
广播:时间已经改变(重新设置)。
"android.intent.action.TIME_SET"
String
TIME_TICK_ACTION
广播:当前时间已经变化(正常的时间流逝)。
"android.intent.action.TIME_TICK"
String
UMS_CONNECTED_ACTION
广播:设备进入 USB 大容量存储模式。
"android.intent.action.UMS_CONNECTED"
String
UMS_DISCONNECTED_ACTION
广播:设备从 USB 大容量存储模式退出。
"android.intent.action.UMS_DISCONNECTED"
String
UNIT_TEST_CATEGORY
类别:应该被用作单元测试(通过 test harness 运行)。
"android.intent.category.UNIT_TEST"
String
VIEW_ACTION
动作:向用户显示数据。
"android.intent.action.VIEW"
String
WALLPAPER_CATEGORY
类别:这个 activity 能过为设备设置墙纸。
"android.intent.category.WALLPAPER"
String
WALLPAPER_CHANGED_ACTION
广播:系统的墙纸已经改变。
"android.intent.action.WALLPAPER_CHANGED"
String
WALLPAPER_SETTINGS_ACTION
动作:显示选择墙纸的设置界面。输入:无。
"android.intent.action.WALLPAPER_SETTINGS"
String
WEB_SEARCH_ACTION
动作:执行 web 搜索。
"android.intent.action.WEB_SEARCH"
String
XMPP_CONNECTED_ACTION
广播:XMPP 连接已经被建立。
"android.intent.action.XMPP_CONNECTED"
String
XMPP_DISCONNECTED_ACTION
广播:XMPP 连接已经被断开。
"android.intent.action.XMPP_DISCONNECTED"
Public Constructor
Intent()
创建空的intent对象。
Intent(Intent o)
拷贝构造函数。
Intent(String action)
用指定的动作创建 intent 对象。
Intent(String action, ContentURI uri)
创建 intent 对象,指定动作和数据 (URI)。
Intent(Context packageContext, Class cls)
创建 intent 对象,指定 component。
Intent(String action, ContentURI uri, Context packageContext, Class cls)
创建 intent 对象,指定动作、数据和组件。
 
公共方法
Public Constructor
Intent addCategory(String category)
向 intent 添加新的类别。
Intent addLaunchFlags(int flags)
向 intent 添加新的启动标记。
boolean filterEquals(Intent other)
判断两个 intent 是否相等:检查他们是否有完全相同的意图(用于过滤)。
int filterHashCode()
生成 intent 的哈希代码,该代码与 filterEquals 有同样的语义,即能用于进行 intent 比较。
String getAction()
获取 intent 要执行的动作,如:VIEW_ACTION。
Set getCategories()
获取 intent 对象所属的所有类别(集合)。
ComponentName getComponent()
获取 intent 关联的具体组件。
ContentURI getData()
获取 intent 对象要操作的数据 (URI)。
Object getExtra(String name, Object def)
获取 intent 的扩展数据。
Object getExtra(String name)
获取 intent 的扩展数据。
Bundle getExtras()
获取 intent 的扩展数据 map。
static Intent getIntent(String uri)
由 URI 创建 Intent。
int getLaunchFlags()
获取 intent 的所有启动标记。
String getScheme()
获取 intent 中数据的 sheme。
String getType()
获取 intent 明确声明的数据类型(显式声明的 MIME 类型,不是推导出来的类型)。
boolean hasCategory(String category)
Intent 是否指定了类别。
Intent putExtra(String name, Object value)
向 intent 添加扩展数据。
void putExtras(Intent src)
将 src 中的所有扩展数据复制到 intent 中。
void putExtras(Bundle extras)
向 intent 添加扩展数据。
void readFromParcel(Parcel in)
无。
void removeCategory(String category)
从 intent 删除一个类别。
void removeExtra(String name)
从 intent 删除扩展数据。
ComponentName resolveActivity(PackageManager pm)
取得用来处理这个 intent 的 activity 组件。
ActivityInfo resolveActivityInfo(PackageManager pm)
取得用来处理这个 intent 的 activity 的信息 (PackageManager.ActivityInfo)。
String resolveType(ContentResolver resolver)
取得 intent 的 MIME 数据类型。(判断顺序:intent 明确指定的类型;intent 数据隐式包含的数据类型)
String resolveType(Context context)
取得 intent 的 MIME 数据类型。(判断顺序:intent 明确指定的类型;intent 数据隐式包含的数据类型)
String resolveTypeIfNeeded(ContentResolver resolver)
如果 resolver 需要,返回 intent 的数据类型,否则返回空。
Intent setAction(String action)
设置 intent 要执行的动作。
Intent setClass(Context packageContext, Class cls)
设置运行 intent 的组件,和 setComponent 功能相同。
Intent setClassName(String packageName, String className)
设置运行 intent 的组件,和 setComponent 功能相同。
Intent setClassName(Context packageContext, String className)
设置运行 intent 的组件,和 setComponent 功能相同。
Intent setComponent(ComponentName component)
设置运行 intent 的组件。
Intent setData(ContentURI data)
设置处理 intent 的时候要操作的数据。
Intent setDataAndType(ContentURI data, String type)
设置 intent 的数据和数据类型 (MIME)。
Intent setLaunchFlags(int flags)
设置启动标记(用来控制 intent 被处理的方式)。
Intent setType(String type)
设置明确的 MIME 数据类型。
String toString()
为 intent 生成一个可读的字符串描述。
String toURI()
void writeToParcel(Parcel out)
2.2.5 Service
服务是在后台长时间运行的应用组件,不和用户直接进行交互。在每一个服务类的包的 AndroidManifest.xml 文件中,必须有一个相应的 <service> 声明。服务必须用 Context.startService() 或者 Context.bindService() 启动。
和其它应用对象一样,服务运行在它们宿主进程的主线程里。这意味着,如果一个服务需要执行阻塞操作(比如网络操作)或者 CPU 敏感的操作(比如 MP3 播放器),它应该分离出一个线程来执行这样的操作
服务类是应用程序的生命周期中的一个重要部分。在这里要讨论的内容有:
Ø         服务的生命周期
Ø         访问权限
Ø         进程生命周期
服务的生命周期
启动服务有两种方法。
ü         如果客户调用 Context.startService(),系统将获得服务(如果服务不存在,系统创建服务,然后调用它的 onCreate() 方法),然后使用调用者提供的参数调用服务的 onStart(int, Bundle) 方法。从此以后,服务开始持续运行,直到 Context.stopService() 或者 stopSelf() 被调用。注意:多次调用 Context.startService() 虽然会导致 onStart() 被多次调用,但是服务本身不会嵌套(原文如此,应该是说服务的实例只有一个,不会启动多个服务)。所以无论调用多少次 Context.startService(),只要调用一次 Context.stopService() 或者 stopSelf(),服务就会停止运行。
ü         客户也可以调用 Context.bindService() 获得到服务的永久连接。如果服务之前没有启动,一样会创建服务然后调用它的 onCreate() 方法;但是不会调用它的 onStart() 方法。服务调用它的 getBinder() 方法,并且将返回的 IBinder 对象传递给客户。连接建立以后,不管客户是否保留这个 IBinder 对象的引用,只要连接还存在,服务都会持续运行。通常返回的 IBinder 对象是一个由 AIDL 实现的复杂接口。
服务可以同时被启动和绑定多个连接。在这种情况下,只要服务被启动,或者存在着到这个服务的连接,服务都会持续运行。当两个条件都不满足时,系统调用服务的 onDestroy() 方法,服务从此被终止。当 onDestroy() 返回的时候,所有的清理工作(停止线程,取消已经注册的 receivers)都已经完成。
 
访问权限
对服务的全局访问权限可以通过服务的 manifest 中的 <service> 元素指定。这样,其它应用需要在它们的 manifest 中声明对应的 <uses-permission> 元素,这样才能启动、停止和绑定到服务。
同时,在执行 IPC 调用之前,服务可以调用 checkCallingPermission(String) 对这次 IPC 调用的权限进行检查。
关于权限和安全方面的信息,请参考 安全模型文档。
 
进程生命周期
只要服务被启动或者被客户绑定(建立连接),Android 系统就尽可能维护一个进程来作这个服务的宿主。当系统内存不足的时候,系统需要杀死进程来出让内存。这时候在下列情况下,服务的宿主进程具有较高的优先级:
如果服务已经被启动,它的宿主进程比任何在屏幕上对用户可见的进程都具有更低的优先级;但是比其它所有不可见的进程都具有更高的优先级。通常对用户可见的进程的数量非常少,所以正在运行的服务在绝大多数时候不会被杀死 —— 除非系统的可用内存极其匮乏。
如果有客户绑定在服务上,服务的宿主进程的优先级至少和客户的优先级一样(不会比客户更低)。这意味着如果客户对用户可见,那么服务本身也会被系统认为对用户可见。
在服务的宿主进程中运行有其它应用组件,比如 activity,可以提高整个进程的优先级,而不是仅仅提高服务本身的优先级。
Values
String
TAG
"Service"
ALARM_SERVICE, BIND_AUTO_CREATE, CONTEXT_IGNORE_SECURITY, CONTEXT_INCLUDE_CODE, INFLATE_SERVICE, KEYGUARD_SERVICE, LOCATION_SERVICE, MODE_APPEND, MODE_PRIVATE, MODE_WORLD_READABLE, MODE_WORLD_WRITEABLE, NOTIFICATION_SERVICE, POWER_SERVICE, WINDOW_SERVICE
         Service()
Service()
公共成员函数
final
Application getApplication()
返回拥有这个服务的应用。
abstract
IBinder getBinder()
返回到这个服务的通信通道 (communication channel)。
final
void stopSelf(int startId)
停止服务,如果它最后一次启动的 ID 是 startID。
final
void stopSelf()
如果服务已经启动,则停止服务。
 
保护成员函数
void onCreate()
在服务第一次被创建的时候被调用。
void onDestroy()
在服务不再被使用,需要被删除时被调用。
void onStart(int startId, Bundle arguments)
客户调用 startService(Intent, Bundle) 直接启动服务的时候被调用。Bundle 是客户提供的参数,startID 是这次服务启动的唯一标识。
applyThemeResource, bindService, broadcastIntent, broadcastIntent, broadcastIntent, checkCallingOrSelfPermission, checkCallingPermission, checkPermission, clearWallpaper, closeExternalStorageFiles, createDatabase, createPackageContext, deleteDatabase, deleteFile, fileList, getAssets, getClassLoader, getContentResolver, getDataDir, getFileStreamPath, getPackageManager, getPackageName, getPackagePath, getResources, getSharedPreferences, getSystemService, getTheme, getWallpaper, openDatabase, openFileInput, openFileOutput, peekWallpaper, registerExternalStorageListener, registerReceiver, registerReceiver, setTheme, setWallpaper, setWallpaper, showAlert, showAlert, showAlert, showAlert, startActivity, startInstrumentation, startService, stopService, unbindService, unregisterReceiver
bindService, broadcastIntent, broadcastIntent, broadcastIntent, checkCallingOrSelfPermission, checkCallingPermission, checkPermission, clearWallpaper, createDatabase, createPackageContext, deleteDatabase, deleteFile, fileList, getAssets, getClassLoader, getContentResolver, getDataDir, getFileStreamPath, getPackageManager, getPackageName, getResources, getSharedPreferences, getString, getSystemService, getText, getTheme, getWallpaper, obtainStyledAttributes, obtainStyledAttributes, obtainStyledAttributes, obtainStyledAttributes, openDatabase, openFileInput, openFileOutput, peekWallpaper, registerReceiver, registerReceiver, setTheme, setWallpaper, setWallpaper, showAlert, showAlert, showAlert, showAlert, startActivity, startInstrumentation, startService, stopService, unbindService, unregisterReceiver
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
[ 编辑] public static final String TAG
常数值: "Service"
[ 编辑] public Service()
[ 编辑] public final Application getApplication()
返回拥有这个服务的应用。
[ 编辑] public abstract IBinder getBinder()
返回到服务的通信通道 (communication channel)。如果客户没有绑定到服务,返回 null。 IBinder 通常是一个复杂接口(参见 AIDL)。
返回值 返回一个 IBinder 对象,客户可以通过它调用服务(的功能)。
[ 编辑] public final void stopSelf(int startId)
如果服务最后一次启动的 ID 是 startID,则停止服务。这个函数和调用 stopService(Intent) 有同样的结果,但是如果客户发起了新的启动请求,而请求还没有进入到 onStart(int, Bundle),这个函数可以避免停止服务。
参数 startId: 最后一次启动时在 onStart(int, Bundle) 中收到的启动标识 (ID)。
参考 stopSelf()
[ 编辑] public final void stopSelf()
如果服务已经被启动,则停止服务,和 stopService(Intent) 有同样的效果。 参见 stopSelf(int)
[ 编辑] protected void onCreate()
在服务第一次被创建时被调用。
[ 编辑] protected void onDestroy()
服务不再被使用而且需要被删除的时候被调用。服务在这里清理所有它占有的资源,包括线程、已经注册的 receivers 等。函数返回后,不应该再调用这个服务的任何方法。事实上,服务已经中止。
[ 编辑] protected void onStart(int startId, Bundle arguments)
在客户每次调用 startService(Intent, Bundle) 直接启动服务的时候被调用。
参数 startId: 表示这次启动请求的整数标识。可以用在 stopSelf(int) 中。 arguments: 客户在 startService(Intent, Bundle) 中提供的 Bundle。可以为空。
参见 stopSelf(int)
2.2.6 NotificationManager
2.2.7 ContentProvider
      Content providers是Andorid应用的重要 构建模块,它提供应用间共享所需要的数据。Content Providers数据封装后,通过 ContentResolver接口给应用进行访问。 只有在应用间共享数据时, Content provider 才是需要的。例如,联系人信息可能被很多应用引用,这时,有必要将数据保存在Content Provider中。如果不需要在应用间共享数据,你可直接将通过 SQLiteDatabase保持在数据库中。
 
2.3 数据存储与检索(共享数据的方法)
    典型的桌面操作系统提供一种公共文件系统——任何应用软件可以使用它来存储和读取文件,该文件也可以被其它的应用软件所读取(也许会有一些权限控制设定)。 Andorid 采用了一种不同的系统:在 Android ,所有的应用软件数据(包括文件)为该应用软件所私有。然而, Android 同样也提供了一种标准方式供应用软件将私有数据开放给其它应用软件。这一章节描述一个应用软件存储和获取数据、开放数据给其它应用软件、从其他应用软件请求数据并且开放它们的多种方式
Android提供如下机制以存储和获取数据。
一个轻量级的存储和获取机制,采用一对简单的数据类型:key和value。它的典型应用是存储应用软件参数。
你可以将文件存储在手机或可移动存储媒介中。默认其他程序不可访问这些文件。
Android API包含有对SQLite的支持。你的应用程序可以创建并使用一个私有SQLite数据库。每个数据库为创建它的包所私有。
内容提供器是一个开放私有数据读写权限的可选组件。受限于该私有数据想施加的任何约束。内容提供器执行标准的数据请求语句、通过标准的数据访问机制返回数据。Android 提供了多种针对标准数据类型的内容提供器,例如个人通讯录。
不要忘记你同样可以使用网络来存储和获取数据
2.3.1参数
你可以存储应用软件启动时需要载入的参数,例如默认问候语或文本字体。调用 Context.getSharedPreferences() 读取和写入参数值,如果你想将参数共享给包内的其它组件,请为参数分配一个名字。或者使用 Activity.getPreferences() 和无名参数以对调用保持私有。你不能跨越包将参数共享。
这里是一个为计算器静音按键模式设置用户参数的例子。
public class Calc extends Activity {
public static final String PREFS_NAME = "MyPrefsFile";
    ...      
   @Override
   protected void onCreate(Bundle state){         
      super.onCreate(state);
   
   ...
   
      // Restore preferences
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
      boolean silent = settings.getBoolean("silentMode", false);
      setSilent(silent);
   }
   
   @Override
   protected void onStop(){
      super.onStop();
   
     // Save user preferences. We need an Editor object to
     // make changes. All objects are from android.context.Context
     SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
     SharedPreferences.Editor editor = settings.edit();
     editor.putBoolean("silentMode", mSilentMode);
     // Don't forget to commit your edits!!!
     editor.commit();
   }
}
 
2.3.2使用文件
    Android 提供接口去读写一个软件的本地文件的streams。调用 Context.openFileOutput()Context.openFileInput(),使用本地文件名和路径去读写文件。另一个软件调用这些方法时使用同样的文件名和路径字符串将无法工作。你只能访问本地文件。如果你有静态文件需要在编译时同软件一起打包,你可以把文件保存在工程的res/raw/<mydatafile>下,并且可以用 Resources.openRawResource (R.raw.mydatafile)来获取它。
2.3.3 SQLite数据库
Android支持SQLite数据库系统并开放数据库管理函数,这使你可以将复杂的数据集合储存到有用的对象中。例如,Android定义了一种由字符串型姓名、字符串型地址、数字型电话号码、一个位图图像和多种其它个人信息描述字段组成的通讯录数据类型。使用 Context.createDatabase()Context.openDatabase()创建读写数据库和适当的读写数据(注意:位图这样的文件数据与本地文件路径一样,以文件路径字符串值形式存放在数据库中)
Android整合sqlite3数据库工具,允许你在SQLite数据库中浏览表内容、运行SQL命令并执行其它有用的函数。
SQLite及其它的所有的数据库,被储存于/data/data/<package_name>/databases
创建多少表、包含多少字段、如何连接,已经超越了这篇文档的讨论范围,但是Android没有施加任何越过SQLite concepts的限制。我们极力推荐包含一个如唯一ID的自增字段以快速查找记录。对于私有数据,这并不需要。但是如果你使用一个内容提供器,你必须包括一个类似唯一ID的字段。请参考NotePad示例程序中的示例类NotePadProvider.java,那是一个创建和组装新数据库的例子。任何数据库可以凭借数据库名被该软件中的任何一个类访问,但不能在该软件范围外访问。
2.3.4内容提供器
访问内容提供器
如果希望公开你的数据,你可以创建(或调用)一个内容提供器。这是一个可以从所有应用软件中存储和获取数据的对象。这也是穿越包共享数据的唯一方式——没有供所有包共享数据的公共存储区域。Android整合了基于多种公共数据类型(音频、视频、图像、个人通讯录信息等等)的内容提供器。你可以在provider包中看到许多Android自带的内容提供器。
实际数据储存的方式取决于它对接口的具体实现。但是所有内容提供器必须执行公共协议去请求数据和返回结果。内容提供器可以使用自定义帮助器功能使被开放的指定数据更容易被存储/获取。
使用内容提供器存储和获取数据
这一节阐述你或其他任何人如何使用内容提供器存储和获取数据。Adnroid为广泛的数据类型开放了多种数据提供器,从音乐文件、图像文件到电话号码。你可以从有用的android.provider包中看到一个开放的内容提供器列表。
Android的内容提供器被客户端宽松地连接。每一个内容提供器开放一个唯一的字符串(URI)来识别将要操作的数据类型,客户端必须使用该字符串来存储和获取相应类型的数据。在《数据请求》章节中我们将对此做更多解释。
这一节请求下列内容
请求数据
制作请求
请求的返还值
请求文件
读取获得的数据
修改数据
添加记录
删除记录
数据请求
每一种内容提供器开放一个唯一公共URI(由ContentURI封装),它将被客户端用于从内容提供器请求/添加/更新/删除数据。URI有2种形式:一是指出该类型数据的所有值(例如所有个人通讯录),二是指出该类型数据的特定记录(例如乔•史密斯的联络信息)
content://contacts/people/ 从设备返回通讯录姓名列表
content://contacts/people/23 返回通讯录中ID=23的单行记录
当应用将请求发送到设备,要求获取整体数据(所有电话号码)或指定数据(鲍勃的电话号码)。Android将返回一个包含指定行的记录集游标。让我们来看一个假定的请求字符串和结果集(结果已被调整的更清晰一些)。
请求字符串 = content://contacts/people/
结果:
_ID
_COUNT
NUMBER
NUMBER_KEY
LABEL
NAME
TYPE
13
4
(425) 555 6677
425 555 6677
California office
Bully Pulpit
Work
44
4
(212) 555-1234
212 555 1234
NY apartment
Alan Vain
Home
45
4
(212) 555-6657
212 555 6657
Downtown office
Alan Vain
Work
53
4
201.555.4433
201 555 4433
Love Nest
Rex Cars
Home
 
注意请求字符串不是一个标准的SQL请求,URI字符串描述了返回数据的类型。这个URI由3部分组成:字符串“content://”;一个描述数据类型的段;一个可选的在特定内容范围内某特定记录的ID。这里有一些请求字符串的例子:
content://media/images 从设备返回所有图像的URI
content://contacts/people/ 从设备返回通讯录中所有姓名列表的URI
content://contacts/people/23 返回通讯录中ID=23的单行记录
2.3.5 Android网络存储
除了选择基于设备的存储,你还可以从可用网络存储和恢复数据。要操作网络,你需要使用下列包:
·   java.net.*
·   android.net.*
3 开发工具箱
3.1 创建自定义android组件
     Android提供了一套可靠的可视化组件(View components,视图组件?)集合,你可以在此基础上构建自己的应用,这个集合中包括的组件有:Button, TextView, EditText, ListView, CheckBox, RadioButton, Gallery, Spinner等等,甚至还包括很多高级的、有着特殊用途的可视化组件,例如AutoCompleteTextView, ImageSwitcher 和 TextSwitcher. 各种布局管理器,如LinearLayout, FrameLayout 等等,也派生自View类结构(译注:原文中意指,各种布局管理器以及控制器也可以看作是可视化组件).
在你的应用程序中,你可以综合运用(combine,联合)这些布局和控制器,让他们展现在屏幕上. 在大多数情况下,这对于开发者来说已经足够用了,但是你应该意识到,其实你是可以通过继承来自己扩展这些Views,Layouts 甚至是高级的控制器的,而且你也可以创建自己的自定义组件.
3.2 Android中可选的API
3.2.1定位服务
3.2.2媒体API
   媒体API可以用来回放MP3或其它音频文件,或是从web上下载的视频。而且媒体API还支持“播放URI”,也就是说,能够播放网络上的流媒体内容。
3.2.3 OpenGL 3D
 Android的主要用户界面框架是一个典型的面向widget的类继承体系。但这只是表面,实际上这个用户界面的底层是一个非常快的带硬件加速的2D和3D的复合引擎。应用程序可以OpenGL ES API来访问系统的3D功能。
 
3.2.4底层硬件访问
Android提供了API来方便应用程序访问底层硬件,例如Bluetooth和WiFi。然而,目前SDK还不包括这些API。当发布这些API时,文档也会被相应地更新。
3.3 Android平台的Google APIService
既然Android是一个开放的手机平台,那么任何人都可以DIY他们自己的设备来运行核心Android操作系统。没有任何规定要求运行Android的手机一定要安装Google的软件或一定要和Google有某种关联。
然而,一些机构或许需要从Google获得一套可选用户应用程序的授权,这些可选的程序使得你能够在你的应用程序中访问Google服务。 通常来说,这些可选的用户程序是一套包含在Android的系统基础映像(base system image)中的软件,并随手机一起发行。
很多设备都会包括这些库,为了方便开发者,我们这里提供了这些API相关的信息。然而,请明确下述列表中的API并不是在所有的 Android设备上都可用。如果手机没有安装Google的软件,那么这些库和类也不会在手机上。这样做的结果是,如果你的应用程序需要使用这些API 而必要的库又不存在时,你的应用可能就无法安装或正确运行。
 
MapView
MapView是一个Android的View对象,通过它第三方的代码可以在手机屏幕上显示和控制Google Map。Google的地图应用程序也提供了 MapActivity 对象,通过这个对象,其它的 Activity 能够显示某个位置的地图, MapView 是MapActivity的一个补充,都可以用来显示地图 。下表归纳了这两种方式的区别。
MapActivity和MapView特性对照表
特性
MapActivity
MapView
Layout嵌入支持
不支持(仅支持独立显示)
支持
用户控制的导航
支持
支持
代码控制的导航
不支持
支持
触发事件被用户代码捕捉
不支持
支持
与Activity相比,使用MapView带来的好处是你能够把Map和你的Layout更紧密地集成在一起;例如,你可以封装MapView, 形成自定义的控件,也可以在MapView的基础上添加很炫的交互方式(比方说用一个倾斜度传感器来探测用户倾斜设备的角度,从而相应以不同的方向来滚动 地图)。MapView的缺点是你需要写更多的代码来使用它;如果你只是想简单地在一个标准的UI控件上显示Google Map,那么使用Activity要简单得多。
 
采用 XMPP P2P 服务
一些应用程序需要在设备与设备之间频繁地交换信息。例如,你可能希望两个设备可以相互发送消息来实现一个交互式的Checker游戏。或者,你也许会开发一个社交应用程序(IM等)来发送一条消息给你的好友。
实现这种功能的一种途径是发送短消息给其它的手机。通过 IntentReceiver, 你的手机来接收到数据时,系统会收到通知,转而去检查这些数据是否包含你的应用程序所需要的数据,如果包含,那么系统就将该数据发送给你的应用程序,同时 阻止其它应用程序直接看到这部分数据。这种机制工作得很好,但是存在两个主要的不足:SMS消息有时候需要好几分钟才能被对方收到,而且用户发送和接受需 要付一定费用。如果你在SMS的基础上开发在线游戏,这款游戏很可能慢得让人沮丧,而且对用户来说玩游戏需要花很多钱。
作为SMS的替代方案,Google提供了一组采用XMPP协议传输消息的API。XMPP包括在线状态通知功能,这就为手机提供了一种很便捷的途径去通知彼此是否在线或是否可以互相发送消息。XMPP还提供了一个和SMS消息传递类似的编程模型,其中消息发送者调用一个 Service的一个方法来发送一条消息,接收者通过广播一个 Intent来接收消息到达通知。但由于XMPP采用的是一个持续的套接字连接,响应时间要比SMS短得多,从而提供十分流畅的用户体验。
XMPP仅维持一个到XMPP服务器的连接,所有的XMPP流量--包括标准的XMPP及时消息和点对点的消息传递系统控制命令都在这个连接上进行传输。
API编程举例
1 SQLite
1、 创建数据库
SQLiteDatabase db =Context.createDatabase ( DATABASE_NAME , DATABASE_VERSION , 0, null );
2、 打开数据库
Context.openDatabase(DATABASE_NAME,null);
3、 建表
Db.execSQL();
4、 插入一行
    ContentValues initialValues = new ContentValues();
    initialValues.put("title", title);
    initialValues.put("body", body);
     db.insert(DATABASE_TABLE, null, initialValues);
5、 删除一行
Db.delete(DATABASE_TABLE,”rowed=”+rowed,null);
6、 查询所有
 Cursor c =
                db.query(DATABASE_TABLE, new String[] {
                    "rowid", "title", "body"}, null, null, null, null, null);
            int numRows = c.count();
            c.first();
            for (int i = 0; i < numRows; ++i) {
                Row row = new Row();
                row.rowId = c.getLong(0);
                row.title = c.getString(1);
                row.body = c.getString(2);
                ret.add(row);
                c.next();
            }
7、 查询一行
 Cursor c =
            db .query( true , DATABASE_TABLE , new String[] {
                "rowid" , "title" , "body" }, "rowid=" + rowId, null , null ,
                null , null );
        if (c.count() > 0) {
            c.first();
            row. rowId = c.getLong(0);
            row. title = c.getString(1);
            row. body = c.getString(2);
            return row;
        } else {
            row. rowId = -1;
            row. body = row. title = null ;
        }
8、 更新
       ContentValues args = new ContentValues();
        args.put( "title" , title);
        args.put( "body" , body);
        db .update( DATABASE_TABLE , args, "rowid=" + rowId, null );
9、 调用
可在 Activity 中如此调用: dbHelper = new DBHelper(this)
学习过程中遇到的问题
1,  eclipse中debug与DDMS的关系?DDMS怎么用?
2,每次应用启动时,模拟器要模拟连网,速度挺慢,能不能省去这步?
3, 有时不能Debug ? 难道有2个的上限吗?
4,书写资源文件时如何提示?
5, DEBUG时表现怪异,不能跟踪源代码?
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐