Service

Service

ServiceAndroid中实现程序后台运行的解决方案,它非常适合执行那些不需要和用户交互而且还要求长期运行的任务。Service的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,Service仍然能够保持正常运行。

需要注意的是,Service并不是运行在一个独立的进程当中的,而是依赖于创建Service时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行。

另外,Service并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,如有需要,就要在Service的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况。

AndroidiOS大移动操作系统中,iOS一开始是不支持后台的,后来意识到这个功能的重要性,才逐渐加入了部分后台功能。而Android正好相反,一开始支持丰富的后台功能,后来意识到后台太过开放的弊端,于是逐渐削减了后台功能。不管怎么说,用于实现后台功能的Service属于四大组件之 一,其重要程度不言而喻。

1 Service的启动流程

Service启动流程

  1. Process A进程采用Binder IPCsystem_server进程发起startService请求;
  2. system_server进程接收到请求后,向zygote进程发送创建进程的请求;
  3. zygote进程fork出新的子进程Remote Service进程;
  4. Remote Service进程,通过Binder IPCsytem_server进程发起attachApplication请求;
  5. system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPCremote Service进程发送scheduleCreateService请求;
  6. Remote Service进程的binder线程在收到请求后,通过Handler向主线程发送CREATE_SERVICE消息;
  7. 主线程在收到Message后,通过发射机制创建目标Service,并回调Service.onCreate()方法;

到此,服务便正式启动完成。当创建的是本地服务或者服务所属进程已创建时,则无需经过上述步骤23,直接 创建服务即可。

Context.startService运行时序图:

从ContentImpl到AMS

startService

Context.bindService()运行时序图:

bindService

bindService

2 Service的生命周期

2.1 启动方式

Service的启动方式主要有两种,分别是startServicebindService

下图是startServicebindService两种方式启动Service的生命周期示意图:

service生命周期

startService方法启动的服务,与启动者没有关系,也不会进行通信。即使启动着销毁了,服务仍然在运行。停止服务使用stopService方法。onCreate -> onStartCommand -> onDestroy

bindService方法启动的服务,服务和启动者绑定,可以进行通信。当启动者销毁时,对应的Service也会销毁(Service的生命周期与其绑定者息息相关)。停止服务使用unbindService方法。onCreate -> onbind -> onUnbind -> onDestroy

生命周期方法:

  • onCreate():创建服务。首次创建服务时,系统会调用此方法(如果服务已经运行,则不会调用此方法),该方法只调用一次
  • onStartCommane():开始服务。当另一个组件通过调用startService方法启动服务时,系统将调用此方法
  • onDestory():销毁服务。当服务不再使用且将要销毁的时候,系统将调用此方法
  • onBind():绑定服务。当另一个组件通过调用bindService方法与服务绑定时,系统将调用此方法
  • onUnbind():解绑服务。当另一个组件调用unbindeService方法与服务解绑时,系统将调用此方法
  • onRebind():当旧的组件与服务解绑后,另一个新的组件与服务绑定,onRebind方法返回true时,系统将调用此方法

startService方法开启服务,如果服务已经开启,多次执行startService不会重复的执行onCreate方法, 而是会调用onStartCommand方法(onStart方法已弃用)。注意,虽然每调用一次startService()方法,onStartCommand()就会执行一次,但实际上每个Service只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()stopSelf()方法,Service就会停止。

bindService方法开启服务,如果服务已经开启,多次执行bindService时,onCreateonBind方法并不会被多次调用。

startService + bindService启动的服务,停止服务应同时使用stopService + unbindService。 根据Android系统的机制,一个Service只要被启动或者被绑定了之后,就会处于运行状态,必须要让以上两种条件同时不满足,Service才能被销毁。所以,这种情况下要同时调用stopService()unbindService()方法,onDestroy()方法才会执行。

通常情况下,如果我们希望Service一旦启动就立刻去执行某个动作,就可以将逻辑写在onStartCommand()方法里。而当Service销毁时,我们又应该在onDestroy()方法中回收那些不再使用的资源。

2.2 startService

创建一个Service:

创建Service
创建Service

以下是MyService中的代码:

class MyService : Service() {

  override fun onBind(intent: Intent): IBinder {
    TODO("Return the communication channel to the service.")
  }
}

并且已经在AndroidManifest.xml中完成了注册。**需要注意,每一个Service都需要在AndroidManifest.xml文件中进行注册才能生效,这是Android四大组件共有的特点。**代码如下所示:

<service
    android:name=".MyService"
    android:enabled="true" 
    android:exported="true">
  
</service>

MyService是继承自系统的Service类的, Service类本身是抽象类,有一个抽象方法onBind()方法,此方法是必须重写的,即使用不到。

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
  @Nullable
  public abstract IBinder onBind(Intent intent);
}

启动和停止Service主要是借助Intent来实现的。

class MyService : Service() {

  override fun onBind(intent: Intent): IBinder {
    TODO("Return the communication channel to the service.")
  }

  override fun onCreate() {
    super.onCreate()
    Log.e("CAH - MyService", "onCreate")
  }

  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    Log.e("CAH - MyService", "onStartCommand")
    return super.onStartCommand(intent, flags, startId)
  }

  override fun onDestroy() {
    Log.e("CAH - MyService", "onDestroy")
    super.onDestroy()
  }
}

// 启动Service
startServiceBtn.setOnClickListener {
  val intent = Intent(this, MyService::class.java)
  startService(intent) 
}

// 停止Service
stopServiceBtn.setOnClickListener {
  val intent = Intent(this, MyService::class.java)
  stopService(intent)
}

点击startServiceBtn,打印日志:

// CAH - MyService: onCreate
// CAH - MyService: onStartCommand

多次点击startServiceBtn,打印日志:

// CAH - MyService: onStartCommand
// CAH - MyService: onStartCommand
// CAH - MyService: onStartCommand

只调用onStartCommand方法,不调用onCreate方法。

点击stopServiceBtn按钮:

// CAH - MyService: onDestroy

另外,Service也可以自行停止运行,只需要在Service内部调用stopSelf()方法即可。

Android 8.0系统开始,应用的后台功能被幅削减。只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。之所以做这样的改动,是为了防止许多恶意的应用程序长期在后台占用手机资源,从而导致手机变得越来越卡。

ServiceonStartCommand方法的返回值各代表什么意思?

调用Context.startService方式启动Service时,如果Android面临内存匮乏的问题,可能会销毁当前运行的Service,待内存充足时可以重建Service。而ServiceAndroid系统强制销毁并再次重建的行为依赖于ServiceonStartCommand()方法的返回值。

以下是onStartCommand的源码:

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {

  public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
    onStart(intent, startId);
    return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
  }

  @Deprecated
  public void onStart(Intent intent, int startId) {
  }

}

START_STICKY:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务 ,并且onStartCommand方法会执行,onStartCommand方法中的intent值为null。 适用于媒体播放器或类似服务。

START_STICKY_COMPATIBILITYSTART_STICKY的兼容性版本,不保证onStartCommand将在被杀死后再次调用。

sticky [ˈstɪki] 黏性的 compatibility [kəmˌpætəˈbɪləti] 共存;和睦相处;(计算机设备的)兼容性

2.3 bindService

bindService的方法启动的服务,和调用者之间是典型的Client-Server模式。调用者是clientService则是Server端。Service只有一个,但绑定到Service上面的Client可以有一个或很多个。

那么,ServiceActivity怎么实现通信的?—— 通过Binder对象

  • Service中添加一个继承Binder的内部类,并添加相应的逻辑方法
  • Service中重写ServiceonBind方法,返回定义的内部类实例
  • Activity中绑定服务,重写ServiceConnectiononServiceConnected时返回的IBinderService中的Binder)调用逻辑方法

另外,Service也可以通过Broadcast广播与Activity通信

比如说,目前我们希望在MyService里提供一个下载功能,然后在Activity中可以决定何时开始下载,以及随时查看下载进度。实现这个功能的思路是创建一个专门的Binder对象来对下载功能进行管理。这就需要借助onBind()方法了。

修改MyService中的代码,如下所示:

class MyService : Service() {

    private val mBinder = DownloadBinder()

    class DownloadBinder : Binder() {
        fun startDownload() {
            Log.e("CAH - MyService - DownloadBinder", "startDownload")
        }

        fun getProgress(): Int {
            Log.e("CAH - MyService - DownloadBinder", "getProgress")
            return 0
        }

    }
    
    override fun onBind(intent: Intent): IBinder {
        Log.e("CAH - MyService", "onBind")
        return mBinder
    }

    override fun onCreate() {
        super.onCreate()
        Log.e("CAH - MyService", "onCreate")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.e("CAH - MyService", "onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        Log.e("CAH - MyService", "onDestroy")
        super.onDestroy()
    }
}

可以看到,这里新建了一个DownloadBinder类,并让它继承自Binder,然后在它的内部提供了开始下载以及查看下载进度的方法。接着,在MyService中创建了DownloadBinder的实例,然后在onBind()方法里返回了这个实例,这样MyService中的工作就全部完成了。

MainActivity(启动者)中:

class MainActivity : BaseActivity() {

