你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 後台定位上傳的代碼實踐

後台定位上傳的代碼實踐

編輯:IOS開發基礎

0.jpg

作者:裡脊串 授權本站轉載。

前言

之前的文章說過 我現在做的是LBS定位的社交APP 其中主要的一個功能就是能夠實時定位社交圈中各個成員的位置 後台實時上傳位置則是非常重要的一個技術點 接下來就來說說我關於這方面的實踐經驗

需求

先來看看實現這個功能的具體需求是什麼 由於我們是實時定位的生活類社交APP 所以我們需要做到一下幾點

1. 如果用戶的位置在持續變化 則隔一段時間上報一次

由於我們希望能夠實時的將用戶的位置變化反饋在APP裡 所以定時的上報是剛需

2. 如果用戶的移動速度很慢 則隔一段距離上報一次

如果用戶是低速率的狀態(比如步行的移動速度大概就是1m/s左右) 這個時候如果還按(1)中的方式來上報的話 由於變化太小 地圖上的點會非常的密集 這種數據的意義不大(而且如果要做軌跡服務的話 這些密集點都是必須有花掉的) 所以這時候我們按照距離間隔來上報

3. 如果用戶的位置在到達某處後沒有變化 則不繼續上報

我們只關心位置的變化 如果用戶的位置沒有變化或者變化很小 其實是不需要上報其位置的(比如進入的公司 或者等一個很長時間的紅燈) 這時候我們就不上報(以達到省電的目的)

4. 切換到後台也要能定位上報

後台上報是必須的 用戶不可能一直運行著我們的APP (iOS4開始就支持了)

5. APP因各種原因終止運行後(用戶主動關閉, 系統殺掉) 也要能定位上報

用戶主動關閉APP的幾率不大 但是因系統調度被殺掉的情況是很普遍的 這個時候我們也要能夠上報 (iOS7開始已支持被殺掉後喚醒)

分析完需求 接下來就開始介紹如何實現

准備

首先做一些准備工作

在target的Capabilities選項中打開Background Modes 並勾選Location updates

blob.png

然後在plist中添加NSLocationAlawaysUsageDescription的鍵 在value中隨便鍵入任何內容

blob.png

完成這兩步 我們的前期工作就完成了 Background Modes是iOS7帶入的新功能 而NSLocationAlawaysUsageDescription為了增強權限機制引入的提示描述 不添加這個的話 定位功能可是使用不了的

代碼

定位肯定要跟CLLocationManager打交道 所以我們先定義一個CLLocationManager的子類 並根據需求中的幾點定義三個變量

@interface MMLocationManager : CLLocationManager
+ (instancetype)sharedManager;
@property (nonatomic, assign) CGFloat minSpeed;     //最小速度
@property (nonatomic, assign) CGFloat minFilter;    //最小范圍
@property (nonatomic, assign) CGFloat minInteval;   //更新間隔
@end

這裡解釋一下這幾個參數

  • minSpeed 如果當前運動速度大於此值 則滿足需求(1) 以時間為更新依據(minFilter) 如果當前運動速度小於此值 則滿足需求(2) 以范圍為更新依據(minInteval)

  • minFilter 最小的觸發范圍 用於需求(1)

  • minInteval 更新間隔 用於需求(2)

接下來是初始化函數

- (instancetype)init
{
    self = [super init];
    if ( self )
    {
        self.minSpeed = 3;
        self.minFilter = 50;
        self.minInteval = 10;
        self.delegate = self;
        self.distanceFilter  = self.minFilter;
        self.desiredAccuracy = kCLLocationAccuracyBest;
    }
    return self;
}

這裡的默認值可以根據需求來調整

然後是位置更新後的處理邏輯 其實也非常的簡單

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *location = locations[0];
    NSLog(@"%@",location);
    //根據實際情況來調整觸發范圍
    [self adjustDistanceFilter:location];
    //上傳數據
    [self uploadLocation:location];
}

而這個adjustDistanceFilter函數 就是整個代碼的核心 會根據當前速度來動態的調整distanceFilter這個參數 以滿足我們的需求

/**
*  規則: 如果速度小於minSpeed m/s 則把觸發范圍設定為50m
*  否則將觸發范圍設定為minSpeed*minInteval
*  此時若速度變化超過10% 則更新當前的觸發范圍(這裡限制是因為不能不停的設置distanceFilter,
*  否則uploadLocation會不停被觸發)
*/
- (void)adjustDistanceFilter:(CLLocation*)location
{
//    NSLog(@"adjust:%f",location.speed);
    
    if ( location.speed < self.minSpeed )
    {
        if ( fabs(self.distanceFilter-self.minFilter) > 0.1f )
        {
            self.distanceFilter = self.minFilter;
        }
    }
    else
    {
        CGFloat lastSpeed = self.distanceFilter/self.minInteval;
        
        if ( (fabs(lastSpeed-location.speed)/lastSpeed > 0.1f) || (lastSpeed < 0) )
        {
            CGFloat newSpeed  = (int)(location.speed+0.5f);
            CGFloat newFilter = newSpeed*self.minInteval;
            
            self.distanceFilter = newFilter;
        }
    }
}

