Two ways to start Service and "Service and Activity data interaction"

Keywords: Android Java

1. Two ways to start the service

There are two ways to start a Service in an Activity. The life cycle of a Service is different when it is started in different ways. Now, four buttons are defined in an Activity, which are startService, stopService, bindService and unbindService. Log logs are printed in each life cycle of the Service to view the execution of the life cycle through logs:

// MainActivity.kt
class MainActivity : AppCompatActivity(){
    var mService: MyService? = null
    var isBind = false
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val intent = Intent(this, MyService::class.java)
        val conn = object : ServiceConnection {
            override fun onServiceDisconnected(name: ComponentName?) {
                Log.e("abc", "-- Activity in onServiceDisconnected --")
            }

            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mService = (service as MyService.MyBinder).getService()
                Log.e("abc", "-- Activity in onServiceConnected --")
            }
        }

        startService.setOnClickListener {
            startService(intent)
        }
        stopService.setOnClickListener {
            stopService(intent)
        }
        bindService.setOnClickListener {
            isBind = bindService(intent, conn, Context.BIND_AUTO_CREATE)
        }
        unBindService.setOnClickListener {
            if (isBind) {
                isBind = false
                unbindService(conn)
            }
        }
    }
}
// MyService.kt
class MyService: Service() {
    override fun onBind(intent: Intent?): IBinder? {
        Log.e("abc", "-- onBind --")
        return MyBinder()
    }

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

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

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

    override fun onUnbind(intent: Intent?): Boolean {
        Log.e("abc", "-- onUnbind --")
        return super.onUnbind(intent)
    }

    inner class MyBinder : Binder() {
        fun getService(): MyService {
            return this@MyService
        }
    }
}

In fact, there used to be a Service life cycle called onStart(), which was later abandoned. Its function was replaced by onStartCommand().

1.1 startService

In MainActivity, you can start and stop the Service as follows:

// start-up
val intent = Intent(this, MyService::class.java)
startService(intent)
// Stop it
stopService(intent)
// Log
-- onCreate --
-- onStartCommand --
-- onDestroy --

1.2 bindService

The bindService mode is a little more complicated:

// binding
val conn = object : ServiceConnection {
    override fun onServiceDisconnected(name: ComponentName?) {}
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) { }
}
bindService(intent, conn, Context.BIND_AUTO_CREATE)
// Stop it
unbindService(conn)
// Log
-- onCreate --
-- onBind --
-- onUnbind --
-- onDestroy --

When binding, the onBind life cycle is executed. When unbinding, the onUnbind is called first, and then the onDestroy is called.

1.3 mixing of two ways

If you start the Service with startService and bind it with bindService once (the order of the two can also be reversed), then the simple stopService or unbindService cannot terminate the Service at this time, which requires the combination of the two. Specific details:

1.3.1 stop service before unbindService

// Log
-- onCreate --
-- onStartCommand --
-- onBind --

// stopService has no response

// unbindService:
-- onUnbind --
-- onDestroy --

There is no response when calling stopService. The Service can only be destroyed when calling unbindService.

1.3.2 unbindService before stopService

-- onCreate --
-- onStartCommand --
-- onBind --

// unbindService
-- onUnbind --

// stopService
-- onDestroy --

Calling unbondservice can only be unbound and cannot be destroyed. Only calling stopService can destroy the Service

1.4 precautions

  1. When startService is called multiple times, the onStartCommand method in the Service will execute multiple times; however, when bindService is used multiple times, onBind will execute only once.
  2. When the bindService mode opens a Service, the Service life cycle is bound to the Activity that opens it. After the Activity is destroyed (onDestroy), the Service opened by the startService mode can continue to survive (you can print the life cycle of the Activity and the Service to view at the same time, which is not illustrated here).

2. Data interaction between service and Activity

In fact, as can be seen from the previous code, in MainActivity, you can get the reference of Service (specifically, onServiceConnected), and the methods in Service calling Activity are mainly described.

2.1 Activity calls Service method

Looking back at the onBind life cycle of the Service, we can see that this method returns an IBinder type, and we specifically return a subclass of its subclass (the subclass of IBinder is Binder, and the subclass of Binder is MyBinder), which is a polymorphism (if you don't understand polymorphism, check the clam yourself, this article doesn't introduce polymorphism, and it's too long to expand):

// MyBinder is an internal class defined by Service itself
inner class MyBinder : Binder() {
        fun getService(): MyService {
            return this@MyService
        }
    }
// Binder source code
public class Binder implements IBinder {
...Code omission
}
// onBind life cycle
override fun onBind(intent: Intent?): IBinder? {
        return MyBinder()
    }
// conn used to open Service in bindService mode
val conn = object : ServiceConnection {
            override fun onServiceDisconnected(name: ComponentName?) {
            }
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mService = (service as MyService.MyBinder).getService()
            }
        }

Now, you can use the mService object to call any method in the Service, such as printtx

 fun printTxt(txt : String) {
        Log.e("abc", "-- this is txt -- $txt")
    }

In fact, I think it's OK to directly new a MyService object mService2, and then myService2.printTxt("install Force"), which seems to make no difference.

2.2 Service actively transfers data to Activity

Service has no reference to Activity, so it can pass data to Activity through interface callback or broadcast.

2.2.1 interface callback

Define interface:

interface CallBack {
    fun call(index: Int)
}

Initialization in Service:

private var callBack:CallBack ?= null
fun setListener(callBack: CallBack){
    this.callBack = callBack
}

Activity implements CallBack (or anonymous inner class):

class MainActivity : AppCompatActivity(), CallBack {
    override fun call(index: Int) {
        Log.e("abc", "This is index value : $index")
    }
}
mService.setListener(this@MainActivity)

// perhaps
mService!!.setListener(object :CallBack{
    override fun call(index: Int) {
        Log.e("abc", "This is index value : $index")
    }
})

With the callBack object in the Service, you can actively transfer data to the Activity.

2.2.2 broadcasting

The third-party framework of Android's own BroadcastReceiver or EventBus is not very different. First register the receiver in the Activity, and then send the broadcast data in the Service. There is no specific example. It should be noted that broadcast is better than callback if a Service delivers data to multiple activities.

Source address

Click to view

Posted by WhiteShade6 on Thu, 23 Apr 2020 20:37:49 -0700