    private lateinit var bindServiceBtn: Button
    private lateinit var unbindServiceBtn: Button

    lateinit var downloadBinder: MyService.DownloadBinder

    private val connection = object : ServiceConnection {
      override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        downloadBinder = service as MyService.DownloadBinder
        downloadBinder.startDownload()
        downloadBinder.getProgress()
      }

      override fun onServiceDisconnected(name: ComponentName?) {
        TODO("Not yet implemented")
      }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      bindServiceBtn = findViewById(R.id.bindServiceBtn)
      unbindServiceBtn = findViewById(R.id.unbindServiceBtn)

      bindServiceBtn.setOnClickListener {
        val intent = Intent(this, MyService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
      }
      
      unbindServiceBtn.setOnClickListener {
        unbindService(connection)
      }

    }
}

这里首先创建了一个ServiceConnection的匿名实现类,并在里面重写了onServiceConnected()方法和onServiceDisconnected()方法。 onServiceConnected()方法方法会在Activity(启动者)与Service成功绑定的时候调用,而 onServiceDisconnected()方法只有在Service的创建进程崩溃或者被杀掉的时候才会调用,这个方法不太常用。

onServiceConnected()方法中,通过向下转型得到了DownloadBinder的实例,有了这个实例,ActivityService就联系起来了。现在就可以在Activity中根据具体的场景来调用DownloadBinder中的任何public方 法,即实现了指挥Service干什么Service就去干什么的功能。这里仍然只是做了个简单的测试,在onServiceConnected()方法中调用了DownloadBinderstartDownload()getProgress()方法。

现在ActivityService其实还没进行绑定呢,这个功能是在Bind Service按钮的点击事件里完成的。构建了一个Intent对象,然后调用bindService()方法将MainActivityMyService进行绑定。bindService()方法接收3个参数:

  • 第一个参数是构建出的Intent对象
  • 第二个参数是前面创建出的ServiceConnection的实例
  • 第三个参数则是一个标志位,这里传入BIND_AUTO_CREATE表示在ActivityService进行绑定后自动创建Service 这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行

如果我们想解除ActivityService之间的绑定调用一下unbindService()方法就可以了。

运行程序,点击Bind Service按钮:

// CAH - MyService: onCreate
// CAH - MyService: onBind
// CAH - MyService - DownloadBinder: startDownload
// CAH - MyService - DownloadBinder: getProgress

连续点击Bind Service按钮,发现没有日志打印,说明onCreate方法和onBind方法只调用了一次。

点击Unbind Service按钮:

// CAH - MyService: onUnbind
// CAH - MyService: onDestroy
2.4 startService+bindService

如果只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService

2.4.1 startService -> bindService

启动服务:

val intent = Intent(this, MyService::class.java)
startService(intent)
bindService(intent, connection, Context.BIND_AUTO_CREATE)

打印日志:

// CAH - MyService: onCreate
// CAH - MyService: onStartCommand
// CAH - MyService: onBind
// CAH - MyService - DownloadBinder: startDownload
// CAH - MyService - DownloadBinder: getProgress

连续启动服务,打印日志:

// CAH - MyService: onStartCommand
// CAH - MyService: onStartCommand

停止服务(stopService -> unbindServiceunbindService -> stopService):

val intent = Intent(this, MyService::class.java)
stopService(intent)
unbindService(connection)

打印日志:

// CAH - MyService: onUnbind
// CAH - MyService: onDestroy

停止服务(stopService):

val intent = Intent(this, MyService::class.java)
stopService(intent)

不打印任何日志。

停止服务(unbindService):

val intent = Intent(this, MyService::class.java)
unbindService(connection)

打印日志:

// CAH - MyService: onUnbind
2.4.2 bindService -> startService

启动服务:

val intent = Intent(this, MyService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
startService(intent)

打印日志:

// CAH - MyService: onCreate
// CAH - MyService: onBind
// CAH - MyService: onStartCommand
// CAH - MyService - DownloadBinder: startDownload
// CAH - MyService - DownloadBinder: getProgress

连续启动服务,打印日志:

// CAH - MyService: onStartCommand
// CAH - MyService: onStartCommand

停止服务(stopService -> unbindServiceunbindService -> stopService):

val intent = Intent(this, MyService::class.java)
stopService(intent)
unbindService(connection)

打印日志:

// CAH - MyService: onUnbind
// CAH - MyService: onDestroy

停止服务(stopService):

val intent = Intent(this, MyService::class.java)
stopService(intent)

不打印任何日志。

停止服务(unbindService):

val intent = Intent(this, MyService::class.java)
unbindService(connection)

打印日志:

// CAH - MyService: onUnbind

总结:

如果只执行stopServiceService.onDestroy方法不会立即执行。 但是,在Activity退出的时候,会执行Service.OnDestroy;

如果只执行unbindService,只有onUnbind方法会执行,onDestory不会执行;

如果要完全退出Service,那么就得执行unbindService() + stopService()

3 Service设置前台服务&提高优先级

3.1 使用前台Service

Android 8.0系统开始,只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。而如果希望Service能够一直保持运行状态,就可以考虑使用前台Service 前台Service和普通Service最大的区别就在于,它一直会有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果,如图所示:

前台Service

由于状态栏中一直有一个正在运行的图标,相当于我们的应用以另外一种形式保持在前台可见状态,所以系统不会倾向于回收前台Service。另外,用户也可以通过下拉状态栏清楚地知道当前什么应用正在运行,因此也不存在某些恶意应用长期在后台偷偷占用手机资源的情况。

代码如下所示:

class MyService : Service() {

  ...

  override fun onCreate() {
    super.onCreate()
    Log.e("CAH", "onCreate executed")
    val manager = getSystemService(Context.NOTIFICATION_SERVICE) as
    NotificationManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      val channel = NotificationChannel(
        "my_service",
        "前台Service通知",
        NotificationManager.IMPORTANCE_DEFAULT
      )
      manager.createNotificationChannel(channel)
    }
    val intent = Intent(this, StackActivity::class.java)
    val pi = PendingIntent.getActivity(this, 0, intent, 0)
    val notification = NotificationCompat.Builder(this, "my_service")
    .setContentTitle("This is content title")
    .setContentText("This is content text")
    .setSmallIcon(R.drawable.chang)
    .setColor(Color.RED)
    .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.cai))
    .setContentIntent(pi)
    .build()
    startForeground(1, notification)
  }

  ...
}

