Android Intent Service

Posted 鲁迅认识的那只猹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Intent Service相关的知识,希望对你有一定的参考价值。

android Intent Service

学习自

Overview

IntentService 是Service的子类,他被用来根据需求处理异步(IntentService中存在一个工作线程)请求(表现为Intent,从类的名字也可以看出来)。 而且这个Service非常省心,当工作完成后会自动停止,不需要我们手动停止。

使用IntentService

下面是一个简单的通过IntentService来更新进度条的示例,示例流程如下:

  1. Activity中祖册临时的广播接受者,来接收消息,然后更新UI
  2. 点击Button开启IntentService,然后开始增加进度
  3. 当增加了进度以后,通过发送广播,来通知UI改变

技术分享图片

IntentService

class TestIntentService(name: String?) : IntentService(name) {
    /**
     * 注意必须要有一个无参数的构造函数
     * 当前环境使用的API是API26
     * IntentService并没有无参数的构造函数
     * 所以我们这里需要自己创建一个
     * 否则会报错
     *
     * name 参数 代表的工作线程的命名
     * */
    constructor() : this("TestIntentService") {

    }

    companion object {
        val ACTION_UPDATE_PROGRESS = "com.shycoder.cn.studyintentservice.UPDATE_PROGRESS"
    }

    private var progress = 0

    private lateinit var mLocalBroadcastManager: LocalBroadcastManager

    private var isRunning = true

    override fun onCreate() {
        super.onCreate()
        this.mLocalBroadcastManager = LocalBroadcastManager.getInstance(this)
    }

    override fun onHandleIntent(intent: Intent?) {

        Log.e("TAG", "onHandleIntent")

        if (intent == null || intent.action != ACTION_UPDATE_PROGRESS) {
            return
        }

        this.isRunning = true
        this.progress = 0

        while (isRunning) {
            SystemClock.sleep(500)
            progress += 10
            if (progress >= 100) {
                this.isRunning = false
            }
            this.sendBroadcast(if (isRunning) "running" else "finish", progress)
        }
    }

    /**
     * send broadcast to activity for notifying change of status and progress
     * */
    private fun sendBroadcast(status: String, progress: Int) {
        val intent = Intent()
        intent.action = MainActivity.ACTION_STATUS_CHANGED
        intent.putExtra("status", status)
        intent.putExtra("progress", progress)
        this.mLocalBroadcastManager.sendBroadcast(intent)
    }

}

Activity的代码

class MainActivity : AppCompatActivity() {

    val mReceiver = TestBroadcastReceiver()
    lateinit var mLocalBroadcastManager: LocalBroadcastManager

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

        //register broadcast
        val intentFilter = IntentFilter(ACTION_STATUS_CHANGED)
        this.mLocalBroadcastManager = LocalBroadcastManager.getInstance(this)
        mLocalBroadcastManager.registerReceiver(mReceiver, intentFilter)
    }

    /**
     * start intent service
     * */
    fun startIntentService(view: View) {
        val intent = Intent(this, TestIntentService::class.java)
        intent.action = TestIntentService.ACTION_UPDATE_PROGRESS
        this.startService(intent)
    }

    override fun onDestroy() {
        super.onDestroy()
        this.unregisterReceiver(this.mReceiver)

    }

    inner class TestBroadcastReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent?) {
            if (intent == null) {
                return
            }
            val status = intent.getStringExtra("status")
            val progress = intent.getIntExtra("progress", 0)
            tvStatus.text = status
            progress_barTest.progress = progress

        }
    }

    companion object {
        val ACTION_STATUS_CHANGED = "cn.shycoder.studyintentservice.STATUS_CHANGED"
    }
}

多次开启IntentService

当我们试着连续点击多次Button(比如说三次), 稍等片刻就会发现进度条满了以后,又重新开始了直到三次位置。打印出来的Log如下:

E/TAG: onHandleIntent
E/TAG: onHandleIntent
E/TAG: onHandleIntent

