1. Service 是什么?
服务(Service) 是 Android 的四大组件之一,是 Android 中完成程序后台运转的解决方案,合适去履行那些不需求和用户交互并且还要求长期运转的使命。
Service 的运转不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,Service 依然能够保持正常运转。
Service 并不是运转在一个独立的进程当中的 ,而是依赖于创立服务时地点的应用程序进程。当某个应用程序进程被杀掉时,一切依赖于该进程的服务也会中止运转。
Service 并不会自动敞开线程,一切代码都是默许运转在主线程中。咱们需求在服务内部手动创立子线程,并在这儿履行详细的使命。
2. Service 的根本用法
2.1 界说一个 Service
如下图,如同创立 Activity 一样创立一个 Service:
有两个属性:
- Exported:表明是否将这个 Service 露出给外部其他程序拜访。
- Enabled:表明是否启用这个 Service。
创立之后的 MyService 承继 Service:
// Android API 34
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
onBind()
是 Service 类中仅有的笼统办法:
// Service.java
@Nullable
public abstract IBinder onBind(Intent intent);
界说一个 Service 不仅完成 onBind()
办法,一般还会完成 onCreate()
、onStartCommend()
和 onDestroy()
:
public class MyService extends Service {
public MyService() {
}
// 服务第一次创立时调用。
@Override
public void onCreate() {
super.onCreate();
}
// 每次发动服务时调用。服务一旦发动就立即去履行某个操作的代码逻辑现在这儿
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
// 服务销毁时调用。收回不再运用的资源
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
}
-
onCreate()
:服务第一次创立时调用。 -
onStartCommend()
:每次发动服务时调用。服务一旦发动就立即去履行某个操作的代码逻辑现在这儿。 -
onDestroy()
:服务销毁时调用。收回不再运用的资源。
Android 四大组件在 AndroidManifest.xml 中进行注册才能收效:
// AndroidManifest.xml
<service
android:name=".MyService"
android:enabled="true"
android:exported="true" />
2.2 startService() 显式敞开服务
主要经过 Intent 去完结服务的发动和中止。
敞开 Service 的两种办法:显现敞开和绑定敞开
在 Activity 中能够直接调用 startService()
和 stopService()
这两个办法显现敞开和中止服务:
if (id == R.id.start_service) {
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);// 经过 startService() 办法发动 MyService 服务
} else if (id == R.id.stop_service) {
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);// 由 Activity 来决定服务何时中止。若想服务自己中止,在 MyService 调用stopSelf()办法
}
点击敞开服务按钮敞开服务之后,在手机设置中正在运转的服务中会看见咱们敞开的服务,也能够经过 log 看看服务是否敞开,中止服务同理:
这儿,完全是 Activity 决定服务何时中止的,假如没有点击按钮,Service 会一向处于运转状态。
假如想让 Service 自己停下来,只需求在 MyService 任何一个位置调用 stopSelf()
。
显现敞开服务流程:
2.3 绑定敞开
经过 startService()
办法显现敞开 Service 后,调用者(Activity)就和 service 没有关联了。
这儿一个很重要的问题:Activity 无法拿到 Service引证。
举个例子,如安在 MyService 中供给一个下载功用,并且在 Activity 中能够决定何时开端下载,以及检查下载进展呢?
经过 bindService()
办法绑定开启服务,能够借助 onBind()
办法,创立一个专门的 Binder 目标来对下载功用进行办理。
新建 DownloadBinder 承继自 Binder,并在其内部供给开端下载和检查进展办法:
public class MyService extends Service {
private DownloadBinder mDownloadBinder = new DownloadBinder();
public class DownloadBinder extends Binder{
public void startDownload(){
Log.d("MyService", "Start");
}
public int getProgress(){
Log.d("MyService","getProgress");
return 0;
}
public void stopDownload(){
Log.d("MyService", "Stop");
}
}
public MyService() {}
@Override
public void onCreate() { super.onCreate(); }
@Override
public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); }
@Override
public void onDestroy() { super.onDestroy(); }
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mDownloadBinder;
}
}
经过 startService()
敞开 Service 时,咱们重写了 onBind()
,直接回来的是null,此刻该办法并没有调用。
第31~34行,当经过 bindService()
敞开 Service 时,需求回来 IBiner 的引证给绑定者运用。
回来的是 DownloadBinder 目标的引证,该目标持有了 MyService 引证。绑定者又是在哪里接纳 IBinder 的引证呢?
在 Activity 中界说的 ServiceConnection 匿名内部类:
创立一个 ServiceConnection 的匿名类,重写了 onServiceConnected()
和onServiceDisconnected()
,这两个办法分别在 Activity 和 Service 成功绑定以及连接断开不时调用:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.DownloadBinder downloadBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.start_service) {
...
} else if (id == R.id.stop_service) {
...
} else if (id == R.id.bind_service) {
Intent bindIntent = new Intent(this, MyService.class);
// service 和 activity 绑定后自动创立服务 BIND_AUTO_CREATE,这会使得 MyService 中 OnCreate() 办法得到履行
bindService(bindIntent,connection,BIND_AUTO_CREATE);
} else if (id == R.id.unbind_service) {
unbindService(connection);
}
}
private ServiceConnection connection = new ServiceConnection() {
// service 和 activity 绑定时调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// service 就是从 onBind() 办法回来的
// 向下转型得到 DownloadBinder 的实例
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// Service 被销毁时调用,内存不足等。
}
};
}
有了 ServiceConnection 引证,接着就需求和 Service 树立联络,树立联络的进程便是绑定敞开 Service 的进程:
private void bindService() {
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
从上面能够看出,绑定敞开的流程:
- 新建 DownloadBinder 类持有 Service 目标。
- 在 Service 的
onBind()
办法中回来 IBinder 引证(mDownloadBinder),该引证持有 Service 引证。 - Activity 调用
bindService()
办法成功后,会调用 ServiceConnection 的onServiceConnected()
办法,Service 和 ServiceConnection 也会树立联络,onServiceConnected()
办法会回来 IBinder 引证(mDownloadBinder)。 - Activity 经过 IBinder 引证(mDownloadBinder)拿到 Service 引证,从而操作 Service。
以上回答了上面的问题:绑定者(Activity)怎么拿到 Service 引证。
那么怎么进行解绑呢?
手动调用 unbindService(serviceConnection)
办法进行解绑,解绑时也需求传入 ServiceConnection 引证,不然无法确定解绑哪个 Service。
手动调用该办法即可解绑 Service。 当 Activity 经过 bindService()
绑定敞开 Service 后,若是 Activity 销毁了,那么相应的 Service 也会被销毁掉。
值得注意的是:
- 若是
startService()
敞开 Service,则无法用unbindService()
办法封闭 Service。 - 若是
bindService()
敞开 Service,则无法用stopService()
封闭 Service。 - 既调用了
startService()
办法,又调用了bindService()
办法的 Service ,这种情况下要同时调用stopService()
和unbindService()
办法,onDestroy()
办法才会履行。
绑定敞开服务的流程:
3. 前台服务
体系内存不足时可能会收回服务。若想要服务一向保持运转状态,能够考虑运用前台服务,防止服务被收回。
前台服务会一向有一个正在运转的图标在体系的状态栏显现,类似于告诉的效果。相当于 Service 以这种办法在前台运转。
Android 9.0开端,需进行权限声明:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
// MyService.java
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService","onCreate executed");
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel notificationChannel = new NotificationChannel("my_service","前台Service告诉 ", NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(notificationChannel);
}
Intent intent = new Intent(this, MyServiceActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
Notification notification = new NotificationCompat.Builder(this,"my_service")
.setContentTitle("This is content title")
.setContentText("真实的平静,不是避开车马喧嚣,而是在心中修篱种菊。尽管如流往事,每一天都涛声仍旧,只需咱们消除执念,便可幽静安定。愿每个人,在纷呈世相中不会迷失荒径,能够端坐磐石上,醉倒落花前。")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.a)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.a))
.setContentIntent(pi)
.build();
startForeground(3,notification);
}
第5行,NotificationManager 类用于向用户展示告诉,对告诉进行办理。
第7行,NotificationChannel 类用于构建一个告诉途径,并运用 NotificationManager 的 createNotificationChannel()
办法进行创立。
告诉途径(Notification Channels),也被称为告诉类别,是Android 8.0(API 级别 26)引入的一个功用,它为开发者供给了更好的办理告诉的办法,也运用户能够有更好地控制他们想要接纳的告诉类型。
NotificationChannel 类和 createNotificationChannel() 办法都是 Android 8.0 体系中新增的 API,因此咱们在运用的时候进行版本判别。
创立一个告诉途径至少需求途径ID、途径名称以及重要等级这三个参数:
- 途径ID:能够随便界说,只需确保大局仅有性就能够。
- 途径名称:给用户看的,需清楚地表达这个途径的用途。
-
重要等级:主要有
IMPORTANCE_HIGH
、IMPORTANCE_DEFAULT
、IMPORTANCE_LOW
、IMPORTANCE_MIN
。用户能够随时手动更改某个告诉途径的重要等级。
第12行,运用 NotificationCompat Builder
对 Notification 进行构造。由于告诉 API 的不稳定性,运用 NotificationCompat 类对告诉相关操作进行兼容完成。
NotificationCompat.Builder(this,"my_service")
第二个参数为途径ID,需求与创立时的 ID 保持一致。
第11行,PendingIntent 能够在某个合适的时机履行某个动作,能够理解为延迟履行的Intent。经过第18行设置,在点击状态栏中的告诉时会进行跳转。
运转程序,点击 START SERVICE 按钮,就会在状态栏中看到相关告诉:
若没有呈现,可能告诉权限还未敞开: