第5章Android组件与事件5.1Android组件
Android应用程序在Android应用框架之上,由一些系统自带的应用程序和用户创建的应用程序组成。组件是可以调用的基本功能模块,Android应用程序就是由组件组成的。一个Android应用程序通常包含4个核心组件和一个Intent,4个核心组件分别是Activity、Service、BroadcastReceiver和ContentProvider。Intent是组件之间进行通信的载体,不仅可以在同一个应用中起传递信息的作用,还可以在不同的应用间传递信息,如图51所示。
 图51Android应用程序组件5.1.1Android组件Activity〖*3〗1. Activity的生命周期与创建Activity是与用户交互的接口,提供了一个用户完成相关操作的窗口。当在开发中创建Activity后,通过调用setContentView(View)方法给该Activity指定一个布局界面,而这个界面就是提供给用户交互的接口。Android系统中是通过Activity栈的方式管理Activity的,而Activity自身则是通过生命周期的方法管理自己的创建与销毁。Activity生命周期流程图如图52所示。
图52Activity生命周期流程图
Activity的形态如下。
(1) Active/Running: Activity处于活动状态,此时Activity处于栈顶,是可见状态,可与用户进行交互。基于Android技术的物联网应用开发第5章Android组件与事件〖2〗(2) Paused: 当Activity失去焦点时,或被一个新的非全屏的Activity,或被一个透明的Activity放置在栈顶时,Activity就转化为Paused状态。但此时Activity只是失去了与用户交互的能力,其所有的状态信息及其成员变量都还存在,只在系统内存紧张的情况下,才有可能被系统回收。
(3) Stopped: 当一个Activity被另一个Activity完全覆盖时,被覆盖的Activity就会进入Stopped状态,此时它不再可见,但是与Paused状态一样保持着其所有的状态信息及其成员变量。
(4) Killed: 当Activity被系统回收时,Activity就处于Killed状态。
所谓典型的生命周期,就是在有用户参与的情况下,Activity经历创建、运行、停止、销毁等正常的生命周期过程。这里先介绍几个主要方法的调用时机,然后再通过代码层验证其调用流程。
(1) onCreate(): 该方法在Activity被创建时调用,它是生命周期第一个调用的方法,创建Activity时一般都需要重写该方法,然后在该方法中做一些初始化的操作,如通过setContentView()设置界面布局的资源,初始化所需要的组件信息等。
(2) onStart(): 此方法被调用时表示Activity正在启动,此时Activity已处于可见状态,只是还没有在前台显示,因此无法与用户进行交互。可以简单理解为Activity已显示,但我们无法看见。
(3) onResume(): 当此方法被调用时,说明Activity已在前台可见,可与用户交互了(处于Active/Running形态)。onResume()方法与onStart()的相同点是,两者都表示Activity可见,只不过onStart()调用时Activity还是后台,无法与用户交互,而调用onResume()时Activity已显示在前台,可与用户交互。当然,从流程图也可以看出,当Activity停止后(onPause()方法和onStop()方法被调用),重新回到前台时也会调用onResume()方法,因此也可以在onResume()方法中初始化一些资源,如重新初始化在onPause()或者onStop()方法中释放的资源。
(4) onPause(): 此方法被调用时,表示Activity正在停止(处于Paused形态)。一般情况下,onStop()方法会紧接着被调用。通过流程图还可以看到的一种情况是: onPause()方法执行后直接执行了onResume()方法,这属于比较极端的现象,这可能是用户操作使当前Activity退居后台后又迅速再回到当前的Activity,此时onResume()方法就会被调用。当然,在onPause()方法中,我们可以做一些数据存储或者动画停止或者资源回收的操作,但是不能太耗时,因为这可能会影响到新的Activity的显示——onPause()方法执行完成后,新Activity的onResume()方法才会被执行。
(5) onStop(): 一般在onPause()方法执行完成后直接执行,表示Activity即将停止或者完全被覆盖(处于Stopped形态),此时Activity不可见,仅在后台运行。同样,在onStop()方法可以做一些资源释放的操作(不能太耗时)。
(6) onRestart(): 表示Activity正在重新启动,当Activity由不可见状态变为可见状态时,该方法被调用。这种情况一般是用户打开一个新的Activity时,当前的Activity就会被暂停(onPause()和onStop()被执行了),接着又回到当前Activity页面,onRestart()方法就会被调用。
(7) onDestroy(): 此时Activity正在被销毁,也是生命周期最后一个执行的方法。一般地可以在此方法中做一些回收工作和最终的资源释放。
下面通过程序验证上面流程中的几种比较重要的情况,见example5.1。  package com.cmcm.activitylifecycle;  import android.content.Intent;
  import android.support.v7.app.AppCompatActivity;
  import android.os.Bundle;
  import android.view.View;
  import android.widget.Button;
  public class MainActivity extends AppCompatActivity {
  Button bt;
  //Activity创建时被调用,@param savedInstanceState
  @Override
  protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  LogUtils.e("onCreate is invoke!!!");
  bt= (Button) findViewById(R.id.bt);
  bt.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
  Intent i = new Intent(MainActivity.this,SecondActivity.class);
  startActivity(i);
  }
  });
  }
  //Activity从后台重新回到前台时被调用
  @Override
  protected void onRestart() {
  super.onRestart();
  LogUtils.e("onRestart is invoke!!!");
  }
  //Activity创建或者从后台重新回到前台时被调用
  @Override
  protected void onStart() {
  super.onStart();
  LogUtils.e("onStart is invoke!!!");
  }
  //Activity创建或者从被覆盖、后台重新回到前台时被调用
  @Override
  protected void onResume() {
  super.onResume();
  LogUtils.e("onResume is invoke!!!");
  }
  //Activity被覆盖到下面或者锁屏时被调用
  @Override
  protected void onPause() {
  super.onPause();
  LogUtils.e("onPause is invoke!!!");
  }
  //退出当前Activity或者跳转到新Activity时被调用
  @Override
  protected void onStop() {
  super.onStop();
  LogUtils.e("onStop is invoke!!!");
  }
  //退出当前Activity时被调用,调用之后Activity就结束了
  @Override
  protected void onDestroy() {
  super.onDestroy();
  LogUtils.e("onDestroy is invoke!!!");
  }
  }
2. Activity间的数据传递与交互
1) 数据传递
假设有两个Activity,即MainActivity与SecondActivity,其中MainActivity是主活动。MainActivity中有一个字符串,现在想把这个字符串传递到SecondActivity,可以用putExtra与getStringExtra在Activity之间传递数据,见示例example5.2。public String getStringExtra(String name){
  return mExtras == null?null:mExtras.getString(name);
  }
  MainActivity需要传递data给SecondActivity,具体使用如下: 
  //MainActivity(sender)
  button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
  String data = " hello world!";
  Intent intent = new Intent(MainActivity.this, SecondActivity.class);
  intent.putExtra("extra_data",data); //{"extra_data":data}
  startActivity(intent);
  }
  });
  // SecondActivity(receiver)
  public class SecondActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.second_layout);
  //通过intent获取数据
  Intent intent = getIntent();
  String data = intent.getStringExtra("extra_data");
  Toast.makeText(SecondActivity.this,data,Toast.LENGTH_SHORT).show();
  }
  }
2) 数据返回
因为数据返回一般是Activity的销毁,而不是调用,因此与1)中的方式不一样。假设现在需要从SecondActivity返回数据给MainActivity: // SecondActivity
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.second_layout);
Button button2 = (Button) findViewById(R.id.button_2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("data_return", "Hello MainActivity");
//绑定result_code与intent的内容
setResult(RESULT_OK,intent);
finish();
}
});
}
}
MainActivity需要接收的参数有request Code、result Code和一个intent。public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
final Button button = (Button) findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivityForResult(intent, 200);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
switch (requestCode){
case 200:
if (requestCode == RESULT_OK){
String returnedData = data.getStringExtra("data_return");
Log.d("MainActivity", returnedData);
}
break;
default:
}
}
 ...
}
3) 在Bundle中传递及保存数据
在Bundle中传递及保存数据是为了防止Activity调用时,当前Activity如果需要在onDestroy()时保存一些临时数据,则可以用构造函数onSaveInstanceState()。
保存数据示例example5.3如下。  @Override
  protected void onSaveInstanceState(Bundle outState){
  super.onSaveInstanceState(outState);
  String tempData = "Something you just typed";
  outState.putString("data_key",tempData);
  }
取出数据:   public class ActivityLifeCycleTest extends AppCompatActivity {
  public static final String TAG = "MainActivity";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
Toast.makeText(ActivityLifeCycleTest.this,TAG+"===onCreate==",Toast.LENGTH_SHORT).show();
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.activity_activity_life_cycle_test);
  if (savedInstanceState != null){
  String data = savedInstanceState.getString("data_key");
  Log.d(TAG, data);
  }
  ...
  }
5.1.2Android组件Service
Service常用于没有用户界面,但需要长时间在后台运行的应用,与应用程序的其他模块(如Activity)一同运行于主线程中。一般通过startService()或bindService()方法创建Service,通过stopService()或stopSelf()方法终止Service。通常,都在Activity中启动和终止Service。
在Android应用中,Service的典型应用是: 音乐播放器,在一个媒体播放器程序中,大概要有一个或多个活动(Activity)供用户选择歌曲并播放。然而,音乐的回放就不能使用活动(Activity)了,因为用户希望能够切换到其他界面时音乐继续播放。这种情况下,媒体播放器活动(Activity)要用Context.startService()启动一个服务在后台运行,保持音乐的播放。系统将保持这个音乐回放服务的运行,直到它结束。需要注意,要用Context.bindService()方法连接服务(如果它没有运行,要先启动它)。当连接到服务后,可以通过服务暴露的一个接口和它通信。对于音乐服务,它支持暂停、倒带、重放等功能。
1. Service与Thread
Service(服务): Android四大组件之一,非常适合执行那些不需要与用户交互且要求长期执行的任务。需要注意: 服务依赖于创建服务时所在的应用程序进程。
Thread(线程): 程序执行的最小单元,可以用Thread执行一些异步操作。
子进程和服务的使用及其区别如下。
(1) 子进程的使用: 通常使用子线程完成耗时任务。但是,有时需要根据任务的执行结果更新显示相应的UI控件,要知道Android的UI与许多其他GUI库一样是线程不安全的,必须在主线程中操作,于是需要采用异步消息机制。为了方便起见,我们还是使用基于异步消息机制的AsyncTask抽象类: 使用AsyncTask的诀窍在于,在doInBackground(运行在子线程中)方法中执行耗时操作,在onProgressUpdate(运行在主线程中)方法中进行UI操作,在onPostExcute(运行在主线程中)方法中执行任务的收尾工作。
(2) 服务的使用: 服务我们最初的理解是在后台处理一些耗时操作,但是不要被其所谓的后台概念迷惑,实际上服务不会自动开启线程,所有的代码都是默认运行在主线程中的,如果直接在服务中进行耗时操作,必定会阻塞主线程,出现ANR的情况。因此,需要在服务中手动创建子线程,在子线程中进行耗时操作。一个比较标准的服务如example5.4所示。public class MyService extends Service {
...
@Override
public int onStartCommand(Intent intent,int flags,int startId)
{
new Thread(new Runnable()
{
public void run()
{
//处理具体逻辑
}
}).start();
return super.onStartCommand(intent,flags,startId);
}
}
但是,这种服务一旦启动,必须调用stopSelf()方法或stopService()方法,才能停止该服务。为了简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类,只需要新建一个MyIntentService,继承IntentService类,在onHandlerIntent()方法中执行耗时操作即可,因为这个方法是在子线程中运行的,且这个服务在运行结束后会自动停止。
2. IntentService的使用
Android中的IntentService继承自Service类,我们在讨论IntentService之前,先想一下Service的特点: Service的回调方法(onCreate、onStartCommand、onBind、onDestroy)都是运行在主线程中的。当通过startService启动Service之后,需要在Service的onStartCommand()方法中写代码完成工作,但是onStartCommand()是运行在主线程中的,如果需要在此处完成一些网络请求或IO等耗时操作,就会阻塞主线程UI无响应,从而出现ANR现象。为了解决这种问题,最好的办法是在onStartCommand()中创建一个新的线程,并把耗时代码放到这个新线程中执行。在onStartCommand()中开启新的线程作为工作线程执行网络请求,这样不会阻塞主线程。可见,创建一个带有工作线程的Service是一种很常见的需求(因为工作线程不会阻塞主线程),所以Android为了简化开发带有工作线程的Service,Android额外开发了一个类——IntentService。
IntentService的特点: 
(1) IntentService自带一个工作线程,当Service需要做一些可能会阻塞主线程的工作时,可以考虑使用IntentService。
(2) 需要将要做的实际工作放入IntentService的onHandleIntent()回调方法中,当通过startService(intent)启动IntentService之后,最终Android Framework会回调其onHandleIntent()方法,并将intent传入该方法,这样就可以根据intent做实际工作,并且onHandleIntent运行在IntentService所持有的工作线程中,而非主线程。
(3) 当通过startService多次启动IntentService,会产生多个job,由于IntentService只有一个工作线程,所以每次onHandleIntent只能处理一个job。面对多个job,IntentService会如何处理?处理方式是onebyone,也就是一个一个按照先后顺序处理,先将intent1传入onHandleIntent,让其完成job1,然后将intent2传入onHandleIntent,让其完成job2……,直至所有job完成,所以说IntentService不能并行执行多个job,只能一个一个地按先后顺序完成,当所有job完成时,IntentService就销毁了,会执行onDestroy()回调方法。
IntentService继承自Service类,并且IntentService重写了onCreate()、onStartCommand()、onStart()、onDestroy()回调方法,IntentService还添加了一个onHandleIntent()回调方法。下面依次解释这几个方法在IntentService中的作用。
(1) onCreate:在onCreate()回调方法中,利用mName作为线程名称,创建HandlerThread,HandlerThread是IntentService的工作线程。HandlerThread执行了start()方法后,其本身就关联了消息队列和Looper,并且消息队列开始循环起来。
(2) onStartCommand: IntentService重写了onStartCommand()回调方法,即在内部调用onStart()回调方法。
(3) onStart:在onStart()方法中创建Message对象,并将intent作为Message的obj参数,这样Message与Intent就关联起来了,然后通过Handler的sendMessage()方法将关联了Intent信息的Message发送给Handler。
(4) onHandleIntent: 在onStart()方法中,通过sendMessage()方法将Message放入Handler所关联的消息队列中后,Handler所关联的Looper对象会从消息队列中取出一个Message,然后将其传入Handler的handleMessage()方法中,在handleMessage()方法中首先通过Message的obj获取到原始的Intent对象,然后将其作为参数传给onHandleIntent()方法让其执行。handleMessage()方法是运行在HandlerThread中的,所以onHandleIntent()也是运行在工作线程中的。执行完onHandleIntent()之后,需要调用stopSelf(startId)声明某个job完成了。当所有job完成时,Android会回调onDestroy()方法,销毁IntentService。
(5) onDestroy:当所有job完成时,Service会销毁并执行其onDestroy()回调方法。在该方法中调用了Handler的quit()方法,该方法会终止消息循环。
总结: IntentService可以在工作线程中完成工作,而不阻塞主线程,但是IntentService不能并行处理多个job,只能依次处理,一个接一个,当所有job完成后,会自动执行onDestroy()方法而无须自己调用stopSelf()或stopSelf(startId)方法。IntentService并不神秘,只是Android对一种常见开发方式的封装,便于开发人员减少开发工作量。IntentService是一个助手类,如果Android没有提供该类,也可以写一个类似的类。IntentService之于Service,类似于HandlerThread之于Handler。
5.1.3BroadcastReceiver组件〖*3〗1. BroadcastReceiver在Android中,Broadcast是一种广泛运用在应用程序之间传输信息的组件,而BroadcastReceiver是接收并响应广播消息的组件: 对发送出来的Broadcast进行过滤接收并响应,它不包含任何用户界面,可以通过启动Activity或者Notification通知用户接收到重要消息,在Notification中有多种方法提示用户,如闪动背景灯、振动设备、发出声音或在状态栏上放置一个持久的图标。
BroadcastReceiver过滤接收的过程如图53所示。
图53BroadcastReceiver过滤接收的过程
需要发送消息时,把要发送的消息和用于过滤的信息(如Action、Category)装入一个Intent对象,然后通过调用Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把Intent对象以广播方式发送出去。
当Intent发送后,所有已经注册的BroadcastReceiver会检查注册时的Intent Filter是否与发送的Intent相匹配,若匹配,则调用BroadcastReceiver的onReceive()方法。因此,在定义一个BroadcastReceiver时,通常需要实现onReceive()方法。
BroadcastReceiver注册有以下两种方式。
(1) 静态地在AndroidManifest.xml中用<receiver>标签声明注册,并在标签内用<intentfilter>标签设置过滤器。
(2) 动态地在代码中先定义并设置好一个Intent Filter对象,然后在需要注册的地方调用Context.registerReceiver()方法,如果取消,就调用Context.unregisterReceiver()方法。
不管是用XML注册的,还是用代码注册的,程序退出时,一般需要注销,否则下次启动程序可能会有多个BroadcastReceiver。另外,若在使用sendBroadcast()方法时指定了接收权限,则只有在AndroidManifest.xml中用<userpermission>标签声明了拥有此权限的BroadcastReceiver时,才有可能接收到发送来的Broadcast。
同样,若在注册BroadcastReceiver时指定了可接收的Broadcast的权限,则只有在包内的AndroidManifest.xml中用<userpermission>标签声明了拥有此权限的Context对