在看完之前一篇文章后,我又找到以下这篇文章,这篇里包含了一个计步器的源代码,我觉得自己来研究一下。

一个稳定好用的android计步器源代码

    作为一个新手,看别人代码只能一步步慢慢研究摸索,先看了工程的AndroidManifest.xml,具体代码如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.base.basepedo"><uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.VIBRATE"/><uses-permission android:name="android.permission.WRITE_SETTINGS" /><uses-feature android:name="android.hardware.sensor.accelerometer" /><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".ui.MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><serviceandroid:name="com.base.basepedo.service.StepService"android:process="com.base.basepedo.step"android:priority="1000"><intent-filter ><!-- 系统启动完成后会调用--><action android:name="android.intent.action.BOOT_COMPLETED"/><action android:name="android.intent.action.DATE_CHANGED"/><action android:name="android.intent.action.MEDIA_MOUNTED" /><action android:name="android.intent.action.USER_PRESENT" /><action android:name="android.intent.action.ACTION_TIME_TICK" /><action android:name="android.intent.action.ACTION_POWER_CONNECTED" /><action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /></intent-filter></service></application></manifest>

这里面除了需要用到的权限之外,只有一个MainActivity和一个StepService的注册。既然是从MainActivity进入,那么就先看MainActivity的内容。在MainActivity里先看onCreate方法。

 @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();setupService();}private void init() {text_step = (TextView) findViewById(R.id.text_step);delayHandler = new Handler(this);}
   private void setupService() {Intent intent = new Intent(this, StepService.class);bindService(intent, conn, Context.BIND_AUTO_CREATE);startService(intent);}

在MainActivity里除了设置布局调用了init()和 setupService()两个方法。init方法中实例化了一个handler,然后在setupService方法里绑定一个服务StepService,就是之前AndroidManifest.xml里看到的那个服务。然后在ServiceConnection的onServiceConnected方法里发送一个消息。消息中msg.replyTo是干什么以前没用过,查阅资料后暂时理解为一个消息管理器,会关联到一个handler,handler就是处理其中的消息。

  private Messenger mGetReplyMessenger = new Messenger(new Handler(this));private Handler delayHandler;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) {}};

MainActivity里基本就没有什么其他的内容了,在onBackPressed设置了后退键将Activity置为后台,在onDestory方法里解除服务的绑定。

    @Overrideprotected void onResume() {super.onResume();}@Overridepublic void onBackPressed() {moveTaskToBack(true);super.onBackPressed();}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(conn);}
}

接下来去看StepService这个服务里到底做了什么。

