Android开发精要2--Android组件模型解析

               

本文的主要内容如下:

2.1 基于Mashup的应用设计

2.2 界面组件Activity解析

2.3 服务组件Service解析

2.4 触发器组件BroadcastReceiver解析

2.5 数据源组件ContentProvider解析

2.6 应用配置文件解析

下面将对这些内容进行详细的介绍。


2.1 基于Mashup的应用设计

基于Mashup的应用,其核心是组件,应用边界、进程边界和网络边界在Android中都变得模糊而且不那么重要了。在Android中,组件执行时的聚合单元是任务(Task),每个任务都由若干个界面组件对象构成,这些组件可能来自不同的应用,运行在不同的进程中,它们彼此独立,就像黑盒子一样,无须关注具体调用者或被调用者的实现细节。
组件间的数据传输,都是通过消息、进程间的通信模型等序列化数据传输的方式来进行,而不是通过对象指针的直接传递,这就使得Android的应用天生具有了良好的跨进程特征。在实际的Android开发中,开发者通常不需要关注应用进程的问题,而只需要把精力放在组件的管理和控制上即可。
基于组件的设计,不仅使得应用具有跨进程性,而且还具有跨网络的特性。当调用方组件发起请求,希望第三方组件帮助提供所需的功能或数据时,能够响应该请求的,不仅可以是一个本地已安装应用的组件,还可以是符合该请求的Web页面,C/S和B/S的差别在Android上也变得模糊了。
实现从跨进程到跨网络的升级,有两处关键的设计。首先是内置浏览器的支持。Android中搭载了移动平台史上最彪悍的浏览器Chrome Lite,它不仅可以完美地支持当前的各种HTML、JS和CSS,还提供对HTML 5和CSS3的支持。浏览器应用中的界面组件,能够处理不同的Web请求,从而使得Web页面的展示能够并入Android的组件体系中。
而另一个关键点在于Android沿用了Web中的服务定位等标准。Web页面是通过URL进行定位的,通过MIME type对其类型进行描述。而在Android中,采取了同样的标准,这就使得每一个Web页面都可以归纳到Android的识别范围之内。

基于Mashup的这种应用架构模式,可以将整个Android设备中安装的应用以及各种Web应用糅合在一起,以帮助用户更好地完成相关事务。对于开发者而言,这样不仅降低了开发成本,还提供了最好的用户体验,真可谓一举多得、互惠互利的应用模型。


2.2 界面组件Activity解析

应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。

Activity之间通过Intent进行通信。在Intent 的描述结构中,有两个最重要的部分:动作和动作对应的数据。

典型的动作类型有:M AIN(activity的门户)、VIEW、PICK、EDIT 等。而动作对应的数据则以URI 的形式进行表示。例如:要查看一个人的联系方式,你需要创建一个动作类型为VIEW 的intent,以及一个表示这个人的URI。

与之有关系的一个类叫IntentFilter。相对于intent 是一个有效的做某事的请求,一个intentfilter 则用于描述一个activity(或者IntentReceiver)能够操作哪些intent。一个activity 如果要显示一个人的联系方式时,需要声明一个IntentFilter,这个IntentFilter 要知道怎么去处理VIEW 动作和表示一个人的URI。IntentFilter 需要在AndroidManifest.xml 中定义。通过解析各种intent,从一个屏幕导航到另一个屏幕是很简单的。当向前导航时,activity 将会调用startActivity(Intent myIntent)方法。然后,系统会在所有安装的应用程序中定义的IntentFilter 中查找,找到最匹配myIntent 的Intent 对应的activity。新的activity 接收到myIntent 的通知后,开始运行。当startActivity 方法被调用将触发解析myIntent 的动作,这个机制提供了两个关键好处:

A、Activities 能够重复利用从其它组件中以Intent 的形式产生的一个请求;

B、Activities 可以在任何时候被一个具有相同IntentFilter 的新的Activity 取代。

AndroidManifest文件中含有如下过滤器的Activity组件为默认启动类当程序启动时系统自动调用它

<intent-filter>       <action android:name="android.intent.action.MAIN" />       <category android:name="android.intent.category.LAUNCHER" /></intent-filter>

2.3 服务组件Service解析

一个Service 是一段长生命周期的,没有用户界面的程序,可以用来开发如监控类程序。

比较好的一个例子就是一个正在从播放列表中播放歌曲的媒体播放器。在一个媒体播放器的应用中,应该会有多个activity,让使用者可以选择歌曲并播放歌曲。然而,音乐重放这个功能并没有对应的activity,因为使用者当然会认为在导航到其它屏幕时音乐应该还在播放的。在这个例子中,媒体播放器这个activity 会使用Context.startService()来启动一个service,从而可以在后台保持音乐的播放。同时,系统也将保持这个service 一直执行,直到这个service 运行结束。另外,我们还可以通过使用Context.bindService()方法,连接到一个service 上(如果这个service 还没有运行将启动它)。当连接到一个service 之后,我们还可以service 提供的接口与它进行通讯。拿媒体播放器这个例子来说,我们还可以进行暂停、重播等操作。

