So I have quite a few model classes in my project and have a singleton class which makes Retrofit calls to convert between the model classes. The method getUsersFromChats in that class takes in an array of Chat model objects, converts each object to a User model object, and adds them to a public ArrayList of User objects.
Code:
//These are instantiated in another method and are not null.
public static ArrayList<User> users;
private static int index;
public void getUsersFromChats(Chat[] chats) {
if (index < chats.length) {
Call<UserResponse> getUser = AuthRetrofitClient
.getInstance()
.getAuthApi()
.getUserById(chats[index].getUserID());
getUser.enqueue(new Callback<UserResponse>() {
#Override
public void onResponse(Call<UserResponse> call, Response<UserResponse> response) {
if (response.isSuccessful()) {
UserResponse ur = response.body();
Log.i("User", ur.getUser().getName());
users.add(ur.getUser());
Log.i("List", users.toString());
index++;
getUsersFromChats(chats);
} else {
try {
String errorBody = response.errorBody().string();
int index = errorBody.indexOf("\"message\":");
if (index != -1) {
String errorSub = errorBody.substring(index + 10);
//errorBody = errorSub.substring(1, errorSub.length() - 2);
}
Toast.makeText(context, errorBody, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<UserResponse> call, Throwable t) {
Log.i("Didn't work", t.toString());
}
});
}
}
I have an Activity which calls that method after getting an array of Chat objects from a Retrofit call and tries to save ArrayList from the singleton class to a local User objects ArrayList.
Call<ChatListResponse> getAllContacts = ChatRetrofitClient
.getInstance()
.getChatAPI()
.loadChats(SharedPrefManager.getInstance(ChatActivity.this).getUser().getToken());
getAllContacts.enqueue(new Callback<ChatListResponse>() {
#Override
public void onResponse(Call<ChatListResponse> call, Response<ChatListResponse> response) {
if(response.isSuccessful()){
ChatListResponse clr = response.body();
if(clr.getChats() == null || clr.getChats().length < 1)
createChat();
else{
for(Chat c: clr.getChats())
Log.i("Testing", c.getUserID());
ConvertFieldsToUserObjects.getInstance(ChatActivity.this).getUsersFromChats(clr.getChats());
ArrayList<User> users = ConvertFieldsToUserObjects.users;
Log.i("Testing", users.toString());
for(User u: users)
Log.i("Testing", u.getId());
Log.i("Testing", person.getId());
if(users.contains(person)){
int i = users.indexOf(person);
chat = clr.getChats()[i];
Toast.makeText(ChatActivity.this, chat.getName(), Toast.LENGTH_SHORT).show();
}
However, this code doesn't work and the local ArrayList is always empty even though the Chat objects list obtained from the Call contains elements. Does this have something to do with the Retrofit calls being asynchronous or is it something else? (The API's, RetrofitClients, and the Response objects work fine as far as I have tested.)
Related
Retrofit makes a call every X seconds and returns list of objects.
If any object of a list has what i need it should notify user.
The problem that i can not solve is retrofit makes a new call every X seconds and returns new List of objects. But objects or one of object may be same and if it is true no need to notify user again. How to do this?
#Override
public void onResponse(Call<LiveScore> call, Response<LiveScore> response) {
if (!response.isSuccessful()) {
Toast.makeText(MainService.this, response.code(), Toast.LENGTH_SHORT).show();
}
liveScore = response.body();
List<Result>results = response.body().getResult();
for (int i = 0; i < results.size(); i++) {
//here i check all object for a match
int homeScore = Integer.parseInt(results.get(i).getScores().getFirstQuarter().get(0).getScoreHome());
int awayScore = Integer.parseInt(results.get(i).getScores().getFirstQuarter().get(0).getScoreAway());
if (homeScore >= 5 && awayScore >= 5) {
//if true and
// if current position of object previously has
//not notification, notify
}
}
}
A quick and maybe naive approach would be something like this :
private final Set<Result> uniqueResults = new HashSet<>();
#Override
public void onResponse(Call<LiveScore> call, Response<LiveScore> response) {
if (!response.isSuccessful()) {
Toast.makeText(MainService.this, String.valueOf(response.code()), Toast.LENGTH_SHORT).show();
return;
}
liveScore = response.body();
for(Result r : response.body().getResult()) {
if(uniqueResults.add(r)) {
// notify user
}
}
});
You will need to override equals() and hashcode() in your Result class as well.
I am writing a class in java which is going to fetch results from a MongoDB database and then return the results back to the main program. My code is as follows:
public Document[] search(Document query, String databaseName, String collectionName) {
Log.i(TAG, "Search query: " + query);
_mongoClient.getDatabase(databaseName).getCollection(collectionName).find(query).continueWith(
new Continuation<List<Document>, Object>() {
#Override
public Object then(#NonNull final Task<List<Document>> task) throws Exception {
if (!task.isSuccessful()) {
Log.e(TAG, "Failed to execute query");
} else {
if (task.getResult().size() == 0) {
Log.i(TAG, "Query failed to return any results");
return null;
}
results = new Document[task.getResult().size()];
for (Integer i = 0; i < task.getResult().size(); i++) {
results[i] = task.getResult().get(i);
Log.i("test", results[i].getString("username").toString());
}
}
return null;
}
});
return results;
}
The issue with my code is that it doesn't wait for the code inside the method for retrieving the database to run. Instead it jumps straight to the return statement.
I have tried using AsyncTask like this:
private class WaitForDB extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
super.onPreExecute();
pd = new ProgressDialog(context);
pd.setMessage("Please wait");
pd.setCancelable(false);
pd.show();
}
protected Void doInBackground(Void... params) {
Document query = new Document("username", "a");
_mongoClient.getDatabase("gapp").getCollection("parent").find(query).continueWith(
new Continuation<List<Document>, Object>() {
#Override
public Object then(#NonNull final Task<List<Document>> task) throws Exception {
if (!task.isSuccessful()) {
Log.e(TAG, "Failed to execute query");
} else {
if (task.getResult().size() == 0) {
Log.i(TAG, "Query failed to return any results");
return null;
}
results = new Document[task.getResult().size()];
for (Integer i = 0; i < task.getResult().size(); i++) {
results[i] = task.getResult().get(i);
Log.i("test", results[i].getString("username").toString());
}
}
return null;
}
});
return null;
}
#Override
protected void onPostExecute(Void result) {
if (pd.isShowing()) {
pd.dismiss();
}
}
}
However, this does not work either. So, I would like to know, is there a way for the program to wait until the data is retrieved from the database? And if yes, how?
I will appreciate any kind of help.
Thanks in advance.
The first code doesn't wait only because you wrote it that way. If you want to observe that, put more log statements outside of error conditions or where you are building the result list, as well as before the return statements. The result list will be built after the return results.
What you can do is pass the "continuation" through to the method as a parameter
public void search(Document query, String databaseName, String collectionName, Continuation<List<Document>, Object> continuation) {
Log.i(TAG, "Search query: " + query);
_mongoClient.getDatabase(databaseName)
.getCollection(collectionName)
.find(query)
.continueWith(continuation);
} //end method
And that's it for that method. Do your processing elsewhere where you need the documents
For example, rather than trying to make this a blocking call for results
Document[] documents = search(...);
// process documents
You need to invoke the function, which waits for results to be received, then executes the inner callback function
// Do not define documents here
search(query, database, collection,
new Continuation<List<Document>, Object>() {
#Override
public Object then(#NonNull final Task<List<Document>> task) throws Exception {
if (!task.isSuccessful()) {
Log.e(TAG, "Failed to execute query");
} else {
List<Document> documents = task.getResults();
// process documents
}
});
// if you did define documents outside this, it'll still be empty
If you want to reduce all this code just to get an actual Document array, you need to define your own interface, but this is the most flexible working solution without extra classes
I'm new on Android development and I'm learning how to use MVP pattern correctly in recently.
Now I'm facing a tricky problem, hope can get some helpful suggestion or solution from here.
First, here is my presenter
public class MVPPresenter {
private MVPView mvpView;
public MVPPresenter(MVPView mvpView) {
this.mvpView = mvpView;
}
public void loadData() {
mvpView.startLoading();
final List<MVPModel> list = new ArrayList<>();
//the part that I trying to extract starts here.
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
mvpView.errorLoading();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
JSONArray value = result.getJSONObject("Value").getJSONArray("hot");
for (int i = 0; i < value.length(); i++) {
MVPModel mvpModel = new MVPModel();
String name = null;
String image = null;
try {
name = value.getJSONObject(i).getString("title");
image = URLCombiner.IP + value.getJSONObject(i).getString("pic");
} catch (JSONException e) {
e.printStackTrace();
}
mvpModel.setName(name);
mvpModel.setImage(image);
list.add(mvpModel);
}
if (list.size() > 0) {
mvpView.successLoading(list);
mvpView.finishLoading();
} else {
mvpView.errorLoading();
}
} else {
mvpView.errorLoading();
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
mvpView.errorLoading();
}
}
});
//the part that I trying to extract ends here.
}
}
As you can see, I'm trying to extract the part which is using OKHttp libs into a class (I call it data manager) which I hope it can handle every task between server and client. But here's the thing, when I trying to pass the result from the data manager to presenter, I got NullPointException because of the async mechanism.
I would like to know how to passing the data, which is come from server in async, to the presenter when the data has finish downloading.
And here's my ideal data manager, I know this might looks stupid but I think this can make my problem more clearly.
public class LoadServerData {
private static JSONArray arrayData = new JSONArray();
public static JSONArray getServerData() {
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
arrayData = result.getJSONObject("Value").getJSONArray("hot"); //the data I would like to return.
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
return arrayData; //this is gonna be an empty data.
}
}
I've reading some article that might can solve my problem, but still not getting any fine answer. Perhaps I've using wrong keyword I think. Hopes you guys can give me some ideas or solutions to help me or inspire me.
P.S. version of OKhttp libs is 3.7.0
Create a simple Listener so it can be called whenever the server call finishes:
public class LoadServerData {
public static interface Listener {
public void onSuccess(JSONArray data);
public void onError(Exception error);
}
public static void getServerData(Listener listener) {
Call call = DataRetriever.getDataByGet(URLCombiner.GET_FRONT_PAGE_ITEMS);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
listener.onError(e);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try {
JSONObject result = new JSONObject(response.body().string());
int errorCode = result.getInt("ErrorCode");
if (errorCode == 0) {
JSONArray arrayData = result.getJSONObject("Value").getJSONArray("hot"); //the data I would like to return.
listener.onSuccess(arrayData);
} else {
listener.onError(...);
}
} catch (JSONException e) {
e.printStackTrace();
listener.onError(e);
}
} else {
listener.onError(...);
}
}
});
}
}
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I've looked at so many questions and answers but none of what i found has actually worked!
So basically if the title doesn't help much then what I'm trying to do is execute an AsyncTask from a dialog but it's not executing and when it does, it'll show up as an null object and if I'm honest it's bloody annoying!
So if anyone can help then that would be great.
The Class is subbed.
Here's the Async class:
static class UpdatePassword extends AsyncTask<String, String, String> {
Context context;
String oldPassword;
String newPassword;
public UpdatePassword(String setOldPassword, String setNewPassword, Context context) {
this.oldPassword = setOldPassword;
this.newPassword = setNewPassword;
this.context = context;
}
#Override protected String doInBackground(String... params) {
HttpRequestUtils httpRequestUtils = new HttpRequestUtils(context);
if (TextUtils.isEmpty(oldPassword) || TextUtils.isEmpty(newPassword)) {
return null;
} else {
String response = null;
String baseUrl = "rest/ws/user/update/password";
ApiResponse apiResponse = null;
try {
response = httpRequestUtils.getResponse(baseUrl + "?oldPassword=" + oldPassword + "&newPassword=" + newPassword, "application/json", "application/json");
if (TextUtils.isEmpty(response)) {
return null;
}
apiResponse = (ApiResponse) GsonUtils.getObjectFromJson(response, ApiResponse.class);
if (apiResponse != null && apiResponse.isSuccess()) {
return apiResponse.getStatus();
}
Log.i("Update", "password call" + apiResponse);
} catch (Exception e) {
e.printStackTrace();
}
return newPassword;
}
}
}
And here's what I'm doing to execute it:
String oldPassword = changePassOld.getText().toString();
String newPassword = changePassNew.getText().toString();
AsyncTask task = new UpdatePassword(oldPassword, newPassword, ProfileFragment.this.getContext());
task.execute();
Edit: I have noticed that i only have doInBackground but even when i had preExecute, it still wouldn't work
AsyncTask doesn't always call but when it does, it comes up null?
It is the most interesting AsyncTask ;)
Generally speaking, you seem to not be returning the data to the point where you expect it (or a return null condition is being hit).
You can instead define a callback.
public interface PasswordChangeListener {
void onPasswordChanged(String oldPass, String newPass);
}
Implement that on your Fragment class
public class ProfileFragment extends Fragment
implements PasswordChangeListener { // See here
...
#Override
public void onPasswordChanged(String oldPass, String newPass) {
Toast.makeText(getActivity(),
String.format("Changed %s to %s", oldPass, newPass),
Toast.LENGTH_SHORT).show();
}
Call your AsyncTask after you add that callback as a parameter
(Context usually goes first, by the way)
new UpdatePassword(
ProfileFragment.this.getActivity(),
oldPassword, newPassword,
ProfileFragment.this).execute();
Suggestion: You should return apiResponse; from doInBackground
And implement onPostExecute on the AsyncTask with that suggestion in mind
#Override
public void onPostExecute(ApiResponse result) {
if (apiResponse != null && apiResponse.isSuccess()) {
if (this.callback != null) {
callback.onPasswordChanged(this.oldPassword, this.newPassword);
} else {
Log.w("Password change", "Password callback not set!");
}
} else {
// TODO: Error handling
}
}
I'm using an asyncronus XML-RPC-Client (https://github.com/gturri/aXMLRPC) in my Project and wrote some methods using the asyncronous Callback-Methods of this Client like this this:
public void xmlRpcMethod(final Object callbackSync) {
XMLRPCCallback listener = new XMLRPCCallback() {
public void onResponse(long id, final Object result) {
// Do something
if (callbackSync != null) {
synchronized (callbackSync) {
callbackSync.notify();
}
}
}
public void onError(long id, final XMLRPCException error) {
// Do something
if (callbackSync != null) {
synchronized (callbackSync) {
callbackSync.notify();
}
}
}
public void onServerError(long id, final XMLRPCServerException error) {
Log.e(TAG, error.getMessage());
if (callbackSync != null) {
synchronized (callbackSync) {
callbackSync.notifyAll();
}
}
}
};
XMLRPCClient client = new XMLRPCClient("<url>");
long id = client.callAsync(listener, "<method>");
}
In other methods I like to call this method (here "xmlRpcMethod") and wait until it finished. I wrote methods like this:
public void testMethod(){
Object sync = new Object();
xmlRpcMethod(sync);
synchronized (sync){
try{
sync.wait();
}catch(Interrupted Exception e){
e.printStackTrace();
}
}
// Do something after xmlRcpFinished
}
But this way of waiting and synchronizing get's ugly when the projects grows larger and I need to wait for many requests to finish.
So is this the only possible / best way? Or does someone knows a better solution?
My first shot to create blocking RPC calls would be:
// Little helper class:
class RPCResult<T>{
private final T result;
private final Exception ex;
private final long id;
public RPCResult( long id, T result, Exception ex ){
// TODO set fields
}
// TODO getters
public boolean hasError(){ return null != this.ex; }
}
public Object xmlRpcMethod() {
final BlockingQueue<RPCResult> pipe = new ArrayBlockingQueue<RPCResult>(1);
XMLRPCCallback listener = new XMLRPCCallback() {
public void onResponse(long id, final Object result) {
// Do something
pipe.put( new RPCResult<Object>(id, result, null) );
}
public void onError(long id, final XMLRPCException error) {
// Do something
pipe.put( new RPCResult<Object>(id, null, error) );
}
public void onServerError(long id, final XMLRPCServerException error) {
Log.e(TAG, error.getMessage());
pipe.put(new RPCResult<Object>(id, null, error));
}
};
XMLRPCClient client = new XMLRPCClient("<url>");
long id = client.callAsync(listener, "<method>");
RPCResult result = pipe.take(); // blocks until there is an element available
// TODO: catch and handle InterruptedException!
if( result.hasError() ) throw result.getError(); // Relay Exceptions - do not swallow them!
return result.getResult();
}
Client:
public void testMethod(){
Object result = xmlRpcMethod(); // blocks until result is available or throws exception
}
Next step would be to make a strongly typed version public T xmlRpcMethod().