一、Android应用程序简介
Android应用程序使用Java做为开发语言。
aapt工具把编译后的Java代码连同其它应用程序需要的数据和资源文件一起打包到一个Android包文件中,这个文件使用.apk做为扩展名,它是分发应用程序并安装到移动设备的媒介,用户只需下载并安装此文件到他们的设备。单一.apk文件中的所有代码被认为是一个应用程序。

从很多方面来看,每个Android应用程序都存在于它自己的世界之中:
•默认情况下,每个应用程序均运行于它自己的Linux进程中。当应用程序中的任意代码开始执行时,Android启动一个进程,而当不再需要此进程而其它应用程序又需要系统资源时,则关闭这个进程。
•每个进程都运行于自己的Java虚拟机(VM)中。所以应用程序代码实际上与其它应用程序的代码是隔绝的。
•默认情况下,每个应用程序均被赋予一个唯一的Linux用户ID,并加以权限设置,使得应用程序的文件仅对这个用户、这个应用程序可见。当然,也有其它的方法使得这些文件同样能为别的应用程序所访问。 使两个应用程序共有同一个用户ID是可行的,这种情况下他们可以看到彼此的文件。从系统资源维护的角度来看,拥有同一个ID的应用程序也将在运行时使用同一个Linux进程,以及同一个虚拟机。

1、引言
在大多数操作系统里,存在独立的一个1对1的可执行文件(如Windows里的exe文件), 它可以产生进程,用户能操作界面图标并和应用进行交互。但在Android里,这是不固定的,理解将这些分散的部分组合到一起是非常重要的。
由于Android这种可灵活变通的特性,你在实现一个应用不同部分时需要理解一些基础技术:
Android 包: (简称 .apk ) ,里面包含应用程序的代码以及资源。这是一个用户能下载并安装他们设备上的文件。
任务:一般情况下用户能当它为一个“应用程序”来启动:通常在桌面上会有一个图标可以来启动任务,这是一个上层的应用,可以将你的任务切换到前台来,放在其他应用的上面。
进程:是一个代码运行的核心过程。通常.apk包里所有代码运行在一个进程里,一个进程对应一个.apk包;然而, 进程 标签常用来改变代码运行的位置,可以是 全部的.apk包 或者是独立的 活动, 接收器, 服务, 或者 提供器组件。

2、任务
记住关键的一点:当用户看到的“应用”,无论实际是如何处理的,它都是一个任务。如果你仅仅通过一些活动来创建一个.apk包,其中有一个肯定是上层入口(通过动作的intent-filter 以及分类android.intent.category.LAUNCHER),然后你的.apk包就创建了一个单独任务,无论你启动哪个活动都会是这个任务的一部分。
任务如果从使用者的观点来看,它是一个应用程序;对开发者来讲,它是贯穿活动着任务的一个或者多个视图,或者一个活动栈。当设置Intent.FLAG_ACTIVITY_NEW_TASK标志启动一个活动意图时,任务就被创建了;这个意图被用作任务的根意图(root Intent),定义区分哪个任务。如果活动启动时没有这个标记,它将被运行在同一个任务里(除非你的活动以特殊模式被启动,这个后面会讨论)。如果你使用 FLAG_ACTIVITY_NEW_TASK标记并且这个意图的任务已经启动,任务将被切换到前台而不是重新加载。
FLAG_ACTIVITY_NEW_TASK必须小心使用:在用户看来,一个新的应用程序由此启动。如果这不是你期望的,你将不会创建一个新的任务。另外,如果用户需要从桌面退出到他原来的地方然后使用同样的意图打开一个新的任务,你需要使用新的任务标记。否则,如果用户在你刚启动的任务里按桌面(HOME)键,而不是退出(BACK)键,你的任务以及任务的活动将被放在桌面程序的后面,没有办法再切换过去。

3、任务亲和力
一些情况下Android需要知道哪个任务的活动附属于一个特殊的任务,即使该任务还没有被启动。这通过任务亲和力来完成,它为任务中一个或多个可能要运行的活动提供一个独一无二的静态名字。默认为活动命名的任务亲和力的名字,就是实现该活动.apk包的名字。这提供一种通用的特性,对用户来说,所有在.apk包里的活动都是单一应用的一部分。
当不带 Intent.FLAG_ACTIVITY_NEW_TASK 标记启动一个新的活动,任务亲和力对新启动的活动将没有影响作用:它将一直运行在它启动的那个任务里。然而,如果使用NEW_TASK标记,亲和力会检测已经存在的任务是否具有相同的亲和力。如果是,该任务会被切换到前台,新的活动会在任务的最上面被启动。
你可以在你的表现文件里的应用程序标签里为.apk包里所有的活动设置你自己的任务亲和力,当然也可以为单独的活动设置标签。这里有些例子演示如何使用:
如果你的.apk包里包含多个用户可启动的上层应用程序,那么你可能想要为每个活动分配不同的亲和力。这里有一个不错的协定,你可以将不同的名字字串加上冒号附加在.apk包名字的后面 。 例如,"com.android.contacts"的亲和力命名可以是"com.android.contacts:Dialer" and "com.android.contacts:ContactsList"。
如果你想替换一个通知,快捷键,或者其它能从外部启动的应用程序的内部活动,你需要在你想替换的活动里明确的设置任务亲和力(taskAffinity)。例如,如果你想替换联系人详细信息浏览界面(用户可以直接操作或者通过快捷方式调用),你需要设置任务亲和力(taskAffinity)为“com.android.contacts”。

4、启动模式以及启动标记
你控制活动和任务通信的最主要的方法是通过设置启动模式的属性以及意图相应的标记。这两个参数能以不同的组合来共同控制活动的启动结果,这在相应的文档里有描述。这里我们只描述一些通用的用法以及几种不同的组合方式。
你最通常使用的模式是singleTop(除了默认为standard模式)。这不会对任务产生什么影响;仅仅是防止在栈顶多次启动同一个活动。
singleTask模式对任务有一些影响:它能使得活动总是在新的任务里被打开(或者将已经打开的任务切换到前台来)。使用这个模式需要加倍小心该进程是如何和系统其他部分交互的,它可能影响所有的活动。这个模式最好被用于应用程序入口活动的标记中。(支持MAIN活动和LAUNCHER分类)。
singleInstance启动模式更加特殊,该模式只能当整个应用只有一个活动时使用。
有一种情况你会经常遇到,其它实体(如搜索管理器SearchManager 或者 通知管理器NotificationManager)会启动你的活动。这种情况下,你需要使用 Intent.FLAG_ACTIVITY_NEW_TASK 标记,因为活动在任务(这个应用/任务还没有被启动)之外被启动。就像之前描述的一样, 这种情况下标准特性就是当前和任务和新的活动的亲和性匹配的任务将会切换到前台,然后在最顶端启动一个新的活动。当然,你也可以实现其它类型的特性。
一个常用的做法就是将Intent.FLAG_ACTIVITY_CLEAR_TOP 和NEW_TASK一起使用。这样做,如果你的任务已经处于运行中,任务将会被切换到前台来, 在栈里的所有的活动除了根活动,都将被清空,根活动的onNewIntent(Intent) 方法传入意图参数后被调用。当使用这种方法的时候 singleTop 或者 singleTask启动模式经常被使用,这样当前实例会被置入一个新的意图,而不是销毁原先的任务然后启动一个新的实例。
另外你可以使用的一个方法是设置活动的任务亲和力为空字串(表示没有亲和力),然后设置finishOnBackground属性。 如果你想让用户给你提供一个单独的活动描述的通知,倒不如返回到应用的任务里,这个比较管用。要指定这个属性,不管用户使用BACK还是HOME,活动都会结束;如果这个属性没有指定,按HOME键将会导致活动以及任务还留在系统里,并且没有办法返回到该任务里。
请确保阅读过文档启动模式属性(launchMode attribute) 以及 意图标记(Intent flags) ,关注这些选项的详细信息。

5、进程
在Android中,进程是应用程序的完整实现,而不是用户通常了解的那样。他们主要用途很简单:
提高稳定性和安全性,将不信任或者不稳定的代码移动到其他进程。
可将多个.apk包运行在同一个进程里减少系统开销。
帮助系统管理资源,将重要的代码放在一个单独的进程里,这样就可以单独销毁应用程序的其他部分。
像前面描述的一样,进程的属性被用来控制那些有特殊应用组件运行的进程。注意这个属性不能违反系统安全: 如果两个.apk包不能共享同一个用户ID,却试图运行在通一个进程里,这种情况是不被允许的,事实上系统将会创建两个不同的进程。
请查看《Android的安全与权限》相关文档以获取更多关于安全限制方面的信息。

