Flutter:我怎样才能永久注册一个传感器(并且永远不会注销它?)
Flutter: how can I permanently register a sensor (and never unregister it?)
TL;DR 我怎样才能让我的应用程序永久 Android 传感器 running/active/registered,即使我关闭它?
Objective:
我正在制作一个 Flutter 应用程序,它使用 pedometer 包
来计算您的步数
它使用 built-in sensor TYPE_STEP_COUNTER
of Android,
returns the # of steps taken since last boot (iOS). On Android, any steps taken before installing the app are not counted.
我是如何实现的:
- 当应用程序在前台 运行 时,每一步都会导致
a
myStepCount
增加 1.
- 在所有其他情况下 (phone 锁定,转到主屏幕,关闭应用程序...), android
TYPE_STEP_COUNTER
传感器 应该
仍然是 运行 在后台,一旦我再次打开我的应用程序,
新的 stepCount
和上次保存的 stepCount
之间的区别(已保存
使用 shared_prefs) 将被计算并添加到 myStepCount
.
重要提示:
TYPE_STEP_COUNTER
sensor must be permanently running/stay registered in the background,即使在我锁定 phone 后,也可以转到主屏幕或关闭应用程序...
观察:
- 在我的 Samsung Galaxy A02s 上,我的应用程序运行得非常好,就像它应该的那样
(如上所述)。那是因为 phone 我也有
Google 安装了 Fit 应用程序,它可以 24/7 全天候跟踪您的步数(因此
TYPE_STEP_COUNTER
传感器已永久注册)。
- 在我的 Samsung Galaxy S7 上,我的应用程序无法正常工作。
myStepCount
当我在应用程序运行时采取步骤时会增加
运行 在前台。但是应用程序关闭时采取的步骤
一旦我再次打开该应用程序,不会 添加到 myStepCount
。
注意:我没有任何其他像 Google 这样的计步应用程序适合这个 phone。
结论:
我需要找到一种方法从我的 Flutter 应用程序注册 TYPE_STEP_COUNTER
传感器,并在我关闭应用程序后保持它的注册状态。
2 次尝试(但未成功)解决方案:
第一次尝试:
从我的 Flutter 代码中调用 Native Android 代码来注册传感器
这是我的 main.dart
文件(为简单起见省略了不重要的部分):
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(App());
}
class App extends StatefulWidget {
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
_activateStepCounterSensor();
} else if (Platform.isIOS) {
//TODO check if anything is needed to to here
}
}
void _activateStepCounterSensor() async {
MethodChannel _stepCounterChannel = MethodChannel('com.cedricds.wanderapp/stepCounter'); //convention
dynamic result = await _stepCounterChannel.invokeMethod('activateStepCounterSensor');
switch (result) {
case "success":
//The following line gets printed when I run the flutter app on my Samsung Galaxy S7:
print('_activateStepCounterSensor(): successfully registered step counter sensor for android');
break;
case "error":
print('_activateStepCounterSensor(): failed to register step counter sensor (not available) for android');
//TODO display errorpage (because app is completely useless in this case)
break;
default:
print('_activateStepCounterSensor(): unknown result: $result');
break;
}
}
//build() and other lifecycle-methods and helper methods: not important for this question
}
这是我的 MainActivity.kt
文件:
package com.cedricds.wanderapp
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.util.Log
import android.widget.Toast
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity2: FlutterActivity(), SensorEventListener {
private val STEP_COUNTER_CHANNEL = "com.cedricds.wanderapp/stepCounter";
private lateinit var channel: MethodChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, STEP_COUNTER_CHANNEL)
channel.setMethodCallHandler { call, result ->
when(call.method){ //this is like switch-case statement in Java
"activateStepCounterSensor" -> {
activateStepCounterSensor(result)
}
}
}
}
private var sensorManager : SensorManager?=null
private var sensor: Sensor ?= null
private fun activateStepCounterSensor(result: MethodChannel.Result) {
//This line gets printed when I run the flutter app, so the method gets called successfully:
Log.d("Android", "Native Android: activateStepCounterSensor()")
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
if (sensor == null) {
Toast.makeText(this, "missing hardware.", Toast.LENGTH_LONG).show()
result.error("error", "error", "error")
} else {
sensorManager?.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL)
//This line gets printed:
Log.d("Android", "Native Android: registered TYPE_STEP_COUNTER")
//and never unregister that listener
result.success("success")
}
}
override fun onSensorChanged(p0: SensorEvent?) {}
override fun onAccuracyChanged(p0: Sensor?, p1: Int) {}
}
尽管少数 print(...)
和 Log.d(...)
按预期打印在控制台中,但该应用程序无法按我预期的方式运行。当我退出应用程序时,例如步行 50 步,然后再次打开应用程序,那 50 步就不见了。似乎传感器在某处未注册。
第二次尝试:
通过删除 unregisterListener(...)
修改计步器包的代码:
我对文件所做的唯一更改是 2 Log.d(...)
语句,更重要的是,注释掉了特定的代码行。
从计步器包修改 SensorStreamHandler.kt
:
package com.example.pedometer
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Looper
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import android.os.Handler
import android.util.Log
class SensorStreamHandler() : EventChannel.StreamHandler {
private var sensorEventListener: SensorEventListener? = null
private var sensorManager: SensorManager? = null
private var sensor: Sensor? = null
private lateinit var context: Context
private lateinit var sensorName: String
private lateinit var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
constructor(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, sensorType: Int) : this() {
this.context = flutterPluginBinding.applicationContext
this.sensorName = if (sensorType == Sensor.TYPE_STEP_COUNTER) "StepCount" else "StepDetection"
sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager!!.getDefaultSensor(sensorType)
this.flutterPluginBinding = flutterPluginBinding
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
Log.d("Pedometer", "Native Android: onListen()")
if (sensor == null) {
events!!.error("1", "$sensorName not available",
"$sensorName is not available on this device");
} else {
sensorEventListener = sensorEventListener(events!!);
sensorManager!!.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
override fun onCancel(arguments: Any?) {
Log.d("Pedometer", "Native Android: onCancel()")
//The only change I did: commenting out the following line:
// sensorManager!!.unregisterListener(sensorEventListener);
}
}
这也没有解决我的问题。因此,如果有人知道如何在我的 flutter 应用程序中永久注册 TYPE_STEP_COUNTER 传感器,请告诉我。
更新:
我已经联系了计步器包的开发人员之一,他建议我使用 flutter_foreground_service(与计步器由相同的 team/company 开发)。有效。
但如果有其他方法(可能类似于我的 2 次失败尝试),我仍然会觉得它很有趣。
TL;DR 我怎样才能让我的应用程序永久 Android 传感器 running/active/registered,即使我关闭它?
Objective:
我正在制作一个 Flutter 应用程序,它使用 pedometer 包
来计算您的步数
它使用 built-in sensor TYPE_STEP_COUNTER
of Android,
returns the # of steps taken since last boot (iOS). On Android, any steps taken before installing the app are not counted.
我是如何实现的:
- 当应用程序在前台 运行 时,每一步都会导致
a
myStepCount
增加 1. - 在所有其他情况下 (phone 锁定,转到主屏幕,关闭应用程序...), android
TYPE_STEP_COUNTER
传感器 应该 仍然是 运行 在后台,一旦我再次打开我的应用程序, 新的stepCount
和上次保存的stepCount
之间的区别(已保存 使用 shared_prefs) 将被计算并添加到myStepCount
.
重要提示:
TYPE_STEP_COUNTER
sensor must be permanently running/stay registered in the background,即使在我锁定 phone 后,也可以转到主屏幕或关闭应用程序...
观察:
- 在我的 Samsung Galaxy A02s 上,我的应用程序运行得非常好,就像它应该的那样
(如上所述)。那是因为 phone 我也有
Google 安装了 Fit 应用程序,它可以 24/7 全天候跟踪您的步数(因此
TYPE_STEP_COUNTER
传感器已永久注册)。 - 在我的 Samsung Galaxy S7 上,我的应用程序无法正常工作。
myStepCount
当我在应用程序运行时采取步骤时会增加 运行 在前台。但是应用程序关闭时采取的步骤 一旦我再次打开该应用程序,不会 添加到myStepCount
。
注意:我没有任何其他像 Google 这样的计步应用程序适合这个 phone。
结论:
我需要找到一种方法从我的 Flutter 应用程序注册 TYPE_STEP_COUNTER
传感器,并在我关闭应用程序后保持它的注册状态。
2 次尝试(但未成功)解决方案:
第一次尝试:
从我的 Flutter 代码中调用 Native Android 代码来注册传感器
这是我的 main.dart
文件(为简单起见省略了不重要的部分):
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(App());
}
class App extends StatefulWidget {
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
_activateStepCounterSensor();
} else if (Platform.isIOS) {
//TODO check if anything is needed to to here
}
}
void _activateStepCounterSensor() async {
MethodChannel _stepCounterChannel = MethodChannel('com.cedricds.wanderapp/stepCounter'); //convention
dynamic result = await _stepCounterChannel.invokeMethod('activateStepCounterSensor');
switch (result) {
case "success":
//The following line gets printed when I run the flutter app on my Samsung Galaxy S7:
print('_activateStepCounterSensor(): successfully registered step counter sensor for android');
break;
case "error":
print('_activateStepCounterSensor(): failed to register step counter sensor (not available) for android');
//TODO display errorpage (because app is completely useless in this case)
break;
default:
print('_activateStepCounterSensor(): unknown result: $result');
break;
}
}
//build() and other lifecycle-methods and helper methods: not important for this question
}
这是我的 MainActivity.kt
文件:
package com.cedricds.wanderapp
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.util.Log
import android.widget.Toast
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity2: FlutterActivity(), SensorEventListener {
private val STEP_COUNTER_CHANNEL = "com.cedricds.wanderapp/stepCounter";
private lateinit var channel: MethodChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, STEP_COUNTER_CHANNEL)
channel.setMethodCallHandler { call, result ->
when(call.method){ //this is like switch-case statement in Java
"activateStepCounterSensor" -> {
activateStepCounterSensor(result)
}
}
}
}
private var sensorManager : SensorManager?=null
private var sensor: Sensor ?= null
private fun activateStepCounterSensor(result: MethodChannel.Result) {
//This line gets printed when I run the flutter app, so the method gets called successfully:
Log.d("Android", "Native Android: activateStepCounterSensor()")
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
if (sensor == null) {
Toast.makeText(this, "missing hardware.", Toast.LENGTH_LONG).show()
result.error("error", "error", "error")
} else {
sensorManager?.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL)
//This line gets printed:
Log.d("Android", "Native Android: registered TYPE_STEP_COUNTER")
//and never unregister that listener
result.success("success")
}
}
override fun onSensorChanged(p0: SensorEvent?) {}
override fun onAccuracyChanged(p0: Sensor?, p1: Int) {}
}
尽管少数 print(...)
和 Log.d(...)
按预期打印在控制台中,但该应用程序无法按我预期的方式运行。当我退出应用程序时,例如步行 50 步,然后再次打开应用程序,那 50 步就不见了。似乎传感器在某处未注册。
第二次尝试:
通过删除 unregisterListener(...)
修改计步器包的代码:
我对文件所做的唯一更改是 2 Log.d(...)
语句,更重要的是,注释掉了特定的代码行。
从计步器包修改 SensorStreamHandler.kt
:
package com.example.pedometer
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Looper
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import android.os.Handler
import android.util.Log
class SensorStreamHandler() : EventChannel.StreamHandler {
private var sensorEventListener: SensorEventListener? = null
private var sensorManager: SensorManager? = null
private var sensor: Sensor? = null
private lateinit var context: Context
private lateinit var sensorName: String
private lateinit var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
constructor(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, sensorType: Int) : this() {
this.context = flutterPluginBinding.applicationContext
this.sensorName = if (sensorType == Sensor.TYPE_STEP_COUNTER) "StepCount" else "StepDetection"
sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager!!.getDefaultSensor(sensorType)
this.flutterPluginBinding = flutterPluginBinding
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
Log.d("Pedometer", "Native Android: onListen()")
if (sensor == null) {
events!!.error("1", "$sensorName not available",
"$sensorName is not available on this device");
} else {
sensorEventListener = sensorEventListener(events!!);
sensorManager!!.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
override fun onCancel(arguments: Any?) {
Log.d("Pedometer", "Native Android: onCancel()")
//The only change I did: commenting out the following line:
// sensorManager!!.unregisterListener(sensorEventListener);
}
}
这也没有解决我的问题。因此,如果有人知道如何在我的 flutter 应用程序中永久注册 TYPE_STEP_COUNTER 传感器,请告诉我。
更新: 我已经联系了计步器包的开发人员之一,他建议我使用 flutter_foreground_service(与计步器由相同的 team/company 开发)。有效。
但如果有其他方法(可能类似于我的 2 次失败尝试),我仍然会觉得它很有趣。