Android Service介绍
一 Service定义
Service是一种可以长期在后台运行而不提供界面的应用组件。服务又可以分为前台服务和后台服务。1.前台服务前台服务是指是能让用户感知到服务正在运行,因此服务必须显示通知。比如QQ音乐的播放,如果打开了播放控制里的状态栏显示,当你播放音乐时,状态栏里会显示你正在播放的音乐,再比如下载软件,也同样会在状态栏里显示下载进度。
2.后台服务后台服务与前台服务相反,用户通常感知不到后台服务正在运行。
注:前台服务的优先级很高,因此出现系统内存不足时系统也不会将前台服务回收,而后台服务在系统内存不足时有可能会被系统回收。
二 Service生命周期
1.service的生命周期
启动Service有个方法,startService()和bindService()。startService()对应关闭的服务的方法为stopSelf()和stopService(),bindService()对应关闭服务的方法为unbindService()。两种不同启动服务的方法也对应了两种不同的生命周期。如下图:
onStartCommand()返回值描述系统在终止服务后又重新启动服务时如何继续运行服务。
(1)START_NOT_STICKY:
onstartCommand()中返回START_NOT_STICKY后,服务被终止了,则除非有待传递的挂起intent,否则系统不会重建服务。这样可以避免在不必要时浪费系统资源。
(2)START_STICKY:onstartCommand()中返回START_STICKY后,服务被终止了,系统会重建服务,除非有待传递的挂起intent,否则系统会调用intent为null的onStartCommand()。
(3)START_REDELIVER_INTENT:onstartCommand()中返回START_REDELIVER_INTENT后,服务被终止了,系统会重建服务,且通过传递给服务的最后一个intent,使用该intent调用onStartCommand()。所有挂起的intent均依次传递。
当使用startService()开启服务时,即使开启服务的应用组件已经被销毁,服务也不会被结束。当使用bindService()绑定服务时,绑定的应用组件被销毁时,服务会被结束。
注:开启服务并不是只有这两种方式,还可以使用startService()启动服务之后,再使用bindService()绑定上该服务,此时调用stopSelf()或stopService()并不能结束服务,还需要调用unbindService(),服务才会真的被关闭。
2.前台服务的开启
如果android设备9.0及以上,需要在AndroidMAnifest.xml中添加FOREGROUND_SERVICE权限。因为前台服务必须显示通知,因此无论使用startService()还是bindService()开启服务,都需要再调用startForeground(),该服务才会显示通知。
注:开启前台服务还可以使用startForegroundService(),startForegroundService()与startService()相似,只不过使用startForegroundService()后,5s内必须调用startForeground(),否则会在logcat中报错。
前台服务显示通知的例子:
public class ForegroundService extends Service {
private final static String TAG = "ForegroundActivity";
public ForegroundService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
//通知渠道
String channelId = "ChannelId";
//Android8.0以上需要通过NotificationManager创建渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel(channelId,"消息推送", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
}
Notification notification = new NotificationCompat.Builder(this)
//添加渠道
.setChannelId(channelId)
//通知的小图标
.setSmallIcon(R.drawable.ic_launcher_background)
//通知的标题
.setContentTitle("This is title")
//通知的内容
.setContentText("This is content")
//通知的优先级
.setPriority(Notification.PRIORITY_DEFAULT)
.build();
startForeground(1,notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return null;
}
}
三 Service与应用组件通信
1.startService()
应用组件可使用intent与服务进行通信,但如果需要服务返回结果给应用组件,则可以可以使用广播,利用广播中的intent给应用组件返回结果。例子:
在Service中:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
//发送广播
Intent broadcastIntent = new Intent("com.stu.broastcast");
broadcastIntent.setPackage("com.homework.stu");
broadcastIntent.putExtra("current_time",System.currentTimeMillis());
Log.d(TAG, "onStartCommand: "+broadcastIntent.getLongExtra("current_time",0));
sendBroadcast(broadcastIntent);
return super.onStartCommand(intent, flags, startId);
}
在Activity中:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_foreground);
Intent intent = new Intent(this,ForegroundService.class);
Button button = findViewById(R.id.btn_startService);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//开启服务
startService(intent);
}
});
//广播注册
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("ForegroundActivity", "onReceive: "+intent.getLongExtra("current_time",0));
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.stu.broastcast");
//动态注册广播
registerReceiver(broadcastReceiver,intentFilter);
}
打印结果如下:1647764154.504 9657-9657/com.homework.stu D/ForegroundActivity: onStartCommand: 1647764154.504 9657-9657/com.homework.stu D/ForegroundActivity: onStartCommand: 16477641545041647764154.514 9657-9657/com.homework.stu D/ForegroundActivity: onReceive: 1647764154504
2.bindService()
使用bindService()绑定服务,主要使用binder进行通信。举个例子。模拟服务下载任务,并将下载进度实时显示在界面上。
界面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ForegroundActivity"
android:orientation="vertical">
<Button
android:id="@+id/btn_startService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击下载">
</Button>
<Button
android:id="@+id/btn_stopService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消下载">
</Button>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="100dp"
android:max="100"
android:layout_gravity="center"
style="@style/Widget.AppCompat.ProgressBar.Horizontal">
</ProgressBar>
</LinearLayout>
Service:
public class ForegroundService extends Service {
private final static String TAG = "ForegroundActivity";
/*
*下载进度
*/
private static int progress = 0;
/*
* 单线程池执行下载任务
*/
private ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor();
public ForegroundService() {
}
@Override
public void onCreate() {
super.onCreate();
//通知渠道
String channelId = "ChannelId";
//Android8.0以上需要通过NotificationManager添加渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel(channelId,"消息推送", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
}
Notification notification = new NotificationCompat.Builder(this)
//添加渠道
.setChannelId(channelId)
//通知的小图标
.setSmallIcon(R.drawable.ic_launcher_background)
//通知的标题
.setContentTitle("This is title")
//通知的内容
.setContentText("正在模拟下载...")
//通知的优先级
.setPriority(Notification.PRIORITY_DEFAULT)
.build();
startForeground(1,notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return new DownLoadBinder();
}
@Override
public void onDestroy() {
super.onDestroy();
//移除状态栏的通知
stopForeground(true);
}
public class DownLoadBinder extends Binder{
/*
*是否停止下载
*/
private boolean isStop = false;
public OnProgressListener onProgressListener;
/*
*开始下载
*/
public void startLoad() {
//模拟下载任务
mSingleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
while (progress<100 && (!isStop)){
//下载进度
progress++;
//监听下载进度
onProgressListener.progress(progress);
Log.d(TAG, "run: "+progress);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
/*
*停止下载
*/
public void stopLoad(){
//取消下载
isStop = true;
mSingleThreadExecutor.shutdownNow();
}
public void setOnProgressListener(OnProgressListener onProgressListener){
this.onProgressListener = onProgressListener;
}
}
/*
*下载进度接口
*/
public interface OnProgressListener{
void progress(int progress);
}
}
Activity:
public class ForegroundActivity extends AppCompatActivity {
private ForegroundService.DownLoadBinder mDownLoadBinder;
private ServiceConnection mServiceConnection ;
/*
* 进度条
*/
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_foreground);
Intent intent = new Intent(this,ForegroundService.class);
// startService(intent);
Button button = findViewById(R.id.btn_startService);
progressBar = findViewById(R.id.progressBar);
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mDownLoadBinder = (ForegroundService.DownLoadBinder) iBinder;
mDownLoadBinder.startLoad();
mDownLoadBinder.setOnProgressListener(new ForegroundService.OnProgressListener() {
@Override
public void progress(int progress) {
Log.d(“ForegroundActivity”, "progress: "+progress);
progressBar.setProgress(progress);
}
});
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mDownLoadBinder = null;
}
};
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//开启服务
bindService(intent,mServiceConnection,BIND_AUTO_CREATE);
}
});
Button cancel = findViewById(R.id.btn_stopService);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mDownLoadBinder.stopLoad();
unbindService(mServiceConnection);
}
});
}
}
四 扩展IntentService
因为service是长期运行在后台的,如果多次调用startService(),让service处理多个请求,很有可能出现ANR的情况,比如service中使用Thread.sleep()线程睡眠十几秒,点击按钮调用startService(),再次按钮时就会出现ANR,这是因为按钮超过时间没有响应。面对处理多个请求的问题,因此扩展出了IntentService。使用intentService的优点:
(1)创建工作队列,将所有的intent逐个传递给onHandleIntent()实现。
(2)处理完所有的intent后会自动停止服务,而不需要再去调用stopSelf()。
举个栗子:
点击一个按钮,在点击事件中调用startService(intent1),startService(intent2)
Activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_intent);
Button btn_intent1 = findViewById(R.id.btn_start_intent);
btn_intent1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent1 = new Intent(getBaseContext(),TestIntentService.class);
intent1.setAction(ACTION_TEST1);
Intent intent2 = new Intent(getBaseContext(),TestIntentService.class);
intent2.setAction(ACTION_TEST2);
startService(intent1);
startService(intent2);
}
});
}
IntentService:
public class TestIntentService extends IntentService {
private final static String TAG = "TestIntentService";
private static final String ACTION_TEST1= "com.homework.stu.action1";
private static final String ACTION_TEST2 = "com.homework.stu.action2";
public TestIntentService() {
super("TestIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_TEST1.equals(action)) {
handleAction1();
} else if (ACTION_TEST2.equals(action)) {
handleAction2();
}
}
}
private void handleAction1() {
Log.d(TAG, "handleAction1: "+System.currentTimeMillis());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void handleAction2() {
Log.d(TAG, "handleAction2: "+System.currentTimeMillis());
}
}
打印结果,先执行完intent1,再去执行intent2:
1647784083.991 20363-20420/com.homework.stu D/TestIntentService: handleAction1: 1647784083991
1647784088.992 20363-20420/com.homework.stu D/TestIntentService: handleAction2: 1647784088992