主要技术点:
- Timer,
- CoroutineScope,
- retorfix2,
- Room,
- 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
}
}