(参考博客 http://www.jianshu.com/p/5d57f7fd84fa)

计步功能

文件结构

思路框图

代码

(项目源代码 https://github.com/14353350/Yuebu-Pedometer)
+ StepCountActivity

在onCreate方法中初始化Handler,onStart方法中开启服务,以备退到后台,再到前台,会触发onStart方法,以此来开启service。

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_step_count);init();}private void init() {...//delayHandler = new Handler(this);...}@Overrideprotected void onStart() {super.onStart();setupService();}private void setupService() {Intent intent = new Intent(this, StepService.class);bindService(intent, conn, Context.BIND_AUTO_CREATE);startService(intent);}@Overrideprotected void onPause(){//请求更新步数delayHandler.sendEmptyMessage(Constant.REQUEST_SERVER);super.onPause();}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(conn);}

以bind形式开启service,故有ServiceConnection接收回调。

    ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {try {messenger = new Messenger(service);Message msg = Message.obtain(null, Constant.MSG_FROM_CLIENT);msg.replyTo = mGetReplyMessenger;messenger.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};

接收从服务端回调的步数

    @Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case Constant.MSG_FROM_SERVER:...delayHandler.sendEmptyMessageDelayed(Constant.REQUEST_SERVER, TIME_INTERVAL);break;case Constant.REQUEST_SERVER:try {Message msg1 = Message.obtain(null, Constant.MSG_FROM_CLIENT);msg1.replyTo = mGetReplyMessenger;messenger.send(msg1);} catch (RemoteException e) {e.printStackTrace();}break;}return false;}
  • StepService

有一个Handler,负责与StepCountActivity进行通讯

    private static class MessenerHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case Constant.MSG_FROM_CLIENT:try {Messenger messenger = msg.replyTo;Message replyMsg = Message.obtain(null, Constant.MSG_FROM_SERVER);Bundle bundle = new Bundle();bundle.putInt("step", StepDcretor.CURRENT_STEP);save();replyMsg.setData(bundle);messenger.send(replyMsg);} catch (RemoteException e) {e.printStackTrace();}break;default:super.handleMessage(msg);}}}

onCreate方法注册关屏、开屏等广播。开启一个线程,执行计步逻辑。同时开启一个计时器,30s往数据库中写入一次数据

    @Overridepublic void onCreate() {super.onCreate();initBroadcastReceiver();new Thread(new Runnable() {public void run() {startStepDetector();}}).start();startTimeCount();}

在注册的广播中,会根据用户是在前台还是后台,对存储时间也是有改变的

    private void initBroadcastReceiver() {final IntentFilter filter = new IntentFilter();// 屏幕灭屏广播filter.addAction(Intent.ACTION_SCREEN_OFF);//日期修改filter.addAction(Intent.ACTION_TIME_CHANGED);//关机广播filter.addAction(Intent.ACTION_SHUTDOWN);// 屏幕亮屏广播filter.addAction(Intent.ACTION_SCREEN_ON);// 屏幕解锁广播filter.addAction(Intent.ACTION_USER_PRESENT);// 当长按电源键弹出“关机”对话或者锁屏时系统会发出这个广播// example:有时候会用到系统对话框,权限可能很高,会覆盖在锁屏界面或者“关机”对话框之上,// 所以监听这个广播,当收到时就隐藏自己的对话,如点击pad右下角部分弹出的对话框filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);mBatInfoReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(final Context context, final Intent intent) {String action = intent.getAction();if (Intent.ACTION_SCREEN_ON.equals(action)) {Log.v(TAG, "screen on");} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {Log.v(TAG, "screen off");//改为60秒一存储duration = 60000;} else if (Intent.ACTION_USER_PRESENT.equals(action)) {Log.v(TAG, "screen unlock");save();//改为30秒一存储duration = 30000;} else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {Log.v(TAG, " receive Intent.ACTION_CLOSE_SYSTEM_DIALOGS");//保存一次save();} else if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {Log.v(TAG, " receive ACTION_SHUTDOWN");save();} else if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {Log.v(TAG, " receive ACTION_TIME_CHANGED");initTodayData();clearStepData();}}};registerReceiver(mBatInfoReceiver, filter);}

在onStartComand中,从数据库中初始化今日步数,并更新通知栏

    @Overridepublic int onStartCommand(Intent intent, int flags, int startId) {initTodayData();updateNotification("今日步数:" + StepDcretor.CURRENT_STEP + " 步");return START_STICKY;}

同时开启Google内置计步器和加速度传感器

    private void startStepDetector() {if (sensorManager != null && stepDetector != null) {sensorManager.unregisterListener(stepDetector);sensorManager = null;stepDetector = null;}sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);getLock(this);addBasePedoListener();addCountStepListener();}
  • StepDcretor

实现了SensorEventListener接口,

public void onSensorChanged(SensorEvent event) {Sensor sensor = event.sensor; synchronized (this) {if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {  calc_step(event);       }   }
}

calc_step方法算出加速度传感器的x、y、z三轴的平均数值(为了平衡在某一个方向数值过大造成的数据误差),接着交给DetectorNewStep方法处理。

    synchronized private void calc_step(SensorEvent event) {average = (float) Math.sqrt(Math.pow(event.values[0], 2)+ Math.pow(event.values[1], 2) + Math.pow(event.values[2], 2));detectorNewStep(average);}

针对波峰和波谷,进行检测

    /** 检测步子,并开始计步* 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 >= 200&& (peakOfWave - valleyOfWave >= ThreadValue) && (timeOfNow - timeOfLastPeak) <= 2000) {timeOfThisPeak = timeOfNow;//更新界面的处理,不涉及到算法preStep();}if (timeOfNow - timeOfLastPeak >= 200&& (peakOfWave - valleyOfWave >= initialValue)) {timeOfThisPeak = timeOfNow;ThreadValue = Peak_Valley_Thread(peakOfWave - valleyOfWave);}}}gravityOld = values;}

通过变量CountTimeState,将计步分为了三种模式,CountTimeState=0时代表还未开启计步器。CountTimeState=1时代表预处理模式,若TEMP_STEP步数如果在规定的时间内一直在增加,那么TEMP_STEP值有效,反之,无效舍弃,目的是为了过滤掉一些手机晃动带来的影响。CountTimeState=2时代表正常计步模式

    private void preStep() {if (CountTimeState == 0) {// 开启计时器time = new TimeCount(duration, 700);time.start();CountTimeState = 1;Log.v(TAG, "开启计时器");} else if (CountTimeState == 1) {TEMP_STEP++;Log.v(TAG, "计步中 TEMP_STEP:" + TEMP_STEP);} else if (CountTimeState == 2) {CURRENT_STEP++;if (onSensorChangeListener != null) {onSensorChangeListener.onChange();}}}

检测波峰

    /** 检测波峰* 以下四个条件判断为波峰:* 1.目前点为下降的趋势:isDirectionUp为false* 2.之前的点为上升的趋势:lastStatus为true* 3.到波峰为止,持续上升大于等于2次* 4.波峰值大于1.2g,小于2g* 记录波谷值* 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值* 2.所以要记录每次的波谷值,为了和下次的波峰做对比* */public boolean DetectorPeak(float newValue, float oldValue) {lastStatus = isDirectionUp;if (newValue >= oldValue) {isDirectionUp = true;continueUpCount++;} else {continueUpFormerCount = continueUpCount;continueUpCount = 0;isDirectionUp = false;}//Log.v(TAG, "oldValue:" + oldValue);if (!isDirectionUp && lastStatus&& (continueUpFormerCount >= 2 && (oldValue >= minValue && oldValue < maxValue))) {peakOfWave = oldValue;return true;} else if (!lastStatus && isDirectionUp) {valleyOfWave = oldValue;return false;} else {return false;}}

动态生成阈值,阈值是为了跟波峰与波谷的差值进行比较,进而判断是否为1步

    /** 阈值的计算* 1.通过波峰波谷的差值计算阈值* 2.记录4个值,存入tempValue[]数组中* 3.在将数组传入函数averageValue中计算阈值* */public float Peak_Valley_Thread(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;}

将阈值进行梯度化,取4组数值,进行梯度化,梯度化的数值是大量测试试出来的

    /** 梯度化阈值* 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) {Log.v(TAG, "超过8");ave = (float) 4.3;} else if (ave >= 7 && ave < 8) {Log.v(TAG, "7-8");ave = (float) 3.3;} else if (ave >= 4 && ave < 7) {Log.v(TAG, "4-7");ave = (float) 2.3;} else if (ave >= 3 && ave < 4) {Log.v(TAG, "3-4");ave = (float) 2.0;} else {Log.v(TAG, "else");ave = (float) 1.7;}return ave;}

Android计步器悦步——计步功能相关推荐

  1. Android计步器悦步——智能聊天+健康贴士

    功能 健康小知识推送 食品营养价值查询 疾病查询 闲聊 主要是通过http请求开放平台的api,前三个使用的百度开放平台,最后一个是图灵机器人 (前三个api好像已经不提供了,不过使用方法都是一样的, ...

  2. Android计步器悦步——百度地图

    (源代码见 https://github.com/14353350/Yuebu-Pedometer) 申请密钥 每个Key唯一对应一个APP,如果您的APP修改了包名或者发布的时候打包的签名文件改变了 ...

  3. android计步器进度条,计步用进度条效果很棒

    效果图: 要实现这样一个进度条,我们要创建一个SportStepCountView,里面要有计算:@SuppressLint("DrawAllocation") @Override ...

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

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

  5. 手机用计算机微信运动记步吗,微信运动怎么用?微信运动计步功能使用方法介绍...

    微信手机版2017v6.5.4 官网最新版 类型:社交聊天大小:38.7M语言:中文 评分:9.3 标签: 立即下载 微信运动计步功能推出已经有较长的一段时间了,但是许多的手机微信用户不知道微信 ...

  6. Android原生计步功能的实现,记录当日步数(仿微信运动),不需要后台service

    Android原生计步功能的实现,记录当日步数(仿微信运动),不需要后台service 概述:通过调用Android4.4以上系统自带的计步传感器Sensor.TYPE_STEP_COUNTER,实现 ...

  7. netty框架android,隻需五步,即可基於Netty框架實現Android內網推送功能。

    隻需五步,即可基於Netty框架實現Android內網推送功能. 一.先引入依賴,客戶端和服務端用的都是同一個依賴netty-all. Android Studio中Gradle配置: compile ...

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

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

  9. Android计步器小Demo

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

最新文章

  1. 最通俗的机器学习介绍
  2. 2017-12-25 Linux学习笔记
  3. PHP的工作原理和生命周期
  4. VSFTP的主动模式和被动模式
  5. 【MySQL入门】(一)安装与配置MySQL
  6. MATLAB系统辨识
  7. 91位图和bigemap大地图的区别
  8. 花了两万块参加培训,简历投了1000多份,两个月了还是没找到工作
  9. 适合新手练习js解密的小站--飞卢小说网登录--第四期
  10. linux常用命令知识点
  11. Ubuntu上开发python的十大IDE
  12. Android-自定义幸运抽奖转盘
  13. 32位乘法器和除法器
  14. 最小二乘法--通俗解释
  15. html里如何将数字转换为条形码,excel中如何把数字变成条形码?
  16. 自 下 而 上 分 析 法 的 一 般 原 理
  17. 设计模式04——Factory Method模式
  18. java xmap_xml和java Bean的直接转换,Xmap
  19. 数据安全前言技术研究联邦学习
  20. IOS截屏的几种方法

热门文章

  1. rpcs3模拟器配置要求是什么?
  2. ASR PRO与 ESP8266 CP2102进行串口通信
  3. 诺基亚Lumia 800C评测
  4. Mqtt开发java开发-消息服务mns中的一些坑
  5. 关于微信小程序自定义Picker样式的picker-view
  6. 浅谈:基于物联网的固定资产管理技术
  7. 收藏 年度大盘点 可信计算相关(国家、部委、省市)法律、法规、政策、规划汇总
  8. 《坤之色——楚雄》孙溟㠭中国艺术
  9. 零基础入门NPL之新闻分类
  10. Cocos Creator 判断Touch位置在节点(Node)内