6、线程
每个进程包含一个或多个线程。多数情况下,Android 避免在进程里创建多余的线程,除非它创建它自己的线程,我们应保持应用程序的单线程性。
注意新的线程不是为活动,广播接收器,服务或者内容提供器实例创建:这些应用程序的组件在进程里被实例化(除非另有说明,都在同一个进程处理),实际上是进程的主线程。这说明当系统调用时这些组件(包括服务)不需要进程远距离或者封锁操作(就像网络呼叫或者计算循环),因为这将阻止进程中的所有其他组件。你可以使用标准的线程 类或者Android的HandlerThread 类去对其它线程执行远程操作。
这里有一些关于创建线程规则的例外:
呼叫IBinder或者IBinder实现的接口,如果该呼叫来自其他进程,你可以通过线程发送的IBinder或者本地进程中的线程池呼叫它们,从进程的主线程呼叫是不可以的。特殊情况下,,呼叫一个服务 的IBinder可以这样处理。(虽然在服务里呼叫方法在主线程里已经完成。)这意味着IBinder接口的实现必须要有一种线程安全的方法,这样任意线程才能同时访问它。
呼叫由正在被调用的线程或者主线程以及IBinder派发的内容提供器 的主方法。被指定的方法在内容提供器的类里有记录。这意味着实现这些方法必须要有一种线程安全的模式,这样任意其它线程同时可以访问它。
呼叫视图以及由视图里正在运行的线程组成的子类。通常情况下,这会被作为进程的主线程,如果你创建一个线程并显示一个窗口,那么继承的窗口视图将从那个线程里启动。

二、Android应用程序组件
Android的核心功能之一就是一个应用程序可以使用其它应用程序的元素(如果那个应用程序允许的话)。比如说,如果你的应用程序需要一个图片卷动列表,而另一个应用程序已经开发了一个合用的而又允许别人使用的话,你可以直接调用那个卷动列表来完成工作,而不用自己再开发一个。你的应用程序并没有吸纳或链接其它应用程序的代码,它只是在有需求的时候启动了其它应用程序的那个功能部分。

为达到这个目的,系统必须在一个应用程序的一部分被需要时启动这个应用程序,并将那个部分的Java对象实例化。与在其它系统上的应用程序不同,Android应用程序没有为应用准备一个单独的程序入口(比如说,没有main()方法), 而是为系统依照需求实例化提供了基本的组件。共有四种组件类型:
1、 Activity
2、 Broadcast Intent Receiver
3、 Service
4、 Content Provider

(1)、Activity介绍
Activity:
在Android的程序中,Activity一般代表手机屏幕的一屏。如果吧手机比作一个浏览器,那么Activity就相当于一个网页。
从一个activity转向另一个的方式是靠当前的activity启动下一个。

View:
窗口显示的可视内容是由一系列视图构成的,这些视图均继承自 View 基类。每个视图均控制着窗口中一块特定的矩形空间。
View是activity与用户进行交互的界面。比如说,视图可以显示一个小图片,并在用户指点它的时候产生动作。Android有很多既定的视图供用户直接使用,包括按钮、文本域、卷轴、菜单项、复选框等等。
上下文视图是位于视图层次根位置的视图对象。
父级视图包含并组织它子视图的布局。
叶节点视图(位于视图层次最底端)在它们控制的矩形中进行绘制,并对用户对其直接操作做出响应。
视图层次是由Activity.setContentView() 方法放入activity的窗口之中的。

Intent:
一个Intent就是一次对将要执行的操作的抽象描述。Intent指明了请求的操作名称以及作为操作对象的数据的URI和其它一些信息,包括动作(Active)和动作对应的数据(Data)。
通过Intent我们可以在多个Activity之间进行跳转。

Intent Filter:
如果说Intent是一个有效请求,一个Internt Fileter则用于描述一个Activity(或Intent Receiver)能够操作那些Intent。

(2)、Broadcast Intent Receiver介绍
Broadcast Intent Receiver是一个专注于接收广播通知信息,并做出对应处理的组件。
可以使用Broadcast Intent Receiver来让应用对一个外部的事件作出响应。
很多广播是源自于系统代码的──比如,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。
应用程序也可以进行广播──比如说,通知其它应用程序一些数据下载完成并处于可用状态。
应用程序可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。
广播接收器没有用户界面。然而,它们可以启动一个activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

