问题
I am trying to send user location in server. Fast i get the location and save in RoomDatabase and get data from RoomDatabase send to server. but i can't get location every 30 minutes when app in background.
//Calling in mainactivity
PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(MyWorker.class, 30, TimeUnit.MINUTES)
.addTag(TAG)
.build();
WorkManager.getInstance().enqueueUniquePeriodicWork("Location", ExistingPeriodicWorkPolicy.REPLACE, periodicWork);
// worker class
public class MyWorker extends Worker {
private static final String DEFAULT_START_TIME = "00:00";
private static final String DEFAULT_END_TIME = "30:00";
private static final String TAG = "MyWorker";
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
/**
* The fastest rate for active location updates. Updates will never be more frequent
* than this value.
*/
private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
UPDATE_INTERVAL_IN_MILLISECONDS / 2;
/**
* The current location.
*/
private Location mLocation;
/**
* Provides access to the Fused Location Provider API.
*/
private FusedLocationProviderClient mFusedLocationClient;
private Context mContext;
/**
* Callback for changes in location.
*/
private LocationCallback mLocationCallback;
public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
mContext = context;
}
@NonNull
@Override
public Result doWork() {
LocationDAO locationDAO = Room.databaseBuilder(mContext, AppDatabase.class, "db-locations")
.allowMainThreadQueries() //Allows room to do operation on main thread
.build()
.getLocationDAO();
Log.d(TAG, "doWork: Done");
Log.d(TAG, "onStartJob: STARTING JOB..");
DateFormat dateFormat = new SimpleDateFormat("HH:mm", Locale.getDefault());
Calendar c = Calendar.getInstance();
Date date = c.getTime();
String formattedDate = dateFormat.format(date);
try {
Date currentDate = dateFormat.parse(formattedDate);
Date startDate = dateFormat.parse(DEFAULT_START_TIME);
Date endDate = dateFormat.parse(DEFAULT_END_TIME);
if (currentDate.after(startDate) && currentDate.before(endDate)) {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(mContext);
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
}
};
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
try {
mFusedLocationClient
.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() {
@Override
public void onComplete(@NonNull Task<Location> task) {
Log.d(TAG, "Location : " + task.getResult());
if (task.isSuccessful() && task.getResult() != null) {
mLocation = task.getResult();
Log.d(TAG, "Location : " + mLocation);
Date today = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss a");
String dateToStr = format.format(today);
LocationModel locationModel =new LocationModel();
locationModel.setLat(String.valueOf(mLocation.getLatitude()));
locationModel.setLng(String.valueOf(mLocation.getLongitude()));
locationModel.setDateToStr(dateToStr);
//locationDAO.insert(locationModel);
SaveTask st = new SaveTask(locationModel);
st.execute();
//Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = mContext.getString(R.string.app_name);
String description = mContext.getString(R.string.app_name);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(mContext.getString(R.string.app_name), name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = mContext.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, mContext.getString(R.string.app_name))
.setSmallIcon(android.R.drawable.ic_menu_mylocation)
.setContentTitle("New Location Update")
.setContentText("You are at " + getCompleteAddressString(mLocation.getLatitude(), mLocation.getLongitude()))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(new NotificationCompat.BigTextStyle().bigText("You are at " + getCompleteAddressString(mLocation.getLatitude(), mLocation.getLongitude())));
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mContext);
// notificationId is a unique int for each notification that you must define
notificationManager.notify(1001, builder.build());
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
} else {
Log.w(TAG, "Failed to get location.");
//Toast.makeText(mContext, "Failed to get location.", Toast.LENGTH_LONG ).show();
}
}
});
} catch (SecurityException unlikely) {
Log.e(TAG, "Lost location permission." + unlikely);
}
try {
mFusedLocationClient.requestLocationUpdates(mLocationRequest, null);
} catch (SecurityException unlikely) {
//Utils.setRequestingLocationUpdates(this, false);
Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely);
}
} else {
Log.d(TAG, "Time up to get location. Your time is : " + DEFAULT_START_TIME + " to " + DEFAULT_END_TIME);
}
} catch (ParseException ignored) {
}
return Result.success();
}
private String getCompleteAddressString(double LATITUDE, double LONGITUDE) {
String strAdd = "";
Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());
try {
List<Address> addresses = geocoder.getFromLocation(LATITUDE, LONGITUDE, 1);
if (addresses != null) {
Address returnedAddress = addresses.get(0);
StringBuilder strReturnedAddress = new StringBuilder();
for (int i = 0; i <= returnedAddress.getMaxAddressLineIndex(); i++) {
strReturnedAddress.append(returnedAddress.getAddressLine(i)).append("\n");
}
strAdd = strReturnedAddress.toString();
}
} catch (Exception e) {
e.printStackTrace();
}
return strAdd;
}
class SaveTask extends AsyncTask<Void, Void, Void> {
LocationModel locationModel;
public SaveTask(LocationModel locationModel) {
this.locationModel = locationModel;
}
@Override
protected Void doInBackground(Void... voids) {
DatabaseClient.getInstance(getApplicationContext()).getAppDatabase()
.getLocationDAO()
.insert(locationModel);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
}
// get data form database
public class GetTasks extends AsyncTask<Void, Void, List<LocationModel>> {
Context mContext;
String requestType;
public GetTasks(Context context, String requestType) {
this.mContext = context;
this.requestType =requestType;
}
@Override
protected List<LocationModel> doInBackground(Void... voids) {
List<LocationModel> taskList = DatabaseClient
.getInstance(mContext)
.getAppDatabase()
.getLocationDAO().getLocation();
return taskList;
}
@Override
protected void onPostExecute(List<LocationModel> tasks) {
super.onPostExecute(tasks);
if (tasks.size()>0) {
LocationPostReg locationPostReg = new LocationPostReg();
updateLocationValue(locationPostReg);
}
}
public static String convertObjToString(Object clsObj) {
//convert object to string json
String jsonSender = new Gson().toJson(clsObj, new TypeToken<Object>() {
}.getType());
return jsonSender;
}
// send data to server
public void updateLocationValue(LocationPostReg locationPostReg){
locationUpdateInterface apiService =
ApiClient.getClient().create(locationUpdateInterface.class);
String locationStr = convertObjToString(locationPostReg);
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),locationStr);
Log.d(TAG, locationStr +" "+ locationPostReg.getUserid() +" ");
Call<LocationSuccessfull> call = apiService.doUpdateLocation(body);
call.enqueue(new Callback<LocationSuccessfull>() {
@Override
public void onResponse(Call<LocationSuccessfull> call, Response<LocationSuccessfull> response) {
int statusCode = response.code();
LocationSuccessfull locationInfo = response.body();
// assert locationInfo != null;
if (locationInfo != null && locationInfo.getSuccess())
{
AsyncTask.execute(() ->
DatabaseClient
.getInstance(mContext)
.getAppDatabase()
.getLocationDAO().deleteAll()
);
Print.e(TAG+ " Remove Data");
}
}
@Override
public void onFailure(Call<LocationSuccessfull> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, "error: "+ t.toString());
}
});
}
回答1:
there are limits on requesting location frequently from the background you can read theme here https://developer.android.com/about/versions/oreo/background-location-limitstext
for requesting from the background frequently, you will have to bring your app to the foreground, a way to do so is to start the service as foreground service and use notification so that the user will know that your app is working on the foreground, or you can use an accessbility service but the user will need to enable that permmision himself (you cant request that permmision from your app)
this is an example of persistence notification that will bring your app to the foreground
fun CreateNotification(str:String,id:Int){
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
var serviceChannel = NotificationChannel(CHANNEL_ID,"name",NotificationManager.IMPORTANCE_HIGH)
var notificationManager= getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(serviceChannel)
}
var intent = Intent(this, MainActivity::class.java)
var pendingIntent =PendingIntent.getActivity(this,0, intent,0)
var stopIntent = Intent(this,CloseBackgroundServiceBroadCast::class.java)
stopIntent.action= CloseBackgroundServiceBroadCast.CLOSE_BACKGROUND_LISTENTER_ACTION
var pendingStop = PendingIntent.getBroadcast(this,30,stopIntent,0)
var notification = NotificationCompat.Builder(this,CHANNEL_ID)
.setContentTitle("title")
.setContentText(str)
.setSmallIcon(yourIcon)
// .addAction(com.example.autogater.R.drawable.dismiss_test,"Stop")
.addAction(R.drawable.ic_menu_search,getString(com.callgate.autogater.R.string.stop),pendingStop)
.setContentIntent(pendingIntent).build()
// notificationManager.notify(0,notification)
startForeground(1,notification)
}
来源:https://stackoverflow.com/questions/61672347/i-am-trying-to-get-location-a-update-it-in-server-in-every-30-minutes-android