AndroidStudio:使用后台服务实时记录位置信息和上传位置信息

主要技术点:

  1. Timer,
  2. CoroutineScope,
  3. retorfix2,
  4. Room,
  5. Notification

运行机制:

定时器每10秒获取一次位置信息,位置信息与上传位置信息对比,移动距离大于5米就记录位置信息,180秒上传一次位置信息。

class LocationService : Service() {
    private lateinit var uploadTimer: Timer
    private val NOTIFICATION_ID = 1399
    private val CHANNEL_ID = "LocationServiceChannel"
    private var count = 0
    private val Tag = "LocationService"
    private val coroutineUpLoaderScope = CoroutineScope(Dispatchers.IO)

    private val gson = Gson()
    private val stringBuilder = StringBuilder()
    private val split = ","
    private val splitGap = "|"
    private var latitude = 0.0
    private var longitude = 0.0
    private var distance = 10.0

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel() // 创建一个空操作的 PendingIntent
        val emptyIntent = Intent()
        val emptyPendingIntent = PendingIntent.getActivity(
            this, 0, emptyIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        val notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder(this, CHANNEL_ID)
        } else {
            @Suppress("DEPRECATION")
            Notification.Builder(this)
        }.setContentTitle("位置服务正在运行")
            .setContentText("应用正在获取您的位置信息")
            .setSmallIcon(R.mipmap.ic_launcher_round)
            .setContentIntent(emptyPendingIntent) // 设置为空操作的 PendingIntent
            .build()
        startForeground(NOTIFICATION_ID, notification)
        startUploadTimer()
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID, "Location Service Channel", NotificationManager.IMPORTANCE_DEFAULT
            )
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
        }
    }

    private fun getLocation() {
        launch {
            count++
            val amLocation =
                AMLocationServerUtil.initLocation(this@LocationService, false) ?: return@launch
            if (!isValidLocation(amLocation)) return@launch
            if (!LoginUserInfo.isLogin()) return@launch
            val orderId= AppApplication.getInstance().orderId

            distance =
                calculateDistance(latitude, longitude, amLocation.latitude, amLocation.longitude)

            if (distance < 5.0) {//如果值不动小于5.0,到一定时间也要更新位置表示在线
                uploadLocation(orderId)
                return@launch
            }
            latitude = amLocation.latitude
            longitude = amLocation.longitude

            val entity = Location()
            entity.orderId= orderId
            entity.latitude = amLocation.latitude.toString()
            entity.longitude = amLocation.longitude.toString()
            entity.time = ChangeStrToDate.getCurrentTime()
            DaoClient.appDatabase.locationDao().insert(entity)
            log(" insert")
            uploadLocation(orderId)
        }
    }

    private fun isValidLocation(location: AMapLocation?): Boolean {
        if (location == null) return false // 纬度
        if (location.latitude <= 0.0f || location.longitude <= 0.0f) return false
        if (location.latitude > 3.0f && location.latitude < 54.0f) return true // 经度
        return location.longitude > 73.0f && location.longitude < 136.0f
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return START_STICKY
    }

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

    override fun onDestroy() {
        super.onDestroy()
        if (::uploadTimer.isInitialized) {
            uploadTimer.cancel()
        }
        coroutineUpLoaderScope.cancel() // 取消协程作用域
    }

    private fun startUploadTimer() {
        if (::uploadTimer.isInitialized) {
            uploadTimer.cancel() // 取消已有定时器
            uploadTimer.purge()  // 清除任务队列
        }
        uploadTimer = Timer()
        uploadTimer.schedule(object : TimerTask() {
            override fun run() {
                getLocation()
            }
        }, 0, 10 * 1000)
    }

    private fun uploadLocation(orderId: String) {
        if (count % 18 != 1) {
            return
        }
        latitude = 0.0
        longitude = 0.0

        if (!TextUtils.isEmpty(orderId)) {
            launch {
                val list = DaoClient.appDatabase.locationDao().getLocationList(orderId)
                val path = uploadData(list)
                stringBuilder.clear()
                val jsonString = gson.toJson(
                    mapOf(
                        "path" to path, "order_group_id" to orderId
                    )
                )
                val toRequestBody = jsonString.toRequestBody("application/json".toMediaType())
                val result = ApiClient.appApi.saveOrderPath(toRequestBody)
                if (result.state == "00") {
                    DaoClient.appDatabase.locationDao().delete()
                }
            }
        }else{
            launch {
                val list = DaoClient.appDatabase.locationDao().getLocationList(orderId)
                val path = uploadData(list)
                stringBuilder.clear()
                val jsonString = gson.toJson(
                    mapOf(
                        "path" to path,
                    )
                )
                val toRequestBody = jsonString.toRequestBody("application/json".toMediaType())
                val result = ApiClient.appApi.savePath(toRequestBody)
                if (result.state == "00") {
                    DaoClient.appDatabase.locationDao().delete()
                }
            }
        }
    }

    private fun uploadData(list: List<Location>): String {
        for (i in list.indices) {
            if (i > 0) stringBuilder.append(splitGap)
            stringBuilder.append(list[i].longitude)
            stringBuilder.append(split)
            stringBuilder.append(list[i].latitude)
        }
        return stringBuilder.toString()
    }

    fun launch(block: suspend CoroutineScope.() -> Unit) {
        coroutineUpLoaderScope.launch(
            CoroutineExceptionHandler { _, _ -> }) {
            try {
                block.invoke(this)
            } finally {
            }
        }
    }

    private fun log(type: String) {
        if (BuildConfig.DEBUG) {
            Log.i(Tag, "$Tag $count $type")
        }
    }

    private fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
        val earthRadius = 6371000.0 // WGS-84 地球平均半径(单位:米)
        val dLat = Math.toRadians(lat2 - lat1)
        val dLon = Math.toRadians(lon2 - lon1)
        val a =
            sin(dLat / 2) * sin(dLat / 2) + cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) * sin(
                dLon / 2
            ) * sin(dLon / 2)
        val c = 2 * atan2(sqrt(a), sqrt(1 - a))
        return earthRadius * c
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值