手机任何状态下超精准振动器触发

问题描述 投票:0回答:1

我的 Android 应用程序 (Jetpack Compose) 需要在非常精确的时间触发手机振动器(最大延迟几毫秒)。

振动器必须在手机和应用程序的任何状态下触发:手机唤醒并且应用程序在前台,手机处于空闲状态并且应用程序在后台,其他应用程序获得大量处理器,...我只想告诉用户“请不要关闭应用程序,而是用手机做任何您想做的事”:-)

为此,我使用(请参阅下面的代码):

  • A 前台服务,有通话权限
  • 一个NTP时钟帮助我补偿本地系统时间的漂移
  • 一个函数 alarmManager.setExactAndAllowWhileIdle() 设置闹钟

它工作得很好,但在某些情况下(手机长时间闲置,很多应用程序运行,...),振动器启动有延迟,可以达到几秒钟,这在我的用例中并不好:-)

您是否看到我应该使用任何其他 Android 例程或工具来使我的振动器触发非常精确?谢谢!

<application>
...
        <receiver android:name=".AlarmReceiver" />
        <service android:name=".VibrationService" android:exported="false" android:foregroundServiceType="phoneCall"/>
...
 </application>
 ...
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            Project4Theme {
                ScheduleVibrationAlarm(context = this)               
                }
            }
        }
    }
}

@Composable
fun ScheduleVibrationAlarm(context: Context) {
    var calendar by remember { mutableStateOf(Calendar.getInstance()) }
    var timeDifference by remember { mutableStateOf<Long?>(null) }

    Column(
    ) {
        Text(text = "Schedule Vibration Alarm")
        Button(onClick = {
            Thread {
                try {
                    // Get NTP time
                    val ntpClient = NTPUDPClient()
                    ntpClient.defaultTimeout = 10000
                    val inetAddress = InetAddress.getByName("pool.ntp.org")
                    val timeInfo = ntpClient.getTime(inetAddress)
                    timeInfo.computeDetails()

                    // Get local system time
                    val localTime = System.currentTimeMillis()

                    // Get difference
                    val ntpTime = timeInfo.message.transmitTimeStamp.time
                    timeDifference = ntpTime - localTime

                    // Set vibrator alarm time (17:00 today)
                    calendar.set(Calendar.HOUR_OF_DAY, 17)
                    calendar.set(Calendar.MINUTE, 0)
                    calendar.set(Calendar.SECOND, 0)
                    calendar.set(Calendar.MILLISECOND, (timeDifference!!.toInt()*-1))
                    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
                    val intent1 = Intent(context, AlarmReceiver::class.java)
                    val pendingIntent1 = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
                    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent1)

                } catch (e: Exception) {
                    Log.e("NTP", "Fail on NTP get", e)
                }
            }.start()
        }) {
            Text(text = "Set Vibrator Alarm")
        }
    }
}

class AlarmReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val vibrationIntent = Intent(context, VibrationService::class.java)
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            context.startForegroundService(vibrationIntent)
        } else {
            context.startService(vibrationIntent)
        }
    }
}

class VibrationService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        // Create a notif (if not, Android kill foreground service :)
        createNotificationChannel()
        val notification = NotificationCompat.Builder(this, "VibrationServiceChannel")
            .setContentTitle("Vibrator")
            .setContentText("Vibrator is running ")
            .build()

        // Start foreground service
        startForeground(1, notification)

        // Vibrate
        val vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator
        vibrator.vibrate(1000)

        // Stop service
        stopSelf()
        return START_NOT_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? = null

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val serviceChannel = NotificationChannel(
                "VibrationServiceChannel",
                "Vibration Service Channel",
                NotificationManager.IMPORTANCE_DEFAULT
            )
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(serviceChannel)
        }
    }
}
android kotlin android-jetpack-compose precision ntp
1个回答
0
投票

您想要的东西在 Android 手机上是不可能实现的。 该操作系统不是实时操作系统,它的设计初衷并不是提供这种时间关键型性能。 如果您确实需要此功能,则需要完全使用不同的操作系统。 或者您需要重新考虑您的要求 - 为什么您需要这些振动精确到秒?

© www.soinside.com 2019 - 2024. All rights reserved.