android 四大组件之一,提供在后台运行的服务,属于计算型组件。
在后台运行,无用户界面,生命周期长。
不与Activity绑定,启动之后会无限期的运行下去,除非遇到内存低情况被回收,需要调用stopService或stopSelf才会停止。
与Activity绑定,绑定之后在后台运行,除非调用unBindService或绑定的Context被销毁。
**生命周期:**onCreate(只执行一次)-onBind-onUnbind-onDestory ,如果先调用了startservice,已经onCreate,也不会再次调用。
**操作:**创建一个Binder继承Binder,通过onBind返回Binder对象,在context中通过serviceConnection取到binder对象并调用bindner的方法,bindService中传入ServiceConnection建立连接。
//在service中自定义Binder
class MyBinder extends Binder{
public void startDownload(){
Log.d(TAG, "startDownload: ");
}
}
//在onBind方法中返回Binder
private MyBinder myBinder = new MyBinder();
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
// TODO: Return the communication channel to the service.
return myBinder;
}
//在Activity 中创建serviceconnection
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
//绑定服务
bindService(new Intent(MainActivity.this,MyService.class),connection,BIND_AUTO_CREATE);
除了可以开启或停止服务,还可以获得Service对象,对Service进行操作。
调用者退出后Service随着调用者退出而销毁。
如果需要service一直保持运行状态(service保活),则可以考虑前台service。效果类似于通知。在service的onStartCommand方法中修改.(需要添加FOREGROUND_SERVICE权限)
//8.0 适配通知栏
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel("service","test", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(channel);
NotificationCompat.Builder service = new NotificationCompat.Builder(this, "service");
service.setContentTitle("执行前台服务的通知");
service.setContentText("执行前台服务的内容");
service.setSmallIcon(R.mipmap.ic_launcher);
notification = service.getNotification();
}else {
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentTitle("执行前台服务的通知");
builder.setContentText("执行前台服务的内容");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentIntent(pendingIntent);
notification = builder.getNotification();
}
//设置为前台服务:参数1:唯一的通知标识。参数二:通知
startForeground(1,notification);
两者无联系。虽然都是在后台执行一下耗时的操作,但是service是运行在主线程的,Thread是开启的子线程运行。
在注册服务的地方添加属性:
android:process=":remote"
远程服务是 运行在另一个进程。此时服务需要与Activity绑定的话需要使用AIDL。
同一个工程下使用:
创建一个AIDL文件,在此文件中定义方法,构建项目会自动生成一个接口文件。改文件是IBinder的子类。
在service文件获取该子类,并在onBinder方法返回。(进程S)
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
// TODO: Return the communication channel to the service.
return mBinder;
}
IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public String toUppercase(String aString) throws RemoteException {
if (!TextUtils.isEmpty(aString)){
return aString.toUpperCase();
}
return null;
}
};
在activity中(进程C)修改serviceconnection
private IMyAidlInterface iMyAidlInterface; //是IBinder的子类 ,AIDL ,mainactivity 和myservice 是不同的进程,此时实现了跨进程通信
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
try {
String hello_world = iMyAidlInterface.toUppercase("hello world");
Log.d(TAG, "onServiceConnected: "+hello_world);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
因此实现了跨进程通信。
不同工程下使用:
把AIDL文件和Activity的内容移至另一个工程即可。使用隐式跳转。在service中添加过滤器,在
Intent intent = new Intent(定义的action);
bindService(intent, connection, BIND_AUTO_CREATE);
Binder实现IBinder接口,Android 中实现跨进程通信的机制。
为了数据的独立性和安全性,一个进程不能访问另一个进程的数据,即Android的进程是相互独立、隔离的。如果需要读取另一个进程的数据就需要IPC机制。
传统的跨进程(socket)通信缺点:1)复制两次,费时间。 2)接收数据的缓存有接收方提供,但接收方不知道需要提供多大合适。
而Binder机制调用系统函数mmap()内存映射,只需要复制一次即可。
利用Binder驱动创建接收缓存区并实现地址映射关系:根据需映射的接收进程信息,实现内核缓存区和接收进程用户空间地址同时映射到同1个共享接收缓存区中。
当调用IntentService时,会回调到onCreate方法中,在该方法中创建了一个HandleThread,并启动这个线程,也创建了一个servicehandler。
HandleThread,继承于Thread类,是一个线程,当线程处于执行状态时,创建了一个looper,并调用loop方法开始无限消息轮询过程。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//....省略部分代码....
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
//核心代码
@Override
public void run() {
mTid = Process.myTid();
//创建looper
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
//开始无限循环消息
Looper.loop();
mTid = -1;
}
//....省略部分代码....
}
创建了一个servicehandler处理消息
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//调用onHandleIntent
onHandleIntent((Intent)msg.obj);
//并停止服务---因此不用手动停止服务
stopSelf(msg.arg1);
}
}
接着调用onStartCommand 喝onStart,在自定义的intentservice中不用重写这两个方法。
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
//获取到intent,调用handler的发送消息
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
发送消息之后,loop轮询到消息并分发给目标处理消息,调用onHandleIntent.是一个抽象方法,必须重写处理耗时任务。
启动intent service不需要创建新的线程?
在onCreate方法里创建 了HandlerThread,这是一个继承自Thread的类,在onCreate中也获取了Looper进行工作。本来有一个线程,无需创建线程。
为什么不建议通过 bindService() 启动 IntentService?
intent service源码中的onBind方法默认返回null,不会回调到onHanldeIntent方法中,没有使用到intent service的优点,与普通service无区别。
为什么多次启动 IntentService 会顺序执行事件,停止服务后,后续的事件得不到执行?
内部使用的是handler机制,多次启动intent service不会重新创建新的线程和服务,而是把消息加到消息队列里,消息队列是一个单链表,消息入列时的操作是根据时间入列,所以会按顺序执行。停止服务后,会将消息队列的消息清空,因此后续的事件得不到执行。
如何实现让IntentService任务可以并行执行?
创建多个intent service。
demo地址祥见:github:https://github.com/MarinaTsang/sqliteAndContentprovider/tree/master/sevicetest