我正在构建一个应用程序来检测Android上的iBeacons。基本功能是通过信标将广告数据存储到设备上并将其上传到服务器。为此,我正在使用Android Beacon Library。
要在Android O的后台运行它,我正在使用前台服务。问题是当应用程序在后台运行超过30分钟时,在检测到信标后,当用户退出信标区域并调用didExitRegion
时,不知何故,该服务会自动终止并重新启动,因此没有数据上传到服务器。同样在重新启动后,在第二次didExitRegion
调用时,它会在将来的某个时间完全停止,它会重新启动,但会重新执行相同的循环。
应用程序在非活动状态约30分钟后进入区域时发生的事件顺序
First Restart after didExit(图片仅供参考) 在这里你可以看到从11区到9区的切换。中途应用程序即时关闭并重新触发,但没有发送推送
Exit after second didExit(图片仅供参考) 接下来:当现在退出此区域时,app会再次停在后台。但这次不会立即重新触发。这是始终发生的确切顺序。
代码片段 BeaconScanner.java
@Override
public void didExitRegion(Region region) {
Log.d(TAG, "Exited A Region");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
notificationHelper.notify(2, notificationHelper.getNotification("BeaconScanner", "Exit Major #"+previousMajor, false));
else
utils.dispatchNotification("Exit Major #"+previousMajor, 1);
Log.e(TAG, "DidSend "+didSend+" has Data "+userData.hasPendingData());
if(didSend && userData.hasPendingData()) {
JSONObject data = userData.getJsonFromUser();
Log.d(TAG, "Timestamp = "+System.currentTimeMillis());
userData.addTimestamp(""+System.currentTimeMillis());
userData.requestDataSync(data);
userData.clearBeaconData();
Log.d(TAG, data.toString());
didSend = !didSend;
}
previousMajor = -1000;
lastBeacon = resetBeacon;
}
user.Java
JSONObject getJsonFromUser() {
Log.d(TAG, "Timestamp as in getJsonFromUser "+timestamp);
JSONObject json = new JSONObject();
try {
json.put("email", email);
json.put("name", name);
JSONArray beaconArray = new JSONArray();
for (Beacon beacon : beaconData){
beaconArray.put(new JSONObject()
.put("major", beacon.getId2().toInt())
.put("minor", beacon.getId3().toInt())
.put("uuid", beacon.getId1().toString())
);
}
json.put("data", beaconArray);
Log.d(TAG, timestamp);
json.put("timestamp", ""+System.currentTimeMillis());
return json;
} catch (Exception e){
Log.e(TAG, e.getMessage());
Crashlytics.log(e.getLocalizedMessage());
}
return json;
}
void requestDataSync(final JSONObject json){
User.syncing = true;
Crashlytics.log(1, "User.java", "Requesting Auth Token");
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
user.getIdToken(true).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
@Override
public void onComplete(@NonNull Task<GetTokenResult> task) {
if(task.isSuccessful()){
final Task<GetTokenResult> t = task;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
trustAllHosts();
URL url = new URL("https://indpulse.com/generatetoken");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setHostnameVerifier(DO_NOT_VERIFY);
connection.setRequestProperty("Authorization", ""+t.getResult().getToken());
connection.setRequestMethod("GET");
Log.d(TAG, t.getResult().getToken());
connection.setDoOutput(true);
connection.connect();
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String packet;
while((packet = br.readLine()) != null){
response.append(packet);
}
JSONObject responseObject = new JSONObject(response.toString());
String idToken = responseObject.getString("token");
Crashlytics.log(1, "User.java", "Auth Token Acquired");
sendData(json, idToken);
} catch (MalformedURLException e){
Log.e(TAG, "Malformed URL "+e.getLocalizedMessage());
Crashlytics.log(e.getLocalizedMessage());
} catch (IOException e){
Log.e(TAG, "IOExeption "+e.getLocalizedMessage());
Crashlytics.log(e.getLocalizedMessage());
} catch (JSONException e) {
Log.d(TAG,"Json error");
Crashlytics.log(e.getLocalizedMessage());
}
}
});
thread.start();
}
}
});
void sendData(JSONObject json, final String idToken){
final String sJson = json.toString();
System.out.println(sJson);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL("https://indpulse.com/android");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Authorization", "Bearer "+idToken);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.connect();
DataOutputStream os = new DataOutputStream(conn.getOutputStream());
os.writeBytes(sJson);
os.flush();
os.close();
Log.i(TAG, String.valueOf(conn.getResponseCode()));
Log.i(TAG , conn.getResponseMessage());
conn.disconnect();
} catch (Exception e){
Log.e("BeaconScanner", e.getLocalizedMessage());
Crashlytics.log(e.getLocalizedMessage());
}
User.syncing = false;
}
});
thread.start();
}
编辑1
需要注意的一点是,信标具有重叠区域,即信标扫描仪将检测该区域中的2个信标。所以最近的信标是由
timeAverageRssi
的最大值决定的,这个错误特别突然出现,在30分钟不活动之后有区域开关,即信标1是最近的,然后信标2成为最近的信标
我怀疑在Android 8+上使用Android Beacon Library版本2.15的前台服务存在错误。虽然我自己没有重现,但理论上说Android 8+阻止了用于从信标扫描服务提供监控回调的Intent服务的使用。
我基于类似的报告问题2.15.1 beta 1在库版本here中构建了一个建议的修复。请尝试此修复程序,看看它是否解决了您的问题。