Android应用程序开发入门——基础概念

本文链接: http://www.deyangdianzi.com/zblog/post/编程天地/AndroidYingYongChengXuKaiFaRuMen-JiChuGaiNian.html


Android 程序是利用Java语言来开发的。编译完成的java代码、数据和资源文件是通过一个叫做aapt的工具进行打包,打包之后会生成一个.apk文件。最终用户可以将.apk文件安装在Android手机上; 一般情况下,一个.apk文件就被称为一个应用程序。

每一个Android应用程序都是运行在一个独立环境中的,这体现在很多方面:
* 默认情况下,每一个Android应用程序都是运行在它自己的linux进程中。当应用程序的任何部分代码需要被执行时,Android会启动这个进程来运行它;当不再需要这个应用(即进程)时,并且其它应用请求系统资源时,Android就会关闭这个进程。
* 每一个进程都拥有一个独立的虚拟机(VM)。所以每一个应用程序相对于其它的应用程序是运行在一个孤岛环境中的。
* 默认情况下,每一个Android应用程序都被分配了一个linux用户id,并且进行了相关的权限设置,所以应用程序的文件只是对本应用程序是可见的(当然,也是一些方式方法来将这些文件导出给其它的应用程序使用)。
多个应用程序是可以分配一个相同的用户id的,这样的话,它们就能够访问彼此的文件了。多个拥有相同用户id的应用程序也可以运行在同一个linux进程中,共用一个虚拟机,以节约系统资源。

1. Application Components(应用部件)

Android的一个主要特性就是一个应用程序可以使用其它应用程序的元素(如果其它应用程序许可)。
比如你的应用程序需要显示一个滚动列表的图像,并且另外的一个应用程序已经开发出了一个适合的滚动列表,而且允许这个滚动列表被其它的应用程序使用。那么你就可以调用那个滚动列表来完成工作,而不需要再去开发新的滚动列表。你的应用并不需要包含那个应用程序的代码,也不需要链接它。你只需在你的程序中需要显示滚动列表的时候,启动那个应用程序的滚动列表的代码片段即可。
为了保证上述机制正常工作,当一个应用程序的某部分被请求和需要时,系统必须能够启动一个应用程序进程,并且初始化代表那一部分的Java对象。所以,和大多数其它的系统不同,Android应用程序没有一个唯一的入口(比如说 main 函数),而是拥有一些系统能够初始化和运行的基本组件。以下是4种基本组件的说明:

1) Activities(活动)

 

一个 activity 代表一个可视化的用户接口(An activity presents a visual user interface for one focused endeavor the user can undertake)。比如说,一个activity可能代表菜单项的列表,用户可以从中选择,或者这个列表显示一些带有标题的照片。一个文本短信的应用程序可能有一个显示通讯录列表的activity,还有一个给指定联系人编写短信的activity,并且可能还有查看以前的短信或是设置的一些activity。虽然它们共同组成了一个紧密结合的用户界面,但是每一个activity都不依赖其它activity。每一个activity都继承(extends)android.app.Activity类。
一个应用程序可能只是由一个activity构成,或者由多个activity构成,就像刚刚提到的文本短消息应用程序。具体一个activity是什么,以及有多少个activity,是由应用程序的设计决定的。典型情况下,有一个activity被标记为主界面(即当系统启动程序后第一个展现在用户面前的界面)。从一个activity跳转到另一个activity是由当前activity发起的。
每一个activity都被赋予一个默认的窗口来进行绘画。典型情况下,窗口会占满整个屏幕,但是也可以比屏幕小和漂浮在其它窗口之上。一个activity也可以使用副窗口(additional windows),比如一个要求用户响应的弹出对话框出现在activity的正中间,或者当用户选择了一个条目之后,出现在用户面前显示重要信息的窗口。
窗口中可视的内容都是android.view.View类的子类(如TextView)的对象。每一个view控制一个位于窗口中的指定矩形区域。父view包含和组织它的子view的布局。叶子view(即在View继承树的最末端)在它自身的控制的矩形区域内进行绘制,并且对用户在这个矩形区域内的动作做出直接响应。所以,view就是activity与用户进行交互的场所。比如一个view显示一张小图片,并且当用户点击这张图片的时候触发一个行为(事件)。Android已经内置了一些view,包括按钮,输入框,滚动条,菜单项和多选框 等等。
通过调用android.app.Activity.setContentView(View)方法,我们可以给一个activity窗口设置一个完整的View层次树。其参数是这个View层次树的最高层的祖先。(想了解更多关于view及其继承关系的信息,请阅读 用户界面 部分)