startForeground()方法接收两个参数:第一个参数是通知的id,类似于notify()方法的第一个参数;第二个参数则是构建的Notification对象。调用startForeground()方法后就会让MyService变成一个前台Service,并在系统状态栏显示出来。

另外,从Android 9.0系统开始,使用前台Service必须在AndroidManifest.xml文件中进行权 限声明才行,如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.servicetest">
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    ...
</manifest>
3.2 设置Service的优先级

intent-fillter中设置Service的优先级
android:priority="1000",其中数字越小优先级越低,1000代表优先级最高。 不过这个的效果并没有设为前台服务好,这个1000的优先级顶多与前台服务的优先级相同。其实Priority属性不仅仅可以用于Service,只要是有intent-fillter的组件都可以,比如说ActivityBroadcastReceiver,这个属性多是用来按指定一个执行顺序的。

<service
     android:name=".MyService"
     android:enabled="true"
     android:exported="true">
  <intent-filter android:priority="1000">
    <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
  </intent-filter>
</service>
3.3 将Service设为黏性服务

只需要将ServiceonStartCommand()方法的返回值设为START_STICKY即可:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  return START_STICKY
}
3.4 在Service.onDestroy()里重启服务

实现原理是Service注册广播,当服务被杀死的时候发送一条广播,监听到对应广播后在broadcastReceiveronReceive()方法里重新开启服务。

缺点就是很可能服务是被因为app直接被kill掉了,并不会走onDestroy()

4 IntentService

4.1 原理

Service中的代码都是默认运行在主线程当中的,如果直接在Service里处理一些耗时的逻辑,就很容易出现ANR(Application Not Responding)的情况。

这个时候就需要用到Android多线程编程的技术了,在Service的每个具体的方法里开启一个子线程,然后在这里处理那些耗时的逻辑。一个比较标准的Service就可以写成如下形式:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  Log.e("MyService", "onStartCommand: ")
  thread {
    // 处理逻辑
  }
  return super.onStartCommand(intent, flags, startId)
}

但是,这种Service一旦启动,就会一直处于运行状态,必须调用stopService()stopSelf()方法或者被系统回收,Service才会停止。所以,如果想要实现让一个Service在执行完毕后自动停止的功能,就可以这样写:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  Log.e("MyService", "onStartCommand: ")
  thread {
    // 处理逻辑
    stopSelf()
  }
  return super.onStartCommand(intent, flags, startId)
}