<span style="font-size:14px;">package com.base.basepedo.service;import android.app.Notification;
import ......public class StepService extends Service implements SensorEventListener {//默认为30秒进行一次存储private static int duration = 30000;private static String CURRENTDATE = "";private SensorManager sensorManager;private StepDcretor stepDetector;private NotificationManager nm;private NotificationCompat.Builder builder;private Messenger messenger = new Messenger(new MessenerHandler());private BroadcastReceiver mBatInfoReceiver;private WakeLock mWakeLock;private TimeCount time;//测试private static int i = 0;//计步传感器类型 0-counter 1-detectorprivate static int stepSensor = -1;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_SETP);replyMsg.setData(bundle);messenger.send(replyMsg);} catch (RemoteException e) {e.printStackTrace();}break;default:super.handleMessage(msg);}}}@Overridepublic void onCreate() {super.onCreate();CURRENTDATE = getTodayDate();initBroadcastReceiver();new Thread(new Runnable() {public void run() {startStepDetector();}}).start();startTimeCount();initTodayData();updateNotification("今日步数:" + StepDcretor.CURRENT_SETP + " 步");}private String getTodayDate() {Date date = new Date(System.currentTimeMillis());SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");return sdf.format(date);}private void initTodayData() {DbUtils.createDb(this, "basepedo");//获取当天的数据,用于展示List<StepData> list = DbUtils.getQueryByWhere(StepData.class, "today", new String[]{CURRENTDATE});if (list.size() == 0 || list.isEmpty()) {StepDcretor.CURRENT_SETP = 0;} else if (list.size() == 1) {StepDcretor.CURRENT_SETP = Integer.parseInt(list.get(0).getStep());} else {Log.v("xf", "出错了!");}}private void initBroadcastReceiver() {final IntentFilter filter = new IntentFilter();// 屏幕灭屏广播filter.addAction(Intent.ACTION_SCREEN_OFF);//关机广播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.d("xf", "screen on");} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {Log.d("xf", "screen off");//改为60秒一存储duration = 60000;} else if (Intent.ACTION_USER_PRESENT.equals(action)) {Log.d("xf", "screen unlock");save();//改为30秒一存储duration = 30000;} else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {Log.i("xf", " receive Intent.ACTION_CLOSE_SYSTEM_DIALOGS");//保存一次save();} else if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {Log.i("xf", " receive ACTION_SHUTDOWN");save();}}};registerReceiver(mBatInfoReceiver, filter);}private void startTimeCount() {time = new TimeCount(duration, 1000);time.start();}/*** 更新通知*/private void updateNotification(String content) {builder = new NotificationCompat.Builder(this);builder.setPriority(Notification.PRIORITY_MIN);//Notification.Builder builder = new Notification.Builder(this);PendingIntent contentIntent = PendingIntent.getActivity(this, 0,new Intent(this, MainActivity.class), 0);builder.setContentIntent(contentIntent);builder.setSmallIcon(R.mipmap.ic_launcher);builder.setTicker("BasePedo");builder.setContentTitle("BasePedo");//设置不可清除builder.setOngoing(true);builder.setContentText(content);Notification notification = builder.build();startForeground(0, notification);nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);nm.notify(R.string.app_name, notification);}@Overridepublic IBinder onBind(Intent intent) {return messenger.getBinder();}@Overridepublic void onStart(Intent intent, int startId) {super.onStart(intent, startId);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return START_STICKY;}private void startStepDetector() {if (sensorManager != null && stepDetector != null) {sensorManager.unregisterListener(stepDetector);sensorManager = null;stepDetector = null;}getLock(this);// 获取传感器管理器的实例sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);//android4.4以后可以使用计步传感器int VERSION_CODES = android.os.Build.VERSION.SDK_INT;if (VERSION_CODES >= 19) {addCountStepListener();} else {addBasePedoListener();}}private void addCountStepListener() {Sensor detectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);if (countSensor != null) {stepSensor = 0;Log.v("base", "countSensor");sensorManager.registerListener(StepService.this, countSensor, SensorManager.SENSOR_DELAY_UI);} else if (detectorSensor != null) {stepSensor = 1;Log.v("base", "detector");sensorManager.registerListener(StepService.this, detectorSensor, SensorManager.SENSOR_DELAY_UI);} else {Log.v("xf", "Count sensor not available!");addBasePedoListener();}}private void addBasePedoListener() {stepDetector = new StepDcretor(this);// 获得传感器的类型,这里获得的类型是加速度传感器// 此方法用来注册,只有注册过才会生效,参数:SensorEventListener的实例,Sensor的实例,更新速率Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// sensorManager.unregisterListener(stepDetector);sensorManager.registerListener(stepDetector, sensor,SensorManager.SENSOR_DELAY_UI);stepDetector.setOnSensorChangeListener(new StepDcretor.OnSensorChangeListener() {@Overridepublic void onChange() {updateNotification("今日步数:" + StepDcretor.CURRENT_SETP + " 步");}});}@Overridepublic void onSensorChanged(SensorEvent event) {//   i++;if(stepSensor == 0){StepDcretor.CURRENT_SETP = (int)event.values[0];}else if(stepSensor == 1){StepDcretor.CURRENT_SETP++;}updateNotification("今日步数:" + StepDcretor.CURRENT_SETP + " 步");}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}class TimeCount extends CountDownTimer {public TimeCount(long millisInFuture, long countDownInterval) {super(millisInFuture, countDownInterval);}@Overridepublic void onFinish() {// 如果计时器正常结束,则开始计步time.cancel();save();startTimeCount();}@Overridepublic void onTick(long millisUntilFinished) {}}private void save() {int tempStep = StepDcretor.CURRENT_SETP;List<StepData> list = DbUtils.getQueryByWhere(StepData.class, "today", new String[]{CURRENTDATE});if (list.size() == 0 || list.isEmpty()) {StepData data = new StepData();data.setToday(CURRENTDATE);data.setStep(tempStep + "");DbUtils.insert(data);} else if (list.size() == 1) {StepData data = list.get(0);data.setStep(tempStep + "");DbUtils.update(data);} else {}}@Overridepublic void onDestroy() {//取消前台进程stopForeground(true);DbUtils.closeDb();unregisterReceiver(mBatInfoReceiver);Intent intent = new Intent(this, StepService.class);startService(intent);super.onDestroy();}@Overridepublic boolean onUnbind(Intent intent) {return super.onUnbind(intent);}//    private  void unlock(){
//        setLockPatternEnabled(android.provider.Settings.Secure.LOCK_PATTERN_ENABLED,false);
//    }
//
//    private void setLockPatternEnabled(String systemSettingKey, boolean enabled) {
//        //推荐使用
//        android.provider.Settings.Secure.putInt(getContentResolver(), systemSettingKey,enabled ? 1 : 0);
//    }synchronized private PowerManager.WakeLock getLock(Context context) {if (mWakeLock != null) {if (mWakeLock.isHeld())mWakeLock.release();mWakeLock = null;}if (mWakeLock == null) {PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE);mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,StepService.class.getName());mWakeLock.setReferenceCounted(true);Calendar c = Calendar.getInstance();c.setTimeInMillis(System.currentTimeMillis());int hour = c.get(Calendar.HOUR_OF_DAY);if (hour >= 23 || hour <= 6) {mWakeLock.acquire(5000);} else {mWakeLock.acquire(300000);}}return (mWakeLock);}
}</span><span style="font-size: 18px;">
</span>