1.1Activating components: intents


当 ContentResolver 发出了一个请求,那么这个请求中所指定的目的 Content providers 会被自动激活。另外三种组件 -- activity, service broadcast receiver -- 是被一种称为 目的消息(intent) 的异步消息来激活的。一个目的消息是从 android.content.Intent 类继承的,并且包含了一些内容信息。
对于 activity 和 service 来说,目的消息指定了请求行为和此行为所针对的数据的URI,以及其它信息。比如它可能传达一个让某个activity显示图片的请求,又或者让用户对某些文本信息进行编辑的请求。
对于 broadcast receiver 来说,intent 对象指定了被广播的行为。比如它可能将拍摄按钮被按下这个事件(行为)宣布给感兴趣的broadcast receiver。
以下是一些激活某一种组件的独立方法:
1)通过将某个intent对象传递给 android.content.Context.startActivity(Intent)android.app.Activity.startActivityForResult(Intent, int) 方法,可以启动一个activity(或者做一些别的操作)。负责响应的activity(目标activity)可以调用 android.app.Activity.getIntent() 方法来获取这个Intent对象。若在这之后有新到达的intent,Android系统会调用 android.app.Activity.onNewIntent(Intent) 方法。
通常情况下,一个activity会负责启动下一个activity,如果想从下一个activity获取启动结果码,可以调用 android.app.Activity.startActivityForResult(Intent, int) 方法。比如说启动了一个让用户挑选图片的activity,那么这个activity可能需要将用户的选择返回给其它的activity。结果码是通过 android.app.Activity.onActivityResult(int, int, Intent) 这个方法返回给调用activity的。
2)通过将某个intent对象传递给 android.content.Context.startService(Intent) 方法可以启动一个service(或者给正在运行的service传递新的指令)。Android会调用这个service的 android.app.Service(Intent, int) 方法,并传入这个Intent对象。
与此类似,也可以通过将一个Intent传递给 android.content.Context.bindService(Intent, ServiceConnection, int) 方法来与一个正在运行的service建立连接。系统会调用目标service的 android.app.Service.onStart(Intent, int) 方法,并将这个Intent对象传入(如果service还没有被启动的话,bindService 方法可以有选择地启动它)。比如一个activity可以和负责播放音乐的service建立连接,这样的话,这个activity就可以给用户提供一些用以控制播放行为的接口。这个activity需要首先调用 bindService() 来与service建立连接,然后就可以调用被service定义的方法来控制播放行为了。
关于和 service 绑定的更详细的信息,在后面的 "远程过程调用" 章节中被提到。
3)应用程序可以通过将一个 Intent 对象传递给android.content.Context.sendBroadcast(Intent)方法,或者android.content.Context.sendOrderedBroadcast(Intent, String)方法,或者android.content.Context.sendStickyBroadcast (Intent)方法以及它们任何的变体形式来产生一个广播。Android系统会通过调用 android.content.BroadcastReceiver.onReceive (Context, Intent)方法来将这个广播公告给所有感兴趣的 BroadcastReceivers。
关于 目的消息 更多更详细的资料,请参考 "目的消息和目的消息筛选器" 章节。

1.2 Shutting down components


