問題背景
最近有需求,在APP啟動后,退到后臺,還要能實現(xiàn)周期獲取位置信息上報服務(wù)器,研究了一下實現(xiàn)方案。
問題分析
一、APP退到后臺后網(wǎng)絡(luò)請求實現(xiàn)
APP退到后臺后,實現(xiàn)周期循環(huán)發(fā)送網(wǎng)絡(luò)請求。目前嘗試了兩種方案是OK,如下:
(1)AlarmManager + 前臺服務(wù) +廣播的方案,可以正常實現(xiàn),大體思路是,啟動一個前臺服務(wù),使用AlarmManager發(fā)起一個定時廣播,然后廣播接收器接收到廣播后,循環(huán)去執(zhí)行service的操作。
(2)使用jetpeck庫提供的worker實現(xiàn),基于PeriodicWorkRequest實現(xiàn)一個周期執(zhí)行的任務(wù),比如周期設(shè)置為15分鐘,可以在后臺穩(wěn)定執(zhí)行。
二、APP退到后臺后獲取地理位置實現(xiàn)
APP申請位置時,用戶選擇了列表中的始終允許后,APP在后臺是可以正常獲取到位置信息的。不過這里有個坑,因為安卓11版本后退位置信息管控策略進(jìn)行了更新,如果APP的target sdk版本大于29時,需要分別申請前臺位置權(quán)限和后臺位置權(quán)限,本APPtargetsdk是小于等于29的,可以同時申請前臺位置權(quán)限和后臺位置權(quán)限。(compileSdkVersion小于29時,會沒有這個后臺位置權(quán)限,需要最好升級到29)
問題解決
下面展示大概的代碼,可以參考實現(xiàn)。
(1)引入依賴
api('androidx.work:work-runtime:2.0.1')
(2)manifest文件中增加申請權(quán)限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 這個權(quán)限用于訪問GPS定位 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 用于訪問wifi網(wǎng)絡(luò)信息,wifi信息會用于進(jìn)行網(wǎng)絡(luò)定位 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
(3)權(quán)限申請(同時申請位置權(quán)限和后臺位置權(quán)限)
RxPermissionHelper helper = new RxPermissionHelper(this);
helper.requestEach(new RxPermissionHelper.PermissionCallback() {
@Override
public void granted(String permissionName) {
LogUtil.writerLog("ACCESS_FINE_LOCATION granted");
}
@Override
public void denied(String permissionName, boolean forever) {
}
@Override
public void result(boolean allGranted) {
}
}, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION);
(4)work類:去執(zhí)行前臺服務(wù)
/**
* work類執(zhí)行定時任務(wù)
*/
public class SimpleWorker extends Worker {
private CurPosUtil curPosUtil;
public SimpleWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Log.d("baorant", "執(zhí)行調(diào)度任務(wù)");
LogUtil.writerLog("執(zhí)行調(diào)度任務(wù)");
startService();
return Result.success();
}
private void startService() {
// curPosUtil = new CurPosUtil(getApplicationContext());
Intent intent = new Intent(getApplicationContext(), RecordService.class);
getApplicationContext().startService(intent);
}
}
(5)RecordService前臺服務(wù)類(需要在manifest文件中配置)
/**
* 一個定時任務(wù)
*
* 方案:使用前臺服務(wù)去執(zhí)行網(wǎng)絡(luò)請求,定時發(fā)送廣播,然后在廣播接收器中重新啟動服務(wù),實現(xiàn)循環(huán)后臺服務(wù)。
*/
public class RecordService extends Service {
private CurPosUtil curPosUtil;
/**
* 每30秒更新一次數(shù)據(jù)
*/
private static final int ONE_Miniute= 30 * 1000;
private static final int PENDING_REQUEST=0;
int count = 0;
public RecordService() {
}
@Override
public void onCreate() {
super.onCreate();
curPosUtil = new CurPosUtil(getApplicationContext());
LogUtil.writerLog("RecordService onCreate");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String NOTIFICATION_CHANNEL_ID = "package_name";
String channelName = "My Background Service";
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,channelName, NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(this,NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_dial_icon) // the status icon
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentText("定時服務(wù)正在運行") // the contents of the entry
.build();
startForeground(2, notification);
}
}
/**
* 調(diào)用Service都會執(zhí)行到該方法
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.writerLog("RecordService: onStartCommand");
// 這里模擬后臺操作
initPos();
return super.onStartCommand(intent, flags, startId);
}
private void initPos() {
curPosUtil = new CurPosUtil(this);
curPosUtil.getCurPos(new CurPosUtil.CurPosCallback() {
@Override
public void getCurPos(double s, double s1, String s2) {
LogUtil.writerLog(DateUtil.timeToDate(String.valueOf(System.currentTimeMillis())));
LogUtil.writerLog("getCurPos: " + s + " " + s1 + " " + s2);
commonLogin(s + " " + s1 + " " + s2);
}
});
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
public void commonLogin(String position) {
RetrofitHelper.getInstance().login(position, "", "", "",
"", "", "", "")
.subscribe(new Observer<Boolean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Boolean aBoolean) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}
(6)activity中啟動周期任務(wù),周期15分鐘循環(huán)執(zhí)行
PeriodicWorkRequest.Builder request =
new PeriodicWorkRequest.Builder(SimpleWorker.class, 15
, TimeUnit.MINUTES).addTag("simpleTask");
LogUtil.writerLog(DateUtil.timeToDate(String.valueOf(System.currentTimeMillis())));
LogUtil.writerLog("點擊執(zhí)行task");
WorkManager.getInstance().enqueue(request.build() );
(7)LogUtil工具類,輸出日志到文件,方便定位文章來源:http://www.zghlxwxcb.cn/news/detail-603555.html
/**
* 日志工具,輸出日志到文件
*/
public class LogUtil {
/**
* 路徑 "/data/data/com包名/files/backLogTest"
*
* @param msg 需要打印的內(nèi)容
*/
public static void writerLog(String msg) {
Log.d("baorant", msg);
// 保存到的文件路徑
final String filePath = App.getContext().getFilesDir().getPath();
FileWriter fileWriter;
BufferedWriter bufferedWriter = null;
try {
// 創(chuàng)建文件夾
File dir = new File(filePath, "backLogTest");
if (!dir.exists()) {
dir.mkdir();
}
// 創(chuàng)建文件
File file = new File(dir, "lowTemperature.txt");
if (!file.exists()) {
file.createNewFile();
}
// 寫入日志文件
fileWriter = new FileWriter(file, true);
bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write( msg + "=======時間 :"+ getCurrentTime()+ "\n");
bufferedWriter.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bufferedWriter != null) {
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static String getCurrentTime() {
Calendar calendar = Calendar.getInstance();
@SuppressLint("SimpleDateFormat")
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(calendar.getTime());
}
}
問題總結(jié)
運行結(jié)果如下:
如結(jié)果所示,基于該方案可以實現(xiàn)APP在后臺,周期循環(huán)獲取位置信息并進(jìn)行上報,有興趣的同學(xué)可以進(jìn)一步深入研究。文章來源地址http://www.zghlxwxcb.cn/news/detail-603555.html
到了這里,關(guān)于安卓開發(fā)后臺應(yīng)用周期循環(huán)獲取位置信息上報服務(wù)器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!