這裡要注意到的是distanceFilter這個參數不能一直進行設置 因為每次設置完以後 再接下來的一秒以後 會立即觸發didUpdateLocations回調(系統的標准最短更新間隔是1秒 即更新頻率為1hz) 所以這裡只有當變化超過10%的時候才會重置distanceFilter

接下來 為了能夠正確的在被殺掉的情況下被喚醒 我們還要做最後一步操作 在AppDelegate的didFinishLaunchingWithOptions中加入下面的代碼

if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
    
    if ( [[MMLocationManager sharedManager] respondsToSelector:@selector(requestAlwaysAuthorization)] )
    {
        [[MMLocationManager sharedManager] requestAlwaysAuthorization];
    }
    //這是iOS9中針對後台定位推出的新屬性 不設置的話 可是會出現頂部藍條的哦(類似熱點連接)
    if ( [self respondsToSelector:@selector(allowsBackgroundLocationUpdates)])
    {
        [MMLocationManager sharedManager].allowsBackgroundLocationUpdates = YES;
    }
    [[MMLocationManager sharedManager] startUpdatingLocation];
}

這是因為被殺掉的APP 在後台被系統喚醒時 launchOptions會包含UIApplicationLaunchOptionsLocationKey**字段來進行標識 這時我們再重新啟動定位功能即可

至此 滿足我們需求的定位功能就完成了 為此我寫了一個demo來驗證(使用模擬器 然後選擇Debug->Location->Freeway Drive) 結果如下

blob.png

blob.png

接下來我們會討論一下相關的幾個問題

討論

為什麼不用定時器來控制定位間隔

網上有很多教程是用NSTimer來實現的 但是其實這樣不是很好 雖然定位的間隔是固定的 但是耗電的問題無法解決 後台會持續的更新定位 無論當前的位置是否在更新 當然 如果你的使用場景就是要每隔一段時間來上傳 就可以使用定時器來處理

使用distanceFilter來處理 會有些什麼問題

由於distanceFilter=currentSpeed*minInteval 那麼間隔的時間因為速度的變化而會有波動 但是這個波動是在可接受范圍的 如果速度加快或者變慢 那麼下一次的更新時間則會相應的縮短或者變長 但是因為我們是在真實生活環境中 速度的變化不可能那麼快 所以這個誤差是可以接受的 另外我們對distanceFilter針對速度進行矯正 因而總體來說 間隔還是會保持在我們與其的范圍內的

為什麼不使用allowDeferredLocationUpdatesUntilTraveled:timeout:

allowDeferredLocationUpdatesUntilTraveled是iOS6推出的一個新的API 看名字我們可以知道這個函數的作用是延遲位置更新 直到移動了xx米或者時間超過了xx秒 那麼這個函數不正好滿足了我們的所有要求麼? 可是萬萬沒想到 事情並不是這樣的 這個函數並不好用

接下來是吐槽時間 ?(????)

為什麼說這個函數不好用呢? 首先 這個函數的要求很多 我們來看看要這個函數起作用要滿足哪些條件

  • 必須iPhone5以及之後的硬件設備才支持

  • desiredAccuracy必須設置為kCLLocationAccuracyBest或者kCLLocationAccuracyBestForNavigation

  • distanceFilter必須設置為kCLDistanceFilterNone

  • 只在APP運行在後台時生效 前台運行時是不會進行延遲處理的

  • 只有系統在低功耗(Low Power State)的時候才有可能生效

關於Low Power State在iOS中的描述 我只在蘋果官網的文檔中找到部分定義

iOS is very good at getting a device into a low power state when it’s not being used. At idle, very little power is drawn and energy impact is low. When tasks are actively occurring, system resources are being used and those resources require energy. However, sporadic tasks can cause the device to enter an intermediate state—neither idle nor active—when the device isn’t doing anything. There may not be enough time during these intermediate states for the device to reach absolute idle before the next task executes. When this occurs, energy is wasted and the user’s battery drains faster.

據我簡單的了解 這個**Low Power State”只有在黑屏的狀態下(不只是鎖屏)才有可能觸發 只要有任何電量屏幕的操作(就連推送也算) 都會使APP退出這個狀態 同時 如果在充電狀態下 也是無法進入的

我嘗試在真機和模擬器上使用這個API 但結果APP還是以1HZ的頻率在定位(設置了kCLDistanceFilterNone的原因)

雖然locationManager:didFinishDeferredUpdatesWithError:在指定的時間後成功的回調了 但是結果還是沒有deffer 於是我查了一下 原來這個函數無法直接進行調試的 因為:

  • 不支持模擬器 deferredLocationUpdatesAvailable用於檢測設備是否支持 模擬器會返回NO

  • 不支持真機調試 因為調試時Xcode會阻止程序休眠 導致程序無法進入低功耗狀態

結論就是…這個東西連調試都沒辦法 所以我也沒有那麼多時間跑到外面去測試這個東西… 況且使用我上述的方法已經基本可以滿足需求… 所以我已放棄繼續研究這個API了 因為就算使用了這個東西 也僅僅是錦上添花而已

如果有哪些同學知道如何正確的使用這個東西 請留言告訴我 萬分感謝!

小結

文中的demo可以在這裡找到 另外demo中用到了Realm來存儲數據(模擬上傳操作) 有興趣的同學可以看一下

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved