安卓应用基本原理

来源:互联网 发布:手机wifi改端口 编辑:程序博客网 时间:2024/04/28 22:21

安卓应用基本原理

谷歌官方原文链接:http://developer.android.com/guide/components/fundamentals.html

初尝翻译,如有不妥,还请见谅。


    安卓应用程序是由Java语言所编写的。Android SDK工具编译你的代码(包括所用任何资源以及文件)到APK里。APK是一个Android package,他以.apk作为后缀。一个APK文件包含所编写的APP的全部内容,同时也是安卓设备安装APP的安装文件。

    一旦APP被安装到了安卓设备,App存活于他自己的盒子里面:
→安卓是一个多用户的Linux操作系统,每个app是一个独立用户。
→默认情况下,安卓系统为每一个app登记一个独立的“Linux用户ID”,这个ID只被系统使用,而且App本身不知道这个ID。系统将为所有文件设置权限,只有拥有对应用户ID的APP可以访问那些文件。
→每个进程都有自己的虚拟机,所以任何APP的代码在运行时,都是独立于其他APP。
→默认情况下,任何APP运行在他自己的Linux进程里面,当一个APP的任何组件需要被运行时,安卓启动他的进程。当这个进程不再被需要,或者系统必须为其他App重用当前进程所占用的内存,系统将会结束当前APP的进程。

    基于上述,安卓系统实现了”principle of least privilege”(“principle of least privilege”是指,每个APP在默认的情况下,只会访问他的工作所必需的那些组件,不会访问任何工作需求外的组件,这个原理的实现提供了一个非常安全的环境,在此环境下,对于系统内的其他部分,如果APP未获得这些部分访问权限,APP是不能访问这些部分)。

app间分享文件方式:
→让两个app拥有同个LinuxID,这样,他们可以互相访问对方文件。可以保存系统资源,还能运行在同一个Linux进程里,可以分享虚拟机(前提是他们有相同证书)。
→app可以请求权限去访问设备数据,比如请求访问联系人,SMS信息,读取Sd卡,相机,蓝牙,等等。一切权限都必须在安装APP的时候由用户所同意。

以下文档将会向你介绍

△定义在你App里面的核心框架组件。
△manifest文件(这是一个用于声明app里所用到的组件、以及请求设备描述用的文件)
△资源(这些资源是指与app代码相分离的、让你的APP在能够在不同配置的设备上面完善功能的资源)。

App组件

App组件是安卓App里所必需的模块(原文叫做building blocks,我没找到合适的词,就用模块)。每个组件都是一个不同的点,系统通过这些“点”来进入你的APP。对于用户而言不是所有组件都是真实的进入点,每个组件都是作为实体存在,同时扮演者指定的角色——每个组件都是一个独立模块,他们共同定义了你APP的全部行为。

有四种不同类型的组件。每种类型的存在都有不同的功能(原文用purpose,我觉得翻译成“功能”通顺一点),每种类型也有他特定的生命周期(生命周期定义了该组件如何被创建以及如何被销毁)。

→Activity(以下我都叫做窗体):

一个窗体代表着一个单独的、带着用户界面的屏幕。比如,一个处理邮件的app,可能会有一个窗体用于显示所有新收到的邮件。尽管这个app里所有窗体协同工作,实现了一个紧凑的用户体验,但每个窗体都是与其他窗体相独立的。比如,任何一个其他的app,可以启动这个邮件app的任何窗体(如果这个邮件app允许了)。再比如说,一个照相的app,当用户想将拍到的照片分享出去,他能启动邮件app里那个负责新建邮件的窗体。

一个窗体是Activity类的子类。你可以在这个链接里学到更多关于窗体的知识:ttp://developer.android.com/guide/components/activities.html

→服务

服务是一个运行于后台的组件,用于执行长时间运行的操作,或者为远程进程而工作。服务不会提供用户界面。比如,服务可以在后台做音乐播放,即使用户正在使用另外一个进程(即用户不在当前播放音乐的进程),或者,服务可能在网络上获取数据,而获取数据的过程不需要用户停留在某个窗体上面。任何其他一个组件,比如窗体,可以启动一个服务让该服务工作,或者通过与该服务绑定来和这个服务通信。
一个服务作为Service的子类被实现,你可以在这里学到更多关于服务的知识。链接:http://developer.android.com/guide/components/services.html


