Android计步器的实现(1)

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

计步器原理

实现计步器几步一般有2种方法:1.利用加速传感器,通过计算二次波峰来确定每一步,这种方法误差比较大,但普适性好;2.利用Android4.4之后的
Sensor.TYPE_STEP_DETECTOR来计步,缺点是这个传感器只有4.4以后的版本才能使用而且手机要有三轴陀螺仪,限制比较大,鉴于微信运动也是
使用的第二种方法,这里也放弃第一种方法的使用。(备注:可以找个方法一的算法对接到方法二上,因为Android自带的计步器太简陋了,一切都需要
自己实现,这个下文有说明)

关于TYPE_STEP_DETECTOR

通过TYPE_STEP_DETECTOR传感器我们只能获得2个数据:1.时间戳(这个在开关机面前就废了,不做考虑);2.从上次开机到现在的总步数(总步数
会在开关机时清零),对,只是总步数……这样就不能确定某一天的步数了。

说明

说明就是这么突然^_^关于跨天很多童鞋会想到Date_Changed这个系统广播,然而该广播不可信任,原因:该广播只会发出一次,
如:用户往后调整过时间,并在00:00(记此时为t)发送过该天的广播,那么当用户再次将时间拨回到正常时间时,再次经过t时刻,
系统不会发送Date_Changed广播;另外,开机广播也不可信任,原因:部分手机厂商会屏蔽掉该广播。

代码结构

as被玩坏了,现在截不了图,直接用这个,毕竟代码不多:

这里写图片描述

实现步骤

1.计步器步数数据库的实现

关于数据库这里没有使用第三方框架,已接入第三方框架的童鞋,请自行修改。数据库结构如下:

这里写图片描述

date:时间戳,表明是哪一天的数据
steps:修正步数,非真实步数,下面会解释(已DES加密)
sensor:修正传感器步数(该值非负,只有off为1或插入新数据时才为0),和上一天
       的sensor及off的状态有关(已des加密),之所以用修正结果,是为了监听异常关机
off:手机是否正常关机,1表示正常关机,0表示未关机,请无视这么命名,这得改啊

数据库操作代码:
/**
 * 这里为了方便查看数据创建了2个表格一个DES加密,一个未加密,根据需要  自行修改
 */
public class Database extends SQLiteOpenHelper {
    
    

    private final static String DB_NAME = "steps";
    private final static String DB_NAME_ENCRYPT = "steps_encrypt";
    private final static int DB_VERSION = 1;

    private static Database instance;
    private static final AtomicInteger openCounter = new AtomicInteger();

    private Database(final Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    public static synchronized Database getInstance(final Context c) {
        if (instance == null) {
            instance = new Database(c.getApplicationContext());
        }
        openCounter.incrementAndGet();
        return instance;
    }

    @Override
    public void close() {
        if (openCounter.decrementAndGet() == 0) {
            super.close();
        }
    }

    /**
     * date : 计步的日期
     * steps : 开关机后存储的步数
     * sensor : 虚拟传感器的步数(非真实值,和上一天是否关机有关),计步器传感器每次变化都会触发
     * off : 是否关机过,默认false(0);关机时置为true(1),待计步服务开启后将当天的off置为false(0) ;
     *
     * @param db
     */
    @Override
    public void onCreate(final SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + DB_NAME + " (date INTEGER, steps INTEGER , sensor INTEGER , off INTEGER)");
        db.execSQL("CREATE TABLE " + DB_NAME_ENCRYPT + " (date INTEGER, steps TEXT , sensor TEXT , off INTEGER)");
    }

    @Override
    public void onUpgrade(final SQLiteDatabase db, int oldVersion, int newVersion) {
        // 关于升级数据库,暂时不处理
    }

    /**
     * Query the 'steps' table. Remember to close the cursor!
     *
     * @param columns       the colums
     * @param selection     the selection
     * @param selectionArgs the selction arguments
     * @param groupBy       the group by statement
     * @param having        the having statement
     * @param orderBy       the order by statement
     * @return the cursor
     */
    public Cursor query(final String[] columns, final String selection,
                        final String[] selectionArgs, final String groupBy, final String having,
                        final String orderBy, final String limit) {
        return getReadableDatabase()
                .query(DB_NAME, columns, selection, selectionArgs, groupBy, having, orderBy, limit);
    }

