先看一下他的简述
Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。
此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行,Service基本上分为两种形式(也就是启动模式吧)
启动状态
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。
绑定状态
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
来一张官方说明
接下来边打边说
先建一个Service
这时AndroidManifest.xml文档为我们生成了这样一段,其实和Activity差不多
那正好先看一下AndroidManifest.xml中一些简单的声明语法都有什么,其格式如下:
<service android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</service>
android:exported:代表是否能被其他应用隐式调用,其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用。
android:name:对应Service类名
android:permission:是权限声明
android:process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
android:isolatedProcess :设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。
android:enabled:是否可以被系统实例化,默认为 true因为父标签 也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。
ok!
接下来我们先试一下startService()
我们重写这几个生命周期的标签在给他写上打印日志,logonUnbind和onBind按理说是不会出现的我们试一下。
怎么启动service呢?很简单和activity一样用Intent就可以
startService(new Intent(this,MyService.class));
结果如下
即使我们back,home甚至打开别的应用一直没有onDestroy
除非你把他后台关闭或者在别的程序中很久让手机自动杀死进程这是为什么呢?
我们在service里面运行一小点东西看看
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
try {
Thread.sleep(5000);
}catch (InterruptedException e) {
e.printStackTrace();
}
Toast.makeText(this,"我还活着",Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
然后方便测试我们让他自己在一个线程上运行
刚学过的
android:process=":remote"
果然即使我Activity,back甚至把开启他的进程都关了他还活着
问什么呢看看上面,因为上面说到,一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。
那我们要给他停止才会出现onDestroy么?怎么停止?
我们在activity的onstop方法中加入下面这个方法就成功了
stopService(intent);
ok我们重新回过头来进一步分析onStartCommand(Intent intent,int flags,int startId),这个方法有3个传入参数,它们的含义如下:
onStartCommand(Intent intent,int flags,int startId)
intent:启动时,启动组件传递过来的Intent,如Activity可利用Intent封装所需要的参数并传递给Service
flags:表示启动请求时是否有额外数据,可选值有0,START_FLAG_REDELIVERY,START_FLAG_RETRY,0代表没有,它们具体含义如下:
START_FLAG_REDELIVERY
这个值代表了onStartCommand方法的返回值为
START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf方法停止服务。其中START_REDELIVER_INTENT意味着当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个Intent调用onStartCommand(),此时Intent时有值的。
START_FLAG_RETRY
该flag代表当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。
startId:指明当前服务的唯一ID,与stopSelfResult(int startId)配合使用,stopSelfResult可以更安全地根据ID停止服务。
实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值,START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT,它们具体含义如下:
START_STICKY
当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
START_NOT_STICKY
当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
START_REDELIVER_INTENT
当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个Intent调用onStartCommand(),任何挂起Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
由于每次启动服务(调用startService)时,onStartCommand方法都会被调用,因此我们可以通过该方法使用Intent给Service传递所需要的参数,然后在onStartCommand方法中处理的事件,最后根据需求选择不同的Flag返回值,以达到对程序更友好的控制。
现在 试一下bindService()
我们看到除了intent还要需要传递两个参数
我们来写一个serviceconnection(是个接口有两个必须实现的方法)
这两个方法是干什么的呢
onServiceConnected访问到sever的操作,另一个就是没有访问的。
然后最后添加BIND_AUTO_CREATE属性(没有就创建)的意思也可以选择0这个选项(不创建)
点击运行
但是onServiceConnected我们发现还有一个参数,而且service和activity不能相互传值这时我们可以使用广播,但是并不推荐因为这样会破坏代码的模块化。
此时我们就要使用ibinder service这个参数,他就是service的onbind返回过来的值我们可以看到onbind的返回值正是ibinder
而binder正是继承了ibinder所以可以创建一个内部类实现调用自身
class ibinder extends Binder{
public MyService getservice(){
return MyService.this;
}
}
如果想要调用service就通过
MyService.ibinder iservice = (MyService.ibinder) service;
iservice.getservice.XXXXX
的方式获得需要的方法或者参数
试一下
package com.hrk.service2;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.view.View;
import android.widget.TextView;
public class MainActivityextends AppCompatActivity {
Intentintent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView text = findViewById(R.id.text);
View viewById = findViewById(R.id.btn);
viewById.setOnClickListener(view -> {
MyService.num +=1;
text.setText(String.valueOf(MyService.num));
});
}
public void btn_1(View view) {
intent =new Intent(this, MyService.class);
startService(intent);
}
public void btn_2(View view) {
intent =new Intent(this, MyService.class);
bindService(intent,conn,BIND_AUTO_CREATE);
}
ServiceConnectionconn =new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService.ibinder iservice = (MyService.ibinder) service;
iservice.getservice();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
public void btn_3(View view) {
stopService(intent);
}
}
package com.hrk.service2;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class MyServiceextends Service {
private static final StringTAG ="ssd";
public static int num =0;
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate: ");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinderonBind(Intent intent) {
Log.i(TAG, "onBind: ");
return new ibinder();
}
class ibinderextends Binder{
public MyServicegetservice(){
return MyService.this;
}
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind: ");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
IntentService(据说基本上没用了)
它本质是一种特殊的Service,继承自Service并且本身就是一个抽象类
它会提供单独的worker线程来处理所有的intent请求
它可以用于在后台执行耗时的异步任务,当任务完成后会自动停止
它拥有较高的优先级,不易被系统杀死(继承自Service的缘故),因此比较适合执行一些高优先级的异步任务
它内部通过HandlerThread和Handler实现异步操作
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
Thread.sleep(20000);
}catch (InterruptedException e) {
e.printStackTrace();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
try {
Thread.sleep(20000);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
分别在service和intentservice中让线程等待20秒模拟一个20秒的任务,分别启动service和intentservice发现启动service会报错出现anr异常而intentservice不会