您的位置:首页 > 移动开发 > Android开发

Android计步器的实现(2)

2016-11-28 16:17 926 查看
上一篇见:


Android计步器的实现(1)

2.时间戳工具

public abstract class Util4Pedometer {

/**
* @return milliseconds since 1.1.1970 for today 0:00:00
*/
public static long getToday() {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis());
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
return c.getTimeInMillis();
}
}


3.获取当天的步数

以往的步数建议服务器获取。

获取当天步数代码:


/**
* 使用该方法前必须对context进行初始化
*/
public class StepsCountTool {

public static Context context;

/**
* 获得当天的步数,若步数异常重置步数
* 这里返回String是因为方便往服务器提交数据时加密(如:RSA)
*/
public static String getTodaySteps() {
if (null == context) {
return "0";
}
int result = 0;
Database db = Database.getInstance(context);
long today = Util4Pedometer.getToday();
// 当天步数为当天的steps数据与sensor数据之和
result = db.getSteps(today) + db.getSensorSteps(today);
if (0 > result) {
// 说明数据异常(只有在跨天且计步器逻辑没有被触发才有可能异常,此处触发几率不大,保险起见,
// 看计步器服务调用该方法的位置可理解),应该重置当前的步数
db.updateSteps(today, -db.getSteps(today));
db.updateSensorSteps(today, 0);
result = 0;
}
return result + "";
}
}


4.计步器服务的实现

计步器要一直在后台运行就使用到了Service,如果不能一直运行则某些情况会使计数偏小,关于如何保活,这是个问题,这里只尽可能的使服务唤醒
(请自行解决,最后会给出源码,仅供参考)。

计步服务代码:


@TargetApi(Build.VERSION_CODES.KITKAT)
public class SensorListener extends Service implements SensorEventListener {

public static boolean isShutdowning = false;// 只有接收到关机广播才会置为true
private Database db;
public static int steps = 0;
private final static int MICROSECONDS_IN_ONE_MINUTE = 60000000;

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}

@Override
public void onSensorChanged(final SensorEvent event) {
// 关机时不往下执行
if (isShutdowning) {
return;
}
// 若步数异常(用户随意调整时间后,开关机等等),修复步数
StepsCountTool.getTodaySteps();
Sensor sensor = event.sensor;
if (sensor.getType() != Sensor.TYPE_STEP_COUNTER) {
return;
}
steps = (int) event.values[0];
db = Database.getInstance(this);
long today = Util4Pedometer.getToday();
long yesterDay = today - 24 * 60 * 60 * 1000;
int correctSensorSteps;
int off = db.getOffStatus(yesterDay);
if (off == 0) {
// 说明昨天没有关机,得到修正值:当前计步器数据 - 上一天sensor修正值,手机异常关机,会使steps的值从0开始
// 正常情况correctSensorSteps 为正
correctSensorSteps = steps - db.getSensorSteps(yesterDay);
if (correctSensorSteps < 0) {
// 说明手机异常关机:扣电池或使用脚本关机导致关机广播没有发出,正常关机时会将所有off置为1,
db.updateOffStatus(yesterDay, 1);
// 异常关机后重启先保存关机前的数据
db.updateSteps(today, db.getSensorSteps(today));
// 再重新计算步数,这样能最大可能的接近真实值,此时steps从1开始
db.updateSensorSteps(today, steps);
} else {
// 如果没有关机,跨天时一定会执行if中的代码,因为insertNewDay的sensor和steps的参数都是0,
// 在第一次监听到计步器改变时,senor的值还未改变
if (db.getSensorSteps(today) <= 0) {
// 这里每天只更新一次sensor,更新后和sensor的和为0,跨天问题解决
// steps此时为0,将sensor偏移量取反放入steps中,
db.updateSteps(today, -correctSensorSteps);
}
db.updateSensorSteps(today, correctSensorSteps);
}
} else if (off == 1) {
// 说明昨天关机,correctSensorSteps 就是传感器的真实值
correctSensorSteps = steps;
db.updateSensorSteps(today, correctSensorSteps);
} else {
// 计步器异常
}
}