虽说这种写法并不复杂,但是总会有一些程序员忘记开启线程,或者忘记调用stopSelf()方法。为了可以简单地创建一个异步的、会自动停止的ServiceAndroid专门提供了一个IntentService类,这个类就很好地解决了前面所提到的两种问题。

IntentServiceService的子类,默认开启了一个工作线程ServiceHandler,使用这个工作线程逐一处理所有启动请求,在任务执行完毕后会自动停止服务。只要实现一个方法onHandleIntent,该方法会接收每个启动请求的Intent,能够执行后台工作和耗时操作。

public abstract class IntentService extends Service {

  private volatile Looper mServiceLooper;
  private volatile ServiceHandler mServiceHandler;

  private final class ServiceHandler extends Handler { // 1

    public ServiceHandler(Looper looper) {
      super(looper);
    }
    
    @Override
    public void handleMessage(Message msg) {
      onHandleIntent((Intent)msg.obj);
      stopSelf(msg.arg1); // 4 
    }
  }


  public IntentService(String name) {
    super();
    mName = name;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper(); // 2 
    mServiceHandler = new ServiceHandler(mServiceLooper); 
  }

  @Override
  public void onStart(@Nullable Intent intent, int startId) {
    // 3
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
  }


  @Override
  public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
  }

  @Override
  public void onDestroy() {
    mServiceLooper.quit();
  }

  @WorkerThread
  protected abstract void onHandleIntent(@Nullable Intent intent);
}

实现原理:

  • 创建一个名叫ServiceHandler的内部类,ServiceHandler extends Handler
  • IntentService.onCreate方法中,获取当前线程的Looper,并且将ServiceHandler和当前线程的LooperMessageQueue绑定
  • IntentService.onStartCommand -> onStart方法中,将intent依次插入到MessageQueue中,逐个处理
  • ServiceHandler.handleMessage方法中,调用ServiceHandler.onHandleIntent方法,处理完成后,会自动停止

可以启动IntentService多次,而每一个耗时操作会以队列的方式在IntentServiceonHandlerIntent回调方法中执行,并且,每一次只会执行一个工作线程,执行完第一个再执行第二个。并且等待所有消息都执行完后才终止服务。

IntentService可以用作后台下载任务,静默上传等。

4.2 实现

新建一个MyIntentService类继承自IntentService,代码如下所示:

class MyIntentService : IntentService("MyIntentService") {
    override fun onHandleIntent(intent: Intent?) {
        Log.e("MyIntentService", "Thread id is ${Thread.currentThread().name}")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e("MyIntentService", "onDestroy")
    }
}

首先要求必须先调用父类的构造函数,并传入一个字符串,这个字符串可以随意指定,只在调试的时候有用。然后要在子类中实现onHandleIntent()这个抽象方法,这个方法中可以处理一些耗时的逻辑,而不用担心ANR的问题,因为这个方法已经是在子线程中运行的了。

这里为了证实一下,在onHandleIntent()方法中打印了当前线程名。另外,根据IntentService的特性,这个Service在运行结束后应该是会自动停止的,所以我们又重写了onDestroy()方法,在这里也打印了一行日志,以证实Service是不是停止了。

最后不要忘记,Service都是需要在AndroidManifest.xml里注册的,如下所示:

<service
    android:name=".MyIntentService"
    android:enabled="true"
    android:exported="true" />

启动IntentService,代码如下所示:

class MainActivity : BaseActivity() {

    private lateinit var startIntentServiceBtn: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        startIntentServiceBtn = findViewById(R.id.startIntentServiceBtn)

        startIntentServiceBtn.setOnClickListener {
            Log.e("MainActivity", "Thread id is ${Thread.currentThread().name}")
            val intent = Intent(this, MyIntentService::class.java)
            startService(intent)
        }
    }
}

可以看到,IntentService的启动方式和普通的Service没什么不同。

观察Logcat中的打印日志,如下所示:

// MainActivity: Thread id is main
// MyIntentService: Thread id is IntentService[MyIntentService]
// MyIntentService: onDestroy

可以看到,不仅MyIntentServiceMainActivity所在的线程名不一样,而且onDestroy()方法也得到了执行,说明MyIntentService在运行完毕后确实自动停止了,集开启线程和自动停止于一身。

参考

https://www.codeleading.com/article/57973935944/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值