第一章  android核心组件和应用框架 1.1 核心组件         android的四大核心组件:提供界面显示的activity,提供后台计算的service,提供进程间通信的intent和提供广播接收的broadcastReceiver。 1.1.1 activity组件      activity是实际与用户交互的组件。有几个子类需要注意: ListActivity, PreferenceActivity 和 TabActivity      activity的类图如下:                           分别介绍类的用途和实现原理:         1.  ListActivity           可以用来实现列表功能。ListActivity提供了对基本的单行,双行列表的封装,同时还支持用户自定义列表。自定义列表是基于ListView来实现的。实现一个列表包括3步:选择或自定义列表项布局文件,是吸纳适配器并加载数据,为ListActivity设置适配器。              列表布局文件决定列表的布局风格,最基本的布局控件只有RelativeLayout和LinearLayout两个。              适配器的选择,android支持两种基本的适配器:基本适配器(BaseAdapter)和游标适配器(CursorAdapter)。基本适配器是最通用的适配器,游标适配器用来适配数据库的数据流的,其他的系统级适配器都是继承这两个适配器。              (1) 系统列表项布局文件                    android目前实现的基本列表项布局文件,分别为:基于单行布局的simple_list_item_1布局文件, 基于简单双行布局的simple_list_item_2布局文件,基于单行单选布局的simple_list_item_single_choice布局文件,基于单行多选布局的simple_lsit_item_multiple_choice布局文件,类似树状图的simple_expandable_list_item_1和 simple_expandable_list_item_2布局文件等。                例如: 基于simple_list_item_1布局文件的一个实现:                          setListAdapter(new ArrayAdapter(this,  android.R.layout.simple_list_item_1,  mStrings));//mStrings为数组类型的数组,simple_list_item_1为列表布局风格的文件                         下面介绍ArrayAdapter为例介绍适配器加载布局文件的方法:                                                               上述ArrayAdapter加载布局文件的过程,可以看出,默认情况下,ArrayAdapter假定整个布局文件是一个TextView,只有指定了mFieldId,及通过如下方法初始化ArrayAdapter时,ArrayAdapter才认为加载了一个自定义布局。                                                           ArrayAdapter的实现由很强的局限性,仅能显示单行的列表。下面介绍实现双行的列表的方法。                              SimpleCursorAdapter的初始化过程如下:                                   SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,  android.R.layout.simple_item_2, c,                                                 new String[]{ Phone.TYPE,  Phone.NUMBER },  new int[]{ android.R.id.text1,  android.R.id.text2 });                               SimpleCursorAdapter的ViewBinder的实现如下:                                                         在ViewBinder的实现过程中,指定了布局文件,游标,数据项,控件ID等,但没有处理布局加载和数据绑定。下面是SimpleCursorAdapter的bindView()的实现,该方法揭示了布局加载和数据绑定的过程。                                                                                                                       从bindView()的实现中可以看出,SimpleCursorAdapter的ViewBinder实际上是用户的一个自定义实现接口,当用户没有进行自定义实现时,SimpleCursorAdapter会通过传递的控件数量进行默认数据绑定。SimpleCursorAdapter还支持图片加载,不过此时游标获取的不再是文本数据,而是图片的URI.                (2)系统适配器                   android中,提供的适配器:BaseAdapter, CursorAdapter, ResourceCursorAdapter,  SimpleCursorAdapter, ArrayAdapter 和 SimpleAdapter等。基本适配器类图如下:                                          BaseAdapter适配器是最基本的适配器,是其他适配器的基类。BaseAdapter适配器可以适配ListView和Spinner等控件,通常会调用BaseAdapter的子类或通过BaseAdapter自定义适配器来实现视图与数据的适配功能。                   在BaseAdapter适配器中,应用了观察者模式,当数据源发生变化时,可以通过显示控件自行刷新。                 (3)自定义适配器                  a .  基于BaseAdapter的自定义适配器                       对于 基于BaseAdapter的自定义适配器,需要注意的是getView()方法的实现,                                                               getView()主要工作是列表项布局文件的加载和数据的绑定。                             b.  基于CursorAdapter的自定义适配器                                         基于CursorAdapter的自定义适配器的实现重点在与bindView()方法和newView()方法。 其中bingView()方法用于绑定数据,newView()方法用于加载布局文件。实例如下:                                                                             考虑到加载列表项时多次操作findViewById()方法,对性能有所影响,因此在android中设计了ViewHolder,以其来进行优化。通过View的setTag()方法和getTag()方法可大幅度提高显示速率。                            (4)复杂场景处理                                   在ListView中一些特殊的场景需要注意,如快速滚动时的显示问题,列表项中可单击空间的处理等。      2.  PreferenceActivity              最要用于偏好设置,在布局上PreferenceActivity以PreferenceScreen为根布局,支持CheckBoxPreference等多种形式的偏好设置。这些偏好值默认存储于应用的SharedPreferences中,通过getSharedPreferences()可以获取SharedPreferences对象,通过Preference.OnPreferenceChangeListener监听器可以监听到偏好值的变化。             下面介绍几种偏好值的实现。           (1)CheckBoxPreference                     不但提供二选一偏好的方法,还支持偏好的说明。下面是CheckBoxPreference布局文件的一个示例:                                           (2)DialogPreference                         目前仅仅是一个接口,             (3)EditTextPreference                         提供了支持输入框的偏好设置功能。通过getEditText()方法获得输入框的内容,通过getText()可获得SharedPreferences中存储的偏好值,通过setText()方法可以将偏好值保存在SharedPreferences中,具体如下:                                             (4)ListPreference                          当某个偏好有多个偏好值可选时,ListPreference就派上了用场,在使用ListPreference时需要注意entries和entryValues属性,其中entries表示界面的内容,而entryValues对应的是实际偏好值。                                                   在ListPreference中,用户只能做单选操作,如果期望能够执行多选操作,则需要用到MultiSelectListPreference 。 MultiSelectListPreference的资源文件配置和ListPreference类似。                 (4)RingtonePreference                          是一个用于设置铃声的特殊偏好控件,目前Android提供的铃声类型包括ringtone, notification, alarm 和all等。其中all表示所有可用的铃声。下面是Ring头呢Preference的配置示例:                                           (5)PreferenceCategory                          其提供了偏好组的功能。下面是一个偏好配置示例:                                   3.  TabActivity                TabActivity的根布局控件为TabHost,它由TabWidget和通常基于FrameLayout的内容显示区域组成。示例如下:                                    (1)自定义Tab                           4.  activity的加载模式                在android中,有4中加载模式:standard,  singleTop ,  singleTask 和 singleInstance                standard加载模式为android默认加载模式,在加载是会创建一个新的activity的实例,类似调用startActivity()方法时设置Intent的标志位为intent.FLAG_ACTIVITY_NEW_TASK。  发起standard加载模式的实例如下:                             Intent  i =  new Intent();                             i.setFlags(I ntent.FLAG_ACTIVITY_NEW_TASK);                              startActivity();               singleTop 加载模式表示当前Activity的实例处于前台并可视时,该实例会收到发送过来的Intent消息,器接收方法如下:                             protected void onNewIntent(Intent  intent){                                    super.onNewIntent(intent);                                     ........                              }               singleTask加载模式表示当前Acitivity栈(Task)中当前Activity实例运行时,该实例会收到相应的Intent消息,接收方法类似于singleTop加载模式。发起singleTask加载模式的示例如下:                              Intent  i  = new  Intent();                               i.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);                              startActivity(i);               singleInstance加载模式表示该Acitivity以单子模式加载时,在当前Activity栈中唯一,接收方法类似于singleTop加载模式。                为保证Activity在系统运行时能正常加载,必须在AndroidManifest.xml中声明所有需要加载的Activity,方法如下:                                                                                 android:label="@string/app_name"                                               android:launchMode="singleInstance"                                                //忽略重力传感器和键盘隐藏等变化                                               android:configChanges="orientation|keyboardHidden" >                                              注意:通常情况下,当单个应用中包含的文件较多时,通常需创建子文件夹进行区分,如将实现Activity的文件放在ui文件夹下,将实现Service, Provider的文件放在service文件夹下,将实现的一些自定义视图放在view文件夹下,将实现一些工具文件放置在util文件夹下。这样声明的方法略有不同,具体如下:                 >   ........   //注意ui前面的”.“表示当前目录      5.  activity的属性配置         (1)配置变化属性                  在android中存在一些基本的配置,如android:mcc,  android:mnc,  android:locale,   android:touchscreen,  android:keyboard,  android:keyboardHidden,  android:navigation,   android:orientatiion,  android:screenLayout,  android:fontScale和android:uiMode等,用户可对这些属性进行配置,若实现与配置相关,则需监听这些属性的变化。                 android:mcc  属性表示MCC(Mobile Country  Code),即 SIM 卡中存储的 IMSI号中的国家代码布恩,当发生国际漫游时,该属性配置会发生变化。                android:mnc  属性表示MNC (Mobile  Network Code), 即 SIM 卡中存储的 IMSI 号中的网络代码部分,当发生不同运营商网络间漫游时,该配置会发生变化,如在中国移动和中国联通的GSM网络间漫游。当然,运营商可以限制此类漫游。                android:locale 属性表示当前显示语言发生变化                android:touchscreen 属性表示触摸屏发生了变化,考虑到当前的实际情况,只有在支持多屏显示时,才需使用该属性。                android:keyboard 属性表示键盘类型发生了变化,如外接了蓝牙键盘等。                android:keyboardHidden 属性表示显示或隐藏键盘,对翻盖或滑盖终端有效。                android:navigation 属性表示导航键发生了变化,如从轨迹球变化为五向键,考虑到当前的实际情况,该配置显然不会发生变化。                android:orientation 属性表示屏幕方向                android:screenLayout 属性表示屏幕布局发生变化。在不支持多屏显示的情况下,该配置不会发生变化。                android:fontScale 属性表示字体发生了变化                android:uiMode 属性表示UI模式发生了变化,如变为车载模式,夜间模式等            默认情况下,当配置发生变化时,当前的Activity会在被销毁后重新生成,通过在AndroidManifest.xml 中为相应的Acitivity针对特定配置声明该属性,则可阻止特定配置变化时Activity被销毁后重新生成。当然,在某种情况下,开发者仍然需要监听到配置的变化,则可在Activity中实现如下方法:                          public  void  onConfigurationChanged(Configuration newConfig)                 android:configChanges 属性的设置方法:  android:configChanges="orientation|keyboardHidden"          (2)屏幕旋转属性                    android:screenOrientatiion属性,在有重力传感器的情况下,必须考虑屏幕的适配情况,其属性值包括:unspecified,  landscape,  portrait,   user,  behind , sensor, nosensor等,其中 unspecified为默认值,旋转策略由系统决定; landscape表示横屏; portrait表示竖屏; user表示当前用户倾向的屏幕方向;behind表示屏幕方向和Activity栈中当前activity下面的activity相同; sensor 表示根据重力传感器确定屏幕方向;           (3)主题属性                   android:theme 属性表示当前activity的主题,通常用于设置标题栏,状态栏等,设置方法:                           android:theme="@android:style/Theme.NoTitleBar"          (4)启动约束属性                   android:exported属性表示启动约束,即是否允许被其他进程调用,如果值为false,则该Activity仅可被同一应用中的组件或拥有相应用户ID的应用的组件调用;若为true, 则可被其他进程调用。                     android:exported属性的默认值与携带的Intent过滤器有关,如果没有携带任何Intent 过滤器,其值为false
1.1.2  Service组件         Service的类图如下:                         将Service纳入编译系统,需要在AndroidManifest.xml中对Service进行显式声明,方法如下:                                    1.   InputMethodService                        InputMethodService提供了一个输入法的标准实现,普通开发者不需要关心。一种输入法在界面上有3部分构成,及软输入视图(soft input view), 候选视图,和全屏模式。            2.   IntentService                       IntentService 作为Service的子类,主要用于处理异步请求,防止服务阻塞。所有的请求将在一个工作进程中处理,工作结束后,线程也结束。在gallery3d应用中,CacheService即是IntentService的一个示例。            3.   MediaScannerService                       MediaScannerService 主要在设备启动和SD卡挂载时执行多媒体文件的扫描工作。处于性能方面的考虑, android区分SD卡和手机内存空间。对于SD卡,MediaScannerService会收到 Action为ACTION_MEDIA_MOUNTED的Intent时进行扫描;  对于手机存储空间,MedaiScannerReceiver会在收到Action为ACTION_BOOT_COMPLETED的Intent(设备启动完毕)时进行扫描。另外,在下载文件时,也可能启动媒体扫描服务。                      媒体扫描服务相关的类如下如:                                                         目前MediaScannerService扫描的多媒体格式定义在MediaFile.java 文件中。当系统开始扫描时,媒体扫描服务会广播一个Action为ACTION_MEDIA_SCANNER_STARTED的Intent, 然后创建一个MediaScanner执行扫描;当扫描结束后,广播一个Action为ACTION_MEDIA_SCANNER_FINISHED的Intent。                      在一个特殊的场合,如录音应用和下载场景中,如果希望产生的多媒体文件接口被加入到数据库中,可以直接调用媒体扫描服务进行单个文件的扫描,但对于删除的文件,无法通过如下方式进行同步:                                    sendBroadcase(new  Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,  Uri.fromFile(file)));           4.  RecognitionService                      RecognitionService是一个抽象服务,仅在开发者希望实现一个新的语音识别器时,才可能用到。为了实现一个新的语音识别器,必须实现如下抽象方法:                              protected  abstract  void  onStartListening( Intent  recognizerIntent,  Callback  listener )                              protected  abstract  void  onCancel( Callback  listener )                              protected  abstract  void  onStopListening( Callback listener )            5.   绑定服务和启动服务                     服务的运行有两种发起方式,即绑定服务和启动服务。当通过绑定服务的方式运行服务时,一旦绑定解除,服务即被销毁,当进行多次绑定时,只有所有绑定均解除,服务才会销毁; 当以启动服务的方式运行服务时,服务并不会随着绑定组件的销毁而销毁,而是服务自我销毁,这种方式适用于文件下载,文件上传等请求后自行运行的场景。              (1)绑定服务                           为了绑定一个服务,需要设置 ServiceConnection 和标志位 ,方法如下:                                   public  abstract  boolean  bindService(Intent  service,  ServiceConnection  conn,  int  flags)                           ServiceConnection可以监控服务的状态,在进行服务绑定时,其标志位可以为BIND_AUTO_CREATE,  BING_DEBUG_UNBIND和 BING_NOT_FOREGROUND等。其中BIND_AUTO_CREATE表示当收到绑定请求时,如果服务尚未创建,则即可创建,在系统内存不足,需要先销毁优先级组件来释放内存,且只有驻留该服务的进程成为被销毁对象时,服务才可被销毁;BING_DEBUG_UNBIND通常用于调试场景中判断绑定的服务是否正确,但其会引起内存泄露,因此非调试目的不建议使用;  BING_NOT_FOREGROUND表示系统将阻止驻留该服务的进程具有前台优先级,仅在后台运行,该标志位在Froyo中引入。                           绑定服务的示例:                                          Intent  intent  = new  Intent();                                          intent.setClassName("com.android.providers.media",  "com.android.providers.media.MediaScannerService");                                          bindService(intent,  mMediaScannerConnection,  BIND_AUTO_CREATE);                           注意绑定服务是以异步的方式运行的。绑定服务必须在当前的上下文环境中进行,在某些场景中,如果无法绑定成功,则可能需要在应用级的上下文环境进行,方法如下:     getApplicationContext().bingService(......);                           如果解除绑定,方法如下:                                           public  abstract  void  unbindService( ServiceConnection  conn )               (2)启动服务                             启动服务: public  abstract  ComponentName  startService(Intent  service)                             自我停止服务:    public  final  void  stopself()                             被动停止服务:    public  abstract  boolean stopService (Intent  service)        6.  服务的声明周期                 两种运行方式下的服务的声明周期:                                                  当外部组件调用其上下文的startService()方法时,即可启动相应的服务。在服务的 onStartCommand()方法中,会返回一个唯一的整数标示符启动请求。启动服务的实例如下:                      Intent  intent = new Intent( this , ExperimentService.class );                      intent.putExtra(EXTRA_EXP_ID,  which);                      intent.putExtra(EXTRA_RUN_ALL,  all);                      startService(intent) ;                  Intent 传递过来的参数可以在onStartCommand()方法中进行处理,示例如下:                                 public int  onStartCommand(Intent  intent,  ing flags ,  int  startId) {                                           if(intent != null){                                                    Message  msg = mServiceHandler.obtainMessage();                                                     msg.arg1 = startId;                                                    msg.obj = intent.getExtras();                                                    mServideHandler.sendMessage(msg);                                           }                                           return  START_REDELIVER_INTENT;                                 }                  停止服务如下:                          stopService(intent) ;                   当外部组件调用其上下文的bingService()方法时,也可绑定相应的服务。如果服务未启动,则调用onCreate()方法启动服务, 当不会调用onStartCommand()方法,只有在所有绑定均解除后,服务才自动停止运行。通过服务的onBind()方法,可以获得一个客户端与服务进行同行的IBinder接口。注意,绑定服务的android组件在销毁前应解除绑定,否则会造成内存泄露。绑定和解除绑定的实例如下:                               mContext.bindService( new  Intent( IBluetoothHeadset.class.getName()), mConnection,  0  );                              mContext.unbindService( mConnection );                                1.1.3   Intent 组件            intent用于进程内或进程间通信的机制,其底层的通信以Binder机制实现,在物理层上则通过共享内存的方式来实现。            主要用于广播和发起意图两中场景,属性有ComponentName,  action , data,  category, extras,  flags等。通常情况下,在进行Intent 的匹配时,需要匹配Action,  Data,  Category 等3个属性。            从ComponentName 属性的明确性可以划分为,显式的intent和隐式的intent,所谓的显式的intent,即明确了目的地,不需要系统进行intent匹配的Intent。在应用内部进行组件调用时,应首选显示Intent., 举例如下;                      intent  i = new  Intent( context,  AccountFolderList.class ) ;                      i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);                   context.startActivity(i);           所谓的隐式Intent, 即没有明确指明目的地,需要系统根据自己的信息进行匹配的Intent. 这类Intent同样用于应用间的相互小勇,有助于降低应用间的耦合性。举例如下:                  Intent  intent = new  Intent(Intent.ACTION_PICK);                  intent.setDadaAndType(Uri.EMPTY,  "vnd.andorid.cursor.dir/track");                  intent.putExtra("album",  Long.valueOf(id).toString());                  intent.putExtra("artist",  mArtistId);                  startActivity(intent);        1  。 属性说明:           (1)ComponentName    为处理Intent消息的android组件,可以是activity,服务等。通常使用方法:                            public  Intent  serClassName( Context  packageContext,  String   className )                     其中,className可以为空,系统会更具intent携带的其他信息来定位相应的组件。           (2)action  表示Intent的类型,可以是查看,删除,发布或其他,最常用的是android.intent.action.MAIN。     android.intent.action.MAIN表示一个应用的入口,通常和  android.intent.category.LAUNCHER联合使用。二者同时使用表示应用程序的启动界面。           (3)data   表示Intent携带的数据,通常和MIME类型联合使用,表示应用可以打开的数据类型,用法如下:                                                                                                                                                                                                                   (4)category   表示intent的策略,目前最常用的如下:                           android.intent.category.DEFAULT      ...LAUNCHER       ...MONKEY      ...OPENABLE   ...BROWSABLE                           对于隐式的intent, 如果创建是没有指定category属性,则系统会默认设置器属性为android.intent.category.DEFAULT, 这是如果在intent过滤器中没有指定category属性为 android.intent.category.DEFAULT,则会造成匹配失败。            (5)Extras  表示intent的附加信息,它在组件间传递信息时非常有用。目前Extras可以支持多种数据类型,如布尔,整型,字符串等,实例如下:                    Intent  i  = new  Intent(this , AlarmAlertFullScreen.class);                     i.putExtra(Alarms.ALARM_INTENT_EXTRA,  mAlarm);                    i.putExtra(SCREEN_OFF,  true);                            startActivity(i);              (6)Flags  表示Intent的标志位。Flags和Activity 的启动模式有密切的关系。      2  。 特殊场景              下面介绍一些Intent应用的特殊的场景,如开机自启动,网络监听,获取内容,SD卡挂载等。             (1) 开机自启动                   接收开机自启动事件的Intent过滤器的设置方法如下:                                                                                                                                  
                                      (2)网络监听                    对网络进行监听的设置如下:                                                                                                   在接收到相关的信息后,可以从中获取网络的状态,方法如下;                               if(action.equals(ConnectivityManager.CONNECTIVITY_ACTION)){                                        NetworkInfo  info = (NetworkInfo);                                        intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);                                        if(info != null &&  info.isConnected){ ...  }                               }                 (3)获取内容                      为了响应获取内容的事件,必须在intent过滤器中设置action属性为android.intent.action.GET_CONTENT,  设置category属性为android.intent.category.DEFAULT, 方法如下:                                                                                                                                                                                                                   (4)SD卡挂载                           与SD卡挂载相关的Intent的action 属性包括:   ACTION_MEDIA_REMOVED被移除,  ACTION_MEDIA_UNMOUNTED卸载但不移除,  ACTION_MEDIA_CHECKING,   ACTION_MEDIA_NOFS表示SD卡存在,但文件系统不兼容或者尚未格式化,    ACTION_MEDIA_MOUNTED,  ACTION_MEDIA_SHARED进入USB连接模式,  ACTION_MEDIA_UNSHARED退出USB模式,  ACTION_MEDIA_BAD_REMOVAL表示SD卡已被移除,但挂载点仍存在,可能发生了某种错误,  ACTION_MEDIA_UNMOUNTABLE表示SD卡存在,但是无法挂载,  ACTION_MEDIA_EJECT表示用于欲卸载SD卡,但SD卡上的部分内容尚处于打开状态   等属性值。                   (5)返回桌面                              为了返回桌面,需要设置category属性为Intent.CATEGORY_HOME, 方法如下:                                       Intent   i  = new Intent(Intent.ACTION_MAIN);                                       i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                                       i.addCategory(Intent.GATEGORY_HOME);                                       startActivity(i);        3.   PendingIntent 的逻辑                   与intent不同,其可设定执行次数,主要用于远程服务通信,闹钟,通知,短信,启动器中,一般的应用则很少采用,PendingIntent常用的方法如下:                    //启动Activity, 类似于startActivity(Intent);                   public  static  PendingIntent  getActivity(Context  context,  int  requestCode,  Intent  intent,  int flags)                   //启动Broadcast,类似于sendBroadcast(intent)                   public  static  PendingIntent  getBroadcast(Context  context,  int  requestCode,  Intent  intent,  int  flags)                   //启动service,类似于 startService                        public  static  PendingIntent  getService(Context  context,  int  requestCode,  Intent  intent,  int  flags)              PendingIntent的标志位有FLAG_ONE_SHOT,    FLAG_NO_CREATE,   FLAG_CANCEL_CURRENT,   FLAG_UPDATE_CURRENT。 其中 FLAG_ONE_SHOT表示返回的PendingIntent仅能执行一次,执行完成后即自动取消;  FLAG_NO_CREATE表示如果描述的PendingIntent不存在,并不创建相应的PendingIntent,而是返回NULL;  FLAG_CANCEL_CURRENT表示如果相应的PendingIntent已经存在,则取消前者,然后创建新的PendingIntent,  这有利于保持数据为最新的,可用于即时通信的通知场景;  FLAG_UPDATE_CURRENT表示更新PendingIntent。               手动取消PendingIntent的方法如下:                       public  void  cancel()               手动发送PendingIntent的方法                       public  void  send()                       public  void  send(int code )               另外, PendingIntent具有类似于Intent的fillIn方法,还支持为intent 追加数据,方法如下:                       public  void  send(Context  context,  int code ,  Intent  intent)               当需要在发送完成时进行后继处理时,需要为PendingIntent 设置回调相关的接口和处理句柄,具体方法如下:                       public  void  send(int code ,  OnFinished  onFinished,  Handler  handler)                       public  void  send(Context  context,   int  code ,  Intent  intent,  OnFinished  onFinished,  Handler  handler)               Handler表示回调所处的线程,如果设置为NULL, 则表示回调将由当前进程的线程池执行。下面是OnFinished接口的一个实现:                          mFinish = new PendingIntent.OnFinished(){                                     public  void  onSendFinished(PendingIntent  pi,  Intent  intent,  int  resultCode, String  resultData,  Bundle resultExtras){                                               mFinishResult = true;                                               if(intent != null) {                                                           mResultAction = intent.getAction();                                                }                                     }                          }       4.  返回结果                 在通常情况下,Intent通信仅是单向的,到哪对于特殊的场景,比如希望拍照后返回响应的文件路径这种情况,android同样支持,方法如下:                                        public  void  startActivityForResult( Intent  intent,  int requestCode )                 其中,requestCode的值用来表示当前的计算,被调用的Activity将成为调用方的子Activity,但注意,如果reguestCode的值小于零,则调用方的Activity无法收到计算结果,等同于调用了startActivity()。另外,能否获得计算结果与携带的Intent有关,当Intent的action 为ACTION_MAIN和ACTION_VIEW等时,调用方会收到RESULT_CANCELED而替代计算结果。举例如下:                       public  final  static  int  NEW_PLAYLIST = 4;                       Intent  intent = new Intent();                       intent.setClass(this,  CreatePlaylist.class);                       startActivityForResult(intent,  NEW_PLAYLIST);                被调用的Activity在完成计算后,将计算结果通过setResult()方法返回调用方的Activity即可。                               public  final  void  setResult (int  resultCode)                其中,resultCode的值可以为RESULT_CANCELED,   RESULT_OK,   RESULT_FIRST_USER, 实例如下:                             setResult(RESULT_OK,  (new  Intent()).serData(uri));//必须随后调用finish()方法,释放当前Activity,这样调用方的activity才能收到返回结果。当计算完成后,在调用方的onActivityResult()中可以收到计算结果。具体方法如下:                           protected  void  onActivityResult(int  requestCode ,  int  resultCode,  Intent  data)                处理返回结果如下;                            switch(requestCode){                                   case NEW_PLAYLIST:                                          if(resultCode == RESULT_OK)                                                  Uri  uri  =  intent.getData();                                          break;                           }  1,1,4  BroadcastReceiver 组件                 广播的本质是基于Intent记性的,广播的接收实现如下:                            public  class  AlertReceiver  extends  BroadcastReceiver                           {                                     public  void  onReceive(Context  context ,  Intent  intent){}                            }                 通常在BroadcastReceiver中并不会执行复杂的计算,后台计算一般在服务中执行。将BroadcastReceiver纳入编译系统的示例如下:                                                                                                                                                                                                                                                                                                                                                             若BroadcastReceiver作为组件的私有类,那么可以通过上下文环境的如下方法实现BroadcastReceiver的注册和解除:                                   public  abstract  Intent  registerReceiver(BroadcastReceiver  receiver,  IntentFilter  filter)                                   public  abstract  Intent  registerReceiver(BroadcastReceiver  receiver,  IntentFilter  filter,  String  broadcastPermission,  Handler  scheduler)                                   public  abstract  void  unregisterReceiver(BroadcastReceiver  receiver)