    /**
     * 插入新的数据
     *
     * @param date
     * @param steps
     * @param correctSensorSteps 需要根据上一天的开关机判断
     * @param off
     */
    public void insertNewDay(long date, int steps, int correctSensorSteps, int off) {
        getWritableDatabase().beginTransaction();
        try {
            ContentValues values = new ContentValues();
            values.put("date", date);
            values.put("steps", steps);
            values.put("sensor", correctSensorSteps);
            values.put("off", off);
            getWritableDatabase().insert(DB_NAME, null, values);
            getWritableDatabase().setTransactionSuccessful();
        } finally {
            getWritableDatabase().endTransaction();
        }
        insertNewDayEncrypt(date , steps , correctSensorSteps , off);
    }
    public void insertNewDayEncrypt(long date, int steps, int correctSensorSteps, int off) {
        getWritableDatabase().beginTransaction();
        try {
            ContentValues values = new ContentValues();
            values.put("date", date);
            values.put("steps", DesTool.encrypt(steps + "" ));
            values.put("sensor", DesTool.encrypt(correctSensorSteps + "" ));
            values.put("off", off);
            getWritableDatabase().insert(DB_NAME_ENCRYPT , null, values);
            getWritableDatabase().setTransactionSuccessful();
        } finally {
            getWritableDatabase().endTransaction();
        }
    }

