最近在項目中研究計步模塊,主要功能記錄當天步數,類似微信運動,支付寶計步,咕咚今日步數。
開發之前的調研工作,搜遍baidu,google,github都沒有找到我想要的demo和文章,大多數都是需要Service?;?。
對于各大手機廠商為了提高電池的續航里程AlertManager、BOOT_COMPLETED、Service的START_STICKY基本上都是不起作用的,Service后臺?;罡遣豢赡堋?br> 下面是我實現的計步模塊和大家一起學習
github地址
之前也有一篇文章寫計步模塊,這篇文章是對上面文章的優化,github代碼已經更新到最新了。
Android計步模塊(類似微信運動)
目前已經對現有的庫進行優化V2.0.0如下這篇文章
Android計步模塊優化(今日步數)V2.0.0
App計步模塊優化的三個過程
第一個過程上線:
由于功能著急上線,項目最開始計步模塊單獨使用加速度傳感器Sensor.TYPE_ACCELEROMETER進行計算步數,同時Service需要在后臺存活才能計步,否則不能計步。
第二個過程計步器:
項目運行一段時間公司開始推廣走路計步這個模塊,所以開始重新開發計步模塊,這次使用了Android4.4以上提供的計步傳感器Sensor.TYPE_STEP_COUNTER來完成,這次重新開發整個計步模塊有了質的飛躍,由于采用了計步傳感器不在需要后臺保活Service,同時計步傳感器的功耗特別低,整個模塊也更省電了。
第三個過程優化計步:
用戶量變大了投訴也變多了,android各種各樣的機型真是讓人蛋疼,終于到了第三個階段優化階段。
目前最多的問題:
1.Android4.4以上的系統但是手機沒有計步協處理器
2.部分手機步數出現暴增現象,有可能一天幾十萬步
3.部分手機出現一天清零好多次。
4.開機計步不能自啟動,需要打開app(我已經監聽BOOT_COMPLETED廣播)
5.隔天分隔(0點分隔)不好用每天早上需要打開app(我已經設置AlertManager)
這篇文章就來介紹現在app的計步模塊,已經解決上述問題1、2、3,至于4、5問題是系統問題正在尋找解決方案,大家也可以幫幫忙在評論中給我提示。
已經將計步模塊單獨封裝成libModule上傳github如果有開發者需要的或者想交流的可以很方便的使用下載,點擊這里下載。
計步方式背景知識
1.加速度傳感器Sensor.TYPE_ACCELEROMETER計步方式:
這種方式是有開源的算法根據加速度傳感器進行計算步數,點擊這里查看原作者源碼;
優點:只要有加速度傳感器的設備都可以使用,相對來說可以使用的設備較多。
缺點:步數的準確性取決于算法且算法比較難優化;需要后臺?;頢ervice否則不能計步;計步算法比較費電;部分手機鎖屏不能計步;
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),還要解決計步傳感器的一些限制(系統重啟清零,不能自動分天,部分手機進程殺死不能計步),還要規避不同手機的問題,我們只能盡量做到不丟失步數,提高計步精度,目前我在測試計步發現支付寶計步非常準,我猜測系統為支付寶做了系統進程。
只有不斷天坑,優化,增加計步準確性,也請個位大神下載代碼一起交流。