一个content provider只有在响应一个来自ContentResolver的请求的时候才是激活状态。类似的,一个broadcast receiver只有在接收适当的广播消息的时候才是激活状态。所以没有必要显式地关闭这两种组件。

提供用户界面的activity则会一直保持激活状态以便与用户进行交互。service也是会一直保持激活状态以便在后台完成指定的任务。所以Android提供了方法来有序地显式关闭activity和service。

调用android.app.Activity.finish()方法可以关闭自己(activity)。一个activity可以调用android.app.Activity.finishActivity(int)方法来关闭另一个activity(由startActivityForResult()方法激活)。

通过调用android.app.Service.stopSelf()方法可以关闭service,也可以调用android.content.Context.stopService(Intent)方法来关闭。
当组件不再被使用时,或者Android需要激活更多组件从而需要系统资源的时候,组件也可能被系统关闭。在后面的 "组件生命周期" 的章节中会讨论这种情况和更详细的信息。

1.3 The manifest file


要让Android能启动一个组件,首先必须要让Android知道这个组件是存在的。应用程序在manifest文件中声明它所用到的组件,这个manifest文件也会绑定到 .apk文件中。
所有程序的manifest文件的名字都是 AndroidManifest.xml,并且都是xml格式。除了声明程序需要用到的组件之外,这个AndroidManifest.xml 也有很多其它功能,比如声明程序运行时需要用到的非默认链接库,标识程序权限。
AndroidManifest.xml 最重要的功能还是声明程序需要用到的组件。一个activity可能就是如下的声明:

<?xml version="1.0" encoding="utf-8"?> <manifest . . . > <application . . . > <activity android:name="com.example.project.FreneticActivity" android:icon="@drawable/small_pic.png" android:label="@string/freneticLabel" . . . > </activity> . . . </application> </manifest>


<activity>
标签中的name属性表明从 android.app.Activity类继承下来的具体的activity类的名字。icon属性和label属性表示包含了在这个activity中显示的图片和文本的资源文件。

与此类似,<service>标签代表一个service组件, <receiver>标签代表一个broadcast receiver组件,而<provider>标签代表一个content provider组件。
Activities,services,和content provider这3中组件若没有在 AndroidManifest.xml文件中声明则不可用。然而,broadcast receiver不但可以声明在AndroidManifest.xml 文件中,还可以在代码里动态创建它们(如android.content.BroadcastReceiver对象),然后调用android.content.Context.registerReceiver(BroadcastReceiver, IntentFilter)方法将其注册到系统中。
关于manifest文件更详细的信息,请参考 "AndroidManifest.xml文件" 章节。

1.4 Intent filters (Intents 筛选器)


一个 Intent 对象可以显式的指定目标组件的名字,这样的话,Android就会找到这个组件(通过manifest文件中的生命)并且激活它。但是如果没有显式的指定目标组件的名字,Android就要负责找到一个最合适的组件来响应这个Intent。Android是通过将这个Intent对象和所有可能的目标组件的intent filter进行比较来筛选出最合适的组件。一个组件的intent filter说明了这个组件可以处理的intent类型。intent filter也是在 manifest 文件中声明。示例如下:

<?xml version="1.0" encoding="utf-8"?> <manifest . . . > <application . . . > <activity android:name="com.example.project.FreneticActivity" android:icon="@drawable/small_pic.png" android:label="@string/freneticLabel" . . . > <intent-filter . . . > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter . . . > <action android:name="com.example.project.BOUNCE" /> <data android:mimeType="image/jpeg" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> . . . </application> </manifest>

示例中的第一个filter是"android.intent.action.MAIN"行为和"android.intent.category.LAUNCHER"类型的组合,这也是一种常用的filter。它表示这个activity是当用户选择启动这个应用程序的时候显示的第一个界面。
第二个filter声明表示这个activity可以处理指定类型的数据的行为。
一个组件可以用户任意多的intent filter,每一个filter声明了一组不同的能力。若组件没有没有声明任何intent filter,则需要在Intent对象中显式指明此组件的名字,才能激活这个组件。
对于在代码中进行创建和注册的broadcast receiver,一个intent filter直接是由一个android.content.IntentFilter对象来表示的。其余情况,filter都是在manifest文件声明的。
关于intent filter的更详细的信息,请参考 "Intent and Intent Filters" 章节。

 

2) Services(服务)


一个Services不拥有可视化的用户界面,它是在一段不确定的时间周期内在后台运行。比如某个service在后台播放音乐,或者是在后台获取网络数据,又或者是进行某个耗时的运算并且将运算结果提供给需要的activity。每一个具体的service是从android.app.Service基类继承的。
最经典的例子就是一个音乐播放器播放播放列表中的歌曲。这个播放器可能拥有一个或多个activity,比如让用户选择歌曲的activity和播放时的 activity等等。然而,真正的播放功能可能并不是由某一个activity来负责的,因为用户可能期望就算跳转到任何一个不同的activity都保持歌曲持续不断地播放。为了达到这个目的,这个播放器的activity需要启动一个负责播放的service。系统会使这个service在后台持续不断地运行,哪怕切换到一个新的activity。
我们也可以连接上(绑定)一个正在运行的service(或者是启动它,前提是这个service还没有运行)。当连接上之后,你就可以通过这个 service导出的接口来与这个service进行通信。针对于播放歌曲的service来说,导出接口可能包括暂停播放、倒退、停止播放和重新播放。
和activity以及其它组件类似,service也是运行在应用程序进程的主线程中的。所以为了不阻塞其它组件的运行和用户界面的响应,service经常会创建一个新的线程来进行耗时操作(比如像播放音乐)。具体信息请参考"进程和线程"部分。

 

3) Broadcast receivers(广播接收)


broadcast receiver是一个只能对广播通告进行接收和响应的组件。有很多的broadcast是由系统发起的,比如说当手机设置的时区发生改变的时候,手机电力不足,某一张图片被移除,又或者是用户切换了手机显示语言。应用程序也可以发起broadcast,比如当一个应用程序成功下载完某些数据后,通知其它的应用程序数据已经准备好。
一个应用程序可以利用任意多的Broadcast receiver来对它感兴趣的广播事件来进行响应。所有的Broadcast receiver都是从 android.content.BroadcastReceiver 类继承下来的。
broadcast receiver并不会显示用户界面。然而,它们可以启动一个activity来将关于接收的广播信息提示给用户,或者它们也可以使用 通知管理器(android.app.NotificationManager) 来提示用户。一个通知有很多途径来引起用户注意,比如点亮手机的背光灯,使手机振动,播放一个提示音等等。经典情况是在状态栏上显示一个小图标,用户可以点击这个小图标以获取详细信息。

 

4) Content providers (内容提供者)


Content provider 的作用是将应用程序指定的数据共享给其它的应用程序。这些数据可以是存放在文件系统中的,或是存放在SQLite数据库文件中,或是以其它任何有意义的方式存放的。一个具体的 Content provider 是继承于 android.content.ContentProvider 类的,并且实现一系列可以使其它应用程序接收和存储由此应用程序控制的数据的相关接口即可。然而应用程序并不会直接调用这些接口,它们会利用 android.content.ContentResolver 类的对象来做这个工作,一个 ContentResolver 可以同任何 Content provider 进行通信;它与 Content provider 一起对相关的进程间通信进行管理。

关于如何使用 Content provider 的更多资料,可以参考 Content Providers 章节的内容。
注:任何时候当出现一个应该被某个组件处理的请求,Android都会保证这个组件在应用程序进程中保持运行,必要的话,还会启动这个组件(必要情况下会创建这个组件对象)使其运行,所以Android也会保证存在这个组件的实例对象。

 

2. Activities and Tasks

