啪!啪!给 JobIntentService 打针, Hilt 号的大针,看你爽不爽?哎呦,Espresso 看不到结果,用 UiAutomator 测。

这篇博客介绍了如何在Android应用中使用JobIntentService,结合MVVM和Dagger-Hilt进行服务管理,并通过Espresso和UiAutomator进行测试。作者详细阐述了JobIntentService的启动和关闭,权限管理,以及在Service中使用LiveData的问题和解决方案。同时,由于Espresso无法检测到Service的结果,文章转向使用UiAutomator进行更有效的测试。
摘要由CSDN通过智能技术生成

0. 简介 Service

Service 不一定用得很长久,那不就成了长佣了吗?我们可以用 JobIntentService ——临时佣人,它跟你的 App 同生共死,真好!但是,启动容易,关闭就毫无头绪了。因为 Service 在后台跑,跟 UI 是不沾边的。如果用 MVVM,我们可以塞 LiveData 。通过方程启动,系统会弹出 “LiveData has not initialed”。如果用 Service 的构造函数,系统会说不接受参数。饶头啊,对不?

没关系,我们可以使用插入式,我提议的是 Dagger-Hilt ,给系统打针。


📦 1. MVVM 包

🌮 Gradle —— 资料库选择:

  • View Binding:
buildFeatures {
   
    viewBinding true
}
//region activity and fragment
// Activity and Fragment
def activity_version = "1.2.1"
implementation "androidx.activity:activity-ktx:$activity_version"
def fragment_version = "1.3.2"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
//endregion

这里随便提提,其实你们可以抄我以前写的,Gradle 太占地方了,所以省略一二。

🔰 MVVM —— 文件排列

在这里插入图片描述

🖐🏻 Helper —— 帮手

  • helper/LogHelper.kt
import android.util.Log

const val TAG = "MLOG"
fun lgd(s:String) = Log.d(TAG, s)
fun lgi(s:String) = Log.i(TAG, s)
fun lge(s:String) = Log.e(TAG, s)
fun lgv(s:String) = Log.v(TAG, s)
fun lgw(s:String) = Log.w(TAG, s)
  • helper/MessageHelper.kt
import android.content.Context
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import android.widget.Toast.LENGTH_SHORT

fun msg(context: Context, s: String, len: Int) =
    if (len > 0) Toast.makeText(context, s, LENGTH_LONG).show()
    else Toast.makeText(context, s, LENGTH_SHORT).show()

🖌️ 2. UI Design 平面设计


et_message :输入资料进Service。
tv_service :Service 的反应。


💼 3. JobIntentService

JobIntentService 是 IntentService 的改良版。

🐔 开始服务

这个服务是用方程启动的——enqueueWork

fun enqueueWork(context: Context, work: Intent) {
   
    enqueueWork(context, MyIntentService::class.java, JOB_ID, work)
}

这个 enqueueWork 有 4 种参数:

  1. Context
  2. Service class
  3. Job ID
  4. Intent

⌛️ 关闭服务

用 instance 关闭。

class MyIntentService: JobIntentService() {
   

    init {
   
        instance = this
    }
    
	companion object {
   
		private lateinit var instance: MyIntentService
		private val JOB_ID = 4343443

		fun enqueueWork(context: Context, work: Intent) {
   ...}

		fun stopService() {
   
			lgd("MyIntentService: Service is stopping...")
			instance.stopSelf()
		}
	}
}

你瞧,自己关自己。


🚪 4. Permission

📍 AndroidManifest.xml

<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
    ...
    <activity android:name=".ui.MainActivity">
        ...
    </activity>
    <service android:name=".service.MyIntentService"
        android:permission="android.permission.BIND_JOB_SERVICE"
        android:exported="true" />
</application>

✒️ ui/MainActivity.kt

// check manifests for permissions
private val REQUIRED_PERMISSIONS = arrayOf(
    Manifest.permission.WAKE_LOCK
)

class MainActivity : AppCompatActivity() {
   

    // app permission
    private val reqMultiplePermissions = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) {
    permissions ->
        permissions.entries.forEach {
   
            lgd("mainAct: Permission: ${
     it.key} = ${
     it.value}")
            if (!it.value) {
   
                // toast
                msg(this, "Permission: ${
     it.key} denied!", 1)
                finish()
            }
        }
    }

    // =============== Variables
    // view binding
    private lateinit var binding: ActivityMainBinding
    // view model
    val viewModel: MainViewModel by viewModels()

    // =============== END of Variables

    override fun onCreate(savedInstanceState: Bundle?) {
   
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // check app permissions
        reqMultiplePermissions.launch(REQUIRED_PERMISSIONS)
    }

    companion object {
   
        const val USER_INPUT = "USER_INPUT"
    }
}

⌚5. Observables & Hilt 观察和打针

👁‍🗨 观察点

我需要提供两个观察点:

  1. isRunning:服务状态。
  2. userInput:客户输入的内容。有两种方式 IntentExtra 和 LiveData 。我将会测试那种有保证。

🔆 app/ServiceApp.kt 提供 Hilt 应用

@HiltAndroidApp
class ServiceApp: Application()

🗡 di/LiveDataModule.kt

@Module
@InstallIn(SingletonComponent::class)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
如果您使用的是 Hilt 2.28及以上版本,则可以不需要在 AndroidManifest.xml 文件中的 Application 标签添加 AndroidEntryPoint 注解,而是在 HiltApplication 中添加 @HiltAndroidApp 注解即可。 至于如何在代码中使用 Hilt 进行依赖注入,您可以使用 @Inject 注解来标记需要注入的依赖,同时在需要使用该依赖的地方使用 @Inject 注解进行注入。此外,您还可以通过 @Module 和 @Provides 注解来自定义依赖的注入行为。 例如,以下是一个使用 Hilt 进行依赖注入的示例代码: ```java @AndroidEntryPoint public class MyActivity extends AppCompatActivity { @Inject MyDependency myDependency; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 使用注入的依赖 myDependency.doSomething(); } } @Module @InstallIn(ActivityComponent.class) public class MyModule { @Provides MyDependency provideMyDependency() { return new MyDependencyImpl(); } } ``` 在上述代码中,我们使用 @AndroidEntryPoint 标注了 MyActivity,表示该 Activity 会使用 Hilt 进行依赖注入。通过 @Inject 注解标记了 MyDependency 字段,表示需要注入 MyDependency 类型的依赖。在 MyModule 中使用 @Provides 注解提供了 MyDependency 的实例。在 MyActivity 中,我们直接使用注入的 MyDependency 实例来调用其方法。 需要注意的是,上述代码是基于 Hilt 2.28及以上版本进行的示例。如果您使用的是旧版本的 Hilt,可能需要做出一些调整。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值