我在Android應用程序中實現了Geofence。我跟着this鏈接在應用程序中實施「Geofence」。我正在使用'Retrofit'庫來調用'HTTP'請求。Geofencing:通過後臺服務發送HTTP請求失敗。給UnknownHostException
App有以下權限:
<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是服務器地址。
我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()
,並通過調用getGeofenceTrasitionDetails()
函數中的scheduleJob()
函數添加了計劃作業。代碼和它一樣。
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's 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
是否仍然有效? FCM
會喚醒網絡從客戶端向服務器發送消息嗎?
請提供設備和操作系統版本的信息。 – User10001
我在一加二(OS-version 6.0),Xiomi-Mi4i(OS-version 5.0.2)設備上試過。 –
你在5.0.2小米設備中遇到同樣的問題嗎?如果是這樣,問題不是因爲打盹模式(至少在此設備上),因爲它是在6.0中添加的「打盹和應用待機管理所有在Android 6.0或更高版本上運行的應用程序的行爲」https://developer.android .com/training/monitoring-device-state/doze-standby.html無論如何,如果可能的話,我以前的批量上傳事件的建議仍然有效。 –