You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Wear OS表盘需ACCESS_FINE_LOCATION权限与网络调用的最佳实现方案

Wear OS表盘获取危险权限与网络调用的最佳方案

一、权限请求核心:通过配置Activity处理

由于Service无法触发系统权限弹窗,必须借助配置Activity完成权限申请,步骤如下:

  • 在AndroidManifest.xml中声明权限并注册配置Activity:
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- Android 10+ 如需后台定位,添加以下权限 -->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    
    <activity android:name=".WatchFaceConfigActivity" android:exported="true" />
    
  • 创建配置Activity,集中处理权限请求:
    利用ActivityResultContracts.RequestMultiplePermissions完成权限申请,授权成功后直接返回表盘Service。
  • 在表盘Service中检查权限状态:
    首次启动或权限缺失时,启动配置Activity引导用户授权;权限通过后再初始化位置相关逻辑。

二、位置获取的优化实现

权限授予后,推荐使用Google Play Services的FusedLocationProviderClient获取位置,兼顾精度与续航:

  • 初始化位置客户端,设置合理更新间隔:根据表盘需求调整(如1分钟一次),避免频繁唤醒设备。
  • 监听表盘可见性:在onVisibilityChanged回调中,仅当表盘可见时开启位置更新,不可见时停止,节省电量。
  • 处理权限回收异常:若用户后续手动关闭权限,捕获SecurityException并重新引导至配置Activity。

三、网络调用的最佳实践

网络请求必须在后台线程执行,结合Wear OS续航特性优化:

  • 用协程处理实时请求:比如获取位置后,在LocationCallback中启动协程调用接口,避免阻塞主线程。
  • 用WorkManager处理周期性同步:若需定期拉取数据(如天气),通过WorkManager设置周期性任务,请求完成后将数据存入本地存储(Room/SharedPreferences)。
  • 异常处理:务必捕获网络异常、超时等情况,避免崩溃影响表盘稳定性。

四、数据传递与UI更新

表盘Service与后台任务/网络请求间的数据传递,推荐简单可靠的方案:

  • 本地存储中转:网络或位置数据写入Room数据库或SharedPreferences,表盘Service读取存储数据更新UI。
  • 实时触发重绘:数据更新后调用invalidate(),确保UI及时刷新。

代码示例片段

配置Activity权限请求

class WatchFaceConfigActivity : AppCompatActivity() {
    private val permissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { results ->
        val allGranted = results.all { it.value }
        if (allGranted) {
            finish()
        } else {
            Toast.makeText(this, "需授予位置权限以显示相关数据", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_config)

        val requiredPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
        } else {
            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
        }

        if (requiredPermissions.any {
                ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
            }) {
            permissionLauncher.launch(requiredPermissions)
        } else {
            finish()
        }
    }
}

表盘Service权限检查与位置更新

class MyWatchFaceService : CanvasWatchFaceService() {
    override fun onCreateEngine(): Engine = MyEngine()

    inner class MyEngine : CanvasWatchFaceService.Engine() {
        private lateinit var locationClient: FusedLocationProviderClient
        private val locationCallback = object : LocationCallback() {
            override fun onLocationResult(result: LocationResult) {
                result.lastLocation?.let { location ->
                    lifecycleScope.launch { fetchRelatedData(location) }
                }
            }
        }

        override fun onCreate(holder: SurfaceHolder) {
            super.onCreate(holder)
            locationClient = LocationServices.getFusedLocationProviderClient(applicationContext)
            checkPermissions()
        }

        private fun checkPermissions() {
            val hasFineLoc = ContextCompat.checkSelfPermission(
                applicationContext, Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
            val hasBgLoc = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                ContextCompat.checkSelfPermission(
                    applicationContext, Manifest.permission.ACCESS_BACKGROUND_LOCATION
                ) == PackageManager.PERMISSION_GRANTED
            } else true

            if (hasFineLoc && hasBgLoc) startLocationUpdates()
            else startConfigActivity()
        }

        private fun startConfigActivity() {
            startActivity(Intent(applicationContext, WatchFaceConfigActivity::class.java).apply {
                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            })
        }

        private fun startLocationUpdates() {
            val request = LocationRequest.create().apply {
                interval = 60000
                fastestInterval = 30000
                priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
            }
            try {
                locationClient.requestLocationUpdates(request, locationCallback, Looper.getMainLooper())
            } catch (e: SecurityException) {
                checkPermissions()
            }
        }

        override fun onVisibilityChanged(visible: Boolean) {
            super.onVisibilityChanged(visible)
            if (visible) checkPermissions()
            else locationClient.removeLocationUpdates(locationCallback)
        }

        private suspend fun fetchRelatedData(location: Location) {
            // 协程中执行网络请求示例
            try {
                val data = apiService.getWeather(location.latitude, location.longitude)
                // 存储数据到SharedPreferences
                withContext(Dispatchers.IO) {
                    getSharedPreferences("watch_face_data", MODE_PRIVATE)
                        .edit()
                        .putString("weather", Gson().toJson(data))
                        .apply()
                }
                invalidate() // 触发表盘重绘
            } catch (e: Exception) {
                Log.e("WatchFace", "Network error", e)
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者user3826764

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 00:22:10