在Android中绑定了碎片和喷气背包的绑定服务
#kotlin #android #mobile #tristan

目录

  1. What we are making
  2. What is a bound service
  3. Steps to create a Bound service
  4. Full code

我在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上与我联系。