Service 的兩種啓動方式和「Service 與 Activity 數據交互」

1. Service 的兩種啓動方式

Activity 中可以有兩種方式啓動 Service,不同方式啓動時 Service 的生命週期也不一樣,現在在 Activity 中定義四個 Button,分別是 startServicestopServicebindServiceunbindService,Service 中各生命週期中分別打印 Log 日誌,通過日誌查看生命週期執行情況:

// 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 中 onServiceDisconnected --")
            }

            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mService = (service as MyService.MyBinder).getService()
                Log.e("abc", "-- Activity 中 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
        }
    }
}

其實以前 Service 還有個生命週期叫 onStart(),後來被棄用了,它的功能由onStartCommand()代替。

1.1 startService

在 MainActivity 中可以這樣啓動和停止 Service:

// 啓動
val intent = Intent(this, MyService::class.java)
startService(intent)
// 停止
stopService(intent)
// Log
-- onCreate --
-- onStartCommand --
-- onDestroy --

1.2 bindService

bindService 方式稍微複雜些:

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

綁定時會執行 onBind 生命週期,解綁時先調用 onUnbind再調用onDestroy

1.3 兩種方式混合

如果先用 startService 方式啓動了 Service,再用 bindService 綁定一次(兩者順序也可以顛倒),那麼此時單純的 stopService 或者 unbindService 是無法終止 Service 的,需要二者聯合使用才行。具體細化:

1.3.1 先 stopService 後unbindService

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

// stopService 無反應

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

調用 stopService 沒有反應,調用 unbindService 時方可銷燬 Service。

1.3.2 先 unbindService 後 stopService

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

// unbindService
-- onUnbind --

// stopService
-- onDestroy --

調用 unbindService 只能解綁(onUnbind)不能銷燬,調用 stopService 時纔可以銷燬 Service

1.4 注意事項

  1. 多次調用 startService 時,Service 中的 onStartCommand方法會執行多次;但多次使用 bindService 時,onBind 只執行一次。
  2. bindService 方式打開 Service 時,Service 的生命週期是和打開它的 Activity 綁定的,而 startService 方式打開的 Service 在 Activity 被銷燬後(onDestroy),還可以繼續存活(可以同時打印 Activity 和 Service 的生命週期查看,這裏不舉例子了)。

2. Service 與 Activity 數據交互

其實從前面的代碼中也可以看出,在 MainActivity 中,可以獲取到 Service 的引用(具體來說是onServiceConnected中),Service 調用 Activity 中的方法主要講講。

2.1 Activity 調用 Service 方法

回看 Service 的onBind生命週期可以發現,該方法返回的是一個 IBinder 類型,我們在具體實現是返回的是它子類的子類(IBinder 的 子類是 Binder,Binder 的子類是 MyBinder),這是一種多態(不懂多態的自己查一下蛤,這篇文章不是介紹多態的,展開說太長了):

// MyBinder 是 Service 我自己定義的裏面的內部類
inner class MyBinder : Binder() {
        fun getService(): MyService {
            return this@MyService
        }
    }
// Binder 源碼
public class Binder implements IBinder {
...代碼省略
}
// onBind 生命週期
override fun onBind(intent: Intent?): IBinder? {
        return MyBinder()
    }
// bindService 方式打開 Service 時用到的 conn
val conn = object : ServiceConnection {
            override fun onServiceDisconnected(name: ComponentName?) {
            }
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                mService = (service as MyService.MyBinder).getService()
            }
        }

現在,可以用 mService 對象調用 Service 中的任何方法,比如 printTxt

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

其實我覺得還可以直接 new 一個 MyService 對象 mService2,然後 myService2.printTxt("裝逼"),好像也沒啥區別。

2.2 Service 主動向 Activity 傳輸數據

Service 沒有 Activity 的引用,所以可以通過接口回調或者廣播的方式向 Activity 傳遞數據。

2.2.1 接口回調

定義接口:

interface CallBack {
    fun call(index: Int)
}

Service 中初始化:

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

Activity 實現 CallBack(或者用 匿名內部類):

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

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

Service 中有了 callBack 對象,就可以主動向 Activity 傳輸數據了。

2.2.2 廣播

用 Android 自帶的 BroadcastReceiver 或者 EventBus 這種第三方框架區別不大,先在 Activity 中註冊接受者,然後在 Service 中發射廣播數據,不具體舉例了。需要說明的是,如果是一個 Service 向多個 Activity 傳遞數據,廣播比回調要好一些。

源碼地址

點擊查看

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章