我们经常会看到微信 QQ 以及其他一些运动app里面都有一个计步功能,那它是怎么实现的呢?

今天我们就来实现一下,以下代码都是从一个整体项目中抽离出来的,为了理解简单方便我把UI部分数据保存部分全部都去掉了,只有单纯的计步逻辑和算法。

log日志显示计步:

编写计步逻辑的流程图,方便理解我的思路:

MainActivity :

public class MainActivity extends AppCompatActivity {private BindService bindService;private TextView textView;private boolean isBind;private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {textView.setText(msg.arg1 + "");}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);textView = (TextView) findViewById(R.id.busu);Intent intent = new Intent(MainActivity.this, BindService.class);isBind =  bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);startService(intent); //绷定并且开启一个服务,绷定是为了方便数据交换,启动是为了当当前app不在活动页的时候,计步服务不会被关闭。需要保证当activity不为活跃状态是计步服务在后台能一直运行!}//和绷定服务数据交换的桥梁,可以通过IBinder service获取服务的实例来调用服务的方法或者数据private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {BindService.LcBinder lcBinder = (BindService.LcBinder) service;bindService = lcBinder.getService();bindService.registerCallback(new UpdateUiCallBack() {@Overridepublic void updateUi(int stepCount) {//当前接收到stepCount数据,就是最新的步数Message message = Message.obtain();message.what = 1;message.arg1 = stepCount;handler.sendMessage(message);Log.i("MainActivity—updateUi","当前步数"+stepCount);}});}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onStart() {super.onStart();}@Overridepublic void onDestroy() {  //app被关闭之前,service先解除绑定,如果不解除绑定下次Activity切换到活动界面的时候又会重新开启一个新的计步线程。super.onDestroy();if (isBind) {this.unbindService(serviceConnection);}}
}

activity绷定并且开启的服务:当前服务实现了SensorEventListener接口,SensorEventListener接口是计步传感器的一个回调接口。

 @Overridepublic void onCreate() {super.onCreate();Log.i("BindService—onCreate", "开启计步");new Thread(new Runnable() {@Overridepublic void run() {startStepDetector();Log.i("BindService—子线程", "startStepDetector()");}}).start();}/*** 选择计步数据采集的传感器* SDK大于等于19,开启计步传感器,小于开启加速度传感器*/private void startStepDetector() {if (sensorManager != null) {sensorManager = null;}//获取传感器管理类sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);int versionCodes = Build.VERSION.SDK_INT;//取得SDK版本if (versionCodes >= 19) {//SDK版本大于等于19开启计步传感器addCountStepListener();} else {        //小于就使用加速度传感器addBasePedometerListener();}}/*** 启动计步传感器计步*/private void addCountStepListener() {Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);Sensor detectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);if (countSensor != null) {stepSensorType = Sensor.TYPE_STEP_COUNTER;sensorManager.registerListener(BindService.this, countSensor, SensorManager.SENSOR_DELAY_NORMAL);Log.i("计步传感器类型", "Sensor.TYPE_STEP_COUNTER");} else if (detectorSensor != null) {stepSensorType = Sensor.TYPE_STEP_DETECTOR;sensorManager.registerListener(BindService.this, detectorSensor, SensorManager.SENSOR_DELAY_NORMAL);} else {addBasePedometerListener();}}/*** 启动加速度传感器计步*/private void addBasePedometerListener() {Log.i("BindService", "加速度传感器");mStepCount = new StepCount();mStepCount.setSteps(nowBuSu);//获取传感器类型 获得加速度传感器Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);//此方法用来注册,只有注册过才会生效,参数:SensorEventListener的实例,Sensor的实例,更新速率boolean isAvailable = sensorManager.registerListener(mStepCount.getStepDetector(), sensor, SensorManager.SENSOR_DELAY_UI);mStepCount.initListener(new StepValuePassListener() {@Overridepublic void stepChanged(int steps) {nowBuSu = steps;//通过接口回调获得当前步数updateNotification();    //更新步数通知}});}/*** 通知调用者步数更新 数据交互*/private void updateNotification() {if (mCallback != null) {Log.i("BindService", "数据更新");mCallback.updateUi(nowBuSu);}}@Overridepublic IBinder onBind(Intent intent) {return lcBinder;}/*** 计步传感器数据变化回调接口*/@Overridepublic void onSensorChanged(SensorEvent event) {//这种类型的传感器返回步骤的数量由用户自上次重新启动时激活。返回的值是作为浮动(小数部分设置为0),// 只在系统重启复位为0。事件的时间戳将该事件的第一步的时候。这个传感器是在硬件中实现,预计低功率。if (stepSensorType == Sensor.TYPE_STEP_COUNTER) {//获取当前传感器返回的临时步数int tempStep = (int) event.values[0];//首次如果没有获取手机系统中已有的步数则获取一次系统中APP还未开始记步的步数if (!hasRecord) {hasRecord = true;hasStepCount = tempStep;} else {//获取APP打开到现在的总步数=本次系统回调的总步数-APP打开之前已有的步数int thisStepCount = tempStep - hasStepCount;//本次有效步数=(APP打开后所记录的总步数-上一次APP打开后所记录的总步数)int thisStep = thisStepCount - previousStepCount;//总步数=现有的步数+本次有效步数nowBuSu += (thisStep);//记录最后一次APP打开到现在的总步数previousStepCount = thisStepCount;}}//这种类型的传感器触发一个事件每次采取的步骤是用户。只允许返回值是1.0,为每个步骤生成一个事件。// 像任何其他事件,时间戳表明当事件发生(这一步),这对应于脚撞到地面时,生成一个高加速度的变化。else if (stepSensorType == Sensor.TYPE_STEP_DETECTOR) {if (event.values[0] == 1.0) {nowBuSu++;}}updateNotification();}/*** 计步传感器精度变化回调接口*/@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}/*** 绑定回调接口*/public class LcBinder extends Binder {BindService getService() {return BindService.this;}}/*** 数据传递接口** @param paramICallback*/public void registerCallback(UpdateUiCallBack paramICallback) {this.mCallback = paramICallback;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//返回START_STICKY :在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。// 不久后service就会再次尝试重新创建,因为保留在开始状态,在创建     service后将保证调用onstartCommand。// 如果没有传递任何开始命令给service,那将获取到null的intent。return START_STICKY;}@Overridepublic void onDestroy() {super.onDestroy();//取消前台进程stopForeground(true);}@Overridepublic boolean onUnbind(Intent intent) {return super.onUnbind(intent);}
}

