I'm preety new in java/android.
I'm writing android app, which takes data from online api.
The problem is, I'm not sure if my concept is correct.
So my app send first request, but I need some of respond data to start next request.
Here is some example more or less how it looks right now:
public class MainActivity extends AppCompatActivity{
private int key = 0;
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new DownloadTask().execute("url of 1st request");
}
private class DownloadTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
try {
return downloadContent(params[0]);
} catch (IOException e) {
return "Unable to retrieve data. URL may be invalid.";
}
}
#Override
protected void onPostExecute(String result) {
if(key == 0){
// I know it's 1st request because my key == 0
JSONObject json = new JSONObject(result);
String id = json.getString("id");
key++;
new DownloadTask().execute( "url of 2nd request/" + id );
}else{
// I know it's 2st request because my key != 0
//here I'm getting data i need
// and I'm going to rest of my app
end(result);
}
}
}
private void end(String result){
//rest of my app
}
}
Code is working fine, but I wanted to know if it's proper way to do it.
Maybe you know another way to do that, I'm not asking for completly new code, but maybe some topic I should find and read.
If you want to do it asynchronously, i will recommend you using CompletableFuture's. It is very simple and look like that:
CompletableFuture
.supplyAsync(() -> yourFirstrequest())
.thenApplyAsync(yourInstance::getResponse)
.thenAcceptAsync(nextRequestClass::sendNextrequest);
or you can separate them by Future's like:
CompletableFuture firstRequest = CompletableFuture.supplyAsync(() -> yourFirstrequest());
CompletableFuture getResponse = firstRequest.thenApply(response -> someActionWithResponse(response));
it is very powerful and convenient framework.
Take a look at the java docs or android specific docs
Related
i am quite new to java async function and observable all these. Is there anyway to make sure the async function return before proceeding?
I am currently using Thread.sleep(100); so that i can get the data i want, but i do not think it is appropriate .
my code:
private void getGroupAllInfo(CallbackContext callbackContext){
Log.d("executing: ", "getGroupAllInfo1");
Observable observable = GsscFactory.executeGetZwaveAllInfo(this.sock, MationPlugin.gatewayId, MationPlugin.account, MationPlugin.password);
observable.subscribe(t -> {
System.out.print(t);
MationPlugin.allInfo.put("data",t.toString());
// callbackContext.success(t.toString());
});
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
this.getGroupAllInfo(callbackContext);
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
callbackContext.success(this.allInfo.getString("data"));
}
I realized that i cannot assign variable inside the observable.subscribe(t -> {}), so i decide to put the data i want into a json object. If there is some alternatives, please tell me. ><
First create a MutableLiveData variable like:
MutableLiveData<String> response = new MutableLiveData<String>();
Then create an observer that observes the response value changes like:
// Create the observer which updates the response when a new value arrives.
final Observer<String> nameObserver = new Observer<String>() {
#Override
public void onChanged(#Nullable final String response) {
// Update the UI like a TextView text or do whatever you want with your [response] value
responseTextView.setText(response);
}
};
// Observe the MutableLiveData, passing the observer.
response.observe(this, nameObserver);
Then post your new value to the response variable in your background task like:
response.postValue("response value");
I've got an Activity consisting of a buch of TextViews (fourteen) and two Buttons.
I have created a custom class named Lesson, wich basically has a constructor and getter methods for its variables.
Now, inside my onCreate() in my Activity I am calling two functions: 1.) populateLessonDetails(myURL) and 2.) populateLessonTextViews().
I have created a private Lesson mLesson; variable inside my Activity, above all the #Overrides, because I'm trying to use this variable to populate it later on.
So, populateLessonDetails(myURL) is basically making a JsonArrayRequest, getting all the data from the JSON inside the onResponse(), saving it to String variables still inside the onResponse() and then, still inside the onResponse() I am trying to populate the mLesson variable, by calling
mLesson = new Lesson(mName, mRoom, mExtra, mAddress, mPC, mCity, mStart, mDate, mRID, mMaxAtt, mCurrentAtt); - the variables used within the constructor are the String variables containing the JSON data.
I Log.i() the JSON data as well as the mLesson variables via its getter methods, and the data is there. Everything is fine.
Now, my populateLessonDetails() ends.
It returns to the onCreate() and continues with the next line of code, wich would be calling populateLessonTextViews().
This is where things went south...
As soon as the function is called I try to get the information stored inside mLesson via its getter methods to set it to the TextViews like so:
//Lesson Name Big
TextView lessonNameTextBig = (TextView) findViewById(R.id.text_activelesson_name_big);
lessonNameTextBig.setText(mLesson.getLessonName());
This is the proper way to do it, I've done it a bunch of times already, but my App crashes at the second line.
I have debugged it and I have noticed that mLesson is empty. My guess would be that me populating it inside the onResponse() of the JsonArrayRequest, which is inside the populateLessonDetails() is only valid for this particular function, the scope of the variable mLesson ends when the function returns to the onCreate() and the mLesson variable is empty again since it died with the function.
Now how can I fix this? Do I have to set mLesson as a parameter for the populateLessonDetails() and then also return it (currently the populate functions are void) ? Then save the return value into another variable of type Lesson and set this new variable as a parameter for the populateLessonTextViews() ?? I've tried a couple of those things but they didn't work, but maybe its just me not doing it right.
This is what my code looks like (the important part):
public class ActiveLesson extends AppCompatActivity {
// there are also some other variables up here
private Lesson mLesson;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_active_lesson);
requestQ = Volley.newRequestQueue(this);
Intent intent = getIntent();
Bundle extras = intent.getExtras();
mDatum = extras.getString("datum");
mRID = extras.getString("rid");
mVon = extras.getString("von");
myActiveLessonURLFiltered += "datum="+mDatum+"&rid="+mRID+"&von="+mVon;
populateLessonDetails(myActiveLessonURLFiltered);
populateLessonTextViews();
}
private void populateLessonDetails(String myActiveLessonURLFiltered) {
JsonArrayRequest lessonJAR = new JsonArrayRequest(myActiveLessonURLFiltered,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response){
try{
for (int i=0; i < response.length(); i++)
{
JSONObject jsonObject = response.getJSONObject(i);
String mName = jsonObject.getString("Name");
String mRoom = jsonObject.getString("Raum");
String mExtra = jsonObject.getString("Zusatz");
String mAdresse = jsonObject.getString("Address");
String mPC = jsonObject.getString("PLZ");
String mCity = jsonObject.getString("City");
String mMaxAtt = jsonObject.getString("maxAnz");
String mCurrentAtt = jsonObject.getString("belegtAnz");
if(mExtra.length()==0 || mExtra == "null")
mExtra="";
if(mRoom.length()==0 || mRoom == "null")
mRoom="";
else
mRoom="Room: "+mRoom;
if(mName.length()==0 || mName == "null")
mName="";
mLesson = new Lesson(mName, mRoom, mExtra, mAdresse,
mPC, mCity, mVon, mDatum, mRID, mMaxAtt, mCurrentAtt);
Log.i("mmLesson"," Lesson with new = "+ mLesson.getLessonName()
+" "+mLesson.getLessonCity());
}
}catch (JSONException e){
e.printStackTrace();
}
}
},
new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error){
error.printStackTrace();
Toast.makeText(ActiveLesson.this, "No Lessons Available",
Toast.LENGTH_LONG).show();
}
}){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Accept", "application/json");
return headers;
}
};
requestQ.add(lessonJAR);
}
private void populateLessonTextViews(Lesson mLesson) {
//Lesson Name Big
TextView lessonNameTextBig = (TextView) findViewById(R.id.text_activelesson_name_big);
lessonNameTextBig.setText(mLesson.getLessonName());
// there are others lines of code like these two,
// but I've left them out, since they are all the same
}
If some could help me out I would appreciate it. Thank you!
The onResponse() method is a callback that is called later when the network request returned a value. The server does not respond with any delay. This means the populateLessonDetails(..) method get called from onCreate triggers an network request and return immedietly to the onCreate() call of this function and steps forward.
You have to take this in consideration. The best way to do this, call inside the onResponse the populateLessonTextViews() method. Then you can be sure that the content has been loaded.
I'm developing an Android app which has a lot of different requests for web services.
Every request is done in a subclass of AsyncTask in this manner:
(new AsyncTask<String, Void, Object1>() {
#Override
protected Object1 doInBackground(String... params) {
// network request and parsing to Object1
Object1 obj = new Object1();
obj1 = Parser.parseObject1(httpClient.execute(...));
return obj1;
}
#Override
protected Object1 onPostExecute(Object1... ret) {
return ret[0];
}
}).execute();
Object1 is a placeholder for different objects (Car, Bicycle, Truck...), each one in a different AsyncTask.
What are my alternatives other than returning the output of httpClient in a String and parsing in the Main Thread (UI Thread)? Avoid parsing in the UI Thread sounds reasonable if it's going to parse a lot of data, am I right?
-= UPDATE =-
Let me rephrase the question: I'm asking for a more intelligent way to develop my application avoiding being repetitive (AsyncTask has a lot of boilerplate code). The way I did was by creating 20+ subclasses of AsyncTask, which clearly is not DRY (do not repeat yourself).
In iOS we have lambda expressions so callbacks done in web requests are very easy and succinct.
You can create classes that contain most of your boilerplate code. E.g.
public class SpecialAsyncTask<T> extends AsyncTask<String, Void, T> {
public interface ResultProvider<T> {
T generateResultInBackground(String... params);
}
public interface ResultConsumer<T> {
void handleResultInForeground(T result);
}
private final ResultProvider<T> mProvider;
private final ResultConsumer<T> mConsumer;
private SpecialAsyncTask(ResultProvider<T> provider, ResultConsumer<T> consumer) {
mProvider = provider;
mConsumer = consumer;
}
#Override
protected T doInBackground(String... params) {
return mProvider.generateResultInBackground(params);
}
#Override
protected void onPostExecute(T result) {
mConsumer.handleResultInForeground(result);
}
public static <T> void execute(ResultProvider<T> provider, ResultConsumer<T> consumer, String... params) {
new SpecialAsyncTask<T>(provider, consumer).execute(params);
}
}
is an example how you could keep Object1 as a generic parameter while being able to specify an object that only needs to implement an interface to handle code that would otherwise have to be inside a new AsyncTask instance.
With a schema like that you could for example define some common code as static content:
class Providers {
public static final ResultProvider<String> HTTP_GETTER = new ResultProvider<String>() {
#Override
public String generateResultInBackground(String... params) {
return MagicHttpLibrary.getContentAsString(params[0]);
}
};
}
And you can just use Providers.HTTP_GETTER as parameter instead of implementing doInBackground. Or create a new class hierarchy of that implement one of those interfaces with different methods to access them (like factories for example)
Use of above example would look for example like below
class User extends Activity implements ResultConsumer<String> {
#Override
protected void onCreate(Bundle savedInstanceState) {
SpecialAsyncTask.execute(Providers.HTTP_GETTER, this , "http://google.com");
SpecialAsyncTask.execute(Providers.HTTP_GETTER, this , "http://yahoo.com");
}
#Override
public void handleResultInForeground(String result) {
Toast.makeText(this, result, Toast.LENGTH_LONG).show();
}
}
and there is more or less no repeated code besides the different method calls. It depends on how you want to use a class and what actually changes in the code to know how to design something like that. Identify the parts that need to be parametrized and move code that repeats into a re-used place (inheritance / composition).
Google's Volley HTTP request library does the request and parsing both in the same worker thread. So, that's a pretty good example to code by.
I'm trying to organize my app code separating AsyncTask classes from Activity or Fragments.
Surfing this forum I've learned about the "interface and listener" solution:
Interface.java
public interface TaskCompleteListener<T> {
public void onTaskComplete(T result);
}
AsyncTask.java
[...]
#Override
protected void onPostExecute(String JSONResponse) {
// using Gson library I convert JSONResponse string to POJO objects...
listener.onTaskComplete(result);
}
Activity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// start AsyncTask...
}
// Inner class implementing interface
public class LoadTaskCompleteListener implements TaskCompleteListener<Object> {
#Override
public void onTaskComplete(Object result) {
updateUI(result);
}
}
public void updateUI(Object result) {
// here you can manage UI updating, using result object
}
}
UPDATE
DataHelper.java
public class DataHelper {
private AsyncTaskCompleteListener<Object> listener;
public DataHelper(AsyncTaskCompleteListener<Obejct> listener) {
this.listener = listener;
}
// Multiple AsyncTask are defined here...
}
Well, I like this pattern so much, but: what if inside activity (or fragment)
I have multiple requests, with different type of results (single object, array, list)
and different business logic for managing them?
I can't handle that using a single callback. Any ideas? Should I add multiple
callbacks inside the interface? I'm really stuck with this.
I've a single file called DataHelper with multiple AsyncTask inside (something like web getters).
Naturally I can give up with this approach if you suggest any other alternative.
Why not return an array of Objects as a result? Then if you have more than one object inside that array you'll know the order in which you put them so you can use more than one set of results at a time.
public interface TaskCompleteListener<T> {
public void onTaskComplete(T... results);
}
Note that a 'handler' can be used instead of an async task and that each handler may return its message to the message looper with the msg wrapping different data. Easy to handle many data types because its just a message component.
Sample here
See 'process_entity() ' where the messages containing result component are sent by sample code.
Note that the definition of onTaskComplete() accepts as argument a generic type, type T:
onTaskComplete(T result);
this means that you can pass in any object type, single object, array, list, etc.
Regarding on how to handle different results using a single callback, you can use a Bundle to put the result in, associated with a key, and then in updateUI() check for that key and take appropriate actions.
Something like this (pseudocode, not sure if it will compile):
#Override
protected void onPostExecute(String JSONResponse) {
Bundle bundle = new Bundle();
bundle.putString("key_json_response", result); //put the response in a Bundle
listener.onTaskComplete(bundle);
}
// .....
public void updateUI(Object result) {
Bundle bundle = (Bundle)result;
if(bundle.containsKey("key_json_response")){
String json = bundle.getString("key_json_response");
// process json
} else if(bundle.containsKey("key_another_response")){
// process another response
}
}
I am a relatively new Android developer and am working on an application right now that makes a lot of calls to a RESTful web service.
I am making each call in an asyncTask but in some files, the amount of different async tasks I have approaches 15. Right now I have them all as private classes inside my activity class. How can I organize them better(i.e. put them in separate files) while still being able to update the UI.. I was thinking of passing the context into the constructor of each asyncTask but I was just wondering if there was a best practice/better way.
Thanks!
Instead of using so many classes for different types of asynctask , I suggest you use this library
you can have a look at here
http://loopj.com/android-async-http/
your code will become very very less , instead of declaring so may asynctask seperately writing bulk of code , you can just use 4 lines of code
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {
#Override
public void onSuccess(String response) {
System.out.println(response);
}
});
I is very efficient in geting the response very quickly.
I hope this will help you out. :)
Passing in the Activity as a constructor parameter sounds like a good plan to me. Basically the same is happening when you declare them as an inner class.
But keep in mind, that there are some drawbacks for using AsyncTasks to load data. Once started, they will continue to run even when the activity is already closed and hold a reference to your activity (it can therefore not be garbage collected).
You may want to look into other concepts like loaders.
Consider using a library to simplify your code base. I wrote droidQuery which, among other things, can be used to simplify AsyncTasks. For example, to get JSON data from example.com, and to have access to context afterwards, you can do this:
$.ajax(new AjaxOptions().url("http://www.example.com")
.context(this)
.type("GET")
.dataType("json")
.success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
JSONObject json = (JSONObject) params[0];
Context context = droidQuery.context();
//TODO:
}
})
.error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = (AjaxError) params[0];
Log.e("Ajax", "Error " + error.status + ": " + error.reason);
}
}));
For lots of different requests that you call a lot, you can also create instances of AjaxOptions for later use, which have different URLs, types, dataTypes, etc:
Map<String, AjaxOptions> requests = new HashMap<String, AjaxOptions>();
//add the example above:
requests.put("example", new AjaxOptions().url("http://www.example.com")
.context(this)
.type("GET")
.dataType("json")
.success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
JSONObject json = (JSONObject) params[0];
Context context = droidQuery.context();
//TODO:
}
})
.error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = (AjaxError) params[0];
Log.e("Ajax", "Error " + error.status + ": " + error.reason);
}
}));
Then later, just perform this task by calling:
$.ajax(requests.get("example"));