Service 服务
线程Thread和服务Service
Server
首先抛出一个问题,服务和线程之间的异同?
在官方文档的解析如下:
A service is simply a component that can run in the background, even when the user is not interacting with your application, so you should create a service only if that is what you need.
If you must perform work outside of your main thread, but only while the user is interacting with your application, you should instead create a new thread in the context of another application component. For example, if you want to play some music, but only while your activity is running, you might create a thread in onCreate(), start running it in onStart(), and stop it in onStop(). Also consider using thread pools and executors from the java.util.concurrent package or Kotlin coroutines instead of the traditional Thread class. See the Threading on Android document for more information about moving execution to background threads.
Remember that if you do use a service, it still runs in your application’s main thread by default, so you should still create a new thread within the service if it performs intensive or blocking operations.
简单的说:
- 服务是不需要跟用户进行交互的。
- 线程在用户需要短暂进行交互的情况下进行使用,官方推荐使用新的线程方式例如
concurrent
和kotlin的方法构建。 - 服务是默认基于主线程的,可以考虑创建一个线程维系高密度和容易进行操作阻碍的服务。
Manifest中记得申明Service
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
- 设置属性,该服务仅本app可用!(一般情况下的一些服务基本上都是不对外共享的)
You can ensure that your service is available to only your app by including the
android:exported
attribute and setting it tofalse
.
- 确保安全性,使用服务启动的时候应该
explicit
明确性强的方式启动,不需要过滤器filter进行筛选。
To ensure that your app is secure, always use an explicit intent when starting a Service and don’t declare intent filters for your services.
创建一个服务
创建一个class必须继承Service才能实现服务的功能,
在这里主要是以下的几个方法:
onStartCommand
和onBind
(这个主要是根activity这些component进行交互的方法)
示例的代码如下:
package com.chris.servicepractice;
import android.app.Activity;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class MyService extends Service {
private static final String TAG = "MyService";
private MyBinder myBinder = new MyBinder();
public MyService() {
}
//这个是必须实现的
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return myBinder;
}
//服务初次构建,只会启动一起,即构建服务的时候
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
//正式启动服务,每一次启动都会调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
Toast.makeText(this, "startCommand", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
//服务消亡
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
super.onDestroy();
}
//和Activity进行交互的一个Binder,在这里可以自己编写相关的操作
public class MyBinder extends Binder{
void startDownload(){
Log.d(TAG, "startDownload");
}
}
}
在Aactivity调用;
//启动服务
Intent startServiceIntent = new Intent(this,MyService.class);
startService(startServiceIntent);
If the service doesn’t also provide binding, the intent that is delivered with startService() is the only mode of communication between the application component and the service.
官方文档原话大意:如果没有实现onBind
那么就这有这个on StartCommand是Activity唯一和Service交互的渠道
- 此外,启动服务的多个请求会导致对服务的onStartCommand()的多个相应调用。但是,停止服务只需要一个停止服务的请求(使用stopSelf()或stopService())。
关闭服务
关闭的方法主要是自己关闭和component关闭;
stopSelf(StartId)
这里的startId是正式调用中的参数,为了避免终其他正在调用该服务时被误停止,所以用一个id进行管理维系;stopService
活动Activity等组件进行关闭。
在进行高效的资源管理下,最好记得服务在不用的时候进行stop,避免浪费!
切记切记:服务是默认在主线程进行的,大消耗的情况下最好再开一个线程,要不容易出现ANR无响应的情况
The Service class is the base class for all services. When you extend this class, it’s important to create a new thread in which the service can complete all of its work; the service uses your application’s main thread by default, which can slow the performance of any activity that your application is running.
组件捆绑和解绑服务
bind和unbind和前面开启关闭服务是大不相同的。
bindService
绑定服务
api源码如下:
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
throw new RuntimeException("Stub!");
}
- 构建一个已实现
Ibinder
的对象。也可直接基础Binder
- 绑定
- 调用该对象才可进行交互,注意⚠️ 直接绑定是不会触发到onStartCommand
unbindServic
解绑服务
api源码
public void unbindService(ServiceConnection conn) {
throw new RuntimeException("Stub!");
}
解绑捆绑都绕不开的
ServiceConnection
和服务进行连接的对象
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package android.content;
import android.os.IBinder;
public interface ServiceConnection {
void onServiceConnected(ComponentName var1, IBinder var2);
void onServiceDisconnected(ComponentName var1);
default void onBindingDied(ComponentName name) {
throw new RuntimeException("Stub!");
}
default void onNullBinding(ComponentName name) {
throw new RuntimeException("Stub!");
}
}
Notification
官方文档推荐
When a service is running, it can notify the user of events using Toast Notifications or Status Bar Notifications.
使用notfication进行交互,利于用户清晰明白服务状态!
可以参考以下的代码,避坑
//NOTIFICATION
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//添加上一个前台服务的通知栏
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, FLAG_UPDATE_CURRENT);
Notification notification = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("notificationID", "chris", NotificationManager.IMPORTANCE_HIGH);
manager.createNotificationChannel(channel);
notification = new Notification.Builder(this,"notificationID")
.setContentText("MyService")
.setContentTitle("This is Content Title")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
}
startForeground(1, notification);
生命周期
多说无益,附上代码吧。
示例代码
视图xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Service" />
<Button
android:id="@+id/stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service" />
<Button
android:id="@+id/bind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Bind Service" />
<Button
android:id="@+id/unbind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Unbind Service"
/>
</LinearLayout>
MainActivity
package com.chris.servicepractice;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
/**
* 在这里主要是简单地了解和使用Service这个四大组件之一
* 基本是基于郭霖大神写的书籍以及google开发文档中关于Service的描述
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button startService;
private Button stopService;
private Button bindService;
private Button unbindService;
private MyService.MyBinder myBinder;
//由于Activity要和Service进行相关的交互,这样才能通过活动得知服务的操作
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//服务连接上之后的操作
myBinder = (MyService.MyBinder) iBinder;
myBinder.startDownload();//模拟方法
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService = findViewById(R.id.start_service);
stopService = findViewById(R.id.stop_service);
bindService = findViewById(R.id.bind_service);
unbindService = findViewById(R.id.unbind_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch(view.getId()) {
case R.id.start_service:
//启动服务
Intent startServiceIntent = new Intent(this,MyService.class);
startService(startServiceIntent);
break;
case R.id.stop_service:
//关闭服务
Intent stopServiceIntent = new Intent(this,MyService.class);
stopService(stopServiceIntent);
break;
case R.id.bind_service:
Intent bindServiceIntent = new Intent(this,MyService.class);
bindService(bindServiceIntent,serviceConnection,BIND_AUTO_CREATE);//最后一个参数具体的意义,我还不是很清楚
break;
case R.id.unbind_service:
unbindService(serviceConnection);
break;
}
}
}
MyService 服务类
package com.chris.servicepractice;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static java.lang.Thread.sleep;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
public class MyService extends Service {
private static final String TAG = "MyService";
private MyBinder myBinder = new MyBinder();
public MyService() {
}
//这个是必须实现的
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
// throw new UnsupportedOperationException("Not yet implemented");
return myBinder;
}
//服务初次构建,只会启动一起,即构建服务的时候
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
//NOTIFICATION
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//添加上一个前台服务的通知栏
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, FLAG_UPDATE_CURRENT);
Notification notification = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("notificationID", "chris", NotificationManager.IMPORTANCE_HIGH);
manager.createNotificationChannel(channel);
notification = new Notification.Builder(this,"notificationID")
.setContentText("MyService")
.setContentTitle("This is Content Title")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
}
startForeground(1, notification);
Log.d(TAG, "onCreate() executed");
}
//正式启动服务,每一次启动都会调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
Toast.makeText(this, "startCommand", Toast.LENGTH_SHORT).show();
try{
sleep(2000);
// stopSelf(startId);
} catch (InterruptedException e) {
e.printStackTrace();
}
return super.onStartCommand(intent, flags, startId);
}
//服务消亡
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
stopForeground(true);//删除通知!
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
//和Activity进行交互的一个Binder,在这里可以自己编写相关的操作
public class MyBinder extends Binder{
void startDownload(){
Log.d(TAG, "startDownload");
}
}
}