如果sdk版本大于等于19,到这里计步服务就能向activity反馈步数了。但是如果sdk版本小于19,通过加速度传感器计数步数还要通过算法来获取:

public class StepCount implements StepCountListener {private int mCount; //当前步数private int count;  //缓存步数,步数3秒内小于10步则不计数private long timeOfLastPeak = 0;//计时  开始时间 步数3秒内小于10步则不计数private long timeOfThisPeak = 0;//计时  现在时间 步数3秒内小于10步则不计数private StepValuePassListener stepValuePassListener;//接口用来传递步数变化private StepDetector stepDetector;//传感器SensorEventListener子类实例public StepCount() {stepDetector = new StepDetector();stepDetector.initListener(this);}@Overridepublic void countStep() {this.timeOfLastPeak = this.timeOfThisPeak;this.timeOfThisPeak = System.currentTimeMillis();Log.i("countStep","传感器数据刷新回调");
//        notifyListener();if (this.timeOfThisPeak - this.timeOfLastPeak <= 3000L) {if (this.count < 9) {this.count++;} else if (this.count == 9) {this.count++;this.mCount += this.count;notifyListener();} else {this.mCount++;notifyListener();}} else {//超时this.count = 1;//为1,不是0}}public void setSteps(int initNowBusu){this.mCount = initNowBusu;//接收上层调用传递过来的当前步数this.count = 0;timeOfLastPeak = 0;timeOfThisPeak = 0;notifyListener();}/*** 用来给调用者获取SensorEventListener实例* @return 返回SensorEventListener实例*/public StepDetector getStepDetector(){return stepDetector;}/*** 更新步数,通过接口函数通过上层调用者*/public void notifyListener(){if(this.stepValuePassListener != null){Log.i("countStep","数据更新");this.stepValuePassListener.stepChanged(this.mCount);  //当前步数通过接口传递给调用者}}public  void initListener(StepValuePassListener listener){this.stepValuePassListener = listener;}
}
@Overridepublic void onSensorChanged(SensorEvent event) {//当传感器值改变回调此方法for (int i = 0; i < 3; i++) {oriValues[i] = event.values[i];}gravityNew = (float) Math.sqrt(oriValues[0] * oriValues[0]+ oriValues[1] * oriValues[1] + oriValues[2] * oriValues[2]);detectorNewStep(gravityNew);}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {//}public void initListener(StepCountListener listener) {this.mStepListeners = listener;}/** 检测步子,并开始计步* 1.传入sersor中的数据* 2.如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步* 3.符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中* */public void detectorNewStep(float values) {if (gravityOld == 0) {gravityOld = values;} else {if (detectorPeak(values, gravityOld)) {timeOfLastPeak = timeOfThisPeak;timeOfNow = System.currentTimeMillis();if (timeOfNow - timeOfLastPeak >= TimeInterval&& (peakOfWave - valleyOfWave >= ThreadValue)) {timeOfThisPeak = timeOfNow;/** 更新界面的处理,不涉及到算法* 一般在通知更新界面之前,增加下面处理,为了处理无效运动:* 1.连续记录10才开始计步* 2.例如记录的9步用户停住超过3秒,则前面的记录失效,下次从头开始* 3.连续记录了9步用户还在运动,之前的数据才有效* */mStepListeners.countStep();}if (timeOfNow - timeOfLastPeak >= TimeInterval&& (peakOfWave - valleyOfWave >= InitialValue)) {timeOfThisPeak = timeOfNow;ThreadValue = peakValleyThread(peakOfWave - valleyOfWave);}}}gravityOld = values;}/** 检测波峰* 以下四个条件判断为波峰:* 1.目前点为下降的趋势:isDirectionUp为false* 2.之前的点为上升的趋势:lastStatus为true* 3.到波峰为止,持续上升大于等于2次* 4.波峰值大于20* 记录波谷值* 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值* 2.所以要记录每次的波谷值,为了和下次的波峰做对比* */public boolean detectorPeak(float newValue, float oldValue) {lastStatus = isDirectionUp;if (newValue >= oldValue) {isDirectionUp = true;continueUpCount++;} else {continueUpFormerCount = continueUpCount;continueUpCount = 0;isDirectionUp = false;}if (!isDirectionUp && lastStatus&& (continueUpFormerCount >= 2 || oldValue >= 20)) {peakOfWave = oldValue;return true;} else if (!lastStatus && isDirectionUp) {valleyOfWave = oldValue;return false;} else {return false;}}/** 阈值的计算* 1.通过波峰波谷的差值计算阈值* 2.记录4个值,存入tempValue[]数组中* 3.在将数组传入函数averageValue中计算阈值* */public float peakValleyThread(float value) {float tempThread = ThreadValue;if (tempCount < ValueNum) {tempValue[tempCount] = value;tempCount++;} else {tempThread = averageValue(tempValue, ValueNum);for (int i = 1; i < ValueNum; i++) {tempValue[i - 1] = tempValue[i];}tempValue[ValueNum - 1] = value;}return tempThread;}/** 梯度化阈值* 1.计算数组的均值* 2.通过均值将阈值梯度化在一个范围里* */public float averageValue(float value[], int n) {float ave = 0;for (int i = 0; i < n; i++) {ave += value[i];}ave = ave / ValueNum;if (ave >= 8)ave = (float) 4.3;else if (ave >= 7 && ave < 8)ave = (float) 3.3;else if (ave >= 4 && ave < 7)ave = (float) 2.3;else if (ave >= 3 && ave < 4)ave = (float) 2.0;else {ave = (float) 1.3;}return ave;}}

全部代码已经上传,android 计步器——完整项目下载

android 计步器相关推荐

  1. Android计步器算法实现(2)

    Android计步器算法实现(2) 前言 算法实现的意义 现实原因 优缺点 算法原理 运动状态判断 计步原理 步长计算原理 Java实现 补充 观测点的作用 数据的存放方式 前言 在之前我也写过两篇关 ...

  2. android 计步器 开发,Android计步器开发

    本文只赘述Android计步器开发里计步的原理. 在Android4.4版本之后,新增了STEP_COUNTER和STEP_DECTECTOR STEP_COUNTER表示自从开机以来,你走的步数累计 ...

  3. Android计步器算法实现

    最近在研究惯性导航和其他导航算法的融合,顺手把计步.步长等一堆算法写成类了,舒服~ 这篇文章我不会具体的讲解实现原理,有兴趣研究的朋友直接看我写的计步算法实现和步长计算. Android系统有自带的计 ...

  4. Android计步器的实现(1)

    最近项目中要加一个计步器的功能,Github上搜索一堆,都是bug漫天飞(微信也有bug^_^,关于bug的原因有:异常开关机.调整手机时间. 正常开关机.跨天问题,这几种原因复合在一起更容易造成计步 ...

  5. Android计步器的实现(2)

    上一篇见: Android计步器的实现(1) 2.时间戳工具 public abstract class Util4Pedometer {/*** @return milliseconds since ...

  6. Android计步器小Demo

    描述 android计步器的实现,自定义的一个弧形进度条,记步通过手机的传感器来实现,也就是说不支持传感器的机子(应该很老的了吧)就没有效果.看看效果图: 自定义View public class S ...

  7. android计步器开源,开源Android项目pedometer计步器源码

    [实例简介] 开源Android项目计步器源码,打开软件后,手握手机,可根据你走路时胳膊摆的次数准确测试出你走了多少步,有意思吧,而且这个项目的源码是开源的,分iPhone版和Android版,此为a ...

  8. Android 计步器实现

    前段时间系统应用到计步器,在网上看了好多,借鉴大神的代码完成了效果,最后还是决定写下来吧,万一哪天在用到了 .... 1.需要在AndroidManifest.xml中添加权限 <uses-pe ...

  9. android 计步器保活,计步器(Android和iOS)

    更新记录 1.1(2021-04-30) 修复ios打包问题 1.0(2021-03-31) 首发版本 查看更多 平台兼容性 Android iOS 适用版本区间:4.4 - 11.0 适用版本区间: ...

最新文章

  1. 第一课 前言 学PHP就是为了做网站
  2. Spring MVC讲解
  3. DLA SQL技巧:行、列转换和JSON数据列展开
  4. python装饰器模式带参数_Python装饰器使用实例:验证参数合法性 请教Python 使用装饰器实现单例模式的原理...
  5. HBase常用Shell命令
  6. 【图像去噪】基于matlab最佳加权双边滤波图像去噪【含Matlab源码 459期】
  7. Winhex手动恢复删除数据
  8. CVPR 2021 Oral | 妙啊!不怕遮挡的图像线段匹配 SOLD2,还能联合自监督线段检测
  9. 爬虫【11】易班刷网薪系统
  10. Ceph RBD 接口和工具 [Ceph RBD API and Tools]
  11. ISCC2021——web部分
  12. win快捷键失效,win+d,win+e等快捷键功能修复
  13. 应用 Serverless 化,让业务开发心无旁骛
  14. C语言技巧:有if时使用likely和unlikely让代码运行更快
  15. 阿里技术专家深入浅出470页Java虚拟机设计与实现文档总结
  16. python安装dlib遇到的错误:AttributeError:module ‘dlib’ has no attribute ‘get_frontal_face_detector’问题解决
  17. PAT 1009. 说反话
  18. php页面表格导出excel表格数据类型,php页面表格导出excel表格数据类型-php导出excel是不是导出整个表的?可不可以导出指......
  19. 关于端口被占用的解决办法
  20. 优思学院|什么是精益生产?企业如何实现精益生产?

热门文章

  1. hs = hht(imf)的输出介绍
  2. win7的无线网络服务如何启动
  3. 百度超级搜霸远程代码执行漏洞
  4. 全图灵第一章:首个Demo项目【游戏类型讨论】
  5. 以太坊-EVM第2部分——固定长度数据类型的表示方法
  6. 计算机二级59什么梗,计算机二级等级考试靠运气是个什么梗?
  7. java调用net的webservice_java和.net互相调用webservice注意事项
  8. 今年行情这么差,到底如何进大厂?
  9. 6阶群的非平凡子群_6阶群的非平凡子群_黄磊小女儿6岁生日会曝光,多妹颜值不输姐姐,黄多多耳饰引争议......
  10. Tomcat 禁用管理界面