patloew / RxLocation

🗺 [DEPRECATED] Reactive Location APIs Library for Android and RxJava 2
Other
488 stars 92 forks source link

GoogleApiClient onConnected() is called on the main thread regardless of subscribeOn() #21

Closed riclage closed 4 years ago

riclage commented 7 years ago

The GoogleApiClient callback onConnected() is always called on the main thread. This is causing the chain to ignore the scheduler specified on subscribeOn().

One solution to this is to use blockingConnect() instead of connect().

patloew commented 7 years ago

I'm not sure if blockingConnect() is a good idea, as existing code might break if no subscribeOn() is specified and the calls then block the main thread.

Edit: Sorry, didn't mean to close the issue ;)

riclage commented 7 years ago

I think it makes sense in this case to throw an exception, no? That's what happens with Retrofit for example.

patloew commented 7 years ago

I won't change the behavior for now. Even if I use blockingConnect(), setResultCallback() still operates on the main thread then. And await() can only be used on looper threads. Also, for location updates, the LocationListener also gives updates on the main thread.

Polyterative commented 5 years ago

This means I have no way to collect location data in background. As soon as I leave the activity/block the main thread the obserable stops from emitting values

Polyterative commented 5 years ago

I was kinda able to do it, using a ForegroundService.

The observable still runs on the Main thread, but I guess here the main thread is the Service 's thread, so it works even if I leave the activity/application.

Sorry for the noisy code, it's still a bit WIP. Here it is

image

class LocationUploadService : Service() {
    private val destroyEvent = PublishSubject.create<String>()

    override fun onBind(intent: Intent): IBinder? {
        return Binder()
    }

    @SuppressLint("MissingPermission", "CheckResult")
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Logger.d("LocationUploadService", "onStart")

        val rxPermission = RealRxPermission.getInstance(applicationContext)
        val rxLocation = RxLocation(applicationContext)

        val locationRequest = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setInterval(10000).setFastestInterval(3000)

        val fineLocationPermissionEvent: Observable<Permission> =
            rxPermission.request(Manifest.permission.ACCESS_FINE_LOCATION).toObservable()

        val serviceO = when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> rxPermission.request(Manifest.permission.FOREGROUND_SERVICE).toObservable()
            else -> Observable.just(Permission.granted("Foreground service"))
        }

        val permissionCheckEvent = Observables.combineLatest(
            fineLocationPermissionEvent,
            serviceO
        )
            .doOnEach { Logger.d(it) }
            .takeUntil(destroyEvent)
            .share()

        // destroy if refues permissions
        permissionCheckEvent.filter { !isAllowed(it) }.takeUntil(destroyEvent).subscribe { this.onDestroy() }

        // continue chain if allows permissions
        val onPermissionGranted = permissionCheckEvent.filter { isAllowed(it) }.take(1).takeUntil(destroyEvent).share()

        // destroy everything after 5
//        onPermissionGranted.take(5).takeUntil(destroyEvent).subscribe { this.onDestroy() }

        // core: observe notification updates
        // create notification and foreground for the service
        onPermissionGranted
            .observeOn(Schedulers.newThread())
            .subscribeOn(Schedulers.newThread())
            .doOnNext {
                Logger.v("Creating service notification...")

                val notificationIntent = Intent(this, DriverActivity::class.java)
                notificationIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP

                val notification = MyNotificationHelper(applicationContext).getGPSNotification(
                    getString(R.string.label_gps_localization_active),
                    getString(R.string.label_trip_running),
                    getString(R.string.app_name),
                    PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT)
                )

                val foregroundID = 1234
                startForeground(foregroundID, notification.build())
            }
            .doOnNext { Logger.v("Starting to listen to location updates...") }
            .switchMap { rxLocation.location().updates(locationRequest) } //listen to location
            .takeUntil(destroyEvent)
            .subscribe({ sendDataToServer(it) }) { Logger.e(it.localizedMessage) }

        return Service.START_NOT_STICKY
    }

    private fun isAllowed(it: Pair<Permission, Permission>) =
        it.first.state().equals(Permission.State.GRANTED) && it.second.state() == Permission.State.GRANTED

    override fun onDestroy() {
        super.onDestroy()

        stopForeground(true)
    }

    private fun sendDataToServer(location: Location) {

        val toUpload = Tracking()

//        toUpload.setLatitude(location.latitude)
//        toUpload.setLongitude(location.longitude)
//        toUpload.setSpeed(location.speed.toDouble())
//        toUpload.setPrecision(location.accuracy.toDouble())
//        toUpload.setDate(OffsetDateTime.now(ZoneOffset.UTC))
//        toUpload.setTrip(currentTripCode)

        if (Utils.isNetworkAvailable(applicationContext)) {

            try { // upload
                Logger.v("Uploading location data to server...")
            } catch (e: ApiException) {
                Logger.e("Tracking upload error: \n", e)
            }

        } else {
            Logger.v("No internet, doing nothing...")
        }
    }

    internal interface IntentActions {
        companion object {
            const val GPS_START = "scheduleGPS"
            const val GPS_STOP = "notification_exit"
        }
    }

}
patloew commented 4 years ago

This library is now deprecated and not maintained anymore. Please switch to the CoLocation library.