目录
我在Google Playstore上的应用程序
github代码
Resources
我们将要制作
- 在本教程中,我们将了解界限,找出如何在片段内绑定一个服务,并最终从服务中获得值并在JetPack组成的内部互动。
什么是服务?
-
正如documentation所述:
A Service is an application component that can perform long-running operations in the background. It does not provide a user interface. Once started, a service might continue running for some time, even after the user switches to another application.
-
这基本上意味着它可以在我们的应用程序做其他事情时为我们做事。但是,重要的是要注意在其托管过程的主线程(UI线程)中运行的服务运行。这意味着,如果我们有任何网络调用,我们应该切换到另一个线程。请咨询THIS图表以确定您是否需要使用Coroutines或Workmanager
什么是绑定的服务?
-
正如documentation所述:
A bound service is the server in a client-server interface. It lets components such as activities bind to the service, send requests and receive responses. A bound service typically lives only while it serves another application component and does not run in the background indefinitely.
-
这意味着,这是我们的UI可以与之互动的服务,我们可以将其打开和关闭片段/活动
创建界限的步骤
- 创建一个界限,有7个步骤:
1)扩展服务接口
2)为服务添加功能
3)在清单文件中注册服务
4)创建连接
5)绑定服务
6)解开服务
7)UI代码
1)扩展服务接口
class BillingService : Service() {
// Binder given to clients.
private val binder = LocalBinder()
override fun onBind(intent: Intent): IBinder {
return binder
}
}
- 从技术上讲,这是服务要求我们实施的唯一代码。 Android系统将在创建上调用OnBind(),以允许其他应用程序组件与此服务进行交互。但是,要使此服务启动并运行,我们需要添加更多代码:
class BillingService : Service() {
// Binder given to clients.
private val binder = LocalBinder()
inner class LocalBinder : Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): BillingService = this@BillingService
}
override fun onBind(intent: Intent): IBinder {
return binder
}
}
- 请注意我们添加的内部类
LocalBinder
。该课程将允许其他应用程序组件通过将Android系统提供的活页夹与我们的服务进行交互,并将其投放给我们的LocalBinder。然后,我们可以使用getService()
直接访问服务。还困惑吗?别担心,只知道LocalBinder可以使其他组件访问我们的服务
2)添加服务方法
- 这可以是您想要的任何代码。这确实取决于您的应用程序的需求,对于此简单实现,它看起来像这样:
class BillingService : Service() {
// Binder given to clients.
private val binder = LocalBinder()
// Random number generator.
private val mGenerator = Random()
/** Method for clients. */
val randomNumber: Int
get() = mGenerator.nextInt(100)
inner class LocalBinder : Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): BillingService = this@BillingService
}
//called by the Android system
// IBinder gets passed to onServiceConnected()
override fun onBind(intent: Intent): IBinder {
return binder
}
}
-
randomNumber
是客户端将访问的内容。如果您的界限执行任何酷行动,这将是您实施它的时候。
3)在清单文件中注册服务
- 本部分是
VERY IMPORTANT!!!
,没有此步骤,您的服务将无法使用。在您的内部AndroidManifest.xml文件中,您需要在应用程序标签内进行服务:
<application>
<service
android:name=".background.BillingService"
android:description="@string/service_description"
/>
</application>
-
android:name
将包含您服务的路径,我的是背景。BillingService,因为我将服务存储在一个称为背景的包装中。我们使用android:description
告诉用户我们的服务的作用。如果用户启用了开发人员设置并正在查看运行/缓存服务,则可以看到这一点。没有描述,用户可以将服务视为恶意并破坏服务。
4)创建连接
- 现在,我们可以将片段内部移动(所有这些都可以在活动中起作用),并创建一个
ServiceConnection
接口的实例。
class MainFragment : Fragment() {
private lateinit var mService: BillingService
private var _uiState: MutableState<Boolean> = mutableStateOf(false)
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName,
service: IBinder) {
// We've bound to LocalService, cast the IBinder and
//get LocalService instance.
val binder = service as BillingService.LocalBinder
mService = binder.getService()
_uiState.value = true
}
override fun onServiceDisconnected(arg0: ComponentName) {
_uiState.value = false
}
}
}
- 首先让我们谈谈:
private lateinit var mService: BillingService
private var _uiState: MutableState<Boolean> =
mutableStateOf(false)
-
mService
是我们一直在实施的服务。这将允许我们的碎片代码与BillingService进行交互。_uiState
解释更为复杂。我们之所以使用它,是因为我们使用的是JetPack组成代码。但是,当我们以后绑定服务并与ServiceConnection
建立连接时,它不会立即返回。从本质上讲,它是异步的。通过在建立连接和_uiState.value = true
时使用MutableState
,它将导致我们的撰写功能重新组合并显示适当的UI
5)绑定服务
- 现在,我们的片段内部仍在内部,我们必须实际绑定服务。我们这样做:
override fun onStart() {
super.onStart()
// Bind to LocalService.
Intent(this.requireContext(), BillingService::class.java).also { intent ->
activity?.bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
}
-
用上面的代码块指出的重要一件事是
Intent
,特别是因为我们正在创建所谓的Explicit Intent。我还想指出,该文档专门指出:To ensure that your app is secure, always use an explicit intent when starting a Service
。因此,隐式意图是安全的。 -
意图是对要执行的操作的抽象描述。使用
Intent(this.requireContext(), BillingService::class.java)
,我们告诉Android系统创建和处理我们的BillingService
。然后与:
.also { intent ->
activity?.bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
- 我们说我们想将服务绑定到我们的活动中。
intent
将代表我们的服务。connection
是一种回调,用于监视我们的服务并没有绑定。Context.BIND_AUTO_CREATE
告诉系统,如果尚未创建该服务。
6)未绑架服务:
- 最后,我们通过调用
unbindService
方法并将其传递给连接对象,将我们的服务固定在活动中。
override fun onStop() {
super.onStop()
activity?.unbindService(connection)
_uiState.value = false
}
7)UI代码
- 在片段的UI
onCreateView()
方法中,我们现在可以放入普通的JetPack组成代码,现在我们拥有_uiState.value
来确定服务的连接状态。
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
setContent {
if(_uiState.value){
val data =mService.randomNumber
Text("$data", fontSize = 60.sp)
}else{
Text("NOT CONNECTED")
}
}
}
- 初始状态将是错误的。但是,当
ServiceConnection
对象更新时,它将触发重新组件并显示所需的UI
完整代码:
服务代码:
class BillingService : Service() {
// Binder given to clients.
private val binder = LocalBinder()
// Random number generator.
private val mGenerator = Random()
/** Method for clients. */
val randomNumber: Int
get() = mGenerator.nextInt(100)
inner class LocalBinder : Binder() {
// Return this instance of LocalService so clients can call public methods
fun getService(): BillingService = this@BillingService
}
//called by the Android system
// IBinder gets passed to onServiceConnected()
override fun onBind(intent: Intent): IBinder {
return binder
}
}
- 片段代码:
class MainFragment : Fragment() {
private var _binding:FragmentMainBinding? = null
private val binding get() = _binding!!
private lateinit var mService: BillingService
private var _uiState: MutableState<Boolean> = mutableStateOf(false)
/** Defines callbacks for service binding, passed to bindService(). */
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
// We've bound to LocalService, cast the IBinder and get LocalService instance.
val binder = service as BillingService.LocalBinder
mService = binder.getService()
Timber.tag("THINGSS").d(className.className)
_uiState.value = true
}
override fun onServiceDisconnected(arg0: ComponentName) {
_uiState.value = false
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val billingViewModel: BillingViewModel by activityViewModels()
lifecycle.addObserver(billingViewModel)
_binding = FragmentMainBinding.inflate(inflater,container,false)
val view = binding.root
binding.composeView.apply{
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
if(_uiState.value){
val data =mService.randomNumber
Text("$data", fontSize = 60.sp)
}else{
Text("NOT CONNECTED")
}
}
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
//I don't think we have to remove this. I think it is done automatically for us.
// lifecycle.removeObserver(billingViewModel)
}
override fun onStart() {
super.onStart()
// Bind to LocalService.
Intent(this.requireContext(), BillingService::class.java).also { intent ->
activity?.bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
activity?.unbindService(connection)
_uiState.value = false
}
}
结论
- 感谢您抽出宝贵的时间阅读我的博客文章。如果您有任何疑问或疑虑,请在下面发表评论或在Twitter上与我联系。