我的一个同事收到通知,说我帮助开发的应用程序在后台时会很快耗尽电池电量。到目前为止,她是我认识的第一个通过我们的应用程序收到此通知的人。
让我描述一下应用程序的功能/需要:当用户启动应用程序时,设备将被本地化一次。之后,用户必须按下按钮以更新其位置。每当用户切换到特定活动时,服务器都会被ping通以更新应用程序中的数据。除了使用该应用程序所需的GPS之外,没有其他权限。仅在服务器被ping通时,并且仅在主动使用该应用程序时更新位置。
这是服务器通信的代码。
/**
* Queries for Stores in the BoundingBox defined by the given GeoCoordinates.
* After the Async- Query was successful the Stores which are in the Resultset but currently not
* loaded are queryed for and added to the global Adapter and an
* ID-Filter is put in place limiting the List to the retrieved ones.
*
* @param topLeft TopLeft- Coordinates for the Query
* @param bottomRight BottomRight- Coordinates for the Query
*/
private void queryForStoresInBoundingBox(lokalfuchs.de.lokalfuchs.api.model.Location topLeft, lokalfuchs.de.lokalfuchs.api.model.Location bottomRight) {
//query elasticsearch for all stores in the given region
StoreService.getInstance().queryForStoresInGeoPointBoundingBox(
topLeft, bottomRight, new ElasticsearchQueryCallback() {
@Override
public void querySuccesful(final List<Integer> ids) {
//from the list of ids which lie in the currently visible region: determine which are not currently loaded
final List<Integer> missingStores = StoreService.getInstance().getStoreAdapter().findMissingItemIds(ids);
final List<Integer> missingOffers = OfferService.getInstance().findMissingStoreIds(ids);
if (!missingStores.isEmpty()) {
//request these missing items and add them in the adapter
StoreService.getInstance().requestStores(null, null, missingStores.toArray(new Integer[missingStores.size()]), new StoreRequestCallback() {
@Override
public void requestSuccesful(final List<Store> stores) {
// !! TODO dont ask for offers without out valid offer ids you will get from elastic search!
OfferService.getInstance().requestOffers(null, missingOffers.toArray(new Integer[missingOffers.size()]), null, null, new OfferRequestCallback() {
@Override
public void requestSuccesful(final List<Offer> offers) {
// TODO replace quick fix, just fetching valid offers with elastic offerIds
// only continue with valid offers
final List<Offer> validOffers = new ArrayList<>();
SimpleDateFormat parserEndDate = new SimpleDateFormat(getResources().getString(R.string.date_pattern));
Date currentDate = new Date();
for (Offer offer : offers) {
try {
Date endDate = parserEndDate.parse(offer.getEndDate());
if (endDate != null && currentDate.compareTo(endDate) < 0) {
validOffers.add(offer);
continue;
}
} catch (ParseException e) {
e.printStackTrace();
}
}
//add to adapter on ui-thread
MapActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Map<Integer, List<Integer>> allStoreOffers = OfferService.getInstance().getOffers();
ItemAdapter adapter = OfferService.getInstance().getOfferAdapter();
final List<Offer> handledOffers = (LocationService.getInstance().getLastLocation() != null) ? OfferService.getInstance().calculateDistancesAndRemoveExpiredOffers(
validOffers, new com.google.maps.model.LatLng(LocationService.getInstance().getLastLocation().getLatitude(), LocationService.getInstance().getLastLocation().getLongitude())) : validOffers;
for (Offer o : handledOffers) {
OfferService.getInstance().getOfferAdapter().getItems().put(o.getId(), o);
List<Integer> storeOffers = allStoreOffers.get(o.getStoreId());
if (storeOffers == null) {
storeOffers = new ArrayList<>();
allStoreOffers.put(o.getStoreId(), storeOffers);
}
if (!storeOffers.contains(o.getId())) {
storeOffers.add(o.getId());
adapter.getItems().put(o.getId(), o);
}
}
Map<Integer, MappableItem> list = StoreService.getInstance().getStoreAdapter().getItems();
for (Store store : stores)
list.put(store.getId(), store);
StoreService.getInstance().getStoreAdapter().setIdFilter(new ItemIdFilter(ids));
}
});
}
@Override
public void requestFailed(int errorCode) {
}
});
}
@Override
public void requestFailed(int errorCode) {
}
});
} else {
//there are no missing items - add the filter immediately instead of after the query
StoreService.getInstance().getStoreAdapter().setIdFilter(new ItemIdFilter(ids));
}
}
@Override
public void queryFailed() {
}
});
}
public void queryForStoresInGeoPointBoundingBox(@NonNull Location top_left, @NonNull Location bottom_right, final @NonNull ElasticsearchQueryCallback callback) {
//if the coordinates are not correctly ordered reoder them
if (top_left.getLat() < bottom_right.getLat()) {
Double temp = top_left.getLat();
top_left.setLat(bottom_right.getLat());
bottom_right.setLat(temp);
}
if (top_left.getLon() > bottom_right.getLon()) {
Double temp = top_left.getLon();
top_left.setLon(bottom_right.getLon());
bottom_right.setLon(temp);
}
this.elasticSearchInterface.elasticSearchStoreIdsGeopointRequest(new ElasticSearchInterface.SearchQuery(top_left, bottom_right, false), LokalFuchs.DEFAULT_SIZE).enqueue(new Callback<ElasticSearchInterface.SearchResult>() {
@Override
public void onResponse(Call<ElasticSearchInterface.SearchResult> call, Response<ElasticSearchInterface.SearchResult> response) {
//check for response-code 200
if (response.code() == 200) {
//prepare resultlist
List<Integer> storeIds = new ArrayList<>();
//and iterate over all hits extracting the ids
for (ElasticSearchInterface.SearchResult.Hits hit : response.body().hits.hits) {
if (hit.fields.id.length > 0)
storeIds.add(hit.fields.id[0]);
}
callback.querySuccesful(storeIds);
} else {
callback.queryFailed();
}
}
@Override
public void onFailure(Call<ElasticSearchInterface.SearchResult> call, Throwable t) {
callback.queryFailed();
}
});
}
我忘了提到数据加载需要很长时间,可能需要4秒钟以上。
这是本地化的代码。
/**
* asking for localisation permission
* <p>
* if ok:
* start location updates
* if not set isRequesting to false
*
* @param activity Activity
*/
public boolean requestLocation(Activity activity) {
if (AppService.getInstance().checkPermission(Manifest.permission.ACCESS_FINE_LOCATION, activity, R.string.missing_gps_permission, null)) {
this.isRequesting = true;
this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
return true;
} else {
return false;
}
}
/**
* register Callback for change of position
*
* @param locationChangedCallback Callback für for position change
*/
public void registerCallback(LocationChangedCallback locationChangedCallback) {
if (!this.locationChangedCallbacks.contains(locationChangedCallback)) {
this.locationChangedCallbacks.add(locationChangedCallback);
}
}
@Override
public void onMapReady(GoogleMap googleMap) {
[...]
//register for location-changes and request the location once
Location location = LocationService.getInstance().getLastLocation();
//if we have a location use it
if (location == null) {
this.googleMap.moveCamera(CameraUpdateFactory.zoomTo(15));
this.googleMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(52.520008, 13.404954)));
LocationService.getInstance().registerCallback(this);
LocationService.getInstance().requestLocation(this);
} else {
LatLng position = new LatLng(location.getLatitude(), location.getLongitude());
this.markMyPosition(position);
this.googleMap.moveCamera(CameraUpdateFactory.zoomTo(15));
this.googleMap.moveCamera(CameraUpdateFactory.newLatLng(position));
}
[...]
}
我检查了她手机上的电池电量消耗详细信息,即使该应用程序处于活动状态且GPS仅使用了不到10秒钟,CPU也没有太多工作要做。该应用程序在过去30天内下载了20 mb。仍然,她在15分钟左右后仍收到通知,并且她必须关闭它才能摆脱通知。到目前为止,她在任何其他应用程序上都没有这个问题,也没有其他人报告过这个问题。
有人知道这个问题可能是什么,或者我能做些什么找出导致这个问题的原因?