一、Service的概述
Service是Android的四大组件之一,是一个能够在后台执行长时操作的应用程序组件。没有Activity一样的用户界面,不能与用户进行交互,不能自己启动,运行在后台。一个Service具有全局唯一性,同一时间在系统内只能存在该Service的一个实例。Service具有更高的进程优先级。
在android应用项目中,若将耗时的操作放在Activity的线程中(如播放音乐),将可能出现以下情况:因接电话等操作,使得应用程序成为后台进程(Activity被完全遮盖)或成为空进程(应用程序中的所有Activity都被销毁)。虽然这时进程中的线程仍然在工作,如在播放音乐,但是后台进程或空进程在系统内存匮乏时,容易被android系统彻底杀掉,这样线程也将停止。为避免这种问题出现,可以将应用程序中的耗时操作放在Service中进行,由于Service的进程优先级为第三级,将大大降低耗时操作被系统杀掉的几率。
注意:1. 一个service被启动后,实际上还是运行在主线程中,即UI线程。若用户要执行比较耗时的操作,要新建一个线程来处理;否则会使UI线程阻塞。2.Service停止后,在Service中运行的线程并没有结束,此时线程在空进程中运行。
二、Service的生命周期
1、startService方式启动Service
(1)服务的启动和停止:
在应用程序中调用Context #startService启动一个服务,停止服务调用Context #stopService,也可以在服务中调用stopSelf方法,服务会立即停止。
(2)生命周期
onCreate()
Service第一次启动时执行,只执行一次。可以进行初始化操作。
onStartCommand(Intent intent,int flags,finalint startId)
每次启动Service都会执行。在此可以实现服务提供的功能,例如,若该服务是在后台下载文件,则在该函数中启动一个新的线程(Thread),在线程中实现下载功能。
onDestory()
当服务没有被远程客户端绑定时,调用stopServiece(),或stopSelf时绘执行该方法。可以在这里进行释放资源操作。
2、bindService方式启动Service
(1)服务的启动和停止
在应用程序中调用Context #bindService绑定一个服务。
a.一个Activity与一个Service只能绑定一次,也就是说如果已经通过bindService进行了绑定,那么就不会再次绑定,即ServiceConnection的回调方法不会执行。
b.一个Activity可以绑定到多个Service,一个Service也可以同时被多个Activity绑定。
c.在绑定Service时,如果将Flag设置为 BIND_AUTO_CREATE 时,如果该Service未创建,则先创建(onCreate()),然后绑定(onBind);如果Service已经创建,则直接绑定(onBind)。
解除服务调用Context #unbindService。
如果Service是通过bindService启动的,那么当unbindService时,Service会被销毁(如果Service被多个Activity绑定,只有当所有的客户端都解绑,该Service才会被销毁)。如果bindService时,Service已经启动了,那么unbindService时,Service不会被销毁。
(2)生命周期
onCreate()
如果Service未启动,Service第一次绑定时执行,只执行一次。如果Service已经通过startService启动,则不会执行。
onBind()
当客户端第一次绑定到Service时执行。一个service可以被多次绑定。onBind()返回一个IBinder对象,客户端调用bindService()绑定该服务时被调用。这个绑定是异步的,bindService()方法会立即返回,并且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须创建一个实现ServiceConnection类,并创建ServiceConnection的实例;然后在,bindService()方法中调用ServiceConnection的实例。注意,onServiceDisconnected()回调,在从服务解绑时并不会调用,只有在服务崩溃时才会调用。如果调用了该函数,则应该认为服务没有连接,可能需要重新调用bindService().
onReBind()
如果onUnbind返回false,该方法不会被执行。如果onUnbind返回true,当已经有客户端正在绑定服务时,其他客户端绑定服务时,不会执行该方法,只有当所有客户端都解绑成功后,并且service实例还存在(没有执行onDestroy),此时有客户端再次绑定才会执行。
onUnbind()
当任意一个已经绑定的客户端第一次解绑时(调用unbindService),会调用onUnbind方法。之后当客户端解绑时是否会调用该方法,由第一次解绑时调用onUnbind方法的返回值决定。如果返回false,那么之后所有的客户端解绑都不会调用该方法,如果返回true,则当最后一个客户端从service解绑时执行该方法。
onDestoty()
三、本地服务
1、startService启动本地服务
一个Activity,包含2个按钮和1个进度条,2个按钮分别是开始按钮、结束按钮。点击“开始”按钮:进度条开始加载;“开始”变成“重启”按钮;显示“结束”按钮(默认情况,“结束”按钮是隐藏状态)。
BaseServiceTest包括了两个主要的类:
StartServiceImpl.java —— Service的子类。当服务被启动时,它会并新建一个线程,每隔200ms将一个数字+2,并通过广播发送出去。
StartServiceTest.java —— 调用StartServiceImpl的Activity。
StartServiceImpl.java的内容如下:
/**
* @desc 服务:每隔200ms将一个数字+2并通过广播发送出去
*
*/
public class StartServiceImpl extends Service {
private static final String TAG = "StartServiceImpl";
// 发送的广播对应的action
private static final String COUNT_ACTION = "com.service.startservice.COUNT_ACTION";
// 线程:用来实现每隔200ms发送广播
private static CountThread mCountThread = null;
// 数字的索引
private static int index = 0;
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
// 终止服务
if ( mCountThread != null) {
mCountThread.interrupt();
mCountThread = null;
}
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
// 非首次运行服务时,执行下面操作
// 目的是将index设为0
if ( mCountThread != null) {
Log.d(TAG, "mCountThread != null");
index = 0;
return START_STICKY;
}
Log.d(TAG, "start thread");
// 首次运行时,创建并启动线程
mCountThread = new CountThread();
mCountThread.start();
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return null;
}
private class CountThread extends Thread {
@Override
public void run() {
index = 0;
try {
while (true) {
// 将数字+2,
index += 2;
// 将index通过广播发送出去
Intent intent = new Intent(COUNT_ACTION);
intent.putExtra("count", index);
sendBroadcast(intent);
// Log.d(TAG, "CountThread index:"+index);
// 若数字>=100 则退出
if (index >= 100) {
if ( mCountThread != null) {
mCountThread = null;
}
return ;
}
// 修改200ms
this.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
StartServiceTest.java的内容如下
:
public class StartServiceTest extends Activity {
private static final String TAG = "StartServiceTest";
private static final String COUNT_ACTION = "com.service.startservice.COUNT_ACTION";
private CurrentReceiver mReceiver;
private Button mStart ;
private Button mStop ;
private Intent mIntent ;
private Intent mServiceIntent ;
private ProgressBar mProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.start_service_test);
mStart = (Button) findViewById(R.id.start);
mStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d(TAG, "click start button");
// 显示“结束”按钮
mStop.setVisibility(View.VISIBLE);
// 将“开始”按钮更名为“重启”按钮
mStart.setText(R.string.text_restart);
// 启动服务,用来更新进度
if (mServiceIntent == null)
mServiceIntent = new Intent("com.service.StartService");
startService(mServiceIntent);
}
});
mStop = (Button) findViewById(R.id.stop);
mStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "click stop button");
if (mServiceIntent != null) {
// 结束服务。
stopService(mServiceIntent);
mServiceIntent = null;
}
}
});
mStop.setVisibility(View.INVISIBLE);
mProgressBar = (ProgressBar) findViewById(R.id.pbar_def);
// 隐藏进度条
mProgressBar.setVisibility(View.INVISIBLE);
// 动态注册监听COUNT_ACTION广播
mReceiver = new CurrentReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(COUNT_ACTION);
this.registerReceiver(mReceiver, filter);
}
@Override
public void onDestroy(){
super.onDestroy();
if(mIntent != null)
stopService(mIntent);
if(mReceiver != null)
this.unregisterReceiver(mReceiver);
}
/**
* @desc 更新进度条
* @param index
*/
private void updateProgressBar(int index) {
int max = mProgressBar.getMax();
if (index < max) {
mProgressBar.setProgress(index);
mProgressBar.setVisibility(View.VISIBLE);
} else {
// 隐藏进度条
mProgressBar.setVisibility(View.INVISIBLE);
// 隐藏“结束”按钮
mStop.setVisibility(View.INVISIBLE);
// 将“重启”按钮更名为“开始”按钮
mStart.setText(R.string.text_start);
}
// Log.d(TAG, "progress : "+mProgressBar.getProgress()+" , max : "+max);
}
/**
* @desc 广播:监听COUNT_ACTION,获取索引值,并根据索引值来更新进度条
* @author skywang
*
*/
private class CurrentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (COUNT_ACTION.equals(action)) {
int index = intent.getIntExtra("count", 0);
updateProgressBar(index);
}
}
}
}
2.bindService启动本地服务
BinderServiceImpl.java的内容
/**
* @desc 服务:每隔200ms将一个数字+2并通过广播发送出去
*
*/
public class BinderServiceImpl extends Service {
private static final String TAG = "skywang-->BinderServiceImpl";
// 发送的广播对应的action
private static final String COUNT_ACTION = "com.service.binderservice.COUNT_ACTION";
// 线程:用来实现每隔200ms发送广播
private static CountThread mCountThread = null;
// 数字的索引
private static int index = 0;
// 创建IBinder对象
private final IBinder mBinder = new LocalBinder();
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
// 终止服务
if ( mCountThread != null) {
mCountThread.interrupt();
mCountThread = null;
}
super.onDestroy();
}
public void startCount() {
Log.d(TAG, "startCount");
// 非首次运行服务时,执行下面操作
// 目的是将index设为0
if ( mCountThread != null) {
Log.d(TAG, "mCountThread != null");
index = 0;
return ;
}
Log.d(TAG, "start thread");
// 首次运行时,创建并启动线程
mCountThread = new CountThread();
mCountThread.start();
}
public void endCount() {
// 终止服务
if ( mCountThread != null) {
mCountThread.interrupt();
mCountThread = null;
}
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mBinder;
}
public class LocalBinder extends Binder {
public BinderServiceImpl getService() {
// Return this instance of LocalService so clients can call public methods
return BinderServiceImpl.this;
}
}
private class CountThread extends Thread {
@Override
public void run() {
index = 0;
try {
while (true) {
// 将数字+2,
index += 2;
// 将index通过广播发送出去
Intent intent = new Intent(COUNT_ACTION);
intent.putExtra("count", index);
sendBroadcast(intent);
// Log.d(TAG, "CountThread index:"+index);
// 若数字>=100 则退出
if (index >= 100) {
if ( mCountThread != null) {
mCountThread = null;
}
return ;
}
// 修改200ms
this.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
BinderServiceTest.java的内容:
public class BinderServiceTest extends Activity {
private static final String TAG = "BinderServiceTest";
private static final String COUNT_ACTION = "com.service.binderservice.COUNT_ACTION";
private CurrentReceiver mReceiver;
private Button mStart;
private Button mStop;
private Intent mIntent;
private Intent mServiceIntent;
private ProgressBar mProgressBar;
private BinderServiceImpl mService;
private boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.binder_service_test);
mStart = (Button) findViewById(R.id.start);
mStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d(TAG, "click start button");
// 显示“结束”按钮
mStop.setVisibility(View.VISIBLE);
// 将“开始”按钮更名为“重启”按钮
mStart.setText(R.string.text_restart);
// 启动计数
mService.startCount();
}
});
mStop = (Button) findViewById(R.id.stop);
mStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "click stop button");
// 结束计数
mService.endCount();
}
});
mStop.setVisibility(View.INVISIBLE);
mProgressBar = (ProgressBar) findViewById(R.id.pbar_def);
// 隐藏进度条
mProgressBar.setVisibility(View.INVISIBLE);
// 启动服务,用来更新进度
mServiceIntent = new Intent("com.service.BinderService");
bindService(mServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
// 动态注册监听COUNT_ACTION广播
mReceiver = new CurrentReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(COUNT_ACTION);
this.registerReceiver(mReceiver, filter);
}
@Override
public void onDestroy(){
super.onDestroy();
if(mIntent != null) {
// 结束服务。
unbindService(mConnection);
mServiceIntent = null;
mBound = false;
}
if(mReceiver != null)
this.unregisterReceiver(mReceiver);
}
private ServiceConnection mConnection = new ServiceConnection() {
/**
* 绑定服务成功的回调函数
*/
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
Log.d(TAG, "onServiceConnected");
// 获取IBinder服务对象
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
/**
* 解除绑定的回调函数
*/
@Override
public void onServiceDisconnected(ComponentName arg0) {
Log.d(TAG, "onServiceDisconnected");
mBound = false;
}
};
/**
* @desc 更新进度条
* @param index
*/
private void updateProgressBar(int index) {
int max = mProgressBar.getMax();
if (index < max) {
mProgressBar.setProgress(index);
mProgressBar.setVisibility(View.VISIBLE);
} else {
// 隐藏进度条
mProgressBar.setVisibility(View.INVISIBLE);
// 隐藏“结束”按钮
mStop.setVisibility(View.INVISIBLE);
// 将“重启”按钮更名为“开始”按钮
mStart.setText(R.string.text_start);
}
// Log.d(TAG, "progress : "+mProgressBar.getProgress()+" , max : "+max);
}
/**
* @desc 广播:监听COUNT_ACTION,获取索引值,并根据索引值来更新进度条
*
*/
private class CurrentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (COUNT_ACTION.equals(action)) {
int index = intent.getIntExtra("count", 0);
updateProgressBar(index);
}
}
}
}
四、AIDL服务(由其他进程通过IPC使用的服务)
AIDL(Android Interface Definition Language) IPC机制(interprocess communication)是面向对象的,轻量级的。通过AIDL定义的接口可以实现服务器端与客户端的IPC通信。在Android上,一个进程不能简单的像访问本进程内存一样访问其他进程的内存。所以,进程间想要对话,需要将对象拆解为操作系统可以理解的基本数据单元,并且有序的通过进程边界,这样底层操作系统才能跨应用程序边界整合它们。通过代码来实现这个数据传输过程是冗长乏味的,所幸的是android提供了AIDL工具来帮我们完成了此项工作。
1、定义AIDL接口
AIDL接口使用后缀名位.aidl的文件来定义,.aidl文件使用java语法编写,并且将该.aidl文件保存在 src/目录下(无论是服务端还是客户端都得保存同样的一份拷贝,也就是说只要是需要使用到该AIDL接口的应用程序都得在其src目录下拥有一份.aidl文件的拷贝)。
编译时,Android sdk 工具将会为 src/目录下的.aidl文件在 gen/ 目录下产生一个IBinder接口。服务端必须相应的实现该IBinder接口。客户端可以绑定该服务、调用其中的方法实现IPC通信。
创建一个用AIDL实现的服务端,需要以下几个步骤:
(1)创建.aidl文件:
该文件(.aidl)定义了客户端可用的方法和数据的接口
(2)实现这个接口:
Android SDK将会根据你的.aidl文件产生AIDL接口。生成的接口包含一个名为Stub的抽象内部类,该类声明了所有.aidl中描述的方法,你必须在代码里继承该Stub类并且实现.aidl中定义的方法。Stub类是Binder的子类,同时也是IplayControllor的实现类。
public static abstract class Stub extends android.os.Binder
implements com.tarena.aidl.IPlayControllor
{
public static com.test.aidl.IPlayControlor asInterface(android.os.IBinder obj)
}
(3)向客户端公开服务端的接口:
实现一个Service,并且在onBinder方法中返回第2步中实现的那个Stub类的子类(实现类)。
IplayControllor.aidl
package com.test.aidl;
interface IPlayControllor{
void play();
void pause();
void next();
void previous();
void seekTo(int seekTo);
}
2、服务端实现AIDL接口
在服务端里的服务程序里实现接口。这样,IBinder就是一个Stub类得对象,该对象为service提供了IPC接口,并将会向客户端公开,这样客户端就可以通过该对象与该service进行交互了。现在,如果客户端(比如一个Activity)调用bindService()来连接该服务端,客户端的onServiceConnected()回调函数将会获得从服务端的onBind()返回的IBinder对象
/**服务端*/
public class MyService extends Service {
//充当远程服务的实现,onBind方法要返回此类的实例
public class MyBinder extends IPlayControllor.Stub {
@Override
public void next() throws RemoteException {
Log.i("info", "播放下一首");
}
@Override
public void pause() throws RemoteException {
Log.i("info", "暂停播放");
}
@Override
public void play() throws RemoteException {
Log.i("info", "播放");
}
@Override
public void previous() throws RemoteException {
Log.i("info", "播放上一首");
}
@Override
public void seekTo(int seekTo) throws RemoteException {
Log.i("info", "跳转:" + seekTo);
}
}
@Override
public void onCreate() {
super.onCreate();
Log.i("info", "Myservice.onCreate");
}
@Override
public IBinder onBind(Intent intent) {
Log.i("info", "Myservice.onBind");
return new MyBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("info", "Myservice.onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("info", "Myservice.onDestroy");
}
}
3.客户端应用程序调用服务
客户端同样得访问该接口类,所以,如果服务端和客户端不在同一进程(应用程序)中,那么客户端也必须在 src/ 目录下拥有和服务端同样的一份.aidl文件的拷贝(同样是指,包名、类名、内容完全一模一样),客户端将会通过这个.aidl文件生成
android.os.Binder接口——以此来实现客户端访问AIDL中的方法。
当客户端在onServiceConnected()回调方法中获得IBinder对象后,必须通过调用IPlayControllor.Stub.asInterface(binder)将其转化成为IPlayControllor类型。
/**客户端*/
public class MainActivity extends Activity {
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//因为Stub类继承了Binder类,又是IPlayController的实现类
controllor = IPlayControllor.Stub.asInterface(binder);
}
};
private IPlayControllor controllor;
public void doClick(View v) {
try {
switch (v.getId()) {
case R.id.btnPlay:
controllor.play();
break;
case R.id.btnPause:
controllor.pause();
break;
case R.id.btnPrevious:
controllor.previous();
break;
case R.id.btnNext:
controllor.next();
break;
case R.id.btnSeekTo:
controllor.seekTo(10000);
break;
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//执行绑定操作
Intent intent = new Intent("com.tarena.action.MY_SERVICE");
bindService(intent, conn, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除绑定
unbindService(conn);
}
}