关于Service你一定要知道的
Service是Android四大组件之一,主要用于执行需要长时间运行的任务或者处理一些耗时的逻辑。Service可以在程序退出后,在后台继续运行。
一、Service的基本用法
启动Service的方法和启动Activity很类似,都需要借助Intent来实现,构建出一个Intent对象,调用startService()方法来启动Service。然后同样构建出了一个Intent对象,并调用stopService()方法来停Service。当启动一个Service的时候,会调用该Service中的onCreate()和onStartCommand()方法。
启动Service:
Intent startIntent =newIntent(this,MyService.class);
startService(startIntent);
关闭service:
Intent pauseIntent =newIntent(this,MyService.class);
stopService(pauseIntent);
onCreate()方法只会在Service第一次被创建的时候调用,如果当前Service已经被创建过了,不管怎样调用startService()方法,onCreate()方法都不会再执行,只会执行onStartCommand()方法。启动Service之后,就可以在onCreate()或onStartCommand()方法里去执行一些具体的逻辑了。
项目中的每一个Service都必须在AndroidManifest.xml中注册。
二、Service和Activity通信
Service中有一个onBind()方法,这个方法就是用来和Activity通信的。通常会新增了一个类继承自Binder类,然后在这个类里写我们的逻辑,再通过onBind()方法将这个类的实例返回到activity中。因此service类应该是这样的:
public class MyService extends Service {
public static final String TAG ="MyService";
private MyBinder mBinder =new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"onCreate() executed");
}
@Override
public int onStartCommand(Intent intent,int flags,int startId) {
Log.i(TAG,"onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG,"onDestroy() executed");
}
@Override
publicI Binder onBind(Intent intent) {
return mBinder;
}
class MyBinder extends Binder {
public void startDownload() {
Log.i("TAG","startDownload() executed");
}
}
}
在activity里与service关联上,首先创建一个ServiceConnection的匿名类,在里面重写了onServiceConnected()方法和onServiceDisconnected()方法,这两个方法会在Activity与Service建立关联和解除关联的时候调用。在onServiceConnected()方法中,通过向下转型得到了MyBinder的实例,即我们在service里返回的Binder的子类的实例。在Activity中根据具体的场景来调用Binder的子类中的任何public方法,即Activitys可以指挥Service干什么Service就去干什么。代码:
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
}
};
Activity和Service的关联由bindService()方法来完成。构建出一个Intent对象,然后调用bindService()方法将Activity和Service进行关联。bindService()方法接收三个参数,第一个参数是Intent对象,第二个参数ServiceConnection的实例,第三个参数是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service,这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。
关联:
Intent bindIntent =newIntent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
解除关联:
unbindService(connection);
注意,任何一个Service在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity建立关联,还可以和任何一个Activity建立关联,而且在建立关联时它们都可以获取到相同的MyBinder实例。
三、销毁Service
stopService()只会让Service停止,unbindService只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。
四、Service和Thread的关系
Service和Thread之间没有任何关系!Thread是用来开启一个子线程,在Thread里执行一些耗时操作就不会阻塞主线程的运行。实际上,Service是运行在主线程里的。可以这样检验:
在activity里打印当前线程编号:
Log.i("MyService","MainActivity thread id is "+ Thread.currentThread().getId());
在service里打印当前service的线程编号:
Log.i("MyService","MyService thread id is "+ Thread.currentThread().getId());
Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,前面不是刚刚验证过Service是运行在主线程里的么?在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。
一个比较标准的Service就可以写成:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 开始执行后台任务
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
class MyBinder extends Binder {
public void startDownload() {
new Thread(new Runnable() {
@Override
public void run() {
// 执行具体的下载任务
}
}).start();
}
}
五、创建前台Service
Service几乎都是在后台运行的。因为Service的系统优先级是比较低的,当系统出现内存不足时,就有可能会回收掉正在后台运行的Service。如果希望Service一直保持运行状态,而不会由于系统内存不足的原因导致被回收,可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。方法就是在service里设置一下:
public class MyService extends Service {
public static final String TAG = "MyService";
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
// 在API11之后构建Notification的方式
Notification.Builder builder = new Notification.Builder
(this.getApplicationContext()); //获取一个Notification构造器
Intent nfIntent = new Intent(this, MainActivity.class);
builder.setContentIntent(PendingIntent.
getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_large)) // 设置下拉列表中的图标(大图标)
.setContentTitle("下拉列表中的Title") // 设置下拉列表里的标题
.setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
.setContentText("要显示的内容") // 设置上下文内容
.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
Notification notification = builder.build(); // 获取构建好的Notification
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
startForeground(1, notification);
Log.d(TAG, "onCreate() executed");
}
.........
}
首先在MyService的onCreate()方法中创建了一个Notification对象,然后调用了它的setLatestEventInfo()方法来为通知初始化布局和数据,并在这里设置了点击通知后就打开MainActivity。然后调用startForeground()方法就可以让MyService变成一个前台Service,并会将通知的图片显示出来。