Service使用步骤如下

       1>继承service类

       2>AndroidManifast.xml配置清单文件中<application>节点里对服务进行配置

              <service name=".SMSService"/>

服务不能自己运行,需要通过Contex.startService()或Contex.bindService()启动服务

通过startService()方法启动的服务于调用者没有关系,即使调用者关闭了,服务仍然运行想停止服务要调用Context.stopService(),此时系统会调用onDestory(),使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onStart(),如果服务已经启动再次调用只会触发onStart()方法

使用bindService()启动的服务与调用者绑定,只要调用者关闭服务就终止,使用此方法启动时,服务首次启动系统先调用服务的onCreate()-->onBind(),如果服务已经启动再次调用不会再触发这2个方法,调用者退出时系统会调用服务的onUnbind()-->onDestory(),想主动解除绑定可使用Contex.unbindService(),系统依次调用onUnbind()-->onDestory();


2.4 触发器组件BroadcastReceiver解析

你的应用可以使用它对外部事件进行过滤只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice 来响应它们收到的信息,或者用NotificationManager 来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

广播类型:

普通广播通过Context.sendBroadcast(Intent myIntent)发送的

有序广播通过Context.sendOrderedBroadcast(intent, receiverPermission)发送的,该方法第2个参数决定该广播的级别,级别数值是在 -1000 到 1000 之间 , 值越大 , 发送的优先级越高;广播接收者接收广播时的级别级别(可通过intentfilter中的priority进行设置设为2147483647时优先级最高),同级别接收的先后是随机的, 再到级别低的收到广播,高级别的或同级别先接收到广播的可以通过abortBroadcast()方法截断广播使其他的接收者无法收到该广播,还有其他构造函数

异步广播通过Context.sendStickyBroadcast(Intent myIntent)发送的,还有sendStickyOrderedBroadcast(intent, resultReceiver, scheduler,  initialCode, initialData, initialExtras)方法,该方法具有有序广播的特性也有异步广播的特性;发送异步广播要: <uses-permission android:name="android.permission.BROADCAST_STICKY" />权限,接收并处理完Intent后,广播依然存在,直到你调用removeStickyBroadcast(intent)主动把它去掉

注意:发送广播时的intent参数与Contex.startActivity()启动起来的Intent不同,前者可以被多个订阅它的广播接收器调用,后者只能被一个(Activity或service)调用

监听广播Intent步骤:

1>             写一个继承BroadCastReceiver的类,重写onReceive()方法,广播接收器仅在它执行这个方法时处于活跃状态。当onReceive()返回后,它即为失活状态,注意:为了保证用户交互过程的流畅,一些费时的操作要放到线程里,如类名SMSBroadcastReceiver

2>            注册该广播接收者,注册有两种方法程序动态注册和AndroidManifest文件中进行静态注册(可理解为系统中注册)如下:

        静态注册,注册的广播,下面的priority表示接收广播的级别"2147483647"为最高优先级

<receiver android:name=".SMSBroadcastReceiver" >  <intent-filter android:priority = "2147483647" >    <action android:name="android.provider.Telephony.SMS_RECEIVED" />  </intent-filter></receiver >

动态注册,一般在Activity可交互时onResume()内注册BroadcastReceiver

IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");registerReceiver(mBatteryInfoReceiver ,intentFilter);//反注册unregisterReceiver(receiver);

注意:

1.生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的

2. 动态注册广播接收器还有一个特点,就是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用

系统常见广播Intent,如开机启动、电池电量变化、时间改变等广播


2.5 数据源组件ContentProvider解析

android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式,

其他应用可以通过ContentResolver类(见ContentProviderAccessApp例子)从该内容提供者中获取或存入数据.(相当于在应用外包了一层壳),

只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中

它的好处:统一数据访问方式。

android系统自带的内容提供者(顶级的表示数据库名,非顶级的都是表名)这些内容提供者在SDK文档的android.provider Java包中都有介绍。见:http://developer.android.com/reference/android/provider/package-summary.html

├────Browser

├────CallLog

├────Contacts

│                ├────Groups

│                ├────People

│                ├────Phones

│                └────Photos

├────Images

│                └────Thumbnails

├────MediaStore

│                ├────Albums

│                ├────Artists

│                ├────Audio

│                ├────Genres

│                └────Playlists

├────Settings

└────Video

 CallLog:地址和接收到的电话信息

 Contact.People.Phones:存储电话号码

 Setting.System:系统设置和偏好设置

使用Content Provider对外共享数据的步骤

1>继承ContentProvider类并根据需求重写以下方法:

 

      

