Android

Android - Service Start/Stop 使用

2019-03-27  本文已影响0人  拾识物者

Service Start/Stop 使用

代码套路

manifest 注册

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

定义

import android.app.Service;
public class MyService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
}

启动

Intent intent = new Intent(this, MyService.class);
startService(intent);

停止

Intent intent = new Intent(this, MyService.class);
stopService(intent);

生命周期

// 启动服务
MyService()
MyService.onCreate()
MyService.onStartCommand()
MyService.onStart() // 该方法已废弃,这里打印是因为默认还是调用了它
// 再次启动
MyService.onStartCommand()
MyService.onStart()
// 停止服务
MyService.onDestroy()

startId

onStartCommand(Intent intent, int flags, int startId) 方法的一个参数,表示这次 Start 请求的一个 token,这个 token 用来传给 stopSelfResult(int startId)

stopSelfResult(int startId) 的官方文档很模糊:

This is the same as calling Context.stopService(Intent) for this particular service but allows you to safely avoid stopping if there is a start request from a client that you haven't yet seen in onStart(Intent, int).

经过试验,发现规则如下:

  1. startId 一个生命周期内,每次 onStartCommand 调用,参数 startId 加 1。
  2. 只有 stop 最后一个 startId,也就是 onStartCommand 最后一次调用中的 startId,服务才会停止,大于小于都不行。

主要是第2点,理解了 stop 最后一个 startId,service 就会停止,就会理解官方文档下面这句话:

Be careful about ordering of your calls to this function.. If you call this function with the most-recently received ID before you have called it for previously received IDs, the service will be immediately stopped anyway. If you may end up processing IDs out of order (such as by dispatching them on separate threads), then you are responsible for stopping them in the same order you received them.

最后一句话:

you are responsible for stopping them in the same order you received them.

是结论:Service 设计者要保证停止 startId 的顺序和启动 startId 的顺序一致。

可以参考一个现成的例子:IntentService

IntentService 接收 Intent 封装的请求,每一次请求就是一次 Service 的启动,就是一次 onStartCommand 调用。因此每个 Intent 会关联一个 startId,IntentService 将这个 startId 保存在了 Message 里面,同样保存在 Message 中的还有 Intent。startId + Intent 构成了一次请求。在 onHandleIntent 执行完成后, IntentService 会调用 stopSelf(startId) 来停止这个请求。

这样将 startId 和请求的处理绑定在一起,就可以在请求处理完成后,直接 stop 这个 startId,保证不会影响后面的请求处理,保证自己处理完成后如果是最后一个会自动停止服务。

实验的方法和代码

创建一个 Activity 添加两个按钮,一个启动服务,一个停止服务。停止服务的代码不用 Intent 方式,而是自定义了一个停止随机 startId 的方法。通过多次试验,可以观察到上文描述的规则。

public class MyService extends Service {
    public static MyService sInstance = null; // 试验用,实际代码不要这么写。
    @Override
    public void onCreate() {
        super.onCreate();
        sInstance = this; // 试验用,实际代码不要这么写。
    }

    private int mLastId = 0;
    private Random random = new Random();

    public void stopOnce() {
        // 随机一个 id,范围是 [1, mLastId + 1],可以验证大于等于小于三种情况。
        int id = random.nextInt(mLastId + 1) + 1;
        Log.d(TAG, "stopOnce: random, id=" + id);
        stopSelfResult(id);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: startId=" + startId);
        mLastId = startId;
        return super.onStartCommand(intent, flags, startId);
    }
}

public class MainActivity ... {
    @Override
    public void onClick(View v) {
        if (id == R.id.startService) {
            Intent intent = new Intent(this, MyService.class);
            startService(intent);
        } else if (id == R.id.stopService) {
            MyService.sInstance.stopOnce();
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读