通过Log和UI的变化,我们可以发现,如果多次开启Service的话,那么 onHandleIntent 方法就会执行多次,这一点与 Service 大相庭径需要我们格外的关注,至于为什么是这种情况,在接下来的源码解析中会提到。

不要使用Bind的方式开启服务

在我们使用 Service 的时候,为了能和Service进行交互,我们会通过Bind的方式开启服务获取与Service进行通信的Binder,但是Bind开启服务的方式并不适用于 IntentService 下面我们来验证一下。

class TestIntentService(name: String?) : IntentService(name) {
    /**
     * 注意必须要有一个无参数的构造函数
     * 当前环境使用的API是API26
     * IntentService并没有无参数的构造函数
     * 所以我们这里需要自己创建一个
     * 否则会报错
     *
     * name 参数 代表的工作线程的命名
     * */
    constructor() : this("TestIntentService") {

    }

    private val mBinder = MyBinder()

    override fun onBind(intent: Intent?): IBinder {
      Log.e("TAG", "onBind")
      return this.mBinder
    }

    inner class MyBinder : Binder() {
    }
    //...
}

绑定服务

class MainActivity : AppCompatActivity() {
    //....
    lateinit var mLocalBroadcastManager: LocalBroadcastManager

    lateinit var mService: TestIntentService.MyBinder

    private val mConn = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mService = service as TestIntentService.MyBinder
        }

        override fun onServiceDisconnected(name: ComponentName?) {
        }

    }

    /**
     * bind intent service
     * */
    fun bindService(view: View) {
        val intent = Intent(this, TestIntentService::class.java)
        intent.action = TestIntentService.ACTION_UPDATE_PROGRESS
        this.bindService(intent, this.mConn, Context.BIND_AUTO_CREATE)
    }
    //...
}

当我们BindService后,查看Log

E/TAG: onBind

我们发现 onHandleIntent 方法的Log并没有打印出来,这时候 IntentService 就是一个普通的Service了,而不具备IntentService的特性。由此我们可以得出结论——不用使用Bind的方式开启IntentService。

IntentService源码解析


public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper; //looper
    private volatile ServiceHandler mServiceHandler; //handler
    private String mName; //线程的名字
    private boolean mRedelivery;

    /**
      这个Handler是子线程的Handler,并不是与UI通信的Handler
    */
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        /**
          调用 onHandleIntent
        */
        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            //当所有的消息都处理完了就结束服务
            stopSelf(msg.arg1);
        }
    }

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

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {

        super.onCreate();
        //实例化HandlerThread
        //HandlerThread继承自Thread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

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

    /**
    众所周知,如果多次开启Service的话,那么 onStart方法就会执行多次
    IntentService在onStart找那个不断地向Handler发送消息
    */
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        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();
    }

    /**
    onBind 方法被重写,返回null
    */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

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

上面的代码很简单,仅仅是对Service进行了一层封装,大致流程如下:

  1. 当创建service的时候,进行IntentService的初始化操作(onCreate),实例化 HandlerThread
  2. onCreate方法执行以后,紧接着就会调用 onStart 方法,这时候就像向Handler发送消息
  3. handler 会进行排队执行
  4. 当所有的消息都处理完成了以后,会将服务结束

为什么不能用Bind的方式开启IntentService

通过查看源码,我想大家已经找到答案了。因为Start方式和Bind的方式开启Service的时候执行的生命周期的方法是不同的,通过Bind的方法开启Service,并不会执行 onStart 生命周期方法。 所以虽然 Bind的方式开启Service会执行onCreate方法来实例化 HandlerThread 但是因为 onStart 方法才向Handler 中发送数据。

以上是关于Android Intent Service的主要内容,如果未能解决你的问题,请参考以下文章

Android Intent Service

java [Intent] Intent片段以启动Activity,Service或发送广播。 #android_snippet #android

android 在5.0以后不允许使用隐式Intent方式来启动Service

Android隐式(远程)启动Service

android开发中service如何接收activity用intent发送来的数据

android小知识点代码片段