Tuesday, August 12, 2014

Hướng dẫn chạy location ở dưới background trên iOS 6.0 và iOS 7.0

Mình bỏ nhiều thời gian nghiên cứu về vấn đề này nên hôm nay xin chia sẻ những kinh nghiệm mà mình tích góp được về vấn đề xử lý location ở dưới background trên iOS 6 và iOS 7. Sau này ra iOS 8 hay 9 mình sẽ cập nhật lại sau ^^.






Những thứ bạn cần nắm trước khi bắt đầu như:
- Hiểu cơ chế, các dịch vụ để  lấy location, cách sử dụng dịch vụ đó? Bạn cần xem tại đây.
Ở đây mình xin tóm tắt apple hỗ trợ chúng ta 2 dịch vụ để lấy location là "Standard Location Service" và "Significant Location Service".
  • "Standard Location Service": Là dịch vụ chuẩn thông thường đề lấy location của system. Nếu dùng service này bạn có thể chỉnh theo tuỳ thích mà bạn muốn, ví dụ như: Luôn luôn cập nhật location, cập nhật location theo thời gian (Lưu ý: nếu bạn ở trạng thái background thì phải tuân thủ thời gian cho phép chạy timer đó, cái này mình sẽ đề cập ở phần sau.), cập nhật location khi đi xa 1 khoảng cách là bao nhiêu, ...
  • "Significant Location Service": Là dịch vụ lấy location theo khi đã khi 1 khoảng cách xa nào đó do system của apple sẽ tự tính và trả về cho client. Apple khuyến khích dùng dịch vụ này ở dưới background, vì nó sẽ tiết kiệm pin nhất.
- Hiểu về độ chính xác của stardard location: ví dụ như kCLLocationAccuracyBestForNavigation, kCLLocationAccuracyBest, kCLLocationAccuracyNearestTenMeters, ... Theo mình được biết thì dự vào thông số này apple sẽ dùng GPS hay Cell Tower (Là các trạm sóng điện thoại để lấy location). Tài liệu tham khảo từ đây . Theo mình thấy thì độ chính xác cũng ảnh hưởng đến lượng pin tiêu thụ càng chính xác thì tốn pin nhiều hơn.

- Hiểu về thuộc tính "pausesLocationUpdatesAutomatically" được hỗ trợ từ iOS 6. Theo mình kiềm tra và test thì thằng này nó sẽ làm tắt location của bạn khi đang ở trạng thái background. Nhưng không biết nó có start lại không? Vì mình chưa có thời gian test. Ai biết về cái này nhiều xin comment ở dưới nha.

- Hiểu về thuộc tính "activityType" cái này mình chưa phân biệt được khi test ứng dụng. Apple nói có thể lấy location theo từng loại phương tiện hay cách thức bạn sử dụng. Ví dụ như bạn viết app dành cho viêc tập thể dục thì dùng loại CLActivityTypeFitness  , nếu bạn đi xe ô tô thì dùng loại CLActivityTypeAutomotiveNavigation,  .... Cái này chúng ta sẽ thảo luận sau khi mình có nhiều thời gian để test hơn.

Theo mình nghĩ thì lý thuyết chắc chỉ cần nhiêu đó là bạn có thể xử lý location 1 cách thuần thục rồi. Tiếp theo mình sẽ nói việc lấy location ở dưới background.

- Cách 1: Dùng significant-change location để lấy location ở trạng thái background: Cách này là cách tốt nhất để tiết kiệm pin cho user và có thể chạy location ở dưới background, nhưng mặt hạn chế của service này là mình không thể thiết lập việc lấy location theo thời gian hoặc khoảng cách di chuyển. Nếu muốn tối ưu pin cho user thì mình nghĩ vẫn dùng cách này là tối ưu nhất. Cách sử dụng service này thì cứ làm theo hướng dẫn trong trang hướng dẫn của Apple là được. Lưu ý theo mình nghĩ khi dùng service này các bạn nên thiết lập thuộc tính pausesLocationUpdatesAutomaticallyNo bởi vì có thể nó sẽ tự động huỷ service của bạn khi không cần thiết. Mình test cảm thấy như vậy tốt nhất không nên để hệ thống làm việc này cho mình để tránh bug tiềm tàng của hệ thống. 

- Cách 2: Là dùng standard location để lấy location: Cách này bạn có thể thiết lập những thông số bạn thích như lấy location theo thời gian, theo khoảng cách, theo loại phương tiện là gì.... Nếu lúc nào bạn cũng để service này chạy thì chắc chắn chạy được ở background, nhưng rất là tốn pin, không ai làm app như vậy cả. Khi bạn muốn dùng service này để lấy location thì nên chú ý những thuộc tính như: pausesLocationUpdatesAutomatically, activityTypeallowDeferredLocationUpdatesUntilTraveled:timeout:

Theo trước giờ mình làm thì khách hàng thường yêu cầu mình có thể viết location có thể chạy được ở dưới bacgkround theo thời gian hay theo khoảng cách di chuyển, mình xin phân tích ở dưới đây:
+ Nếu chạy location theo thời gian thì bạn cần phải để ý thời gian cho phép chạy những tác vụ trước khi hệ thống đưa xuống trạng thái background hoàn toàn. Thời gian cho phép trên iOS 5 và iOS 6 là 10 phút, nhưng iOS 7 thì chỉ có 3 phút. Vì thế trong khoảng thời gian này bạn phải gọi service location lên để đánh thức hệ thống chứ không nó rơi vào trạng thái background hoàn toàn thì ứng dụng của mình không thể chạy lại. Theo mình tìm hiểu trên mạng thì thời gian cho phép tối ưu trên iOS 7 là chỉ khoảng 1 phút thôi. Vì thế nếu chúng ta làm theo cách này thì cũng tốn pin lắm, cách này chỉ dùng cho những trường hợp khẩn cấp thôi. Hướng dẫn làm theo cách này các bạn có thể tham khảo tại đây.

*** Mình tìm ra cách có thể theo thời gian định kỳ có thể gởi location lên server được như thế này: Mình tham khảo hướng dẫn code ở trên và làm lại thử theo cách cứ 2 phút rưỡi (của ios 7, ios 6 thì có thể chỉnh cao hơn, có thể là 9 phút rưỡi) mình cho chạy location 1 lần và thiết lập giá trị tự động pause là No. Đúng theo thời gian setting như 5 phút, 15 phút, 30 phút, ... thì mình có thể chạy location theo đúng thời gian mong muốn để gởi lên server. -> Cách này đánh lừa hệ thống ios để ta có thể chạy suốt ở dưới background được. 

+ Nếu chạy location theo khoảng cách thì bạn nên chú ý thuộc tính pausesLocationUpdatesAutomaticallyNo. Theo Apple nói thì thuộc tính này kết hợp với activityType để có thể ngừng service khi không cần thiết. Nhưng theo mình test thì cảm giác nó bị ngừng hẳn chứ không được chạy lại. Vì thế tốt nhất chúng ta nên tắt nó đi, chứ không location của mình sẽ bị tắt khi xuống background. Làm cách này thì hơi tốn pin vì mình nghĩ có thể do lúc nào nó cũng chạy service nên mới tốn pin như vậy.

+ Theo mình nghĩ nếu bạn viết ứng dụng mà chạy trên xe ô tô hay tập thể dục bạn có thể kết hợp 2 thuộc tính pausesLocationUpdatesAutomaticallyactivityType để viết có thể chúng sẽ chạy được đó, cái này khi nào mình có thời gian sẽ test thử xem sao.     

+ Nếu bạn viết 1 ứng dụng cho chạy đến địa điểm nào đó thì tắt location để tiết kiệm pin thì nên dùng chức năng Deferring Location. Chức năng này mình chưa test thử nhưng thấy apple có ghi như vậy, ai có kinh nghiệm về cái này rồi thì share cho mọi người biết với nha.

+ Cách làm location chạy được ở background theo thời gian trên iOS 7.0: Theo mình xem log của 1 app làm chức năng theo dõi vị trí của user như Vismo. Mình để ý người ta có dùng significant-change location và cả remote notification để có thể gọi location theo thời gian được. Vì khi notification từ server trả về ta có thể gọi dịch vụ location để đánh thức system lại. Cách này cũng khá hay nhưng đòi hỏi app đó phải có internet thì mới làm dc.

Lưu ý:1 số app viết location chạy dưới background và phải gởi data lên server hay lưu xuống datbase thì nên để ý những library đó có hỗ trợ hay không nha, chứ không sẽ bị crash app. Ví dụ như connection bình thường thì có AFNetwork, nếu không dùng library thì bạn nên sử dụng NSOperationQueue để gọi connection. Còn nếu bạn dùng socket thì mình biết GCDAsyncSocket có thể hỗ trợ bạn. Database thì mình thấy có Core Database cũng hỗ trợ chạy dưới background, hướng dẫn tại đây hoặc tại thoughbot. Kinh nghiệm của mình chỉ cần tạo 2 NSManagedObjectContext là có thể làm được rồi. 
Ngoài ra khi đang chạy ở background bạn không nên cập nhật giao diện hay khởi tạo Alert vì hệ thống không cho phép ta làm việc đó, chúng ta nên có 1 đoạn kiểm tra những code như vậy ở background.






1 comment:

  1. Bài viết rất hay và bổ ích, cảm ơn bạn !

    ReplyDelete

Note: Only a member of this blog may post a comment.