Xây dựng ứng dụng theo dõi khoảng cách xe

4
Dịch vụ dạy kèm gia sư lập trình
Bài này thuộc phần 4 của 8 phần trong series Học Firebase cơ bản

Bài viết trước, mình đã hướng dẫn các xây dựng ứng dụng theo dõi vị trí của một xe dựa trên GPS và Firebase. Trong lúc làm bài hướng dẫn đó, mình chợt nảy ra ý tưởng nâng cao hơn chút. Đó là xây dựng ứng dụng theo dõi khoảng cách có thể tính toán được khoảng cách của xe với vị trí hiện tại của bạn.

Tại sao mình lại có ý tưởng đó? Chắc hẳn bạn đã từng sử dụng ứng dụng Uber hay Grab rồi đúng không?

Mình thấy tính năng tính toán khoảng cách khi tài xế bắt đầu di chuyển rất hay ho. Không dài dòng nữa, chúng ta cùng nhau tự xây dựng code ứng dụng theo dõi khoảng cách xe của riêng mình nhé.

Xây dựng ứng dụng theo dõi khoảng cách

Trong bài viết này, mình sẽ không xây dựng một ứng dụng đồ sộ kiểu như Grab hay Uber. Đơn giản, mình sẽ chỉ hướng dẫn các bạn xây dựng một số tính năng cơ bản như:

  • Hiển thị vị trí hiện tại của người dùng lên bản đồ
  • Dịch chuyển marker vị trí người dùng trên bản đồ khi người dùng di chuyển
  • Tính toán khoảng cách sử dụng Google Distance Matrix API.

#1. Chuẩn bị trước cho ứng dụng theo dõi khoảng cách

  1. Android Google Maps API key
  2. Google Distance Matrix API key

Bạn cần đăng ký với Google để có thể lấy hai key này. Các bạn đăng ký tại đây: Maps API keyDistance Matrix API key

#2. Cấu hình dự án ứng dụng theo dõi khoảng cách

Sau khi bạn tạo dự án mới bằng Android Studio, bạn vào build.gradle để thêm thư viện sau:

// PlayServices dependency
implementation 'com.google.android.gms:play-services-maps:16.0.0'
implementation 'com.google.android.gms:play-services-location:16.0.0'

// Map services dependency
implementation 'com.google.maps:google-maps-services:0.2.4'

#3.  Hiển thị vị trí hiện tại lên bản đồ

Trong bài viết này, mình sử dụng ViewModelLiveData để lưu trữ vị trí hiện tại của người dùng.

Vì vậy, nếu hoạt động được tạo lại do thay đổi cấu hình. Như xoay ngang điện thoại, nó sẽ ngay lập tức nhận vị trí trước đó của người dùng

// 1 
private val locationLiveData = NonNullMediatorLiveData<Location>()

// 2
fun currentLocation() : LiveData<Location> = locatinLiveData

// 3
object : LocationCallback() {

        override fun onLocationResult(locationResult: LocationResult?) {
             super.onLocationResult(locationResult)
             locationResult?.let {
                 val location = it.lastLocation
                 val accuracy = currentLocation.accuracy
                 if (!location.hasAccuracy() || accuracy > 35f) return
                 locationLiveData.value = location                
             }        
        }

Mình sẽ giải thích về đoạn code trên:
– Lớp NonNullMediatorLiveData kế thừa từ lớp MediatorLiveData. Mục đích là vừa kế thừa những hàm tính năng của MediatorLiveData, lại vừa hạn chế những lỗi liên quan đến

LocationCallback được sử dụng để nhận Location. Phương thức onLocationResult được gọi khi vị trí của thiết bị khả dụng. Trong hàm trên, đầu tiên mình kiểm tra xem độ chính xác của vị trí có nằm trong bán kính 35 mét không? Nếu vị trí trong bán kính thì chỉ cần chúng ta thiết lập vị trí vào locationLiveData.

Bạn có thể đọc thêm về độ chính xác vị trí tại đây.

Nhiều bạn sẽ thắc mắc là tại sao mình lại nói lớp NonNullMediatorLiveData kế thừa từ lớp MediatorLiveData mà không thấy khai báo ở đâu cả?

Thực chất mình khai báo kế thừa trong  class NonNullMediatorLiveData như bên dưới:

class NonNullMediatorLiveData<T> : MediatorLiveData<T>()

#4.  Dịch chuyển marker vị trí người dùng trên bản đồ khi người dùng di chuyển

viewModel.currentLocation()
         .nonNull()
         .observe(this) {
              if(currentMarker != null)
                   animateMarker(currentMarker)
              else 
                   currentMarker = addNewMarker()    
          }

Các bạn thêm đoạn mã này vào Activity.

Lưu ý là bạn có thể thấy một thay đổi nho nhỏ trong việc tracking vị trí. Phương pháp tracking location đó là:

  • Đầu tiên, mình kiểm tra xem marker đánh dấu vị trí đã được khởi tạo rồi hay chưa (nếu NULL là chưa khởi tạo).
  • Nếu đã khởi tạo thì chúng ta chỉ cần thêm điểm đánh dấu mới vào bằng cách gọi animateMarker(currentMarket). Còn không thì chúng ta tạo mới một marker.

Dưới đây là kotlin extension function mà chúng ta sử dụng ở trên cho ví dụ LiveData.

fun <T> LiveData<T>.nonNull(): NonNullMediatorLiveData<T> {
    val mediator: NonNullMediatorLiveData<T> = NonNullMediatorLiveData()
    mediator.addSource(this) { it?.let { mediator.value = it } }
    return mediator
}

fun <T> NonNullMediatorLiveData<T>.observe(owner: LifecycleOwner, observer: (t: T) -> Unit) {
    this.observe(owner, android.arch.lifecycle.Observer {
        it?.let(observer)
    })
}

#4. Tính toán khoảng cách sử dụng Google Distance Matrix API

// 1
private val distanceTracker = NonNullMediatorLiveData<Long>()

// 2
fun distanceTracker(): LiveData<Long> = distanceTracker

// 3
fun startLocationTracking() {
        locationTrackingCoordinates = locationLiveData.value
        compositeDisposable.add(Observable.interval(10, TimeUnit.SECONDS)
                .subscribeOn(appRxScheduler.threadPoolSchedulers())
                .subscribe({ _ -> makeDistanceCalculationCall() }
                        , { _ -> startLocationTracking() }))
    }

// 4
private fun makeDistanceCalculationCall() {
  val tempLocation = locationLiveData.value
  val origin = arrayOf(locationTrackingCoordinates.latitude.toString() + "," + locationTrackingCoordinates.longitude)
  val destination = arrayOf(tempLocation.latitude.toString() + "," + tempLocation.longitude.toString())
  DistanceMatrixApi.getDistanceMatrix(googleMapHelper.geoContextDistanceApi(), origin, destination)
          .mode(TravelMode.WALKING)
          .setCallback(object : PendingResult.Callback<DistanceMatrix> {
                 override fun onResult(result: DistanceMatrix) {
                     locationTrackingCoordinates = tempLocation
                     val temp = result.rows[0].elements[0].distance.inMeters
                     totalDistance += temp
                     distanceTracker.postValue(totalDistance)
                 }

                 override fun onFailure(e: Throwable) {
                 }
          })
}

Các  bạn thêm đoạn mã ở trên vào lớp ViewModel.

  • Mình vẫn sử dụng NonNullMedoderLiveData cho distanceTracker  để cập nhật tổng khoảng cách bất cứ khi nào tính được khoảng cách mới.
  • Chỉ hiển thị public LiveData thông qua hàm distanceTracker().
  • Hàm startLocationTracking() được gọi khi ứng dụng nhận vị trí đầu tiên.

Bạn thấy trong hàm  này, chúng ta có một bộ đếm thời gian với khoảng thời gian mười giây. Cứ sau mười giây, chúng ta lại gọi hàm tính khoảng cách từ Google API. Tại sao lại 10s?

Vì chúng ta không thực hiện việc tính theo thời gian thực được nên đành dùng phương pháp tiktok này. Mình nghĩ 10s là thời gian đủ ngắn rồi.

Chúng ta lưu trữ vị trí hiện tại của người dùng trong tempLocation. Sau đó, chúng ta chỉ gọi hàm Distance Matrix.

Lớp DistanceMatrixApi là class trong thự viện mà chúng ta đã thêm trong build.gradle ở đầu bài viết. Bạn có thể đọc thêm về cách sử dụng thư viện ở đây trên Github.

Trong phương thức onResult(), chúng ta cần cập nhật locationTrackingCoordinates. Lấy khoảng cách tính bằng mét và đặt tổng khoảng cách thành distanceTracker.

Lưu ý, bạn thấy mình sử dụng phương thức postValue để cập nhật distanceTracker. Lý do là chúng ta cập nhật vị trí từ background thread lên UI nên không thể làm theo cách thông thường được, mà phải thông qua hàm postValue.

Như vậy là chúng gần như đã hoàn thành các tính toán logic rồi đấy.

Công việc cuối cùng là khai báo license khoảng cách vào Activity.

viewModel.distanceTracker()
         .nonNull()
         .observe(this) {
            distanceCoveredTextView.text = it
          }

Đến đây thì bạn đã hoàn thành xây dựng code ứng dụng theo dõi khoảng cách sử dụng Google API rồi. Nếu bạn vẫn còn chưa hiểu thì có thể download toàn bộ source code của bài viết tại đây

Rất hi vọng, bài viết này sẽ giúp ích cho các bạn trong việc tạo nên ứng dụng theo dõi khoảng cách. Đừng quên like và chia sẻ với bạn nhé.

Xem tiếp các bài trong Series
Phần trước: Tạo tính năng xác thực số điện thoại bằng FirebasePhần kế tiếp: Tạo Push Notification với Firebase trong ứng dụng Android
Dịch vụ phát triển ứng dụng mobile giá rẻ - chất lượng
Bài trướcPhân biệt Rom Stock và Rom Cook và cách up Rom cho Android chuẩn
Bài tiếp theoInfluence Content Marketing – Chiến lược Marketing “truyền miệng”
Sơn Dương
Tên đầy đủ là Dương Anh Sơn. Tốt nghiệp ĐH Bách Khoa Hà Nội. Mình bắt đầu nghiệp coder khi mà ra trường chẳng xin được việc đúng chuyên ngành. Mình tin rằng chỉ có chia sẻ kiến thức mới là cách học tập nhanh nhất. Các bạn góp ý bài viết của mình bằng cách comment bên dưới nhé !

4
Bình luận. Cùng nhau thảo luận nhé!

avatar
  Theo dõi bình luận  
Mới nhất Cũ nhất Nhiều voted nhất
Thông báo
elyspa
Guest
elyspa

Mình làm chỉ đường đi cho khách từ vị trí của khách tới spa á anh

elyspa
Guest
elyspa

Mình cần làm bản đồ chỉ đường đi cho website tới spa nhưng đọc k hiểu lắm bạn có thể giúp k ạ