Я применил Geofence в приложении android. Я выполнил эту ссылку для реализации "Geofence" в приложении. Я использую библиотеку "Retrofit" для вызова запроса "HTTP".
Приложение имеет следующие разрешения:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Вот мой код "IntentService":
public class GeofenceService extends IntentService
{
private static final String TAG = GeofenceService.class.getName();
public static final int GEOFENCE_NOTIFICATION_ID = 0;
public GeofenceService() {
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
// Retrieve the Geofencing intent
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
createLoggerFile();
// Handling errors
if ( geofencingEvent.hasError() ) {
String errorMsg = getErrorString(geofencingEvent.getErrorCode() );
Logger.Important(true, TAG, "onHandleIntent() :: errorMessage : "+errorMsg );
return;
}
// Retrieve GeofenceTrasition
int geoFenceTransition = geofencingEvent.getGeofenceTransition();
// Check if the transition type
if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT ||
geoFenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL)
{
Log.d(TAG, "onHandleIntent() :: geoFenceTransition : " + geoFenceTransition);
// Get the geofence that were triggered
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Create a detail message with Geofences received
String geofenceTransitionDetails = getGeofenceTrasitionDetails(geoFenceTransition, triggeringGeofences );
// Send notification details as a String
sendNotification( geofenceTransitionDetails );
}
}
// Create a detail message with Geofences received
private String getGeofenceTrasitionDetails(int geoFenceTransition, List<Geofence> triggeringGeofences) {
// get the ID of each geofence triggered
ArrayList<String> triggeringGeofencesList = new ArrayList<>();
for ( Geofence geofence : triggeringGeofences ) {
triggeringGeofencesList.add( geofence.getRequestId() );
pingGoogle(); // here is I am pinging google
callingHttpRequest(); // calling Http request. Also I called this request through application class, but still it is not worked in background.
}
String status = null;
if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER )
status = "Entering ";
else if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT )
status = "Exiting ";
else if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL )
status = "Staying ";
return status + TextUtils.join( ", ", triggeringGeofencesList);
}
// Send a notification
private void sendNotification( String msg ) {
Log.d( TAG, "sendNotification: " + msg );
// Intent to start the main Activity
Intent notificationIntent = new Intent(getApplicationContext(), DrawerActivity.class);;
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(DrawerActivity.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// Creating and sending Notification
NotificationManager notificatioMng =
(NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
notificatioMng.notify(
GEOFENCE_NOTIFICATION_ID,
createNotification(msg, notificationPendingIntent));
}
// Create a notification
private Notification createNotification(String msg, PendingIntent notificationPendingIntent) {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder
.setSmallIcon(R.drawable.ic_phi_notification_logo)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.geo))
.setColor(Converter.getColor(getApplicationContext(), R.color.default_pure_cyan))
.setContentTitle(JsonKey.TRIGGER)
.setContentText(msg)
.setContentIntent(notificationPendingIntent)
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)
.setAutoCancel(true);
return notificationBuilder.build();
}
// Handle errors
private static String getErrorString(int errorCode) {
switch (errorCode) {
case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE:
return "GeoFence not available";
case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES:
return "Too many GeoFences";
case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS:
return "Too many pending intents";
default:
return "Unknown error.";
}
}
private void callingHttpRequest() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10 / 2, TimeUnit.SECONDS)
.sslSocketFactory(sslSocketFactory().getSocketFactory())
.build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
API api = retrofit.create(***.class);
Call<ResponseBody> req = api.callGeofencingTrigger(***);
req.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
try {
String string = response.body().string();
Log.d (TAG, "onResponse() :: success");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
Log.d (TAG, "onFailure() :: t : "t.getMessage());
}
});
}
}
Всякий раз, когда устройство получает триггер геозонности, он отлично работает и дает правильные уведомления о запуске, когда приложение находится в фоновом режиме или на переднем плане (введите/вывести/оставить) или даже если пользователь убил приложение из последних задач. Когда я вызываю HTTP-запрос, когда приложение находится на переднем плане, оно работает отлично, и оно печатает успех в журнале.
onResponse() :: success
Но когда приложение убито из недавних задач, а устройство получило какой-либо триггер геозонности (введите/выйдут/уйдут), HTTP-запрос не будет выполнен должным образом. Он дает:
onFailure() :: t :
</br>java.net.UnknownHostException: Unable to resolve host
"host_name": No address associated with hostname
где host_name - адрес сервера.
I ping google или 8.8.8.8 ip из фоновой службы. Все еще сталкиваются с такими же проблемами. Это также отлично работает, когда приложение находится на переднем плане, но после убийства приложение не работает.
Итак, почему эта ошибка? Не связана ли сетевая связь, когда приложение не находится в последних задачах?
< ------------------------------------------------ -------------------------------------------------- ------------------------ >
Я пробовал следующие вещи. Получив ответы от @Xavier и @Stevensen
Я использую firebase-jobscheduler в своем приложении для вызова запроса HTTP
. Вот мой код:
В моем манифесте я добавил следующую службу:
<service
android:exported="false"
android:name="com.****.service.TriggerJobService">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
</service>
Это мой модифицированный класс GeofenceService. Я просто удалил callingHttpRequest()
и добавил задание расписания, вызвав функцию scheduleJob()
в функции getGeofenceTrasitionDetails()
. И код такой же, как и он.
public class GeofenceService extends IntentService
{
private static final String TAG = GeofenceService.class.getName();
public static final int GEOFENCE_NOTIFICATION_ID = 0;
public GeofenceService() {
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
// Retrieve the Geofencing intent
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
createLoggerFile();
// Handling errors
if ( geofencingEvent.hasError() ) {
String errorMsg = getErrorString(geofencingEvent.getErrorCode() );
Logger.Important(true, TAG, "onHandleIntent() :: errorMessage : "+errorMsg );
return;
}
// Retrieve GeofenceTrasition
int geoFenceTransition = geofencingEvent.getGeofenceTransition();
// Check if the transition type
if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT ||
geoFenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL)
{
Log.d(TAG, "onHandleIntent() :: geoFenceTransition : " + geoFenceTransition);
// Get the geofence that were triggered
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Create a detail message with Geofences received
String geofenceTransitionDetails = getGeofenceTrasitionDetails(geoFenceTransition, triggeringGeofences );
// Send notification details as a String
sendNotification( geofenceTransitionDetails );
}
}
// Create a detail message with Geofences received
private String getGeofenceTrasitionDetails(int geoFenceTransition, List<Geofence> triggeringGeofences) {
// get the ID of each geofence triggered
ArrayList<String> triggeringGeofencesList = new ArrayList<>();
for ( Geofence geofence : triggeringGeofences ) {
triggeringGeofencesList.add( geofence.getRequestId() );
scheduleJob(); // <code>**Here I schedule job**</code>
}
String status = null;
if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER )
status = "Entering ";
else if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT )
status = "Exiting ";
else if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL )
status = "Staying ";
return status + TextUtils.join( ", ", triggeringGeofencesList);
}
// Send a notification
private void sendNotification( String msg ) {
Log.d( TAG, "sendNotification: " + msg );
// Intent to start the main Activity
Intent notificationIntent = new Intent(getApplicationContext(), DrawerActivity.class);;
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(DrawerActivity.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// Creating and sending Notification
NotificationManager notificatioMng =
(NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
notificatioMng.notify(
GEOFENCE_NOTIFICATION_ID,
createNotification(msg, notificationPendingIntent));
}
// Create a notification
private Notification createNotification(String msg, PendingIntent notificationPendingIntent) {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder
.setSmallIcon(R.drawable.ic_phi_notification_logo)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.geo))
.setColor(Converter.getColor(getApplicationContext(), R.color.default_pure_cyan))
.setContentTitle(JsonKey.TRIGGER)
.setContentText(msg)
.setContentIntent(notificationPendingIntent)
.setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)
.setAutoCancel(true);
return notificationBuilder.build();
}
// Handle errors
private static String getErrorString(int errorCode) {
switch (errorCode) {
case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE:
return "GeoFence not available";
case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES:
return "Too many GeoFences";
case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS:
return "Too many pending intents";
default:
return "Unknown error.";
}
}
private void scheduleJob()
{
Bundle bundle = new Bundle();
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(getApplicationContext()));
Job.Builder builder = dispatcher.newJobBuilder();
builder.setExtras(bundle);
builder.setTag(requestId);
builder.setService(TriggerJobService.class);
builder.setTrigger(Trigger.executionWindow(10, 30));
builder.setReplaceCurrent(true);
builder.addConstraint(Constraint.DEVICE_CHARGING);
builder.addConstraint(Constraint.ON_ANY_NETWORK);
builder.addConstraint(Constraint.ON_UNMETERED_NETWORK);
dispatcher.mustSchedule(builder.build());
}
}
Это код моего TriggerJobService:
public class TriggerJobService extends JobService
{
private static final String TAG = TriggerJobService.class.getName();
private int count;
@Override
public boolean onStartJob(JobParameters job)
{
Log.d(TAG, "onStartJob() :: " + job.getTag());
// Return true as there more work to be done with this job.
//TODO have to send request to cloud
Bundle bundle = job.getExtras();
callingHttpRequest(); // here is I am calling 'HTTP' request
return true;
}
@Override
public boolean onStopJob(JobParameters job)
{
Log.d(TAG, "onStopJob() :: " + job.getTag());
// Return false to drop the job.
return false;
}
private void callingHttpRequest() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10 / 2, TimeUnit.SECONDS)
.sslSocketFactory(sslSocketFactory().getSocketFactory())
.build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
API api = retrofit.create(***.class);
Call<ResponseBody> req = api.callGeofencingTrigger(***);
req.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
try {
String string = response.body().string();
Log.d (TAG, "onResponse() :: success");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
Log.d (TAG, "onFailure() :: t : "t.getMessage());
}
});
}
}
Снова он вызывает то же самое. Он отлично работает и дает правильные уведомления о запуске, когда приложение находится в фоновом режиме или на переднем плане (введите/вывести/оставить) или даже если пользователь убил приложение из последних задач. Также это планирование надлежащей работы. И вызывая запрос HTTP
, когда приложение находится на переднем плане, оно работает отлично, и оно печатает успех в журнале.
onResponse() :: success
Но когда приложение убито из недавних задач, а устройство получило какой-либо триггер геофотометрии (введите/задержите/оставьте), то приложение расписания заданий и вызов HTTP
не выполняется должным образом. Он дает:
onFailure() :: t :
</br>java.net.UnknownHostException: Unable to resolve host
"host_name": No address associated with hostname
Итак, согласно @Xavier и @Stevensen, мое приложение не пробуждает сеть, если оно убивает последние задачи. Я попытался с firbase-JobSchedule
, но все еще сталкивался с такой же ошибкой выше. Требуется ли приложению специальное permission
для вызова запроса HTTP
, а приложение убивает из последних заданий? или FCM
является лучшим вариантом для этого. Но все-таки есть вопрос, будет ли работать FCM
, даже если приложение будет убивать из недавних задач? does FCM
пробудит сеть для отправки сообщения серверу с клиента?