@Override
public IBinder onBind(final Intent intent) {
return null;
}

@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
if (isShutdowning) {
return START_STICKY;
}
db = Database.getInstance(this);
// 每次启动服务都将今天的状态置为0
db.updateOffStatus(Util4Pedometer.getToday(), 0);
// restart service every hour to get the current step count
((AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE))
.set(AlarmManager.RTC, System.currentTimeMillis() + AlarmManager.INTERVAL_HOUR,
PendingIntent.getService(getApplicationContext(), 2,
new Intent(this, SensorListener.class),
PendingIntent.FLAG_UPDATE_CURRENT));
return START_STICKY;
}

@Override
public void onCreate() {
super.onCreate();
reRegisterSensor();
}

@Override
public void onTaskRemoved(final Intent rootIntent) {
super.onTaskRemoved(rootIntent);
// Restart service in 500 ms
((AlarmManager) getSystemService(Context.ALARM_SERVICE))
.set(AlarmManager.RTC, System.currentTimeMillis() + 500, PendingIntent
.getService(this, 3, new Intent(this, SensorListener.class), 0));
}

@Override
public void onDestroy() {
super.onDestroy();
if (null != db) {
db.close();
}
try {
SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
sm.unregisterListener(this);
} catch (Exception e) {
e.printStackTrace();
}
reRegisterSensor();
}

@TargetApi(19)
private void reRegisterSensor() {
if (Build.VERSION.SDK_INT < 19) {
return;
}
SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);
if (null == sm) {
// 无法获得传感器管理组件
} else {
// 获得传感器管理组件
}
try {
sm.unregisterListener(this);
} catch (Exception e) {
e.printStackTrace();
}
if (null == sm.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)) {
// 没有计步器
} else {
// 有计步器
}
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_STEP_COUNTER),
SensorManager.SENSOR_DELAY_NORMAL, 5 * MICROSECONDS_IN_ONE_MINUTE);
}
}


5.手机正常关机的处理

/**
* 监听到关机广播后,如下处理
*/
public class ShutdownReceiver extends BroadcastReceiver {

@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public void onReceive(final Context context, final Intent intent) {
// 如果没接收到关机广播直接退出等下次启动时重新初始化
if (!"android.intent.action.ACTION_SHUTDOWN".equals(intent.getAction())) {
return;
}
// if the user used a root script for shutdown, the DEVICE_SHUTDOWN
// broadcast might not be send. Therefore, the app will check this
// setting on the next boot and displays an error message if it's not
// set to true
// 然而异常关机和正常安装app无法区分,此sp暂时无用
context.getSharedPreferences("pedometer", Context.MODE_PRIVATE).edit()
.putBoolean("correctShutdown", true).commit();
// 接收到关机广播的时候SensorListener不应该再执行
SensorListener.isShutdowning = true;
Database db = Database.getInstance(context);
long today = Util4Pedometer.getToday();
long yesterDay = today - 24 * 60 * 60 * 1000;
int correctSensorSteps = 0;
int off = db.getOffStatus(yesterDay);
if (off == 0) {
// 说明昨天没有关机
correctSensorSteps = SensorListener.steps - db.getSensorSteps(yesterDay);
// 将今天的步数存入steps偏移量中,传入correctSensorSteps后会加上steps自身,详见Database代码
db.updateSteps(today, correctSensorSteps);
} else if (off == 1) {
// 说明昨天关机,偏移量就是计步器的实际值
correctSensorSteps = SensorListener.steps;
db.updateSteps(today, correctSensorSteps);
} else {
// 计步器异常
}
// 将今天的sensor偏移量置为0,下面这三行代码是为了解决在关机时更新整个数据库不能完成的情况,
// 这样在用户不随意修改系统时间时可以最优处理
db.updateSensorSteps(today, 0);
db.updateOffStatus(yesterDay, 1);
db.updateOffStatus(today, 1);
// 更新整个数据库将off置为1,sensor置为0, steps置为每天的步数
db.updateAll();
db.close();
}
}


到这里大体思路都实现了,其他代码等我发布源码后查看。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息