I do a huge polling when user login into my app. At the moment, we don't have much payload. But, the payloads are growing by the 100s daily at the moment. Which makes us have around 300 records per-user. And this, is the first week. So, we started optimizing for the next thousands. API has been well modified, but the Android app is shaky.
I have a request where a user have max of Two Territories for now, and I need to fetch all the Items in each Territory. So, I did this:
/**
* This is to get all user territories and for each and every territory, fetch all items there;
*/
private Observable<ItemListResponse> getAlltemIByTerritory() {
List<String> territories = PrefUtils.getUserTerritories(context);
return Observable.from(territories).flatMap(territory -> fetchAllTerritoryItemPaged(territory, 1));
}
/**
* This is to fetch all items in a passed territory. There's a per_page of 21.
*/
private Observable<ItemListResponse> fetchAllTerritoryItemPaged(String territory, int page) {
Log.e(TAG, "Each territory page: " + page);
return itemAPI.getIems("Bearer " + PrefUtils.getToken(context), page, 21, territory)
.flatMap(itemListResponse -> {
Meta meta = itemListResponse.getBakeResponse().getMeta();
Observable<ItemListResponse> thisPage = Observable.just(itemListResponse);
if (meta.getPage() != meta.getPageCount() && meta.getPageCount() > 0) {
Observable<ItemListResponse> nextPage = fetchAllTerritoryItemPaged(territory, page + 1);
return thisPage.concatWith(nextPage);
} else {
return thisPage;
}
});
}
Utilizing the Observable
public void getAlltemIByTerritory(APIRequestListener apiRequestListener) {
realm.executeTransaction(realm1 -> realm1.where(RealmItem.class).findAll().deleteAllFromRealm());
getAlltemIByTerritory()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ItemListResponse>() {
#Override
public void onCompleted() {
Log.e(TAG, "Completed Item");
apiRequestListener.didComplete(WhichSync.ITEM);
unsubscribe();
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
handleError(e, apiRequestListener, WhichSync.ITEM);
unsubscribe();
}
#Override
public void onNext(ItemListResponse itemListResponse) {
Log.e(TAG, "In the handler next " + itemListResponse.toString());
realm.executeTransaction(realm1 -> {
for (Item itemData : itemListResponse.getItemResponse().getData()) {
RealmItem realmItem = realm1.where(RealmItem.class).equalTo("id", itemData.getId()).findFirst();
if (realmItem != null)
realmItem.deleteFromRealm();
realm1.copyToRealm(Item.copyBakeryToRealm(itemData));
}
});
apiRequestListener.onProgress(itemListResponse.getItemResponse().getMeta(), itemListResponse.getItemResponse().getData().size(), WhichSync.ITEM);
}
});
}
Log.e(TAG, "Each territory page: " + page); This line and this line Log.e(TAG, "In the handler next " + itemListResponse.toString()); reads to 23 for a 1780 record. With 21 records per_page. And then, the second line stop showing. Then, the view has hang. And then, when the first one is done, I then see the second spitting out logs afterwards.
This works fine, just that when the record is over 700 (The first try was with a 1780 record), there's a huge lag on the UI. And on mostly pre-lollipops it crashes with NoClassDefined Exception and sometimes it works. But, the lag is still always there.
Any help with optimizing the code, thanks.
Tail Recursion here:
/**
* This is to fetch all items in a passed territory. There's a per_page of 21.
*/
private Observable<ItemListResponse> fetchAllTerritoryItemPaged(String territory, int page) {
Log.e(TAG, "Each territory page: " + page);
return itemAPI.getIems("Bearer " + PrefUtils.getToken(context), page, 21, territory)
.flatMap(itemListResponse -> {
Meta meta = itemListResponse.getItemResponse().getMeta();
Observable<ItemListResponse> thisPage = Observable.just(itemListResponse);
if (meta.getPage() != meta.getPageCount() && meta.getPageCount() > 0) {
Observable<ItemListResponse> nextPage = fetchAllTerritoryItemPaged(territory, page + 1);
return thisPage.concatWith(nextPage);
} else {
return thisPage;
}
});
}
is the one causing the problem. Luckily, Rx provides us with BehaviorSubject.
So, I did something like this instead
private Observable<ItemListResponse> fetchAllTerritoryItemPaged(String territory, int page) {
BehaviorSubject<Integer> pageControl = BehaviorSubject.create(page);
Observable<ItemListResponse> ret = pageControl.asObservable().concatMap(integer -> {
if (integer > 0) {
return itemAPI.getIems("Bearer " + PrefUtils.getToken(context), integer, 21, territory)
.doOnNext(itemListResponse -> {
Meta meta = itemListResponse.getItemResponse().getMeta();
if (meta.getPage() != meta.getPageCount() && meta.getPageCount() > 0) {
pageControl.onNext(integer + 1);
} else {
pageControl.onNext(-1);
}
});
} else {
return Observable.<ItemListResponse>empty().doOnCompleted(pageControl::onCompleted);
}
});
return Observable.defer(() -> ret);
}
Which not only remove the lag, but also request comes in pretty fast
Related
I have a method to ping all computers on a list called computerItemList and check if it's online or not. I want to do it by sending parallel requests to API. Code below simulates response from server and it is working but I need information when all requests finish unblocking ping button. How to do that?
Generally, I want to execute async command on all items on list and then wait until all finish.
public void pingComputers() {
for (ComputerItem computerItem : _computerItemList) {
Observable.just(computerItem)
.subscribeOn(Schedulers.io())
.subscribe(item -> {
int sleepTime = randomizer.nextInt(1000) + 200;
int status = randomizer.nextInt(ComputerStatus.values().length - 1) + 1;
Log.d(TAG, Thread.currentThread().getName() + " >>pingSelected: " + item + " sleep: " + sleepTime);
Thread.sleep(sleepTime);
item.setStatus(ComputerStatus.values()[status]);
updateComputerList();
});
}
}
UPDATE
I wrote something like this. Is it okay?
public void ping2() {
Observable.fromIterable(_computerItemList)
.flatMap(computerItem -> {
return Observable.just(computerItem)
.subscribeOn(Schedulers.io())
.map(item -> {
int sleepTime = randomizer.nextInt(1000) + 200;
Log.d(TAG, "ping2: Sleeping for " + sleepTime);
Thread.sleep(sleepTime);
int status = randomizer.nextInt(ComputerStatus.values().length - 1) + 1;
item.setStatus(ComputerStatus.values()[status]);
updateComputerList();
return item;
});
}).doOnComplete(() -> Log.d(TAG, "ping2: COMPLETE")).subscribe();
}
UPDATE - IT IS WORKING! But is it okay???
public void executeCommand(CommandType command) {
isWorking.setValue(true);
Observable.fromIterable(_computerItemList)
.subscribeOn(Schedulers.io())
.flatMap(computerItem -> createPingObservable(computerItem))
.observeOn(AndroidSchedulers.mainThread())
.doOnComplete(() -> isWorking.setValue(false))
.subscribe();
}
private Observable<ComputerItem> createPingObservable(ComputerItem computerItem) {
return Observable.just(computerItem)
.subscribeOn(Schedulers.io())
.map(item -> {
int sleepTime = randomizer.nextInt(1000) + 200;
int status = randomizer.nextInt(ComputerStatus.values().length - 1) + 1;
Log.d(TAG, Thread.currentThread().getName() + ">>> executeCommand: PING");
Thread.sleep(sleepTime);
item.setStatus(ComputerStatus.values()[status]);
updateComputerList();
return item;
});
}
UPDATE - IT IS WORKING! But is it okay???
No, it's not ok to call updateComputerList(); that way, you would have to synchronized it because you will be calling it from multiple threads, each of them trying to mutate the state.
I would do it this way:
Observable.fromIterable(list)
.flatMap(computerItem -> createPingObservable(computerItem))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(computerItem -> updateComputerList(computerItem));
}
Also, don't use Thread.sleep(sleepTime);, just use
.delay(randomizer.nextInt(1000) + 200, TimeUnit.MILLISECONDS)
My app is making repeat API requests to download chunks of data. Unfortunately the server limits the API requests to 3 per second. In the code below, how can I rate limit the requests to X per second ?
private void getHistoricalPrices(String currency, String start_date, String end_date, int granularity){
// show download status
currentDlPageIndex++;
view.showDownloadingStatus(currentDlPageIndex, totalDlPages);
// make the API call
addDisposable(
model.isNetworkAvailable()
.doOnSuccess(isNetworkAvailable -> {
if (!isNetworkAvailable) {
showErrorMessage();
Timber.v("no internet");
}
})
.filter(isNetworkAvailable -> true)
.flatMapSingle(isNetworkAvailable -> model.getHistoricalPrices(currency, start_date, end_date, String.valueOf(granularity)))
.subscribeOn(rxSchedulers.io())
.observeOn(rxSchedulers.mainThread())
.subscribe((Response<List<List<String>>> responseData) -> {
if (responseData != null && responseData.code() == HTTP_OK) {
List<List<String>> response = responseData.body();
if (response != null) {
// create list from downloaded data
ArrayList<HistoricPrice> tmpHistoricPriceList = new ArrayList<>(response.size());
for (List<String> rawHistoricPrice : response) {
HistoricPrice historicPrice = new HistoricPrice(rawHistoricPrice.get(0), rawHistoricPrice.get(1), rawHistoricPrice.get(2), rawHistoricPrice.get(3), rawHistoricPrice.get(4), rawHistoricPrice.get(5));
tmpHistoricPriceList.add(0, historicPrice);
}
// add the downloaded list to the main list being recreated
model.historicPriceList.addAll(tmpHistoricPriceList);
Timber.d("added %d records to memory", response.size());
// if there's more to download, download another chunk
if (intermediateDateSecs != null && intermediateDateSecs < endDateSecs){
startDateSecs = tmpHistoricPriceList.get(tmpHistoricPriceList.size()-1).time + granularity;// add "granularity" to startDateSecs to avoid getting two exact data for the same time
Date startDate = new Date(startDateSecs * 1000L);//requires milliseconds, not epoch
String startStrDate = DateUtils.fromDateToString(startDate);
intermediateDateSecs = startDateSecs + ((ApiService.MAX_HISTORIC_RETURN_VALUES - 1) * granularity);
if (intermediateDateSecs > endDateSecs) intermediateDateSecs = endDateSecs;
Date intermediateDate = new Date(intermediateDateSecs * 1000L);
String intermediateStrDate = DateUtils.fromDateToString(intermediateDate);
getHistoricalPrices(currency, startStrDate, intermediateStrDate, granularity);
} else {
// no more to download, save data
Timber.d("downloaded total of %d records", model.historicPriceList.size());
view.hideDownloadingStatus();
showSaveDataMessage();
}
}
}
}, error -> {
showErrorMessage();
})
);
}
you can see that the method getHistoricalPrices() calls itself to continue downloading. This implementation works well, except for the server complaining when there's too many API requests per second.
You can perform your request every X milliseconds / seconds like so:
long interval = 0L; // Your desired interval here in milliseconds
Observable.interval(0, interval, TimeUnit.MILLISECONDS)
.takeUntil(x -> isTaskComplete == true)
// Other configuration calls go here
.subscribe(); // make request here or in doOnNext()
I am new in working with UpNp and DLNA. I am using cling library and DLNA code from here. In this code, everything working perfectly but my need is something else. It is giving me all media folders(Videos, Audio and Images) from selected device in network. But what I want is non media files from selected device on the network i.e., .docx, .srt, .txt etc.
Here is some code portion:
upnpService.getControlPoint().execute(
new ContentBrowseActionCallback(ContentActivity.this,
service, createRootContainer(service), mContentList,
mHandler));
Root Container:
protected Container createRootContainer(Service service) {
Container rootContainer = new Container();
rootContainer.setId("0");
rootContainer.setTitle("Content Directory on "
+ service.getDevice().getDisplayString());
Log.e("createRootContainer", "service.getDevice().getDisplayString() : " + service.getDevice().getDisplayString());
return rootContainer;
}
ContentBrowseActionCallback:
public class ContentBrowseActionCallback extends Browse {
private static Logger log = Logger
.getLogger(ContentBrowseActionCallback.class.getName());
private Service service;
private Container container;
private ArrayList<ContentItem> list;
private Activity activity;
private Handler handler;
public ContentBrowseActionCallback(Activity activity, Service service,
Container container, ArrayList<ContentItem> list, Handler handler) {
super(service, container.getId(), BrowseFlag.DIRECT_CHILDREN, "*", 0,
null, new SortCriterion(true, "dc:title"));
this.activity = activity;
this.service = service;
this.container = container;
this.list = list;
this.handler = handler;
}
public void received(final ActionInvocation actionInvocation,
final DIDLContent didl) {
log.fine("Received browse action DIDL descriptor, creating tree nodes");
activity.runOnUiThread(new Runnable() {
public void run() {
try {
list.clear();
// Containers first
for (Container childContainer : didl.getContainers()) {
Log.e("Item", "childContainer : " + childContainer.getTitle());
log.fine("add child container "
+ childContainer.getTitle());
list.add(new ContentItem(childContainer, service));
}
// Now items
for (Item childItem : didl.getItems()) {
Log.e("Item", "childItem : " + childItem.getTitle());
log.fine("add child item" + childItem.getTitle());
list.add(new ContentItem(childItem, service));
}
} catch (Exception ex) {
log.fine("Creating DIDL tree nodes failed: " + ex);
actionInvocation.setFailure(new ActionException(
ErrorCode.ACTION_FAILED,
"Can't create list childs: " + ex, ex));
failure(actionInvocation, null);
handler.sendEmptyMessage(ContentActivity.CONTENT_GET_FAIL);
}
ConfigData.listPhotos.clear();
Iterator iterator = didl.getItems().iterator();
while (iterator.hasNext()) {
Item item = (Item) iterator.next();
Log.e("received", "item.getTitle() : " + item.getTitle());
ContentItem contentItem = new ContentItem(item,
ContentBrowseActionCallback.this.service);
if ((contentItem.getItem().getTitle().toString() != null)
&& (contentItem.getItem().getResources() != null)) {
List list = contentItem.getItem().getResources();
if ((list.size() != 0)
&& (((Res) list.get(0)).getProtocolInfo() != null)
&& (((Res) list.get(0)).getProtocolInfo()
.getContentFormat() != null)) {
Log.e("received", "list.get(0).getProtocolInfo() : " + ((Res) list.get(0)).getProtocolInfo());
Log.e("received", "list.get(0).getProtocolInfo().getContentFormat() : " + ((Res) list.get(0)).getProtocolInfo().getContentFormat());
Log.e("received", "list.get(0).getProtocolInfo().getContentFormat().substring() : " + ((Res) list.get(0)).getProtocolInfo().getContentFormat().substring(0,
((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.indexOf("/")));
if (((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.substring(
0,
((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.indexOf("/"))
.equals("image")) {
ConfigData.listPhotos
.add(new ContentItem(
item,
ContentBrowseActionCallback.this.service));
} else if (((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.substring(
0,
((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.indexOf("/"))
.equals("audio")) {
ConfigData.listAudios
.add(new ContentItem(
item,
ContentBrowseActionCallback.this.service));
} else {
Log.e("received", "item : " + item.getTitle());
ConfigData.listVideos
.add(new ContentItem(
item,
ContentBrowseActionCallback.this.service));
}
}
}
}
handler.sendEmptyMessage(ContentActivity.CONTENT_GET_SUC);
}
});
}
public void updateStatus(final Status status) {
}
#Override
public void failure(ActionInvocation invocation, UpnpResponse operation,
final String defaultMsg) {
}
}
I googled a lot about this point but nothing in hand. I got few ideas about container id and how it works.
Please let me know how to achieve this scenario. I also don't have much idea about how DLNA and UPNP works. Please also help me to understand DLNA and UPNP better.
Thank You!
Ok, After diving into code from more than a week, I got basic idea how DLNA and UPNP works. How callbacks are made and how it fetches media files from device. So, now I am able to answer my own question.
HOW TO GET NON MEDIA FILES:
Whenever discovery service found your own device, it will prepare media server which will fetch all media from your device. First it creates a Node for specific type of media.e.g., Videos, Photos, Audios etc. Here is the code:
ContentNode rootNode = ContentTree.getRootNode();
// Video Container
Container videoContainer = new Container();
videoContainer.setClazz(new DIDLObject.Class("object.container"));
videoContainer.setId(ContentTree.MY_FOLDER_ID);
videoContainer.setParentID(ContentTree.ROOT_ID);
videoContainer.setTitle("My Folder Name");
videoContainer.setRestricted(true);
videoContainer.setWriteStatus(WriteStatus.NOT_WRITABLE);
videoContainer.setChildCount(0);
rootNode.getContainer().addContainer(videoContainer);
rootNode.getContainer().setChildCount(
rootNode.getContainer().getChildCount() + 1);
ContentTree.addNode(ContentTree.MY_FOLDER_ID, new ContentNode(
ContentTree.MY_FOLDER_ID, videoContainer));
This way you can add your own folder.
To fetch files you need to use Cursor to get files from your device. Something like this:
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_NONE;
Uri externalUri = MediaStore.Files.getContentUri("external");
String[] filePathColumn = {MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATA};
Cursor cursor = contentResolver.query(externalUri, filePathColumn,
selection, null, null);
cursor.moveToFirst();
do {
String id =
cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
String path =
cursor.getString(cursor.getColumnIndex(filePathColumn[1]));
String serverPath = "http://" + mediaServer.getAddress() + "/"
+path;
} while (cursor.moveToNext());
cursor.close();
Thus, serverPath will become your file path and you can access your file from another device. You can filter your files using file extension.
I must say this is huge project which I am using, so there are many changes I did to achieve my requirement. But, this answer justify my question.
Thank You!
I have two observables(A, B), and I want the first to finish running before the second runs. But, that's not even the problem I'm having. The problem is that, when A is added before B, B doesn't run at all unless I place B before A then, the two runs. But, the scenario I'm in is like thus:
A - Pickup
B - Delivery
There are three types of orders. Pickup Only, Delivery Only and Pickup And Delivery. Pickups need to run before Deliveries in every situation. A Delivery only already have Pickup marked as true. A Pickup only, needs to be picked up and delivered on it being closed. Which is why I need Pickup to send all locally saved pickups first before sending deliveries. So, I did this:
Pickup
private Observable<UpdateMainResponse> getDeliveredOrders() {
String token = PrefUtil.getToken(context);
BehaviorSubject<Integer> pageControl = BehaviorSubject.create(1);
Observable<UpdateMainResponse> ret = pageControl.asObservable().concatMap(integer -> {
if (integer - 1 != deliveryUpdate.size()) {
Log.e(TAG, "DeliveredOrders: " + deliveryUpdate.size());
RealmOrderUpdate theDel = deliveryUpdate.get(integer-1);
Log.e(TAG, "DeliveryUpdate: " + theDel.toString());
DeliverOrder pickupOrder = new DeliverOrder();
pickupOrder.setUuid(theDel.getUuid());
pickupOrder.setCode(theDel.getDest_code());
pickupOrder.setDelivered_lat(theDel.getLoc_lat());
pickupOrder.setDelivered_long(theDel.getLoc_long());
return apiService.deliverOrder(theDel.getOrderId(), token, pickupOrder)
.subscribeOn(Schedulers.immediate())
.doOnNext(updateMainResponse -> {
try {
Log.e(TAG, updateMainResponse.toString());
realm.executeTransaction(realm1 -> theDel.deleteFromRealm());
} catch (Exception e) {
e.printStackTrace();
} finally {
pageControl.onNext(integer + 1);
}
});
} else {
return Observable.<UpdateMainResponse>empty().doOnCompleted(pageControl::onCompleted);
}
});
return Observable.defer(() -> ret);
}
Delivery
private Observable<UpdateMainResponse> getPickedOrders() {
Log.e(TAG, "PickedOrders: " + pickUpdate.size());
String token = PrefUtil.getToken(context);
BehaviorSubject<Integer> pageControl = BehaviorSubject.create(1);
Observable<UpdateMainResponse> ret = pageControl.asObservable().concatMap(integer -> {
Log.e(TAG, "MainPickedInteger: " + integer);
if (integer - 1 != pickUpdate.size()) {
RealmOrderUpdate thePick = pickUpdate.get(integer - 1);
Log.e(TAG, "PickedUpdate: " + thePick.toString());
PickupOrder pickupOrder = new PickupOrder();
pickupOrder.setUuid(thePick.getUuid());
pickupOrder.setCode(thePick.getSource_code());
pickupOrder.setPicked_lat(thePick.getLoc_lat());
pickupOrder.setPicked_long(thePick.getLoc_long());
return apiService.pickupOrder(thePick.getOrderId(), token, pickupOrder)
.subscribeOn(Schedulers.immediate())
.doOnNext(updateMainResponse -> {
try {
Log.e(TAG, updateMainResponse.toString());
realm.executeTransaction(realm1 -> thePick.deleteFromRealm());
} catch (Exception e) {
e.printStackTrace();
} finally {
pageControl.onNext(integer + 1);
}
});
} else {
return Observable.<UpdateMainResponse>empty().doOnCompleted(pageControl::onCompleted);
}
});
return Observable.defer(() -> ret);
}
Zipper
private Observable<ZipperResponse> batchedZip() {
return Observable.zip(getPickedOrders(), getDeliveredOrders(), (updateMainResponse, updateMainResponse2) -> {
List<UpdateMainResponse> orders = new ArrayList<>();
bakeries.add(updateMainResponse);
bakeries.add(updateMainResponse2);
return new ZipperResponse(orders);
});
}
Utilizing Zipper
public void generalUpload(APIRequestListener listener) {
batchedZip.subscribe(new Subscriber<ZipperResponse>() {
#Override
public void onCompleted() {
listener.didComplete();
unsubscribe();
}
#Override
public void onError(Throwable e) {
listener.handleDefaultError(e);
unsubscribe();
}
#Override
public void onNext(ZipperResponse zipperResponse) {
Log.e(TAG, zipperResponse.size());
}
});
}
Problem
I don't know why getDeliveredOrders() doesn't get called unless I move it to the first before getPickedOrders()
Reading through Rx Documentation for Zip I can see that it's not going to work as I expected where all of getPickedOrders() runs first before getDeliveredOrders() runs. It'll have to do it one by one. E.g: One of Pickup and then One of Delivery
Any help to understand what's going on would be appreciated. Thanks
Ok, so if I got that right:
Pickup only: need to run through the Pickup process, then they complete.
Delivery only: need to run through the Delivery process, then they complete.
Pickup and Delivery: need to run through Pickup first, then through Delivery.
On a very high level, almost preudo-code, why does this process not work?
Observable<Item> performPickup(Item item);
Observable<Item> performDelivery(Item item);
Observable<Items> items = ...;
items
.flatMap(item -> item.needsPickup() ? performPickup(item) : Observable.just(item))
.flatMap(item -> item.needsDelivery() ? performDelivery(item) : Observable.just(item))
.doOnNext(completedItem -> ...)
If you have different sources for the three types:
Observable<Item> items = Observable.merge(
pickupSource(),
deliverySource(),
pickupAndDeliverySource());
I am using parse for my data in android app. for some reason the code inside query.findInBackground is not getting executed.
public List<Date> sessionHeaderFetch(){
Log.d("test", "session fetch entry");
ParseQuery<Sessions> query = ParseQuery.getQuery(Sessions.class);
final List<Date> sessionHeaders = null;
query.findInBackground(new FindCallback<Sessions>() {
#Override
public void done(List<Sessions> sessionsObjects, com.parse.ParseException e) {
Log.d("test", "session internal entry");
if (e == null) {
Log.d("test", "Retrieved " + sessionsObjects.size() + " sessions");
for (Sessions session : sessionsObjects) {
sessionHeaders.add(session.getNetsDate());
};
} else {
Log.d("score", "Error: " + e.getMessage());
}
}
});
Log.d("test", "session last value");
return sessionHeaders;
}
the code inside public void done() is not all invoked.
I don't think you have understood how to query correctly in Parse.
When you define the parse query. The get query should contain the name of the table that you are trying to query. Also the queries returned will be ParseObjects normally, so I would expect that your callback should be new FindCallback().
I've adjusted the parse query below.
ParseQuery<Sessions> query = ParseQuery.getQuery("ParseTableName");
final List<Date> sessionHeaders = null;
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> sessionsObjects, com.parse.ParseException e) {
Log.d("test", "session internal entry");
if (e == null) {
// Find succeeded, so do something
} else {
Log.d("score", "Error: " + e.getMessage());
}
}
});
Obviously you will need to replace "ParseTableName" with the name of the table that you are trying to query in parse.
I actually solved it by using an ArrayList, there were some problems with the List initialization and also the results are fetched asynchronous manner so my functions which calls these functions were getting null values so I had to write by calling functions that calls the parse fetch functions asynchronously as well.
but the query can be written like this and it works.
public void sessionFetch(Date headers, final ParseIOListener<Sessions> listener){
ParseQuery<Sessions> query = ParseQuery.getQuery(Sessions.class);
Log.d("test", "input header" + headers );
query.whereEqualTo("NetsDate",headers);
final ArrayList<Sessions> sessionsData = new ArrayList<Sessions>();
query.findInBackground(new FindCallback<Sessions>() {
#Override
public void done(List<Sessions> sessionsObjects, com.parse.ParseException e) {
if (e == null) {
for (Sessions session : sessionsObjects) {
Log.d("test", "output objects" + session.toString() );
sessionsData.add(session);
Log.d("test", "Retrieved -- sessions" + sessionsObjects.size() );
}
listener.onDataRetrieved(sessionsData);
} else {
listener.onDataRetrieveFail(e);
}
}
});
}
If you want more details on implementing this, check this link
The reason why you feel the code is not being invoked is because you are returning from the method before the ".findInBackground" operation has completed. Remember the query is performed on a background thread. So this is how your code will run:
ParseQuery query = ParseQuery.getQuery(Sessions.class);
final List sessionHeaders = null;
------> 1. query.findInBackground (Popped onto background thread)
return sessionHeaders; (by the time you get here, sessionHeaders will probably still be null).
So change the logic of the code and wait till the callback returns before doing any processing on the "sessionHeaders" object.