→内容提供者

一个内容提供者管理着一个数据集合,这个集合是app里面所有可分享的 数据集合。你可以将数据存储在文件系统里、SQLite数据库、web,或者其他任何你的APP能访问到的、持久的存储设备。通过内容提供者,其他app可以查询甚至是修改你的app所存的数据(如果你的app的内容提供者允许了)。比如,安卓系统提供了一个内容提供者,可以管理用户的联系人信息。像这样,任何取得了适当权限的app,都可以通过查询内容提供者的一部分(比如ContactsContract.Data)去读取或写入指定人的信息。

对于那些你的App私有的,不对外所分享的数据,内容提供这同样有用。比如,Note Pad示例app使用内容提供者去笔记。
链接:http://developer.android.com/resources/samples/NotePad/index.html

一个内容提供者是”ContentProvider”的子类实现,同时,他必须去实现一系列的标准的API,以便其他的app可以执行这些事务。

→广播接收者

广播接收者是一个组件,专门用于响应系统所发送的广播。许多广播都是来自与系统地——比如,一条广播宣布手机屏幕关闭、电池的电量低下、或者捕捉到了一张图片。你自己所开发的app也能启动广播——比如,让其他的app知道有些数据已经下载到系统了,而且已经对他们可用了。尽管广播接收者不对用户展示画面,但当某个广播时间发送之后,广播接收者可能会增加一个状态栏通知去提醒用户。广播最常见的用法是,作为通向其他组件的“通道”,以及进行及少量的工作。比如,他可能会启动一个服务,让服务去执行一些工作,这些工作都是基于广播接收者所收到的信息的。

每个广播接收者都是”BroadcadtReceiver”子类实现,每条广播都有一个Intent(以下简称“意图”)对象传递,你可以在这个链接得到更多广播接收者的信息。
链接:http://developer.android.com/reference/android/content/BroadcastReceiver.html

安卓系统的设计里有个很独特的方面:任何app都可以打开其他app的组件。比如,如果你想让用户从设备照相机里捕捉一张图片,如果设备里面有其他app已经实现这个功能,你可以直接去使用他,不需要在你自己的app里开发一个窗体去实现该功能。你也不用将照相机app里的代码合并、或者是连接到你的app里。你可以仅仅启动相机app里面拥有该功能的窗体,让该窗体捕捉图片,当该窗体完成工作之后,这个图片将会返回到你自己的app里面,然后你可以去使用这张图片。

当系统启动了一个组件,它将为该app去启动一个进程(如果这个app原先没有在运行)然后实例化该组件所要用的所有的类。比如,当你启动了照相机app里那个能够获取照片用的窗体,这个窗体将运行在隶属于照相机app的那个进程,而不是运行在你的app所在的进程。因此,不同与大多数其他系统的app,安卓app没有一个单独的进入点的(没有main方法)。

因为系统将每一个app都运行在了单独的进程里,进程有着文件权限,这些文件权限限制了对其他app的访问。所以,你不可以直接激活别的app里面的组件。但是,安卓系统可以激活任意一个app的组件。所以,你必须要发送一条带着你的意图的消息给系统,让系统去启动你指定的组件。然后,系统间会为你激活那个组件。


激活组件

四分之三类型的组件——窗体、服务、广播接收者——都能被一个叫做意图的异步消息所激活。“意图”能在运行时将各个组件彼此绑定(你可以认为”Intents”是一个像其他组件请求某个行为的信使),不论这个组件是否隶属你这个app。

一个intent就是一个”Intent”对象,这个对象带着一个消息,消息指定要激活的那个组件、或者指定要激活的组建类型——intent既可以是显式的也可以是隐式的。

对于窗体以及服务而言,一个“意图”定义一个要执行的行为(比如,去”显式”或者”发送”某些东西)同时可能还指定了那个行为会用到的数据的URI(组件执行被请求的行为那时会用到的数据)。比如,一个意图可能请求一个窗体去打开一张图片、或者打开一个网页(此时就要指定图片的URI或者网页的URI)。在某些案例里面,你可能会启动一个窗体同时希望接收一个结果,此时,被启动的窗体所返回的结果也是放在“意图”里面(比如,你可以发送一个intent让用户选择某一个联系人,然后再返回给你——此时,所返回的“intent”将包含着一个URI,这个URI指向了被选中的那一个联系人)。

对于广播接收者,“意图”仅仅是定义了被广播的消息(比如,一条表明电池电量偏低的广播里,仅仅包含着一个描述行为的字符串“battery is low”)。

其他类型的组件:内容提供者,他不是被intent所激活的。当一条来自“ContentResolver”的请求指向某个内容提供者那时,那个内容提供者被激活。内容解析器处理一切想要与内容提供者直接做交互的事务,因此,那些想要通过内容提供者去操作事务的组件,不再需要与内容提供者做交互了,他们只要调用内容解析器的方法就行。这种方式在内容提供者和想要操作数据的组件之间,留下了个抽象层。

对于每一类型的组件都有个不相同的激活方法:


→你可以通过将“意图”传入startActivity()、或者startActivityForResult()(当你想要获得返回的结果时,调用后者)来打开一个窗体。
→你可以通过将“意图”传入startService()打开一个服务。或者,你可以通过将”Intent”传入bindService()方法绑定一个服务。
→你可以同过将“意图”传入sendBroadcast()、sendOrderedBroadcast()或者sendStickyBroadcast()启动广播。
→你可以通过调用”ContentResolver”对象里的query()方法执行对”content provider”的查询。

The Manifest File

在安卓系统能够去启动组件之前,系统必须通过读取app的”AndroidManifest.xml”文件(“mainfest”文件)知道哪些组件存在。你的app必须要在此文件当中声明所有所用到的组件。还有,此文件必须放置在app项目路径的根目录。

为了声明app的组件,该文件会完成一系列的事情,比如:

→登记app所请求的所有用户权限,比如网络访问权限或者读取用户联系人的权限。
→根据app所调用的API,声明app所需的最小API的等级。
→声明app所用的或者所请求的软件以及硬件资源,比如相机、蓝牙服务,或者是多点触控。
→app需要关联的API库(这些库是指安卓框架API以外那些),比如Goole Maps library。
→还有其他等等等等。

声明组件

“manifest”文件的主要工作是告知系统app的组件。比如,它可以像下面所示那样声明一个窗体:

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

在<application>元素里,”android:icon”属性为用于标识app的图标指定了一个资源。
在<activity>元素里,”android:name”属性指定了”Activity”子类的完整类名(就是指定了你自己那个窗体完整类名),”android:label”为窗体上用户可见标签指定了一个字符串。

你必须通过以下的方式声明所有app的组件:
→<activity>元素用来声明窗体。
→<service>元素用来声明服务。
→<receiver>元素用来声明广播的接收者。
→<provider>元素声明内容提供者。

窗体、服务和内容提供者,如果你使用了他们但没有在”manifest”文件声明,则他们对系统而言都是不可见得,而且,通常他们也不能够运行。然而,广播接收者既可以在”manifest’文件里面进行声明,也可以在代码里面进行动态声明(作为一个”BroadcastReceiver”对象),如果使用动态声明,需要通过”registerReceiver()”方法将他注册到系统。


声明组件的功能

正如在“激活组件”里说说的,你可以使用“意图”去激活窗体、服务以及广播接收者。你可以在“意图”里面显式地去指定目标组件(使用组件类名就可显式指定)。然而,“意图”真正的作用在于隐式“意图”的概念。一个隐式“意图”仅仅描述要执行的操作类型(或者,连带你所要执行的操作所需要的数据),然后,让系统在设备里面寻找一个可以完成你所指定的操作的,然后系统启动这个组件。如果由多个组件能执行“意图”所描述的那个操作,那就让用户去选择使用哪个。

系统通过比对安装在设备上的app的“manifest”文件里的“intent filters(意图过滤器)”所描述的各个app能接收的意图,识别哪个组件可以响应你的意图。


当你在你的”manifest”文件里面声明一个窗体,可同时加上“意图过滤器”,过滤器里声明你的窗体能处理得“意图”,如此,这个窗体就能响应来自其他app的“意图”。你可以通过添加<intent-filter>元素声明你的组件的意图过滤器,添加<intent-filter>元素时,将该元素作为组件元素的子元素。


比如,如果你制作一个邮件的app,里面有个床体可以构建新的邮件,你可以声明一个意图过滤器,让你的窗体能够响应“send(发送)”意图(这么做是为了能够发送一封新的邮件),如下所示:、

<manifest ... >    ...    <application ... >        <activity android:name="com.example.project.ComposeEmailActivity">            <intent-filter>                <action android:name="android.intent.action.SEND" />                <data android:type="*/*" />                <category android:name="android.intent.category.DEFAULT" />            </intent-filter>        </activity>    </application></manifest>


然后,如果有一个APP创建一个带有”ACTION_SEND”的意图,然后将该意图传入”startActivity()”,系统就有可能启动你的窗台,让用户能起草以及发送一封邮件。


声明app的需求

安卓有各种各样的设备驱动,但不是所有设备都拥有同样的功能和能力。为了避免将你的app安装到了一台,不能满足你的APP所需的所有功能的设备上,需要做一件很重要的事情,就是通过在你的“manifest”文件里面声明你的app所需要的设备和软件,来为你的app所能支持的设备类型定义一个配置文件。这些声明大多数都只是一些信息而且系统不会去读他们,但是诸如”Google Play”这样的外部服务将会去读取他们,这是为了当用户在他们的设备上搜索应用那时,能为用户提供过滤。


比如,如果你的APP是需求相机功能,以及安卓2.1的设备,那你应该在你的”manifest”文件里面声明这些需求,如下:

<manifest ... >    <uses-feature android:name="android.hardware.camera.any"                  android:required="true" />    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />    ...</manifest>


现在,没有相机功能的设备,以及安卓2.1以下的设备不能从“Google Play”安装你的app。


然而,你也可以声明你的设备将会用到相机,但是不是必须用到。这样的话,你的app必须将”required”设置成了”false”,然后,在运行时动态检测设备是否有照相机,或者在某些情况下不用照相机的功能。


App资源


一个安卓app不只是有代码组成——他需要资源,与代码相独立开的资源,比如图片,音频文件,以及任何与app的视觉呈现相关的资源。比如,你应该将与窗体UI相关的动画、菜单、风格、颜色,以及布局都定义在一个xml文件里。通过使用app资源,可以更加容易地更新你的app的各种属性——因为不需要再修改代码——通过提供可替代的资源集就行了——这让你的app在“适配不同的设备”这方面更加完善(比如不同语言的设备或不同尺寸的设备)。


SDK编译工具会为你的安卓项目里的每个资源,添加一个单独的整型的ID,使用这个整形ID,你可以在你的代码里面引用ID所指向的资源、也可以在那些定义在XML文件里的资源里面,引用ID所指向的资源。比如,如果你的app包含了一个叫做“logo,png”图片文(存在res/drawable/路径),SDK工具将会产生一个资源叫“R.drawable.logo”的ID,你可以用“R.drawable.logo”引用这个图片文件然后插入你的UI里面。


这种行为(这种行为指的是提供与源代码相分离的资源)最重要的一个用途在于,它能够让你为不同配置的设备提供可选择的资源。比如,通过将界面字串(原文是“UI Strings”)定义在XML文件里,你可以将字符串转化为其他语言,并将这些转化后的语言各自存在独立的文件里。然后,根据你提供给资源路径名的语言限定符(比如res/values-fr/)以及用户所设置的语言,安卓系统会将合适的语言呈现在你的UI上面。



为了让你能够提供可选择的资源,安卓系统支持多种语言限定符。限定符是一串被包含在你的资源目录的名字里面的短字符串,通过限定符系统能知道应该选用你提供的资源里面的哪一个。在另一些例子里面,你应该经常根据屏幕的不同方向和不同尺寸的屏幕,为你的窗体增加不同的布局。比如,当设备是处于竖屏状态,你可能希望布局上的按钮是纵向排列,当设备又处于横屏之后,那些按钮应该水平对齐。想要根据屏幕方向改变布局,你可以提供两套不同的布局,然后分别为这两套布局所在资源目录提供各自的限定符。然后系统会为当前设备屏幕提供合适布局。




0 0