Android計步模塊優化(今日步數)

最近在項目中研究計步模塊,主要功能記錄當天步數,相似微信運動,支付寶計步,咕咚今日步數。 開發以前的調研工做,搜遍baidu,google,github都沒有找到我想要的demo和文章,大多數都是須要Service保活。 對於各大手機廠商爲了提升電池的續航里程AlertManager、BOOT_COMPLETED、Service的START_STICKY基本上都是不起做用的,Service後臺保活更是不可能。 下面是我實現的計步模塊和你們一塊兒學習 github地址 以前也有一篇文章寫計步模塊,這篇文章是對上面文章的優化,github代碼已經更新到最新了。 Android計步模塊(相似微信運動)java

本篇文章簡書地址

App計步模塊優化的三個過程

第一個過程上線: 因爲功能着急上線,項目最開始計步模塊單獨使用加速度傳感器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項目結構以下圖:

TodayStepCounter項目結構圖.png

由圖可見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;
        }
    }
}

計步模塊原理

計步模塊流程圖

計步模塊流程圖.png

講解流程圖:

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根據這個標誌來判斷是否重啓進行步數合併,主要是增長精度有時開機廣播不能收到。

加速度傳感器計步流程圖

加速度傳感器計步流程圖.png

講解流程圖:

Android4.4如下或者一些特殊的手機不提供計步傳感器,我只能用加速度傳感器計步,加速度傳感器的原理就是利用必定的算法模擬出步數(加速度傳感器計步算法不在本篇文章討論的範圍以內),用這種方式計步Service必定要在後臺存活不然不能計步,這種方式跨天分隔步數利用Intent.ACTION_TIME_TICK廣播回調來判斷當前時間和上次PreferencesHelper記錄的時間是否相同若是不一樣步數歸零從0開始計步,步數的記錄採用PreferencesHelper來保存,防止當天重啓手機系統步數歸零。

計步傳感器計步流程圖

計步傳感器計步流程圖.png

講解流程圖:

Android4.4以上的可使用計步傳感器進行計步,至於計步傳感器的有點上面已經介紹了。這種方式部分手機能夠不須要程序自啓動權限。

跨天分隔步數採用兩種方式:

1.第一種方式和上面同樣採用Intent.ACTION_TIME_TICK廣播,這裏很少說了。

2.第二種方式採用AlertManager方式也就是設置0點鬧鐘,在這個0點廣播中對步數進行分隔,這個AlertManager不是每一個手機均可以啓動的。

手機系統重啓判斷採用四種方式:

1.開機廣播監聽BOOT_COMPLETED,這個監聽不是每一個手機均可以收到,若是收到能夠啓動Service,而後作步數合併使計步模塊從上次關機時的步數開始累加,若是收不到只能用下面幾種方式增長重啓的判斷了。

2.關機廣播監聽ACTION_SHUTDOWN,這個監聽不是每一個手機均可以收到,若是收到,在用戶手動啓動 Service中能夠判斷系統重啓了。

3.記錄運行時間判斷手機重啓,上次運行的時間大於當前運行時間判斷爲重啓,只是增長精度,極端狀況下連續重啓,會判斷不出來。

4.上次傳感器步數總和,當前傳感器步數小於上次傳感器步數確定是從新啓動了,只是用來增長精度不是絕對的

計步傳感器計步核心流程

計步核心流程.png

這個流程圖的講解都在圖片上。

提升計步精度:

1.設置app後臺自啓動

2.各類安全軟件設置app爲白名單,爲了保證app不被任何安全軟件在後臺殺死。 以上兩種方式保證通知欄中一直顯示app的步數。

3.手機系統重啓,若是通知欄中沒有顯示步數,表示app沒有收到開機監聽,須要手動啓動app,不然步數會丟失 。

app一直在後臺存活確定會耗電,部分手機能夠在後臺關閉的狀況下計步,可是這種方式須要天天早上打開一次app讓計步模塊對步數進行清零不然步數會丟失。

須要優化:

1.每次傳感器回調都會寫三次SharedPreferences。

2.計步模塊在後臺存活,天天過0點開始計步都會丟失一些步數,丟失的步數跟啓動計步傳感器須要的步數有關,例如:個人測試機連續走10步才能夠啓動計步傳感器回調,因此就丟失10步。

總結:

Android計步就是在和android系統做鬥爭,各類系統監聽回調都很差用(AlertManagerBOOT_COMPLETEDJobScheduler),還要解決計步傳感器的一些限制(系統重啓清零,不能自動分天,部分手機進程殺死不能計步),還要規避不一樣手機的問題,咱們只能儘可能作到不丟失步數,提升計步精度,目前我在測試計步發現支付寶計步很是準,我猜想系統爲支付寶作了系統進程。

只有不斷天坑,優化,增長計步準確性,也請個位大神下載代碼一塊兒交流。

相關文章
相關標籤/搜索