    /**
     * 更新steps(关机、插入新数据、异常关机、数据异常时才会调用)
     *
     * @param date
     * @param correctSensorSteps 应该传的传感器数值,不一定为读取到的传感器数值
     */
    public void updateSteps(long date, int correctSensorSteps) {
        Cursor c = getReadableDatabase().query(DB_NAME, new String[]{
   
   "date"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        if (c.getCount() == 0) {
            // 没有记录,说明之前传感器没有动,所以一概为0 (sensor在计步传感器发生改变时会改变,
            // 此时为0,是为了区别计步器是否监听到了运动,为0显然传感器没有改变);off传1是因为:
            // 当天的会在启动监听计步的服务时置为0,如果前一天没数据置为1视为关机状态,便于逻辑的实现
            insertNewDay(date, 0, 0, 1);
        } else {
            getWritableDatabase().execSQL(
                    "UPDATE " + DB_NAME + " SET steps = steps + " + correctSensorSteps + " WHERE date = " + date);
        }
        c.close();
        updateStepsEncrypt(date , correctSensorSteps);
    }
    public void updateStepsEncrypt(long date, int correctSensorSteps) {
        Cursor c = getReadableDatabase().query(DB_NAME_ENCRYPT , new String[]{
   
   "date"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        if (c.getCount() == 0) {
            // 同上
            insertNewDayEncrypt(date, 0, 0, 1);
        } else {
            int steps = 0 ;
            try{
                steps = Integer.parseInt(DesTool.decrypt(getStepsEncrypt(date))) ;
            }catch (Exception e){

            }
            getWritableDatabase().execSQL(
                    "UPDATE " + DB_NAME_ENCRYPT + " SET steps = \"" + DesTool.encrypt((steps + correctSensorSteps) + "") + "\" WHERE date = " + date);
        }
        c.close();
    }

    /**
     * @param date the date in millis since 1970
     * @return steps的值
     */
    public int getSteps(long date) {
        Cursor c = getReadableDatabase().query(DB_NAME, new String[]{
   
   "steps"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        int re = 0;
        if (c.getCount() == 0) {
        } else {
            re = c.getInt(0);
        }
        return re;
    }
    public String getStepsEncrypt(long date) {
        Cursor c = getReadableDatabase().query(DB_NAME_ENCRYPT , new String[]{
   
   "steps"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        String re = null ;
        if (c.getCount() == 0) {
            re = "" ;
        } else {
            re = c.getString(0);
            if(null == re){
                re = "" ;
            }
        }
        c.close();
        return re;
    }

    /**
     * 更新某一天(date)的传感器步数(sensor)
     *
     * @param date               所属的时间,in millis since 1970
     * @param correctSensorSteps
     */
    public void updateSensorSteps(long date, int correctSensorSteps) {
        Cursor c = getReadableDatabase().query(DB_NAME, new String[]{
   
   "date"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        if (c.getCount() == 0) {
            // 没有记录,这一行执行不到,因为更新sensor的操作总在其他getXXX之后执行
            insertNewDay(date, 0, correctSensorSteps, 0);
        } else {
            getWritableDatabase().execSQL(
                    "UPDATE " + DB_NAME + " SET sensor = " + correctSensorSteps + " WHERE date = " + date);
        }
        c.close();
        updateSensorStepsEncrypt(date , correctSensorSteps);
    }
    public void updateSensorStepsEncrypt(long date, int correctSensorSteps) {
        Cursor c = getReadableDatabase().query(DB_NAME_ENCRYPT, new String[]{
   
   "date"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        if (c.getCount() == 0) {
            // 没有记录
            insertNewDayEncrypt(date, 0, correctSensorSteps, 0);
        } else {
            getWritableDatabase().execSQL(
                    "UPDATE " + DB_NAME_ENCRYPT + " SET sensor = \"" + DesTool.encrypt(correctSensorSteps + "") + "\" WHERE date = " + date);
        }
        c.close();
    }

    /**
     * @param date the date in millis since 1970
     * @return 存储的传感器的步数
     */
    public int getSensorSteps(long date) {
        Cursor c = getReadableDatabase().query(DB_NAME, new String[]{
   
   "sensor"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        int re;
        if (c.getCount() == 0) {
            re = 0;
        } else {
            re = c.getInt(0);
        }
        c.close();
        return re;
    }
    public String getSensorStepsEncrypt(long date) {
        Cursor c = getReadableDatabase().query(DB_NAME_ENCRYPT, new String[]{
   
   "sensor"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        String re = null ;
        if (c.getCount() == 0) {
            re = "" ;
        } else {
            re = c.getString(0);
            if(null == re){
                re = "" ;
            }
        }
        c.close();
        return re;
    }

    /**
     * 更新off的状态
     *
     * @param date
     * @param offStatus
     */
    public void updateOffStatus(long date, int offStatus) {
        Cursor c = getReadableDatabase().query(DB_NAME, new String[]{
   
   "date"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        if (c.getCount() == 0) {
            // 同上
            insertNewDay(date, 0 , 0 , offStatus);
        }else {
            getWritableDatabase().execSQL(
                    "UPDATE " + DB_NAME + " SET off = " + offStatus + " WHERE date = " + date);
        }
        c.close();
        updateOffStatusEncrypt(date , offStatus) ;
    }
    public void updateOffStatusEncrypt(long date, int offStatus) {
        Cursor c = getReadableDatabase().query(DB_NAME_ENCRYPT , new String[]{
   
   "date"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        if (c.getCount() == 0) {
            // 没有记录,同上
            insertNewDay(date, 0 , 0 , offStatus);
        }else {
            getWritableDatabase().execSQL(
                    "UPDATE " + DB_NAME_ENCRYPT + " SET off = " + offStatus + " WHERE date = " + date);
        }
        c.close();
    }

    /**
     * 得到off的值 0 = false ; 1 = true ;
     *
     * @param date
     * @return
     */
    public int getOffStatus(long date) {
        Cursor c = getReadableDatabase().query(DB_NAME, new String[]{
   
   "off"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        int hasOffed = 0;
        if (c.getCount() == 0) {
        } else {
            hasOffed = c.getInt(0);
        }
        c.close();
        return hasOffed;
    }
    public int getOffStatusEncypt(long date) {
        Cursor c = getReadableDatabase().query(DB_NAME_ENCRYPT, new String[]{
   
   "off"}, "date = ?",
                new String[]{String.valueOf(date)}, null, null, null);
        c.moveToFirst();
        int hasOffed = 0;
        if (c.getCount() == 0) {
        } else {
            hasOffed = c.getInt(0);
        }
        c.close();
        return hasOffed;
    }
    /**
     * 用于关机时修改整个数据库(所以数据库中内容不宜过多,要定期清理,清理方法暂时略过)
     */
    public void updateAll() {
        getWritableDatabase().execSQL(
                "UPDATE " + DB_NAME + " SET off = 1,steps = steps + sensor,sensor = 0");
        updateAllEncrypt() ;
    }

    public void updateAllEncrypt() {
        Cursor c = getReadableDatabase().query(DB_NAME_ENCRYPT , new String[]{
   
   "date"}, "date > ?",
                new String[]{String.valueOf(1)}, null, null, null);
        if (c.getCount() == 0) {
            // 没有记录,说明之前传感器没有动,不进行任何操作
        } else {
            while (c.moveToNext()){
                long date = c.getLong(0) ;
                int steps = getSteps(date) ;
                int sensor = getSensorSteps(date) ;
                getWritableDatabase().execSQL(
                        "UPDATE " + DB_NAME_ENCRYPT + " SET off = 1,steps = \"" + DesTool.encrypt((steps + sensor) + "")
                                + "\",sensor = \"" + DesTool.encrypt("0") + "\" WHERE date = " + date);
            }
        }
        c.close();
    }

}
这一篇够长了,看着累,转下一篇:

Android计步器的实现(2)

猜你喜欢

转载自blog.csdn.net/niuzhucedenglu/article/details/53378265#comments_16341224