1.2 应用框架解析            android设计框架来进行相关的管理,主要有Service框架,Activity管理机制,Broadcast机制,对话框框架,标题栏框架, 状态栏框架,通知机制和ActionBar框架等。 1.2.1  Service 框架          服务作为执行应用后台运算和框架层运算的基本组件。根据通信的方式和应用场景,服务有不同的类型。从通信的方式来看,服务可分为本地服务和远程服务,其中远程服务根据通信方式又可分为基于AIDL的服务和基于Message的服务两种。远程服务是android中跨进程通信的主要形式之一。从应用的场景看,服务可分为应用服务和系统服务。     1.   本地服务            如果服务没有跨进程的需求,应将服务设计为本地服务,如下实现了一个本地服务的最基本的形式:                       public  class  HelloService  extends Service {                                public  IBinder  onBind(Intent  arg0)                                {      return null;      }                        }           在本地服务中,必须实现onBind()方法。如果不需要绑定服务(即调用bindService()方法),可以返回null。一个具有绑定服务的本地服务实现如下:                       public  class  LocalService extends  Service{                                 public  class  LocalBinder  extends  Binder{                                          LocalService  getService(){                                                    return  LocalService.this;                                          }                                    }                                 public  IBinder  onBind(Intent  intent){                                           return mBinder;                                 }                                 private  final  IBinder  mBinder = new LocalBinder();                       }             默认情况下,Service依然运行在主线程中,而非另开线程,若希望有大量运算量的后台计算,则在实现本地服务时,必须在Service中创建当独的线程来执行相应的运算。     2.  基于AIDL的远程服务             基于AIDL的远程服务,本质上沿袭了分布式计算的思想。android实现分布式计算,并能够跨语言调用。实现基于AIDL的远程服务需要4个步骤:           (1)创建AIDL文件。           (2)将AIDL文件纳入编译系统           (3)实现接口方法           (4)绑定服务客户端             完成编码工作后,android会在编译过程中,自动为相应的AIDL文件生成对应的桩(Stub),简化开发的难度。             分别介绍实现基于AIDL的远程服务的4个步骤:            (1)创建AIDL文件                     实现简单,本身是一个以”I“开头的接口文件。下面是一个实现实例:”                              interface  ITestService{                                        boolean  getSthEnabled();                                       void  setSthEnabled(boolean  on);                              }            (2)将AIDL文件纳入编译系统                       为了生成响应的桩,必须将AIDL文件纳入编译系统,其在frameworks/base/Android.mk中实现:                              LOCAL_SRC_FILES += \                                      core/java/android/os/ITestService.aidl \                       在应用层,以IMediaPlaybackService为例,其在Android.mk中实现通常如下:                                LOCAL_SRC_FILE := $(call  all-java-files-under, src) \                                         src/com/android/music/IMediaPlaybackService.adil                       在框架层和应用层将AIDL文件纳入编译系统,两者写法不同。             (3)实现接口方法                       只需要继承相应接口的Stub子类即可,但必须实现接口所定义的所有方法,实例如下;                            public  class  ITestServiceImpl  extends  ITestService.stub{                                     public  boolean  getSthEnabled() {     ............     }                                     public  void  setSthEnabled(boolean on){     ...............    }                             }                        为了便于客户端绑定,通常会将桩封装到一个服务中,方法如下:                              public  class  TestService  extends  Service{                                     public  IBinder  onBind(Intent  arg0){                                              return  new   ITestServiceImpl(getApplicationContext());                                     }                              }           (4)绑定服务客户端                  为了与服务进行通信,必须在客户端绑定远程服务,在应用层的实现中,如果是跨进程调用的,必须将相应的ITestService文件复制到客户端所在的进程中。假设服务位于com.miaozl.text包中,在客户端实现进程调用时,方法如下:                         private  ITestService  mTest = null;                         public void  onCreate(){                                  Intent  intent = new Intent();                                  intent.setComponent( new ComponentName("com.miaozl.test" , "com.miaozl.test.service.ITestService") );                                  bindService(intent,  mTestConnection,  Context.BIND_AUTO_CREATE);                         }                   如果是在本地进程中,实现如下:                         bindService( new Intent(this, ITestService.class), mTestConnection,   BIND_AUTO_CREATE  )                   而mTestConnection的是实现则不区分是本地应用调用还是夸进程调用,具体如下:                         private ServiceConnection  mTestConnection = new ServiceConnection(){                                   public  void  onServiceConnected(ComponentName name,  IBinder  service){                                           mTest=ITestService.Stub.asInterface(service);//绑定方法                                   }                                   public  void  onServiceDisconnected(ComponentName  name){                                            mTest = null;                                   }                         }                  注意: 绑定服务是以异步的方式进行的,对于必须为同步的场景,是无法实现绑定服务的。   3. 基于Messenger的远程服务            基于Messenger的远程服务同样是跨进程的,其本质是将本地服务和Messager结合,以便实现进程间的通信。基于Messenger实现远程服务的实例如下:                     public  class AlertService extends Service{                              final  Messenger  mMessenger  = new Messenger(new ServiceHandler());                              private  final class ServiceHandler extends Handler{                                         public ServiceHandler(Looper  looper){                                                 super(looper);                                         }                                         public  void  handleMessage(Message msg){                                                 processMessage(msg);                                         }                              }                              void processMessage(Message msg){   .........   }                              public IBindler onBind(Intent intent){  //服务必须是实现的方法                                         return mMessenger.getBinder();
                             }                     }                     通过Messenger,服务的调用者可以方便地发送Message,实例如下:                              Messenger  mService = null;                              private  ServiceConnection mConnection = new ServiceConnection(){                                       public  void  onServiceConnected(ComponentName className, IBinder service){                                                 mService = new Messenger(service);                                                 Message msg = Message.obtain(null, MessengerService.MSG_SET_VALUE, this.hashCode(), 0);                                                 mService.send(msg);                                       }                              }  4.  系统服务           系统服务主要由3部分构成: *Service.java  ,  I*.aidl ,  *Manager.java  。另外还需要在SystemServer.java增加框架层封装,在ContextImpl.java增加应用层接口。           为了实现系统服务,需要实现5部分的内容: 接口文件,客户端文件,桩文件(系统自动实现),服务端文件,系统调用接口。        (1)接口文件,  通常,接口文件仅用到了基本的数据类型,如果需要用到复杂的数据类型,则需要对数据进行序列化。如下为IAlarmManager.aidl文件的具体实现:                  interface  IAlarmManager{                           void  set(int type, long triggerAtTime,  in PendingIntent operation);                           void  setRepeating(ing  type,  long triggerAtTime,  long interval,  in PendingIntent operation);                           void  setInexactRepeating(int type,  long  triggerAtTime,  long  interval,  in PendingIntent operation);                           void  setTime(long millis);                           void  setTimeZone(String zone);                           void  romove(in PendingIntent operation);                  }                 在AIDL文件中,通常定义了客户端需要调用的接口中的方法。         (2)客户端文件                 在android系统中,客户端文件对系统服务的额调用非常简单,其调用方法类似于普通方法调用,只是需要捕获RemoteException异常。下面是AlarmManager的部分实现:                          public  class  AlarmManager                          {                                    private final IAlarmManager mService;                                    AlarmManager(IAlarmManager  service){                                             mService = service;                                    }                                    public  void  set(int type,  long  triggerAtTime,  PendingIntent operation){                                             try{                                                       mService.set(type, triggerAtTime, operation);                                              }catch (RemoteException ex){}                                    }                           }          (3)服务端文件                  服务端的实现是实现系统服务的最重要的工作,下面是AlarmManagerService的部分实现:                          class  AlarmManagerService  extends IAlarmManager.Stub{                                  public  void set(int type, long  triggerAtTime,  PendingIntent  operation){                                           setRepeating(type, triggerAtTime, 0 , operation);                                  }                          }            (4)系统调用接口                   为了方便应用层进行调用,需要在ContextImpl.java中实现统一的接口封装,并在Context.java中定义如下接口:                             public  static  final String  ALARM_SERVICE = "alarm";                   在ContextImpl.java中实现统一接口封装的代码如下:                             class ContextImpl  extends  Context{                                       private  static  AlarmManager  sAlarmManager;                                       public  Object  getSystemService(String name){                                                  if(ALARM_SERVICE = "service”){                                                             return getAlarmManager();                                                   }                                       }
                                      private  AlarmManager getAlarmManager(){                                                 synchronized(sSync){                                                         if(sAlarmManager == null){                                                                  IBinder  b = ServiceManager.getService(ALARM_SERVICE);                                                                  IAlarmManager  service = IAlarmManager.Stub.asInterface(b);//  ?                                                                  sAlarmManager = new AlarmManager(service);                                                         }                                                 }                                                 return sAlarmManager;                                       }                             }                   完成以上工作后,如果需要闹钟服务,可按照下列方式执行调用:                             AlarmManager  alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);                   注意:框架层的变动会导致SDK的变动,所以需要更具设计者的需求选择@hide或make update-api来更新current.xml  5.  服务配置            为了对服务进行配置,必须在AndroidManifest.xml中对服务进行配置,其方法如下:                                  对于远程服务,若希望被其他进程调用,则必须开放相应的权限,开放权限的方法如下:                                                              android:exported;"true"  />             如果服务具有一些敏感的信息,需要对其进行权限配置,这在对安全要求较高的应用中十分重要。权限配置方法如下:                                   如果希望服务运行在单独的进程中,则可以应用如下的方法进行配置:                        
1.2.2 Activity管理机制          android的管理是通过Activity栈和Task来进行的。         1. Activity栈            android的管理主要通过android栈来进行的。当一个activity启动时,系统会根据配置或调用方式,将activity压入一个特定的栈中,系统处于运行状态,当按下Back键或触发finish()方法时,activity会从栈中被压出,进而被销毁,到那个有新的activity压入栈时,如果原activity仍可见,则原activity的状态变为暂停状态,如果activity完全被遮挡,则其状态变为停止。  2.Task            Task与activity栈有密切的关系。一个Task对应一个activity栈,Task是根据用户体验组成的运行期逻辑单元,其与应用的区别在于,Task中的activity可以由不同的应用组成。在实际的终端使用中,在主界面长按Home键弹出一个网格界面即是当前运行的Task而非应用。
           Task的定义为与 framework/base/services/java/com/androd/server/am目录下的TaskRecord.java中,一个Task由tasked ,  affinity, clearOnBackground,  intent ,  affinityIntent,  origActiivty, realAcitvity,  numActivities, lastActiveTime, rootWasReset, stringName等属性构成。           在activity中,有不少属性与Task 相关,如 android:allowTaskReparenting,   android:taskAffinity等。        (1)Task间移动配置              android:allowTaskReparenting 属性用来配置是否允许activity从启动它的Task移动到和该Activity设置的TaskI亲和性相同的Task中,        (2)Task状态设置              android:alwaysRetainTaskState 属性用于配置是否保留Activity所在的Task状态, 默认为false                            clearTaskOnLaunch                 当Task从主界面重新启动时,是否需要清除除根activity外的的所有activity,默认为false                            finishOnTaskLaunch                当Task从主界面重新启动时,特定的activity是否需要被销毁,默认为false         (3)Task亲和性              Task亲和性,由android:taskAffinity属性定义。如果希望activity启动时,运行在特定的Task中,必须显式设置这个属性。              注意:只有通过标志为FLAG_ACTIVITY_NEW_TASK的intent启动activity时,该activity的android:taskAffinity属性才有效,系统才会将就要有相同Task亲和性的Task 切换到前台,然后启动该activity,否则该activity忍让运行咋启动它的task中。
1.2.3 Broadcast机制        广播涉及顺序广播,无序广播,广播接收器等概念。     1.  顺序广播          当广播需要以类似消息链的方式进行时,应采用顺序广播,顺序广播的接收器可以抛弃或继续传递消息。具体事例如下:                  Intent  LlcpLinkIntent = new Intent();                   LlcpLinkIntent.setAction(NfcAdapter.ACTION_LLCP_LINK_STATE_CHANGED);                  LlcpLinkIntent.putExtra(NfcAdapter.EXTRA_LLCP_LINK_STATE_CHANGED,  NfcAdapter.LLCP_LINK_STATE_ACTIVATED);                  mContext.sendOrderedBroadcast(LlcpLinkIntent,  NFC_PERM);      2.  无序广播            无序广播是异步的,广播接收器除了接收广播外,无法对无序广播的行为产生影响。下面示例:                  Intent  intent = new Intent(Intent.ACTION_ARIPLANE_MODE_CHANGED);                  intent.putExtra("state", enabling);                  mContext.sendBroadcast(intent);     3.广播接收器           为了接收广播,必须在AndroidManifest.xml中配置广播接收器或通过java实现广播接收器,配置方法:                                                                                                                                        代码中配置:                      IntentFilter  bootFiler = new IntentFilter( Intent.ACTION_BOOT_COMPLETED );                      mContext.registerReceiver( monitor,  bootFiler );
1.2.4 对话框框架         在android中,目前有4中创建对话框的方式,分别为AlertDialog,  ProgressDialog,  DatePickerDialog,  TimePickerDialog。  其中AlertDialog是最通用的对话框形式, ProgressDialog用于描述进度信息,后两者主要用于日期和时间的场景中。         1. AlertDialog           默认情况下,通过setMessage()来设置显示的文字信息,通过setView()加载视图。创建对话框通过AlertDialog.Builder进行。           创建对话框的一般步骤:                >定义对话框的ID,备用;                        >在onCreateDialog()中创建对话框                >通过showDialog()显示对话框                >通过dismissDialog()隐藏对话框           在某场景下,由于正在执行无法中断的计算,弹出的对话框不希望被用户取消,在可使用下列方法设置:                  public  void  setCancelable(boolean  flag)  //使用于所有对话框           由于Dialog管理机制问题,Dialog具备记忆功能,这在需要数据更新的场景中,稍麻烦,解决方法是在  onPrepareDialog() 方法中进行数据更新。           注意: 由于无法在对话框队列中记忆对话框状态,dismissDialog()方法必须和showDialog()成对出现,若无showDialog()与之配对会发生异常。      2.  ProgressDialog            进度对话框,常用于耗时的操作,应将Progress Dialog 放在主线程之外执行,否则极易出现androidANR消息。常见的形式如下:                    ProgressDialog  mWaitDialog = new ProgressDialog();                    mWaitDialog.setMessage(getString(R.string.waiting)) ;                    mWaitDialog.setIndeterminate(true);                    mWaitDialog.setCancelable(false);             完整的进度对话框还需要定义一个ID,            showDialog(iD);      dismissDialog(ID);             若开发着不希望用户通过Back键手动销毁对话框,可进行设置   mWaitDialog.setCancelable(true);             若希望能监听进度对话框取消的消息,可如下实现:                      mWaitDialog.setOnCancelListener( new  OnCancelListener() ){                               public  void  onCancel(DialogInterface  dialog){  .......  }                      }             进度条支持两种风格的进度显示:一种是进度条,一种是环形转动。这两种进度显示对应的风格分贝为 ProgressDialog.STYLE_HORIZONTAL  和 ProgressDialog.STYLE_SPINNER,   默认的风格为后者,设置方法:                  mProgressDialog.setProgressStyle( ProgressDialog.STYLE_HORIZONTAL );              ProgressDialog支持的进度复读为 0 ~~~10000              其同时支持主进度和辅进度,其本质是是实现对ProgressBar的封装。   3.  DatePickerDialog          日期对话框,创建时通常需要设置初始年,月,日和监控日期变化的回调函数。                     DatePickerDialog(this,  mDateSetListener,  mYear,  mMonth,  mDay);          下面是日期对话框回调函数的实现:                     private  DatePickerDialog.onDateSetListener  mDateSerListener = new  DatePickerDialog.OnDateSerListener(){                               public  void  OnDateSet(DatePicker view,  int  year, int  monthOfYear,  int  dayOfMonth){                                          mYear = year;   mMonth = monthOfYear;       mDay = dayOfMonth;      updateDisplay();                               }                     };           代码中更新日期对话框的显示,方法:     dialog.updateTime(mYear,  mMonth,  mDay);   4.  TimePickerDialog           方法类似DatePickerDialog                1.2.5 标题栏框架      目前标题栏显示支持进度显示,允许用户隐藏,自定义标题栏。目前android标题栏支持FEATURE_NO_TITLE,  FEATURE_PROGRESS,   FEATURE_LEFT_ICON,   FEATURE_RIGHT_ICON,   FEATURE_INDETERMINATE_PROGRESS, 等多种定制。     1.  隐藏标题栏          两种实现方式,在AndroidManifest.xml中和代码中实现               xml中,在activity中添加     android:theme="@android:style/Theme.NoTitleBar"               代码中,        requestWindwoFeature(android.view.Window.FEATURE_NO_TITLE);      2. 自定义标题栏          注意标题栏的FEATURE_CUSTOM_TITLE 不能和FEATURE_LEFT_ICON,   FEATURE_NO_TITLE等同时使用,下面是设置方法:                    requestWindowFeature( Window.FEATURE_CUSTOM_TITLE );                    getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,  R.layout.custom_title_1  );     3.  进度显            android中支持主进度和辅进度两种进度显示,辅进度在本地应用中较少使用。在网络环境中,如在进行流媒体播放时,可以用主进度表示播放进度,用辅进度标示下载进度。二者显示范围为 0~~100。 如下示例:                     requestWindowFeature(Window.FEATURE_PROGRESS);                     setProgressBarVisibility(true);                     setProgress(progressHorizontal.getProgress() * 100);                     setSecondaryProgress(progressHorizontal.getSecondaryProgress() * 100);            实际开发中,无法使用明确的进度显示,如向服务器发送请求后,等待反馈的过程,这时就用到了不定进程显示,下面是不定进程显示示例:                      requestWindowFeature( Window.FEATURE_INDETERMINATE_PROGRESS );                      setProgressBarIndeterminateVisibility(true);       4.  图标显示             标题栏又标题,进度条,左标题,右标题构成。下面以左标题为例介绍:                     requestWindowFeature( Window.FEATURE_LEFT_ICON );                     getWindow().setFeatureDrawableResource( Window.FEATURE_LEFT_ICON,  R.drawable.ic_list_bookmark );
1.2.6 状态栏框架          与传统终端状态栏一样,android状态栏提供电量信息,蜂窝信息,SMS, MMS, 邮件,WIFI信号,蓝牙信号,闹钟等系统的状态信息。另外状态栏还有通知栏的功能。                               其中,起主要作用的是 StatusBarPolicy,  它承担着接收系统发来的Intent的信息,更新状态显示的功能,他是服务StatusBarManagerService的客户端。 StatusBarManagerService在创建时,会加载config_statusBarIcons数组。在framework\base\core\res\res\values目录下,config.xml中定义了config_statusBarIcons数组确定了状态图标的加载顺序。        整个状态栏框架是通过StatusBarService来实现。在StatusBarService初始化时,初始化了一个用于显示statusbar的StatusBarView。 在StatusBarView中定义了状态栏的实现布局,而具体的布局问及那是在framework\base\packages\systemui\res\layout\status_bar.xml实现的。      1. 状态栏的隐藏              两种方式: AndroidManifest.xml中实现:      android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"                  代码中实现:         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN );                                                requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题栏             上述隐藏方式值适合静态场景,在隐藏标题栏后再动态显示状态栏已经超出以上两种方法的能力。 此时可通过鞋面方法动态的隐藏和系那是状态栏:                      getWindow().addFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN ); //隐藏                      getWindow().clearFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN ); //显示     2.  电量显示           当StatusBarPolicy 收到Action为ACTION_BATTERY_CHANGED的Intent时,StatusBarPolicy会通知StatusBarManager进行电量图标的更新。同时还响应ACTION_BATTERY_LOW,   ACTION_BATTERY_OKAY,  ACTION_POWER_CONNECTED的intent。     3.  蜂窝信息          android对蜂窝协议的支持十分充分,目前支持GSM,  UMTS, CDMA, 4G等。       4.WIFI信息           对于wifi信号,android可以响应Action为 WifiManager.NETWORK_STATE_CHANGED_ACTION,  WifiManager.WIFI_STATE_CHAGED_ACTION,  WifiManager.RSSI_CHANGED_ACTION的intent, 相应的更新方法:  updateWifi()    5.蓝牙信息          对蓝牙信息,android可以响应BluetoothAdapter, BluetoothHeadset,  BluetoothA2dp 和 BluetoothPbap的状态变化,相应的更新方法:  updateBluetooth();
1.2.7 通知机制      多种方式向用户反馈系统状态信息,Toast提醒,通知栏提醒,对话框提醒的等。其中Toast常用于页面的显示,通知栏提醒常用于交互事件的通知,一般非常重要的通知以对话框的形式给出。      Toast 和Notification 均由框架层的 NotificationManagerService维护  1.Toast       简单提示用户信息,常只显示文本。也可以自定义。       自定义位置:      toast.setGravity( Gravity.TOP|Gravity.LEFT,  0,  0 );       自定义视图:      LinearLayout  dialog = (LinearLayout)LayoutInflater.from(this).inflate(R.layout.retry_sending_dialog, null);                                  Toast  undeliveredDialog = new Toast(this);                                   undeliveredDialog.setView(dialog);                                  undeliveredDialog.setDuration(Toast.LENGTH_LONG);                                  undeliveredDialog.show();          注意: Toast不能在AsyncTask的doInBackground()方法中运行,如果要实现类似的效果,可在Handler 中进行处理。  2. Notification           适合于交互事件的通知,常用于短消息,即时消息,下载,外围设备的状态变化的场景中。           Notification支持文字显示,振动,三色灯,振铃音等提示形式,默认情况下,仅显示消息标题,消息内容和时间。下面是一个基本实现:                    NotificationManager  nm = (NotificationManager)getSystemService( NOTIFICATION_SERVICE );                    CharSequence  from = "Joe";                    CharSequence  message = "kthx.meet u for dinner. cul";                    PendingIntent  contentIntent = PendingIntent.getActivity(this,  0 ,  new Intent(this,  IncomingMessageView.class), 0);                    String  tickerText = getString(R.string.incoming_message_ticker_text,  message);                    Notification  notif = new Notification(R.drawable.stat_sample,  tickerText,  System.currentTimeMillis());                    notif.setLatestEventInfo(this,  from,  message,  contentIntent);                    notif.vibrate = new Long[]{100,  250,  100,  500};                    nm.notify(R.string.incoming_message_ticker_text,  notify);  (1) Notification 管理             android通过标示符来管理Notification,发起一个Notification的方法如下;                       notificationManager.notify( notificationId, mNotification );             取消Notification的方法有很多,如果希望用户单击后Notificatino即被清除,则相应的方法如下:                                     notification.flags |= FLAG_AUTO_CANCEL;             如果希望手动清除某项,相应的方法:    mNotificationMgr.cancel( LOW_MEMORY_NOTIFICATION_ID );             若希望清除所有Notification时,相应的方法:    mNotificationMgr.cancelAll();
  
(2)振动提醒
            通常用于比较紧迫的场景,示例如下:                        Notification  n  = new Notification();                        n.vibrate = new Long[]{ 0,  700,  500,  1000 };//振动方式:延迟0ms,然后振动700ms,接着振动1000ms                        mNM.notify( 1, n );             若希望设置为默认的振动方式,相应的方法: notification.defaults |= Notification.DEFAULT_VIBRATE;    (3)三色灯提醒             只有设置了Notification的标志位为FLAG_SHOW_LIGHTS,才能支持三色灯提醒。创建三色灯提醒的Notification示例如下:                        Notification n = new Notification();                        n.flags |= Notification.FLAG_SHOW_LIGHTS;                        n.ledARGB = 0xff0000ff;                        n.ledOnMS = 300;                        n.ledOffMS = 300;                        mNM.notify( 1, n );             若希望设置默认三色灯提醒,相应的方法:  notification.defaluts |= Notification.DEFAULT_LIGHTS;    (4)振铃声提醒              Notification支持默认铃声,自定义铃声,android多媒体数据库等多种提醒方式,相应的配置方法:                      notification.defaults |= Notification.DEFAULT_SOUND;   //默认铃声                      notification.sound = Uri.prase( "file:///sdcard/notification/ringer.mp3" );  //自定义铃声                      notification.sound = Uri.withAppendedPath( Audio.Media.INTERNAL_CONTENT_URI,  "6" );  //基于android多媒体数据库的提醒方式    (5)提醒标志位              Notification支持FLAG_SHOW_LIGHTS三色灯提醒,  FLAG_ONGOING_EVENT发起事件,  FLAG_INSISTENT振铃音将持续到Notification取消或Notification窗口打开 ,  FLAG_ONLY_ALERT_ONCE发起Notification后,振铃音或震动均只执行一次,  FLAT_AUTO_CANCEL用户单击后自动消失,   FLAG_NO_CLEAR全部清除时,Notification才会清除,FLAG_FOREGROUND_SERVICE表示正运行的服务  等多种标志位提醒    (6)自定义视图              自定义视图的布局文件中,仅支持FrameLayout,  LinearLayout,  RelativeLayout等布局控件。自定义视图步骤如下:             1》 创建自定义视图,   考虑到通知栏的兼容性,自定义视图需避免复杂的设计             2》 获取远程视图对象,  操作自定义视图,必须获取远程视图对象,并将远程视图对象和Notification关联起来,方法如下:                           RemoteViews  expandedView = new RemoteViews(Constants.THIS_PACKAGE_NAME,  R.layout.status_bar_ongoing_event_progrss_bar);                           expandedView.setTextViewText( R.id.descriptioni,  item.description );                           expandedView .setProgressBar( ...... );                           Notification n = new Notification();                           expandedView .setImageResource(R.id.appIcon,  android.R.drawable.stae_sys_download);                           n.flags |= Notification.FLAG_ONGOING_EVENT;                           n.contentView = expandedView;                                   3》设置PendingIntent,    目前Notification支持多种Intent来响应单击事件,清除事件,处理紧急情况的全屏事件等。                      为了在Notification被单击时能响应事件,需要设置Notification的contentIntent变量,响应的方法:                                Intent  intent =  new  Intent(Contents.ACTION_LIST);                                intent.setClassName(Constants.THIS_PACKAGE_NAME,  BluetoothOppReceiver.class.getName());                                intent.setData(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));                                n.contentIntent = PendingIntent.getBroadcast(mContext,  0,  intent,  0);                      在执行清除全部的Notification 操作时,通过设置Notification的deleteInten变量可以响应这一件事,响应的方法:                                Intent  deleteIntent = new Intent();                                deleteIntent.setClass(context,  AlertReceiver.class);                                 deleteIntent.setAction(DELETE_ACTION);                                 notification.deleteIntent = PendingIntent.getBroadcast(context,  0 , deleteIntent,  0);                       为了响应紧急实际事件,需要设置Notification的fullScreenIntent 变量,响应的方法:                                                   4》 发起Notification                            发起方法:     private  static  final  int  HELLO_ID = 1;                                                   mNotificationManager.notify( HELLO_ID,  notification);      1.2.8  搜索框架     为了实现搜素,开发者,需要完成的4方面的工作:              实现搜素配置的文件;  实现显示搜索结果的activity;  实现执行搜素的算法;  发起搜素        1.   实现搜素的配置文件               搜素配置文件存储于res\xml\目录下,并命名为 searchable.xml,搜素配置文件的属性定义 如下:                http://www.cnblogs.com/over140/archive/2011/12/29/2305650.html  含义解释链接                                                      目前android支持的搜素模式包括 showSearchLabelAsBadge,   showSearchIconAsBadge(已被弃用),  queryRewriteFromData,  queryRewriteFromText, 。根据需要进行属性设置。如下为android的一个属性配置文件:                                                        android:searchMode="showSearchLabelAsBadge"                                  android:voiceLanguageModel="free_form"                                    android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"                                  android:voicePromptText="@string/search_invoke"                                  android:searchSuggestAuthority="com.example.android.apis.SuggestionProvider"                                  android:searchSuggestSelection="?"                       />      2. 实现显示搜索结果的Activity              为了接收搜索管理器返回的搜素关键字,在AndroidManifest.xml中必须配置相应的Intent过滤器和元数据。实例如下:                                                             android:label="@string/search_query_results">                                                                                                                                                 
                                                                                  android:resource="@xml/searchable" />                                       如果应用中的所有Activity都支持搜素,那么可以在AndroidManifest.xml中进行如下配置:                                                                                           android:value="SearchQueryResult" />                                      接收数据的方法如下:                        Intent  queryIntent = getIntent();                        String  queryAction = queryIntent.getAction();                        if( Intent.ACTION_SEARCH.equals(queryAction) ){                                 String  queryString = queryIntent.getStringExtra(SearchManager.QUERY);//获取关键字                        }        3. 实现执行搜素的算法             在执行搜素时,由于搜素的目标和环境不同很难规范,故在Android中,提供了一个支持数据库搜索的内容提供器,即SearchRecentSuggestionsProvider。             如果希望android执行全局搜索时,数据能够被扫描到,需要配置相关的阅读权限。下面是彩信中的一个实现:                                                                                            android:readPermission="android.permission.GLOBAL_SEARCH" />                                                                      android:readPermission="android.permission.GLOBAL_SEARCH" />                             4.  发起搜素             可以通过搜素按键或菜单等入口发起搜素。默认情况下, 调用onSearchRequested()方法即可发起搜素。             希望自定义搜素请求,可重载onSearchRequested()方法接口即可。 为了调用搜素对话框,可使用startSearch()方法
1.2.9 ActionBar框架        actionbar的布局:                               Action项的执行和普通菜单一样,也是 通过onOptionsItemSelected()方法进行的。还引入了 通过setDisplayHomeAsUpEnable()方法可以激活ActionBar中应用图标对单击事件的响应,响应的方法如下:                  actionbar.setDisplayHomeAsUpEnabled(true);        通过ActionBar还可以设置自定义的视图,即ActionView,本质是菜单的一种实现。        在ActionBar中,应用图标对单击事件的响应也是通过onOptionsItemSelected()方法进行的,其对应的ID为 android.R.id.home.    ActionBar通常需要和Fragment交互。        1. 隐藏ActionBar              两种方式:  AndroidManifest.xml中设置activity的theme属性可隐藏ActionBar,方法如下:                                                    代码中实现;      ActionBar  actionbar = getActionBar();        actionbar.hide();      actionbar.show();         2.  action项管理             Acion本质是特殊的菜单项,有图标和文字组成。Action项有4中属性可配置,分别为:                  SHOW_AS_ACTION_ALWAYS总作为action项显示                          SHOW_AS_ACTION_NEVER永远不作为action项显示                   SHOW_AS_ACTION_IF_ROOM控件足够时显示                               SHOW_AS_ACTION_WITH_TEST显示action项的文字部分           (1)利用配置文件配置action项                  Action项在菜单配置文件中的配置和普通菜单项的区别在于需要设置showAsAction属性:                          <?xml  version="1.0"  encoding="utf-8" ?>                          
                                android:showAsAction="ifRoom|withText" />                                     (2)利用java代码配置action项                           public  void  onCreateOptionsMenu(Menu menu,  MenuInflater  inflater){                                     menu.add("Menu la").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);                           }       3. ActionView              ActionView即在ActionBar上出现的Widget,用于实现一些快捷的操作,实现有两种方式:             无论是加载布局文件还是加载视图类,均可通过配置文件实现。下面是加载布局文件的实例:                      
                               android:actionLayout="@layout/searchview"  />                                     下面是加载视图类的实例:                        其他属性同上,将 actionLayout 属性去掉,添加:   android;actionViewClass="android.widget.SearchView"               为了操作ActionView,先获得其句柄:     SearchView  searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();        4.  添加Tab页          ActionBar可以显示Tab页,在Activity中进行Fragment间切换,每个Tab可以包含的元素有标题和图标,类似于TabWidget,向ActionBar中添加Tab页的步骤为:          (1)创建ActionBar.TabListener,  并实现其方法          (2)设置ActionBar的导航模式为 NAVIGATION_MODE_TABS          (3)创建Tab页          (4)添加Tab页          下面是设置导航模式,创建并添加Tab页的示例:                   final ActionBar  actionBar = getActionBar();                     actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);                      Fragment  artistsFragment = new ArtistsFragment();   //创建Fragment                   //创建并添加Tab页,以及设置TabListener                     actionBar.addTab(actionBar.newTab().setText(R.string.tab_artists).setTabListener(new  TabListener(artistsFragment)));            为了监听Tab的选择,创建TabListener并实现其方法如下:                   private  class MyTabListener implements ActionsBar.TabListener{                             private  TabContentFragment  mFragment;                             public  MyTabListener(  TabContentFragment  fragment){                                        mFragment = fragment;                             }                             public void   onTabSelected(Tab  tab, FragmentTransaction  ft){                                       ft.add(R.id.fragment_content,  mFragment,  null);                             }                             public  void  onTabUnselected(Tab tab,  FragmentTransaction  ft){                                       ft.remove( mFragment );                             }                             public  void  onTabReselected(Tab tab, FragmentTransaction  ft){   .........                             }                   }            通过ActionBar的 getSelectedNavigationIndex()方法和getSelectedTab()方法可以获知当前选中的是哪个Tab页。     5. 下拉菜单          下拉菜单主要是基于SpinnerAdapter来处理数据的。实现的步骤如下:              (1)设置导航模式        (2)实现并加载资源文件    (3)创建并设置onNavigationListener          设置导航模式的方法,调用setNavigationMode()方法将导航模式设置为NAVIGATION_MODE_LIST即可,具体方法:                        ActionBar  actionBar = getActionBar();                        actionBar.setNavigationMode(ActionBar. NAVIGATION_MODE_LIST);          资源的实现方式有数组和资源文件两种,下面是资源文件的实现示例:                                                                                         Mercury                                  Venus                                                                 加载资源文件的方法:               SpinnerAdapter  mSpinnerAdapter = ArrayAdapter.cresteFromResource(this,   R.array.action_list,  android.R.layout.simple_spinner_dropdown_item);           为监听选择,需创建并设置OnNavigationListener 。创建OnNavigationListener的方法如下:                         mOnNavigationListener = new OnNavigationListener(){                                  String[]  strings = getResources().getStringArray(R.array.action_list);                                  public  boolean  onNavigationItemSelected(int position,  long itemId){                                              .....                return true;                                   }                        }            设置OnNavigationListener 是通过setListNavigationCallbacks()方法实现的,示例如下:                         actionBar.setListenerNavigationCallbacks(mSpinnerAdapter,  mOnNavigationListener);                                                         
               第二章   资源框架详解 2.1 布局文件        1. 加载布局              可通过setContentView()隐式加载,若希望显式加载,可铜鼓getLayoutInflater()方法来获取LAYOUT_INFLATER_SERVICE服务将布局文件实例化,也可以获取已有的LayoutInflater对象的副本来实例化布局文件。布局文件实例化的方法:             》通过getLayoutInFlater()方法,具体如下:        View  demo = getLayoutInflater().Inflater(R.layout.demo ,  null);             》通过系统服务,具体如下:    LayoutInflater  inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);                                                              View  demo = inflater.Inflater(R.layout.demo,  null);             》通过已有LayoutInflater对象的副本,具体如下:    LayoutInflater  inflater = from(conext);                                                                                                View  demo = inflater.Inflater(R.layout.demo,  null);          2. 密度的逻辑              三种,dp, px,  dip ,其中dip最常用,dip的设置与分辨率无关,与屏幕密度有关          3. 特殊标签             在android布局文件中,除了普通的UI空间标签外,还有几种特殊的标签:viewStub,  requestFocus,  merge 和 include           (1)viewStub标签,实际是一种特殊的控件,默认情况下,其所包含的控件是不可见的,并不占任何内存空间,开发者可通过setVisibility()和 inflate()加载viewStub标签所包含的布局,实例如下:                     android:layout=”@layout/otacall_card“                                          android:layout_width=""             android:layout_height="" />                  其布局文件是通过android:layout属性引用外部布局文件。加载其所含的布局的实例如下:                                 ViewStub  otaCallCardStub = (ViewStub)mInCallScreen.findViewById(R.id.otaCallCardStub);                                otaCallCardStub.inflate();           (2)requestFocus标签, 可以使相应的UI控件获得焦点,使用时在UI控件的内部。