复制代码
    public boolean onCreate();//处理初始化操作       /**        * 插入数据到内容提供者(允许其他应用向你的应用中插入数据时重写)        * @param uri        * @param initialValues 插入的数据        * @return        */       public Uri insert(Uri uri, ContentValues initialValues);       /**        * 从内容提供者中删除数据(允许其他应用删除你应用的数据时重写)        * @param uri        * @param selection 条件语句        * @param selectionArgs 参数        * @return        */       public int delete(Uri uri, String selection, String[] selectionArgs);       /**        * 更新内容提供者已存在的数据(允许其他应用更新你应用的数据时重写)        * @param uri        * @param values 更新的数据        * @param selection 条件语句        * @param selectionArgs 参数        * @return        */       public int update(Uri uri, ContentValues values, String selection,                     String[] selectionArgs);       /**        * 返回数据给调用者(允许其他应用从你的应用中获取数据时重写)        * @param uri        * @param projection 列名        * @param selection 条件语句        * @param selectionArgs 参数        * @param sortOrder 排序        * @return        */       public Cursor query(Uri uri, String[] projection, String selection,                     String[] selectionArgs, String sortOrder) ;                /**        * 用于返回当前Uri所代表数据的MIME类型        * 如果操作的数据为集合类型(多条数据),那么返回的类型字符串应该为vnd.android.cursor.dir/开头        * 例如要得到所有person记录的Uri为content://com.bravestarr.provider.personprovider/person,     *   那么返回的MIME类型字符串应该为"vnd.android.cursor.dir/person"        * 如果操作的数据为单一数据,那么返回的类型字符串应该为vnd.android.cursor.item/开头        * 例如要得到id为10的person记录的Uri为content://com.bravestarr.provider.personprovider/person/10,     *   那么返回的MIME类型字符串应该为"vnd.android.cursor.item/person"        * @param uri        */       public String getType(Uri uri)
复制代码

这些方法中的Uri参数,得到后需要进行解析然后做对应处理,Uri表示要操作的数据,包含两部分信息:

       1.需要操作的contentprovider

       2.对contentprovider中的什么数据进行操作,一个Uri格式:结构头://authorities(域名)/路径(要操作的数据,根据业务而定)

              content://com.bravestarr.provider.personprovider/person/10

说明:contentprovider的结构头已经由android规定为content://

authorities用于唯一标识这个contentprovider程序,外部调用者可以根据这个找到他

路径表示我们要操作的数据,路径的构建根据业务而定.路径格式如下:                                                        

       要操作person表行号为10的记录,可以这样构建/person/10

       要操作person表的所有记录,可以这样构建/person

2>在AndroidManifest.xml中使用<provider>对ContentProvider进行配置注册(内容提供者注册它自己就像网站注册域名),ContentProvider采用authoritie(原意授权,可理解为域名)作为唯一标识,方便其他应用能找到

复制代码
<application        android:icon="@drawable/ic_launcher"        android:label="@string/app_name" >        <!-- authorities属性命名建议:公司名.provider.SomeProvider-->        <provider android:name=".PersonProvider" android:authorities="com.bravestarr.provider.personprovider"/>         ...</application>
复制代码

2.6 应用配置文件解析

AndroidManifest.xml配置文件对于Android应用开发来说是非常重要的基础知识,本文旨在总结该配置文件中重点的用法,以便日后查阅。下面是一个标准的AndroidManifest.xml文件样例。

[html]  view plain copy print ?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <manifest>  
  4.   
  5.     <!-- 基本配置 -->  
  6.     <uses-permission />  
  7.     <permission />  
  8.     <permission-tree />  
  9.     <permission-group />  
  10.     <instrumentation />  
  11.     <uses-sdk />  
  12.     <uses-configuration />  
  13.     <uses-feature />  
  14.     <supports-screens />  
  15.     <compatible-screens />  
  16.     <supports-gl-texture />  
  17.       
  18.     <!-- 应用配置 -->  
  19.     <application>  
  20.           
  21.         <!-- Activity 配置 -->  
  22.         <activity>  
  23.             <intent-filter>  
  24.                 <action />  
  25.                 <category />  
  26.                 <data />  
  27.             </intent-filter>  
  28.             <meta-data />  
  29.         </activity>  
  30.           
  31.         <activity-alias>  
  32.             <intent-filter> . . . </intent-filter>  
  33.             <meta-data />  
  34.         </activity-alias>  
  35.           
  36.         <!-- Service 配置 -->  
  37.         <service>  
  38.             <intent-filter> . . . </intent-filter>  
  39.             <meta-data/>  
  40.         </service>  
  41.           
  42.         <!-- Receiver 配置 -->  
  43.         <receiver>  
  44.             <intent-filter> . . . </intent-filter>  
  45.             <meta-data />  
  46.         </receiver>  
  47.           
  48.         <!-- Provider 配置 -->  
  49.         <provider>  
  50.             <grant-uri-permission />  
  51.             <meta-data />  
  52.         </provider>  
  53.           
  54.         <!-- 所需类库配置 -->  
  55.         <uses-library />  
  56.   
  57.     </
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值