(3)、Service介绍
服务没有可视化的用户界面,而是在一段时间内在后台运行的程序。比如说,一个服务可以在用户做其它事情的时候在后台播放背景音乐、从网络上获取一些数据或者计算一些东西并提供给需要这个运算结果的activity使用。每个服务都继承自Service基类。
一个媒体播放器播放播放列表中的曲目是一个不错的例子。播放器应用程序可能有一个或多个activity来给用户选择歌曲并进行播放。然而,音乐播放这个任务本身不应该为任何activity所处理,因为用户期望在他们离开播放器应用程序而开始做别的事情时,音乐仍在继续播放。为达到这个目的,媒体播放器activity应该启用一个运行于后台的服务。而系统将在这个activity不再显示于屏幕之后,仍维持音乐播放服务的运行。
你可以连接至(绑定)一个正在运行的服务(如果服务没有运行,则启动之)。连接之后,你可以通过那个服务暴露出来的接口与服务进行通讯。对于音乐服务来说,这个接口可以允许用户暂停、回退、停止以及重新开始播放。
如同activity和其它组件一样,服务运行于应用程序进程的主线程内。所以它不会对其它组件或用户界面有任何干扰,它们一般会派生一个新线程来进行一些耗时任务(比如音乐回放)。

(4)、Content Provider介绍
Content Provider将一些特定的应用程序数据供给其它应用程序使用。数据可以存储于文件系统、SQLite数据库或其它方式。
Content Provider继承于ContentProvider 基类,为其它应用程序取用和存储它管理的数据实现了一套标准方法。
应用程序并不直接调用这些方法,而是使用一个 ContentResolver 对象,调用它的方法作为替代。ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。
Content Provider常见接口:query、insert、update、delete。

三、Android应用程序工程文件组成
从Eclipse的Package Explorer视图中观察Android应用程序工程文件由如下部分组成:
1、src:源文件目录,存放.Java文件。
2、gen:包含R.java文件。它由Eclipse自动生成和修改,不需要我们去维护。下面的res文件夹中发生的任意变化,R.java都会重新编译,同步更新。
3、androidXX.XX:android库文件。
4、assets:存放多媒体等文件。
5、res:存放应用用到的资源文件,分如下目录:
(1)drawable-hdpi:存放高分辨率图片资源。
(2)drawable-mdpi:存放中分辨率图片资源。
(3)drawable-ldpi:存放低分辨率图片资源。
(4)layout:存放.xml格式的布局文件。
(5)values:存放字符串(strings.xml)、颜色(colors.xml)、数组(arrays.xml)。
6、AndroidManifest.xml:应用程序的配置文件。这个配置文件里面必须声明应用的名称,应用所用到的Activity,Service,receiver等。
AndroidManifest.xml主要包含以下功能:
• 命名应用程序的Java包,这个包名用来唯一标识应用程序;
• 描述应用程序的组件-活动,服务,广播接收者,以及组成应用程序的内容提供器;对实现每个组件和公布其能力(比如,能处理哪些意图消息)的类进行命名。这些声明使得Android系统了解这些组件以及在什么条件下可以被启动;
• 决定应用程序组件运行在哪个进程里面;
• 声明应用程序所必须具备的权限,用以访问受保护的部分API,以及和其它应用程序交互;
• 声明应用程序其他的必备权限,用以组件之间的交互;
• 列举测试设备Instrumentation类,用来提供应用程序运行时所需的环境配置及其他信息,这些声明只在程序开发和测试阶段存在,发布前将被删除;
• 声明应用程序所要求的Android API的最低版本级别;
• 列举application所需要链接的库;


更多相关文章

  1. Android系统
  2. Android4.0 Design之UI设计易犯的错误2
  3. Android官方入门文档
  4. android:屏蔽按键进入安全模式
  5. 分析点击android桌面app图标启动应用程序的过程
  6. Android(安卓)应用程序快速启动的秘诀
  7. Android官方技术文档翻译——Ant 任务
  8. Android应用程序框架层和系统运行库层日志系统源代码分析
  9. android AsyncTask用法简单简绍

随机推荐

  1. conda不是内部或者外部命令
  2. 使用自定义qemu二进制文件与libvirt失败?
  3. MQTT协议应用:外网手机控制内网树莓派
  4. 没有这样的专栏:blog_article.date
  5. 在不知道序列长度的情况下,在Python中展开
  6. 开启我的Python量化之路
  7. 50. Pow(x,n) Leetcode Python
  8. 【机器学习算法-python实现】最大似然估
  9. 毕业设计-基于深度神经网络的语音关键词
  10. FieldErro:无法将关键字'date_added'解析