默认情况下,在一个程序中的所有activity都在一个affinity中,这个也使这些activity属于同一个task。但是我们可以在<activity>标签中设置taskAffinity属性来为每一个activity指定一个单独的affinity。不同的应用程序的activity可以被指定为相同的affinity,在同一个应用程序的多个activity也可以被指定为在不同的affinity中。在两种情况下,affinity会起作用:
1) 启动activity的Intent对象中包含FLAG_ACTIVITY_NEW_TASK标志。
2) 一个activity将allowTaskReparenting属性 设置为true。
FLAG_ACTIVITY_NEW_TASK 标志:
默认情况下,通过startActivity()方法激活的activity会加入到和调用者相同的task中,也会放到和存放调用者相同的stack中。但如果传递给 startActivity()方法的的Intent对象包含了FLAG_ACTIVITY_NEW_TASK标志,系统就会把这个新激活的activity放到一个不同的task中去。正如这个标志的字面意义那样,它是一个新的task。然而,这种情况不是绝对的。如果已经存在了一个和新激活的activity拥有相同的 affinitiy,那么这个activity就会直接放入这个新的task中。
allowTaskReparenting 属性:
若一个activity的allowTaskReparenting属性被设置为true,那么这个activity就可以从它启动时初始的task移动到新的展示的task中(it can move from the task it starts in to the task it has an affinity for when that task comes to the fore)。比如,在一个关于旅游的应用程序中一个activity是报告选择的城市的天气情况的,它的affinity和这个应用程序中的其它activity都是相同的(默认情况),但是它的allowTaskReparenting属性被设置为true。这时,此应用另外一个activity启动了这个报告天气情况的activity,那么这个报告天气情况的activity就被加入到那个调用activity相同的task中。然而,当这个关于旅游的应用程序离开前台,这个报告天气情况的activity会被重新分配到新的当前显示的task中。(However, when the travel application next comes forward, the weather reporter will be reassigned to and displayed with that task)
如果一个.apk文件包含了从用户角度来说多个应用程序的话,你可能希望给acitvity分配不同的affinity。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
[14本经典Android开发教程]-4-Android应用程序开发36技 本书共分为4篇: ※ 第一篇:介绍应用框架概念、原理和特性。 ※ 第二篇:阐述应用框架之设计技巧。亦即,如何打造应用框架。 (注:如果你的职务是「使用」Android框架來开发应用程式的话,可以跳过本篇,直接进入第三篇。) ※ 第三篇:說明及演練Android应用程式设计的36技。 ※ 第四篇:介绍Android框架与硬体之间C组件的开发流程及工具。 已上传3本: [14本经典Android开发教程]-3-Android SDK 中文开发文档 http://download.csdn.net/detail/cleopard/8380429 [14本经典Android开发教程]-1-Android开发入门到精通 http://download.csdn.net/detail/cleopard/8355245 [14本经典Android开发教程]-2-Android开发手册—API函数详解 http://download.csdn.net/detail/cleopard/8374487 剩余11本稍后上传!@或直接从这里寻找@ http://download.csdn.net/user/cleopard/album @更多@ http://cleopard.download.csdn.net/ 福利 http://xuemeilaile.com 17份软件测试文档 http://download.csdn.net/album/detail/1425 13份WPF经典开发教程 http://download.csdn.net/album/detail/1115 C#资料合辑二[C#桌面编程入门篇] http://download.csdn.net/album/detail/957 C#资料合辑一[C#入门篇] http://download.csdn.net/album/detail/669 [Csharp高级编程(第6版)](共8压缩卷) http://download.csdn.net/album/detail/667 10个[精品资源]Java学习资料合辑[一] http://download.csdn.net/album/detail/663 10个C#Socket编程代码示例 http://download.csdn.net/album/detail/631 6份GDI+程序设计资源整合[全零分] http://download.csdn.net/album/detail/625 2014年移动游戏行业数据分析 http://download.csdn.net/detail/cleopard/8340331 一文读懂2014年全球互联网广告新生态 http://download.csdn.net/detail/cleopard/8340303

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值