服务,是Android四大组件之一, 属于 计算型组件
提供 需在后台长期运行的服务,如:复杂计算、音乐播放、下载等
无用户界面、在后台运行、生命周期长
一个运行在后台执行长时间运行的操作组件,它不提供任何用户界面,作为与Activity同级的组件,它依旧是运行在主线程中(由于是在主线程,所以需开一个线程来执行耗时操作)。
一个组件还可以与一个Service进行绑定来实现组件之间的交互,甚至可以执行IPC(Inter-Process Communication)进程间通信。
Service可以在后台执行很多任务,比如处理网络事务,播放音乐,文件读写或者与一个内容提供者交互,等等。
1、4个手动调用方法
startService() 启动服务
stopService() 关闭服务
bindService() 绑定服务
unbindService() 解绑服务
2、5个内部自动调用方法
onCreat() 创建服务
onStartCommand() 开始服务
onDestroy() 销毁服务
onBind() 绑定服务
onUnbind() 解绑服务
(1)作用:启动Service服务
(2)自动调用方法:onCreate()、onStartCommand()
a)一个Service被sartService多次启用,onCreate()只会调用一次,onStartCommand()可以多次调用(=startService()调用次数)
b)onStartCommand()必须返回一个整数=描述系统因异常(1.内存不足2.进程关闭等)在杀死onStartCommand()后的服务后应该如何继续运行
public class MyService extends Service {
private static final String TAG = MyService.class.getSimpleName();
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.e(TAG, "postDelayed");
// 制造异常,kill 掉该 Service
int a = 1 / 0;
}
}, 3000L);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand, intent: " + intent + ", startId: " + startId);
return Service.START_STICKY_COMPATIBILITY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
super.onDestroy();
}
}
测试
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(MainActivity.this, MyService.class));
}
}
测试结果:
1、使用 START_STICKY 作为返回值
// 服务重新创建并重启,但是 intent 对象被置空了
03-22 20:43:42.199 11233-11233/com.example.test E/MyService: onCreate
03-22 20:43:42.209 11233-11233/com.example.test E/MyService: onStartCommand, intent: null, startId: 2
03-22 20:43:45.209 11233-11233/com.example.test E/MyService: postDelayed
2、使用 START_NOT_STICKY 作为返回值
// 服务没有重新创建
3、使用 START_REDELIVER_INTENT 作为返回值
// 服务重新创建并启动,intent 对象没有被置空
03-22 20:46:00.879 14792-14792/com.example.test E/MyService: onCreate
03-22 20:46:00.879 14792-14792/com.example.test E/MyService: onStartCommand, intent: Intent { cmp=com.example.test/.MyService }, startId: 1
03-22 20:46:03.889 14792-14792/com.example.test E/MyService: postDelayed
(1)作用:关闭Service服务
(a)未启动的服务(本身关闭)(b)启动&绑定&未解绑的服务无法调用onDestroy()——>无法关闭服务
(2)自动调用的方法:onDestroy()
(3)示意图:
(1)作用:绑定Service服务
(2)自动调用的方法:onCreate()、onBind()
(3)示意图:
(1)作用:解绑Service服务
(2)自动调用的方法:onUnbind()、onDestroy()
(3)示意图:
(1)流程图:
(2)备注:
1、一个Service的onCreate()只调用一次(只有一个Service实例),onStartCommand()调用次数=startService调用次数
2、startService与stopService配对(必须通过stopService关闭Service)
3、只使用startService(),无法与Activity交互,绑定者无法操作Service
(1)流程图:
(2)备注:
1、一个Service的onCreate()只调用一次(只有一个Service实例),onBind()调用次数=BindService调用次数,多个绑定者可以绑定到同一个服务上
2、bindService与unbindService配对,当该Service所有绑定者解绑后,系统会自动销毁服务(不需手动stop)
3、bindService能让Activity与Service交互,绑定者通过一个iBinder接口与服务通信
(1)流程图:
(2)备注:
1、关于操作Service:
(1)startService()、stopService()只能开启&关闭Service,但无法操作Service
(2)bindService()、unbindService()除了绑定Service还能操作Service
2、关于Service销毁:
(1)startService()开启的Service,绑定者退出后Service仍存在
(2)bindService()开启的Service,绑定者退出后,Service随着调用者退出销毁
1、按运行地点分(1)本地服务(2)远程服务
2、按运行类型分(1)前台服务(2)后台服务
3、按功能分(1)可通信服务(2)不可通信服务
1、特点:(1)运行在主线程(2)主线程被终止后,服务也会终止(3)节约资源,且不需要AIDL/IPC,简便
2、场景:(最常用)需要依附某个进程,不需要常驻的服务,如音乐播放
3、使用:
步骤1:新建子类继承Service类——需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()方法
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
System.out.println("执行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("执行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("执行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
步骤2:构建用于启动Service的Intent对象
步骤3:调用startService()启动Service、调用stopService()停止服务
//构建启动服务的Intent对象
Intent startIntent = new Intent(this, MyService.class);
//调用startService()方法-传入Intent对象,以此启动服务
startService(startIntent);
//构建停止服务的Intent对象
Intent stopIntent = new Intent(this, MyService.class);
//调用stopService()方法-传入Intent对象,以此停止服务
stopService(stopIntent);
步骤4:在AndroidManifest.xml里注册Service
2.1)特点:
2.2)应用场景:系统级别服务(常驻),多个应用程序共享同一个后台服务(跨进程通信)
2.3)远程服务Service(含AIDL&IPC讲解)
2.4)IntentService用法&源码(客户端远程调用服务器端的远程Service)
1.AIDL与IPC(使用AIDL进行IPC)
IPC:Inter-Process Communication,即跨进程通信
AIDL:Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
2.多进程通信中2个角色:
服务端:
步骤1:新建定义AIDL文件,并声明该服务需要向客户端提供的接口
// 在新建的AIDL_Service1.aidl里声明需要与Activity进行通信的方法
package scut.carson_ho.demo_service;
interface AIDL_Service1 {
void AIDL_Service();
}
步骤2:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()、blabla)
public class MyService extends Service {
// 实例化AIDL的Stub类(Binder的子类)
AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {
//重写接口里定义的方法,用于客户端调用
@Override
public void AIDL_Service() throws RemoteException {
System.out.println("客户端通过AIDL与远程后台成功通信");
}
};
//重写与Service生命周期的相关方法
@Override
public void onCreate() {
super.onCreate();
System.out.println("执行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("执行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("执行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("执行了onBind()");
//在onBind()返回继承自Binder的Stub类型的Binder,非常重要
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("执行了onUnbind()");
return super.onUnbind(intent);
}
}
步骤3:在AndroidMainfest.xml中注册服务 & 声明为远程服务
//该Service可以响应带有scut.carson_ho.service_server.AIDL_Service1这个action的Intent。
//此处Intent的action必须写成“服务器端包名.aidl文件名”
客户端:
步骤1:拷贝服务端的AIDL文件到客户端目录下,并进行编译
步骤2:使用Stub.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法
//定义aidl接口变量
private AIDL_Service1 mAIDL_Service;
//创建ServiceConnection的匿名类
private ServiceConnection connection = new ServiceConnection() {
//重写onServiceConnected()方法和onServiceDisconnected()方法
//在Activity与Service建立关联和解除关联的时候调用
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//使用AIDLService1.Stub.asInterface()方法获取服务器端返回的IBinder对象
//将IBinder对象传换成了mAIDL_Service接口对象
mAIDL_Service = AIDL_Service1.Stub.asInterface(service);
try {
//通过该对象调用在MyAIDLService.aidl文件中定义的接口方法,从而实现跨进程通信(客户端调用服务端方法)
mAIDL_Service.AIDL_Service();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
步骤3:通过Intent指定服务端的服务名称和所在包,绑定远程Service
//通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
//参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
Intent intent = new Intent("scut.carson_ho.service_server.AIDL_Service1");
//Android5.0后无法只通过隐式Intent绑定远程Service
//需要通过setPackage()方法指定包名
intent.setPackage("scut.carson_ho.service_server");
//绑定服务,传入intent和ServiceConnection对象
bindService(intent, connection, Context.BIND_AUTO_CREATE);
1.1)特点:在通知栏显示通知(用户可以看到)
1.2)应用场景:服务使用时需要让用户知道并进行相关操作,如音乐播放服务(服务被终止时,通知栏的通知也会消失)
1.3)使用方法:
Service类onCreate()增加通知栏显示功能
//添加下列代码将后台Service变成前台Service
//构建"点击通知后打开MainActivity"的Intent对象
Intent notificationIntent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notificationIntent,0);
//新建Builer对象
Notification.Builder builer = new Notification.Builder(this);
builer.setContentTitle("前台服务通知的标题");//设置通知的标题
builer.setContentText("前台服务通知的内容");//设置通知的内容
builer.setSmallIcon(R.mipmap.ic_launcher);//设置通知的图标
builer.setContentIntent(pendingIntent);//设置点击通知后的操作
Notification notification = builer.getNotification();//将Builder对象转变成普通的notification
startForeground(1, notification);//让Service变成前台Service,并在系统的状态栏显示出来
后台:后台任务运行完全不依赖UI,即时Activity被销毁/程序被关闭,只要进程还在,后台任务就可继续运行
2.1)特点:处于后台的服务(用户无法看到)
2.2)应用场景:服务使用时不需要让用户知道并进行相关操作,如天气更新,日期同步(服务被终止时,用户是无法知道的)
1.1)特点:a)用bindServiceb)调用者退出后,随调用者销毁c)只有绑定Service服务(Binder类、bindService()、onBind()、unbindService()、onUnbind())才能与Activity通信
1.2)应用场景:后台服务需要与Activity进行通信
1.3)使用方法:
步骤1:在新建子类继承Service类,并新建一个子类继承自Binder类、写入与Activity关联需要的方法、创建实例
public class MyService extends Service {
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
System.out.println("执行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("执行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("执行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("执行了onBind()");
//返回实例
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("执行了onUnbind()");
return super.onUnbind(intent);
}
//新建一个子类继承自Binder类
class MyBinder extends Binder {
public void service_connect_Activity() {
System.out.println("Service关联了Activity,并在Activity执行了Service的方法");
}
}
}
步骤2:在Activity通过调用MyBinder类中的public方法来实现Activity与Service的联系(Activity指挥Service执行的功能)
1、构建绑定服务的Intent对象
//构建绑定服务的Intent对象
Intent bindIntent = new Intent(this, MyService.class);
//调用bindService()方法,以此停止服务
bindService(bindIntent,connection,BIND_AUTO_CREATE);
//参数说明
//第一个参数:Intent对象
//第二个参数:上面创建的Serviceconnection实例
//第三个参数:标志位
//这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service
//这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行
//调用unbindService()解绑服务
//参数是上面创建的Serviceconnection实例
unbindService(connection);
2、创建ServiceConnection匿名类,,在Activity和Service建立和解除关联时调用
//创建ServiceConnection的匿名类
private ServiceConnection connection = new ServiceConnection() {
//重写onServiceConnected()方法和onServiceDisconnected()方法
//在Activity与Service建立关联和解除关联的时候调用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//在Activity与Service解除关联的时候调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//实例化Service的内部类myBinder
//通过向下转型得到了MyBinder的实例
myBinder = (MyService.MyBinder) service;
//在Activity调用Service类的方法
myBinder.service_connect_Activity();
}
};
2.1)特点:a)用startService启动b)调用者退出后,随调用者销毁
2.2)应用场景:该后台服务不进行任何通信
1.本地服务(普通服务、不可通信服务)
2.可通信服务
3.前台服务
4.远程服务
1、android:name: 服务类名。可以是完整的包名+类名。也可使用. 代替包名。
2、android:exported: 其他应用能否访问该服务,如果不能,则只有本应用或有相同用户ID的应用能访问。默认为false。
3、android:process: 服务所运行的进程名。默认是在当前进程下运行,与包名一致。
4、android:permission: 申请使用该服务的权限,如果没有配置下相关权限,服务将不执行
Service运行在主线程(不能处理耗时操作,否则会出现ANR),Thread运行在工作线程
Service在进程运行
Thread在Activity运行
Service是用于后台服务的,保证程序挂到后台时某些组件仍能正常工作。然而Service不是独立的进程,是默认运行在主线程中的。所以如果直接在服务里去处理一些耗时的逻辑,就很容易出现ANR(Application Not Responding)的情况。
所以这个时候就需要用到Android多线程编程的技术了,我们可以在服务内开启线程,采用thread+handler方式处理耗时操作。但服务一旦启动,就会一直处于运行状态,必须调用stopSelf()/stopService()才能让服务停止。编写逻辑较复杂。
我们可以引用IntentService。
IntentService是继承Service的,那么它包含了Service的全部特性,当然也包含service的生命周期,那么与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去执行耗时操作。
IntentService是一个通过Context.startService(Intent)启动可以处理异步请求的Service,使用时你只需要继承IntentService和重写其中的onHandleIntent(Intent)方法接收一个Intent对象,工作完成后会自动停止服务。所有的请求的处理都在一个工作线程中完成,它们会交替执行(但不会阻塞主线程的执行),一次只能执行一个请求。
这是一个基于消息的服务,每次启动该服务并不是马上处理你的工作,而是首先会创建对应的Looper,Handler并且在MessageQueue中添加的附带客户Intent的Message对象,当Looper发现有Message的时候接着得到Intent对象通过在onHandleIntent((Intent)msg.obj)中调用你的处理程序,处理完后即会停止自己的服务,意思是Intent的生命周期跟你的处理的任务是一致的,所以这个类用下载任务中非常好,下载任务结束后服务自身就会结束退出。
1、提供一个无参的构造函数,并且必须在其内部调用父类的有参构造函数。
2、在其子类中去实现onHandlerIntent()这个抽象方法,在这个方法中可以去处理一些具体的逻辑。(用于处理耗时操作,该方法在子线程中执行)
public class MyIntentService extends IntentService{
public MyIntentService(){
super("MyIntentService"); //调用父类的有参构造函数
}
@Override
protected void onHandlerIntent(Intent intent){
//打印当前线程的id
Log.d("MyIntentService","Thread id is"+ Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService","onDestroy executed");//运行结束后服务自动停止
}
}
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.id.activity_main);
Button startIntentService=(Button)findViewById(R.id.start_intent_service);
startIntentService.setOnClickListener(this);
}
@Override
public void onClick(View v){
switch(v.getId()) {
...
case R.id.start_intent_service:
//打印主线程的id
Log.d("MainActivity","Thread id is"+Thread.currentThread().getId());
Intent intentService=new Intent(this,MyIntentService.class);
startService(intentService);//启动服务
break;
default:
break;
}
}
}
IntentService是继承并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统的Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们手动去控制或stopSelf()。
另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。它本质上就是一个封装了HandlerThread+Handler的异步框架
当Activity通过调用bindService(Intent service, ServiceConnection conn,int flags),我们可以得到一个Service的一个对象实例,然后我们就可以访问Service中的方法,同时利用回调接口实现Service中进度变化主动通知Activity更新UI
(1)新建一个回调接口,通过回调接口实现当Service中进度发生变化主动通知Activity更新UI
(回调接口的理解见:一个经典例子让你彻彻底底理解java回调机制)
public interface OnProgressListener {
void onProgress(int progress);
}
(2)新建一个Service类
public class MsgService extends Service {
public static final int MAX_PROGRESS = 100;//进度条最大值
private int progress = 0;//进度条进度值
private OnProgressListener onProgressListener;//更新进度的回调接口
public void setOnProgressListener(OnProgressListener onProgressListener) {
//注册回调接口的方法,供外部调用
this.onProgressListener = onProgressListener;
}
public int getProgress() {
//增加get()方法,供Activity调用,返回progress
return progress;
}
/**
* 模拟下载任务,每秒钟更新一次
*/
public void startDownLoad(){
new Thread(new Runnable() {
@Override
public void run() {
while(progress < MAX_PROGRESS){
progress += 5;
//进度发生变化通知调用方
if(onProgressListener != null){
onProgressListener.onProgress(progress);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
@Override
public IBinder onBind(Intent intent) {
return new MsgBinder();//返回Binder对象
}
public class MsgBinder extends Binder{
public MsgService getService(){
return MsgService.this;//返回当前service对象
}
}
}
(3)新建一个ServiceConnection对象,它是一个接口,Activity与Service绑定后,在onServiceConnected回调方法中返回服务对象。
onServiceConnected用于执行Activity与Service绑定后执行相关操作。
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//返回一个MsgService对象
msgService = ((MsgService.MsgBinder)service).getService();
}
};
(4)Activity代码
public class MainActivity extends Activity {
private MsgService msgService;
private ProgressBar mProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//绑定Service
Intent intent = new Intent("com.example.communication.MSG_ACTION");
bindService(intent, conn, Context.BIND_AUTO_CREATE);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
Button mButton = (Button) findViewById(R.id.button1);
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//开始下载
msgService.startDownLoad();
}
});
}
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//返回一个MsgService对象
msgService = ((MsgService.MsgBinder)service).getService();
//注册回调接口来接收下载进度的变化
msgService.setOnProgressListener(new OnProgressListener() {
@Override
public void onProgress(int progress) {
mProgressBar.setProgress(progress);
}
});
}
};
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
}
利用系统的LocalBroadcastManager,Service send message, Activity receive message;Service向Activity发送消息,可以使用广播,当然Activity要注册相应的接收器。适用于Service要向多个Activity发送同样的消息。
当进度变化时发送一条广播,然后在Activity的注册广播接收器,接受广播后更新progressbar
public class MainActivity extends Activity {
private ProgressBar mProgressBar;
private Intent mIntent;
private MsgReceiver msgReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//动态注册广播接收器
msgReceiver = new MsgReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.example.communication.RECEIVER");
registerReceiver(msgReceiver, intentFilter);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
Button mButton = (Button) findViewById(R.id.button1);
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//启动服务
mIntent = new Intent("com.example.communication.MSG_ACTION");
startService(mIntent);
}
});
}
@Override
protected void onDestroy() {
//停止服务
stopService(mIntent);
//注销广播
unregisterReceiver(msgReceiver);
super.onDestroy();
}
/**
* 广播接收器
* @author len
*
*/
public class MsgReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//拿到进度,更新UI
int progress = intent.getIntExtra("progress", 0);
mProgressBar.setProgress(progress);
}
}
}
public class MsgService extends Service {
/**
* 进度条的最大值
*/
public static final int MAX_PROGRESS = 100;
/**
* 进度条的进度值
*/
private int progress = 0;
private Intent intent = new Intent("com.example.communication.RECEIVER");
/**
* 模拟下载任务,每秒钟更新一次
*/
public void startDownLoad(){
new Thread(new Runnable() {
@Override
public void run() {
while(progress < MAX_PROGRESS){
progress += 5;
//发送Action为com.example.communication.RECEIVER的广播
intent.putExtra("progress", progress);
sendBroadcast(intent);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startDownLoad();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Service 持有 Activity的Handler 对象,Service 通过往该Handler send message 的方式,达到通信的目的。
利用反射或者注释的方式实现对注册类的注册,然后遍历当前的注册表,通过key进行查询,然后dispatch,调用相应的事件处理方法。(不同的开源框架实现有所区别)