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
相关产品推荐
相关产品推荐