其实注释写的蛮详细的,但是我作为一个渣渣新手,乍一看要晕了。不管咋样还是一点一点开始看吧,首先看到的是这个服务继承了SensorEventListener接口,就是我之前转的那篇入门中提到过的。Android应用程序中使用传感器要依赖于android.hardware.SensorEventListener接口。通过该接口可以监听传感器的各种事件。那么就一定会实现onSensorChanged和onAccuracyChanged方法,果断去找,果然找到具体代码如下:

    @Overridepublic void onSensorChanged(SensorEvent event) {//   i++;if(stepSensor == 0){StepDcretor.CURRENT_SETP = (int)event.values[0];}else if(stepSensor == 1){StepDcretor.CURRENT_SETP++;}updateNotification("今日步数:" + StepDcretor.CURRENT_SETP + " 步");}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {}

当传感器的值发生变化时,会调用onSensorChanged方法。当传感器的精度变化时会调用onAccuracyChanged方法。onSensorChanged方法里根据stepSensor(计步传感器类型 0-counter 1-detector)的类型设置StepDcretor.CURRENT_SETP的值,然后调用updateNotification方法更新通知栏。更新通知栏的操作并不复杂就不细看了,但是StepDcretor.CURRENT_SETP这玩意儿是干什么的?打开StepDcretor这个类又是一堆代码,无从下手,看到这里,没办法我只能转回去看StepService的生命周期onCreate方法,不管怎么样Service还得按生命周期走。在onCreate方法里看到,首先获取了当前的日期,然后初始化一个广播接收者,接着开启子线程执行startStepDetector方法。接着开启一个倒计时器,再初始化今天的数据和更新通知栏。

  @Overridepublic void onCreate() {super.onCreate();CURRENTDATE = getTodayDate();initBroadcastReceiver();new Thread(new Runnable() {public void run() {startStepDetector();}}).start();startTimeCount();initTodayData();updateNotification("今日步数:" + StepDcretor.CURRENT_SETP + " 步");}
<pre name="code" class="java">
private void initBroadcastReceiver() {final IntentFilter filter = new IntentFilter();// 屏幕灭屏广播filter.addAction(Intent.ACTION_SCREEN_OFF);//关机广播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.d("xf", "screen on");} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {Log.d("xf", "screen off");//改为60秒一存储duration = 60000;} else if (Intent.ACTION_USER_PRESENT.equals(action)) {Log.d("xf", "screen unlock");save();//改为30秒一存储duration = 30000;} else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {Log.i("xf", " receive Intent.ACTION_CLOSE_SYSTEM_DIALOGS");//保存一次save();} else if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {Log.i("xf", " receive ACTION_SHUTDOWN");save();}}};registerReceiver(mBatInfoReceiver, filter);}
 

getTodayDate()方法获取当前系统日期,然后在initBroadcastReceiver方法里注册一个监听了一堆内容的广播接收者,具体监听什么注释里写得很详细,然后根据收到的广播来修改duration存储时间或者是保存步数。save方法里乍一看就是向数据库里更新一下当前日期和步数,暂时先不做深究。

 private void save() {int tempStep = StepDcretor.CURRENT_SETP;List<StepData> list = DbUtils.getQueryByWhere(StepData.class, "today", new String[]{CURRENTDATE});if (list.size() == 0 || list.isEmpty()) {StepData data = new StepData();data.setToday(CURRENTDATE);data.setStep(tempStep + "");DbUtils.insert(data);} else if (list.size() == 1) {StepData data = list.get(0);data.setStep(tempStep + "");DbUtils.update(data);} else {}}

接下来继续看startStepDetector方法,首先对sensorManager和stepDetector的状态做了个判断不为空的话置为空,然后getLock获取电源管理锁,保证熄屏CPU运转。关于这个锁http://blog.csdn.net/q_zhe/article/details/7172220这里有具体介绍。然后
获取传感器管理器的实例,在根据API不同版本分别执行addCountStepListener和addBasePedoListener两个方法。

private void startStepDetector() {if (sensorManager != null && stepDetector != null) {sensorManager.unregisterListener(stepDetector);sensorManager = null;stepDetector = null;}getLock(this);// 获取传感器管理器的实例sensorManager = (SensorManager) this.getSystemService(SENSOR_SERVICE);//android4.4以后可以使用计步传感器int VERSION_CODES = android.os.Build.VERSION.SDK_INT;if (VERSION_CODES >= 19) {addCountStepListener();} else {addBasePedoListener();}}

先看addCountStepListener方法,API版本大于19执行这个方法,首先先拿到两种传感器,TYPE_STEP_COUNTER 计步传感器,用于记录激活后的步伐数。TYPE_STEP_DETECTOR 步行检测传感器,用户每走一步就触发一次事件。根据拿到的情况来注册监听,sensorManager.registerListener(StepService.this, countSensor, SensorManager.SENSOR_DELAY_UI);查资料后得知这里第一个参数是Listener,第二个参数是所得传感器类型,第三个参数值获取传感器信息的频率 。如果两种传感器都没拿到,就还是执行addBasePedoListener方法。在addBasePedoListener方法里获得加速度传感器,注册监听,然后调stepDetector里的回调接口更新通知栏。具体代码片段如下:

 private void addCountStepListener() {Sensor detectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);if (countSensor != null) {stepSensor = 0;Log.v("base", "countSensor");sensorManager.registerListener(StepService.this, countSensor, SensorManager.SENSOR_DELAY_UI);} else if (detectorSensor != null) {stepSensor = 1;Log.v("base", "detector");sensorManager.registerListener(StepService.this, detectorSensor, SensorManager.SENSOR_DELAY_UI);} else {Log.v("xf", "Count sensor not available!");addBasePedoListener();}}private void addBasePedoListener() {stepDetector = new StepDcretor(this);// 获得传感器的类型,这里获得的类型是加速度传感器// 此方法用来注册,只有注册过才会生效,参数:SensorEventListener的实例,Sensor的实例,更新速率Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// sensorManager.unregisterListener(stepDetector);sensorManager.registerListener(stepDetector, sensor,SensorManager.SENSOR_DELAY_UI);stepDetector.setOnSensorChangeListener(new StepDcretor.OnSensorChangeListener() {@Overridepublic void onChange() {updateNotification("今日步数:" + StepDcretor.CURRENT_SETP + " 步");}});}

至此StepService服务里的重要代码也看得差不多了。还剩最后一个类StepDcretor。这里面写的应该就是具体计步的方法了。

package com.base.basepedo.service;import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.util.Log;
import com.base.basepedo.utils.CountDownTimer;
import java.util.Timer;
import java.util.TimerTask;public class StepDcretor implements SensorEventListener {//存放三轴数据float[] oriValues = new float[3];final int valueNum = 4;//用于存放计算阈值的波峰波谷差值float[] tempValue = new float[valueNum];int tempCount = 0;//是否上升的标志位boolean isDirectionUp = false;//持续上升次数int continueUpCount = 0;//上一点的持续上升的次数,为了记录波峰的上升次数int continueUpFormerCount = 0;//上一点的状态,上升还是下降boolean lastStatus = false;//波峰值float peakOfWave = 0;//波谷值float valleyOfWave = 0;//此次波峰的时间long timeOfThisPeak = 0;//上次波峰的时间long timeOfLastPeak = 0;//当前的时间long timeOfNow = 0;//当前传感器的值float gravityNew = 0;//上次传感器的值float gravityOld = 0;//动态阈值需要动态的数据,这个值用于这些动态数据的阈值final float initialValue = (float) 1.7;//初始阈值float ThreadValue = (float) 2.0;private final String TAG = "StepDcretor";/*** 0-准备计时   1-计时中  2-准备为正常计步计时  3-正常计步中*/private int CountTimeState = 0;public static int CURRENT_SETP = 0;public static int TEMP_STEP = 0;private int lastStep = -1;// 加速计的三个维度数值public static float[] gravity = new float[3];public static float[] linear_acceleration = new float[3];//用三个维度算出的平均值public static float average = 0;private Timer timer;// 倒计时4秒,4秒内不会显示计步,用于屏蔽细微波动private long duration = 4000;private TimeCount time;OnSensorChangeListener onSensorChangeListener;public interface OnSensorChangeListener {void onChange();}public StepDcretor(Context context) {super();}public void onAccuracyChanged(Sensor arg0, int arg1) {}public OnSensorChangeListener getOnSensorChangeListener() {return onSensorChangeListener;}public void setOnSensorChangeListener(OnSensorChangeListener onSensorChangeListener) {this.onSensorChangeListener = onSensorChangeListener;}public void onSensorChanged(SensorEvent event) {Sensor sensor = event.sensor;synchronized (this) {if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {calc_step(event);}}}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;}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 == 3) {CURRENT_SETP++;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;}if (!isDirectionUp && lastStatus&& (continueUpFormerCount >= 2 && (oldValue >= 11.76 && oldValue < 19.6))) {peakOfWave = oldValue;return true;} else if (!lastStatus && isDirectionUp) {valleyOfWave = oldValue;return false;} else {return false;}}/** 阈值的计算* 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;}/** 梯度化阈值* 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;}class TimeCount extends CountDownTimer {public TimeCount(long millisInFuture, long countDownInterval) {super(millisInFuture, countDownInterval);}@Overridepublic void onFinish() {// 如果计时器正常结束,则开始计步time.cancel();CURRENT_SETP += TEMP_STEP;lastStep = -1;
//            CountTimeState = 2;Log.v(TAG, "计时正常结束");timer = new Timer(true);TimerTask task = new TimerTask() {public void run() {if (lastStep == CURRENT_SETP) {timer.cancel();CountTimeState = 0;lastStep = -1;TEMP_STEP = 0;Log.v(TAG, "停止计步:" + CURRENT_SETP);} else {lastStep = CURRENT_SETP;}}};timer.schedule(task, 0, 3000);CountTimeState = 3;}@Overridepublic void onTick(long millisUntilFinished) {if (lastStep == TEMP_STEP) {Log.v(TAG, "onTick 计时停止");time.cancel();CountTimeState = 0;lastStep = -1;TEMP_STEP = 0;} else {lastStep = TEMP_STEP;}}}
}

这个类同样继承了SensorEventListener接口,看他的onSensorChanged和onAccuracyChanged方法,在onSensorChanged方法中对传感器类型做了判断,是加速度传感器的话执行calc_step方法,在calc_step方法里获取传感器在xyz,三个坐标轴上的加速的绝对值的和,把结果传入DetectorNewStep方法计算步子。判断

gravityOld上次传感器的值,如果为0就把传过来的值赋给gravityOld否则在用DetectorPeak方法传入新值和旧值判断波峰。先记录下lastStatus即上一点的状态,上升还是下降,然后比较新值和旧值如果newValue >= oldValue就设置和更新 isDirectionUp、continueUpCount 否则就将contineUpCount赋给continueUpFormerCount(上一点的持续上升的次数,为了记录波峰的上升次数)。最后判断当上升的标志位和上一次的状态都为true并且旧值在规定范围内的话,这个值就是波峰值返回true如果上一次状态为false则这个值为波谷值返回false。这里看的有点晕,大概就是判断是波峰还是波谷把,回到DetectorNewStep方法里,如果是波峰就记录这次的时间和上次的时间,两次时间差在200到2000之间并且波峰与波谷的差大于阈值时就更新这次波峰的时间和界面,在preStep方法里设置接口回调,如果两次时间差大于200波峰波谷差大于等于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;}

基本就是这样了,总结一下就是开启一个服务,在服务里注册一个广播接收者监听屏幕和电源情况保存数据,并且新建子线程,在子线程里开启计步检测,根据不同的API版本获取传感器管理器和传感器实例并注册监听,然后更具传感器返回的数据,计算波峰波谷阈值之类的符合要求即为一步,然后更新通知栏和界面。关于计步的算法什么波峰波谷值阈值的计算还是看不太懂,但是大概的流程看懂了。虽然代码总量不多,但是作为一个新手小白还是花了一会时间的,因为基础知识不扎实经验又少,看的时候遇到没用过的类和方法就去查资料再去看资料也花了不少时间,不管怎样,进步都是一点一点来的,我在此把我的学习过程记录下来。

源码:https://blog.csdn.net/xf396191344/article/details/50467570

-----------------------------------------------------------------------------------------------------------------------

后来我去看了这篇文章:http://xueshu.baidu.com/s?wd=paperuri%3A%28158baa288e6ece0034de6f3111e49606%29&filter=sc_long_sign&tn=SE_xueshusource_2kduw22v&sc_vurl=http%3A%2F%2Fwww.docin.com%2Fp-1313459281.html&ie=utf-8   对计步算法稍微理解了一点,就是对三个方向加速度进行检测,连续检测到一次波峰和一次波谷记为一步,然后在根据人的步频做一些范围的限定校正,提高计步的精度。

一个计步器源代码的研究相关推荐

  1. 关于一个CCIE考试题目的研究(重分发)

    关于一个ccie考试题目的研究(重分发).rar关于一个ccie考试题目的研究(重分发) 转载于:https://blog.51cto.com/cisco/5525

  2. 知识工程学:一个新的重要研究领域

    知识工程学:一个新的重要研究领域 黄荣怀 (北京师范大学网络教育实验室 100875) 李茂国 (教育部高等教育司100816) 沙景荣 (北京师范大学网络教育实验室 100875) 摘要 知识工程是 ...

  3. 干货 | 如何做一个简单的访谈研究?

    Hello,大家好! 这里是壹脑云科研圈,我是喵君姐姐~ 心理学中研究中,大家常用的研究方法大多是实验法.问卷调查法等,这些均是定量研究. 其实,作为质性研究中常用的访谈法,可对个体的内心想法进行较为 ...

  4. 一个稳定好用的android计步器源代码

    在市面上浏览过众多的计步软件,可惜没有开源的代码,而github上的几个开源的计步代码,要么就是记得不准,要么就是功能不完善,不稳定,于是决心自己写一个,分享给大家使用,希望大家一起来完善. 已上传g ...

  5. Jumony(外一)HTML和数据,同时发布第一个CTP源代码。

    Jumony(一)从扩展方法到流畅的程序体验 Jumony(二)jQuery的设计艺术和选择器 Jumony(三)第一个公开预览版,在.NET里玩转jQuery选择器! 首先是兑现诺言,上一篇文章中曾 ...

  6. 计算机是人工智能研究的一个领域吗,人工智能研究的五个领域

    人工智能的应用领域非常广,人工智能作为一种计算机科学的一个分支,从事人工智能研究的人还很少.资力企服通过近期AI相关类型企业资质办理逐渐上升的特点了解到,国家对人工智能专业人才的渴求度很大,应用领域也 ...

  7. Java实现的一个编译器源代码(Win11)

    本文的源代码摘自编译器龙书<Compilers : principles, techniques, and tools>第二版的附录A"一个完整的前端"(A Compl ...

  8. 一个从源代码里提取中文字符串的java类

    2019独角兽企业重金招聘Python工程师标准>>> 工作中需要优化代码里的中文警示语和异常信息,实在比较多,所以就写了个程序专门从代码里提取中文字符串. java做的,比较简单, ...

  9. [分享]一个搜索源代码的搜索引擎

    可以按照语言可许可证来搜索源代码,目前还是Beta版,尝试了一下,感觉还不错,分享给大家 http://www.koders.com/ 此主题相关图片

最新文章

  1. 葡萄城报表介绍:复杂报表
  2. MFC中的InitInstance()函数应该怎样理解
  3. hibernate 模糊查询及not in子查询
  4. apt-get软件包管理命令 和 apt-key命令
  5. 台式电脑如何重装系统windows10
  6. 2021牛客寒假算法基础集训营4,签到题AGJ
  7. [Share]如何做好一份前端工程师的简历?
  8. 规约转换装置的一些概念
  9. Wordpress世界最牛?做网站用国内cms建站系统才是正道
  10. 关于 虚拟机交叉编译目标机程序,使用filezilla的ftp方式传输到板子上没问题,后来同样环境传入应用运行宕机 的解决方法
  11. 如何关闭windows安全中心(四种方法)
  12. HTML5小游戏之爱心鱼
  13. Mybatis-Plus 条件构造器(三)
  14. JPA设置表名和实体名,表字段与实体字段的对应
  15. SecureCRT标签永久显示IP地址
  16. 2021年电赛 E题 数字传输
  17. 【iOS】Web Color 的 OC 实现
  18. 开源复杂网络分析软件中社团发现算法总结
  19. Y-Combinator
  20. html文字加描边效果,怎么用css给文字加描边?

热门文章

  1. 透过爷爷的一生,浅析选择的重要性
  2. 2019年西安市民办小升初西工大附中分校面谈录取学生名单
  3. 全国人大财经委加快数据要素市场建设专项座谈会在京召开,聚合数据左磊受邀参会
  4. 送你4句口诀 云存储选型不再犯难
  5. 脆弱性和安全风险分析
  6. 工作这么多年,所经历5个骗子上司
  7. 菜的抠脚团队正式成立
  8. Blockly与Scratch3.0的比较分析
  9. 爆笑区块链段子送给你~
  10. 腾讯游戏10大经典案例(二):天堂等于天下