最近在项目中研究计步模块,主要功能记录当天步数,相似微信运动,支付宝计步,咕咚今日步数。 开发以前的调研工做,搜遍baidu,google,github都没有找到我想要的demo和文章,大多数都是须要Service保活。 对于各大手机厂商为了提升电池的续航里程AlertManager、BOOT_COMPLETED、Service的START_STICKY基本上都是不起做用的,Service后台保活更是不可能。 下面是我实现的计步模块和你们一块儿学习 github地址 以前也有一篇文章写计步模块,这篇文章是对上面文章的优化,github代码已经更新到最新了。 Android计步模块(相似微信运动)java
第一个过程上线: 因为功能着急上线,项目最开始计步模块单独使用加速度传感器Sensor.TYPE_ACCELEROMETER进行计算步数,同时Service须要在后台存活才能计步,不然不能计步。android
第二个过程计步器: 项目运行一段时间公司开始推广走路计步这个模块,因此开始从新开发计步模块,此次使用了Android4.4以上提供的计步传感器Sensor.TYPE_STEP_COUNTER来完成,此次从新开发整个计步模块有了质的飞跃,因为采用了计步传感器不在须要后台保活Service,同时计步传感器的功耗特别低,整个模块也更省电了。git
第三个过程优化计步: 用户量变大了投诉也变多了,android各类各样的机型真是让人蛋疼,终于到了第三个阶段优化阶段。 目前最多的问题:github
1.Android4.4以上的系统可是手机没有计步协处理器算法
2.部分手机步数出现暴增现象,有可能一天几十万步数据库
3.部分手机出现一天清零好屡次。json
4.开机计步不能自启动,须要打开app(我已经监听BOOT_COMPLETED广播)安全
5.隔天分隔(0点分隔)很差用天天早上须要打开app(我已经设置AlertManager)微信
这篇文章就来介绍如今app的计步模块,已经解决上述问题一、二、3,至于四、5问题是系统问题正在寻找解决方案,你们也能够帮帮忙在评论中给我提示。 已经将计步模块单独封装成libModule上传github若是有开发者须要的或者想交流的能够很方便的使用下载,点击这里下载。app
1.加速度传感器Sensor.TYPE_ACCELEROMETER计步方式:
这种方式是有开源的算法根据加速度传感器进行计算步数;
优势:只要有加速度传感器的设备均可以使用,相对来讲可使用的设备较多。
缺点:步数的准确性取决于算法且算法比较难优化;须要后台保活Service不然不能计步;计步算法比较费电;部分手机锁屏不能计步;
2.计步传感器Sensor.TYPE_STEP_COUNTER计步方式:
官方解释翻译(本人英文不是很好根据理解翻译,若有错误请指出):
这个传感器是返回手机系统启动到当前时间的全部步数。手机系统重启传感器返回步数为0。还返回一个时间戳,表示最后一次步数的时间。这个计步传感器是个硬件,功耗很是低。若是你想记录步数,注册该传感器不要注销,他能自动在后台计步,在app唤醒的时候会返回计步总数。应用程序须要注册该传感器,不然不能返回步数。 优势:硬件计步准确性高;功耗小;只要注册不用后台Service自动计步; 缺点:Android4.4系统以上的部分手机;手机系统重启计步器清零;不能返回步数明细(步数对应时间),只是返回当前时间的总步数。
计步模块两种计步方式都采用: 判断是否支持Sensor.TYPE_STEP_COUNTER若是支持采用计步传感器,若是不支持用加速度传感器计步。 使用加速度传感器计步须要用户本身手动设置后台自启动,不然不能计步。 使用计步传感器须要在程序中克服他的缺点:手机系统重启计步器清零;不能返回步数明细(步数对应时间),只是返回当前时间的总步数。
先介绍接入方法,在介绍计步模块原理
1.先下载计步demo TodayStepCounter
2.demo项目结构以下图:
由图可见todaystepcounterlib是计步模块封装好的Module,它对外提供的接口就是ISportStepInterface.aidl
3.如何接入:
查看对外接口ISportStepInterface.aidl以下代码:
// ISportStepInterface.aidl package com.today.step.lib; interface ISportStepInterface { /** * 获取当前时间运动步数 */ int getCurrentTimeSportStep(); /** * 获取当天步数列表,json格式 */ String getTodaySportStepArray(); }
查看使用代码MainActivity.java,里面关键代码有注释很是简单
public class MainActivity extends AppCompatActivity { private static String TAG = "MainActivity"; private static final int REFRESH_STEP_WHAT = 0; //循环取当前时刻的步数中间的间隔时间 private long TIME_INTERVAL_REFRESH = 500; private Handler mDelayHandler = new Handler(new TodayStepCounterCall()); private int mStepSum; private ISportStepInterface iSportStepInterface; private TextView mStepArrayTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化计步模块 TodayStepManager.init(getApplication()); mStepArrayTextView = (TextView)findViewById(R.id.stepArrayTextView); //开启计步Service,同时绑定Activity进行aidl通讯 Intent intent = new Intent(this, TodayStepService.class); startService(intent); bindService(intent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //Activity和Service经过aidl进行通讯 iSportStepInterface = ISportStepInterface.Stub.asInterface(service); try { mStepSum = iSportStepInterface.getCurrentTimeSportStep(); updateStepCount(); } catch (RemoteException e) { e.printStackTrace(); } mDelayHandler.sendEmptyMessageDelayed(REFRESH_STEP_WHAT, TIME_INTERVAL_REFRESH); } @Override public void onServiceDisconnected(ComponentName name) { } }, Context.BIND_AUTO_CREATE); } class TodayStepCounterCall implements Handler.Callback{ @Override public boolean handleMessage(Message msg) { switch (msg.what) { case REFRESH_STEP_WHAT: { //每隔500毫秒获取一次计步数据刷新UI if (null != iSportStepInterface) { int step = 0; try { step = iSportStepInterface.getCurrentTimeSportStep(); } catch (RemoteException e) { e.printStackTrace(); } if (mStepSum != step) { mStepSum = step; updateStepCount(); } } mDelayHandler.sendEmptyMessageDelayed(REFRESH_STEP_WHAT, TIME_INTERVAL_REFRESH); break; } } return false; } } private void updateStepCount() { Log.e(TAG,"updateStepCount : " + mStepSum); TextView stepTextView = (TextView)findViewById(R.id.stepTextView); stepTextView.setText(mStepSum + "步"); } public void onClick(View view){ switch (view.getId()){ case R.id.stepArrayButton:{ //显示当天计步数据详细,步数对应当前时间 if(null != iSportStepInterface){ try { String stepArray = iSportStepInterface.getTodaySportStepArray(); mStepArrayTextView.setText(stepArray); } catch (RemoteException e) { e.printStackTrace(); } } break; } default:break; } } }
讲解流程图:
1.整个计步模块是由一个运行在单独进程的Service(TodayStepService)来提供,因为运行在单独的进程因此对外提供的接口采用aidl形式(ISportStepInterface)。
2.零点分隔广播(TodayStepAlertReceive):用来解决跨天计步模块归零问题,因为计步传感器不会根据天来分割只是返回当前步数的总和,因此须要这个广播来对计步模块进行分割,只要跨天了计步模块就归零从0开始计步。
3.开机广播(TodayStepBootCompleteReceiver):开机广播用来解决手机重启计步传感器归零问题,因为计步传感器手机重启会归零,因此收到开机广播会作步数合并,启动Service从上次关机的步数开始累加。
4.数据库(TodayStepDBHelper):用来记录当天步数明细,一个时间对应一个步数
5.加速度传感器计步(TodayStepDcretor):因为android4.4如下或者一些特殊的手机不提供计步传感器因此这些机型采用加速度传感器进行计步,经过OnStepCounterListener监听返回给TodayStepService .
6.计步传感器计步(TodayStepCounter):android4.4以上提供了计步协处理器,能够经过计步传感器计步功耗小,计步准,经过OnStepCounterListener监听返回给TodayStepService .
7.关机监听(TodayStepShutdownReceiver):用来判断手机是否关机,当重启手机打开计步Service根据这个标志来判断是否重启进行步数合并,主要是增长精度有时开机广播不能收到。
讲解流程图:
Android4.4如下或者一些特殊的手机不提供计步传感器,我只能用加速度传感器计步,加速度传感器的原理就是利用必定的算法模拟出步数(加速度传感器计步算法不在本篇文章讨论的范围以内),用这种方式计步Service必定要在后台存活不然不能计步,这种方式跨天分隔步数利用Intent.ACTION_TIME_TICK广播回调来判断当前时间和上次PreferencesHelper记录的时间是否相同若是不一样步数归零从0开始计步,步数的记录采用PreferencesHelper来保存,防止当天重启手机系统步数归零。
讲解流程图:
Android4.4以上的可使用计步传感器进行计步,至于计步传感器的有点上面已经介绍了。这种方式部分手机能够不须要程序自启动权限。
跨天分隔步数采用两种方式:
1.第一种方式和上面同样采用Intent.ACTION_TIME_TICK广播,这里很少说了。
2.第二种方式采用AlertManager方式也就是设置0点闹钟,在这个0点广播中对步数进行分隔,这个AlertManager不是每一个手机均可以启动的。
手机系统重启判断采用四种方式:
1.开机广播监听BOOT_COMPLETED,这个监听不是每一个手机均可以收到,若是收到能够启动Service,而后作步数合并使计步模块从上次关机时的步数开始累加,若是收不到只能用下面几种方式增长重启的判断了。
2.关机广播监听ACTION_SHUTDOWN,这个监听不是每一个手机均可以收到,若是收到,在用户手动启动 Service中能够判断系统重启了。
3.记录运行时间判断手机重启,上次运行的时间大于当前运行时间判断为重启,只是增长精度,极端状况下连续重启,会判断不出来。
4.上次传感器步数总和,当前传感器步数小于上次传感器步数确定是从新启动了,只是用来增长精度不是绝对的
这个流程图的讲解都在图片上。
1.设置app后台自启动
2.各类安全软件设置app为白名单,为了保证app不被任何安全软件在后台杀死。 以上两种方式保证通知栏中一直显示app的步数。
3.手机系统重启,若是通知栏中没有显示步数,表示app没有收到开机监听,须要手动启动app,不然步数会丢失 。
app一直在后台存活确定会耗电,部分手机能够在后台关闭的状况下计步,可是这种方式须要天天早上打开一次app让计步模块对步数进行清零不然步数会丢失。
1.每次传感器回调都会写三次SharedPreferences。
2.计步模块在后台存活,天天过0点开始计步都会丢失一些步数,丢失的步数跟启动计步传感器须要的步数有关,例如:个人测试机连续走10步才能够启动计步传感器回调,因此就丢失10步。
Android计步就是在和android系统做斗争,各类系统监听回调都很差用(AlertManager、BOOT_COMPLETED、JobScheduler),还要解决计步传感器的一些限制(系统重启清零,不能自动分天,部分手机进程杀死不能计步),还要规避不一样手机的问题,咱们只能尽可能作到不丢失步数,提升计步精度,目前我在测试计步发现支付宝计步很是准,我猜想系统为支付宝作了系统进程。
只有不断天坑,优化,增长计步准确性,也请个位大神下载代码一块儿交流。