Managing multiple Async tasks in android - java

Hey everyone so I am just starting a part two for online training app and trying to adapt my async task to get movie reviews from the movie db. Having a totally different async task just for that seems like there should be a better way. Here is the current async task implementation that only gets the movie data.
The question is how do I add another async task to this in order to retrive the movie reviews as well from this url /movie/{id}/videos.
public FetchMovieData(Context context, GridView grid, boolean sortType, ITaskCompleteListener listener) {
mContext = context;
this.mMoviesGrid = grid;
this.mSortByMostPopular = sortType;
this.mTaskCompleteListener = listener;
}
#Override
protected Void doInBackground(String... params) {
// These two need to be declared outside the try/catch
// so that they can be closed in the finally block.
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
try {
URL url;
if(mSortByMostPopular)
url = new URL(mContext.getString(R.string.picasso_url_popular_movies));
else
url = new URL(mContext.getString(R.string.picasso_url_highest_rated));
// Create the request to OpenWeatherMap, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
mMovieJsonStr = null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
mMovieJsonStr = null;
}
mMovieJsonStr = buffer.toString();
} catch (IOException e) {
Log.e("PlaceholderFragment", "Error ", e);
// If the code didn't successfully get the weather data, there's no point in attempting
// to parse it.
mMovieJsonStr = null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e("PlaceholderFragment", "Error closing stream", e);
}
}
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if(mMovieJsonStr != null)
Constants.mMovies = MovieDataParser.getMovieData(mMovieJsonStr);
mTaskCompleteListener.onTaskCompleted(); //Task completed alert UI that we have our data
}
So some one had suggested using Retrofit instead of having multiple async tasks. This seems like a good idea but I am having a lot of trouble understanding how it is supposed to work. Currently I have a WebService class an interface and am trying to use it to retrieve both movies and am going to add reviews then trailers. The issue is if I set the base url as "http://api.themoviedb.org" I get url must start with "/" in logcat.
Current code:
public class WebService {
public List<Movie> getMovies() {
RestAdapter retrofit = new RestAdapter.Builder()
.setEndpoint("http://api.themoviedb.org")
.build();
MovieDBService service = retrofit.create(MovieDBService.class);
return service.listMovies("movies");
}
}
public interface MovieDBService {
#GET("/3/discover/{switchterm}sort_by=popularity.desc&api_key=d273a1a1fb9390dab9 7ac0032b12366a")
List listMovies(#Path("switchterm") String switchterm);
}
//In code getting movies
WebService service = new WebService();
List movies = service.getMovies();

I think you have a lots of possibilities for doing this.You can follow this approach: add a second call to another AsyncTask when the first is finish, and pass to it a list of strings with the video ids:
public class FetchMovieData extends AsyncTask<Void, Void, Void> {
protected Boolean doInBackground() {
try {
String movieJSONString = getJSONMovies();
String[] ids = parseIdsFromJSON(movieJSONString);
if(ids.lenth != 0) {
FetchMovieReviews moviesReviewsAsyncTask = new FetchMovieReviews();
moviesReviewsAsyncTask.execute(ids);
} else {
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
protected String getJSONMovies() {
//with the code you post, return the json string
}
protected String[] parseIdsFromJSON(String JSON) {
//parse the json and get the ids and return
//return {"1","2","3"}
}
}
public class FetchMovieReviews extends AsyncTask<String[], Void, Void> {
protected Void doInBackground(String[]... params) {
for(String id : params[0]) {
//call the web service and pass the id
}
return null;
}
}
You can put all the functionality for manage the calls to the web services in a MoviesRESTCalls class, and for manage the json in a MoviesJSONParser class or something like that, and the code is going to be much more clear.

So what I ended up with was this using the the Retrofit library for the web service. Thanks for the help everyone and let me know your thoughts.
public Context mContext;
private MovieJSON mMovieData;
private ReviewJSON mMovieReviews;
private VideoJSON mMovieVideos;
public boolean mSortByMostPopular;
ITaskCompleteListener mTaskCompleteListener;
public FetchMovieData(Context context, boolean sortType, ITaskCompleteListener listener) {
mContext = context;
this.mSortByMostPopular = sortType;
this.mTaskCompleteListener = listener;
}
public void getMovies() {
new FetchMovies().execute();
}
public void getReviews() {
new FetchReviews().execute();
}
public void getVideos() {
new FetchTrailers().execute();
}
private class FetchMovies extends AsyncTask<String, Void, Void > {
#Override
protected Void doInBackground(String... params) {
WebService service = new WebService();
//TODO Re-Implement sorting
mMovieData = service.getMovies();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if(mMovieData != null)
Constants.mMovies = MovieDataParser.getMovieData(mMovieData);
mTaskCompleteListener.onTaskCompleted(); //Task completed alert UI that we have our data
}
}
private class FetchReviews extends AsyncTask<String, Void, Void > {
#Override
protected Void doInBackground(String... params) {
WebService service = new WebService();
mMovieReviews = service.getReviews();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if(mMovieReviews != null)
Constants.mReviews = MovieDataParser.getReviewData(mMovieReviews);
mTaskCompleteListener.onTaskCompleted(); //Task completed alert UI that we have our data
}
}
private class FetchTrailers extends AsyncTask<String, Void, Void > {
#Override
protected Void doInBackground(String... params) {
WebService service = new WebService();
mMovieVideos = service.getVideos();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if(mMovieVideos != null)
Constants.mTrailers = MovieDataParser.getVideoData(mMovieVideos);
mTaskCompleteListener.onTaskCompleted(); //Task completed alert UI that we have our data
}
}
//web service
public class WebService {
RestAdapter mRetrofit;
MovieDBService mService;
public WebService() {
mRetrofit = new RestAdapter.Builder()
.setEndpoint("http://api.themoviedb.org")
.build();
mService = mRetrofit.create(MovieDBService.class);
}
public MovieJSON getMovies() {
return mService.listMovies("");
}
public ReviewJSON getReviews() {
return mService.listReviews("76341");
}
public VideoJSON getVideos() {
return mService.listVideos("76341");
}
}

Related

Android: Getting null value in islatedProcess

I have an App that run an isolated process.
The problem is, i'm getting correctly UserID from another Service but when I execute getUsersNotfs(url) I'm getting null on String s onPostExecute: 05-14 11:23:39.806 17705-17705/com.example.diabetes:offline_notifications I/System.out: null
But if I run this without an isolated process the String is correct and I get the correct results ([{"RecordID":"1","UserID":"1","RecordDate":"2018-05-08 00:00:00"}])
This is my code:
Passing UserID:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
uid = intent.getStringExtra("UserID");
startTimer();
return START_STICKY;
}
public void getUserNotfs(final String urlWebService) {
class GetJSON extends AsyncTask<Void, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
System.out.println(s);
}
#Override
protected String doInBackground(Void... voids) {
try {
URL url = new URL(urlWebService);
System.out.println(url);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
StringBuilder sb = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String json;
while ((json = bufferedReader.readLine()) != null) {
sb.append(json + "\n");
}
return sb.toString().trim();
} catch (Exception e) {
return null;
}
}
}
GetJSON getJSON = new GetJSON();
getJSON.execute();
}
Thanks
Isolated process will have it's own instance hence you are not getting the value.
Try to use some persistent storage like SQLite,keep the value there and access from your isolated process.
You will get that.
As mentioned in https://developer.android.com/guide/topics/manifest/service-element#isolated
Your service is isolated from the rest of the system and has no permissions of its own. That means it dont have INTERNET permission.Hence can't fetch data.

How to pass a string in AsyncTask?

So I have a URL within Method1 like so
public void Method1 (String x) {
String Url = "http://MYURL.com/?country=" + x + "&api_key=APIKEY";
new AsyncTaskParseJson().execute();
}
I need to pass the Url into my AsyncTask which is as follows
public class AsyncTaskParseJson extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {}
#Override
protected String doInBackground(String... arg0) {
try {
// create new instance of the httpConnect class
httpConnect jParser = new httpConnect();
// get json string from service url
String json = jParser.getJSONFromUrl(ServiceUrl);
// save returned json to your test string
jsonTest = json.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String strFromDoInBg) {
textLastLocation = (TextView) findViewById(R.id.lastlocation);
textLastLocation.setText(jsonTest);
}
}
I need it so the ServiceUrl = the Url from the method. I can't figure out how to do this even from looking at other peoples questions and answers
The first parameter on AsyncTask<First, Second, Third> will define the parameter to be passed on execute(), so you define it as String and pass the url. Then:
public void Method1 (String x) {
String Url = "http://MYURL.com/?country=" + x + "&api_key=APIKEY";
new AsyncTaskParseJson().execute(url);
}
On your AsyncTask, you can get it on the arg0 (array), i(ndex based on the order on how you passed it on execute())
public class AsyncTaskParseJson extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {}
#Override
protected String doInBackground(String... arg0) {
String url = arg0[0]; // this is your passed url
try {
// create new instance of the httpConnect class
httpConnect jParser = new httpConnect();
// get json string from service url
String json = jParser.getJSONFromUrl(ServiceUrl);
// save returned json to your test string
jsonTest = json.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String strFromDoInBg) {
textLastLocation = (TextView) findViewById(R.id.lastlocation);
textLastLocation.setText(jsonTest);
}
}

AsyncTask in android app for more than one REST endpoint

In Android is there a better way than using a single AsyncTask with a parameter to work out which REST endpoint to call?
e.g. I need to call:
www.test.com/api/room/id
www.test.com/api/room/id/booking
AsyncTask is designed for a single doInBackground() method that does a single thing, e.g. call:
www.test.com/api/room/id
I don't want to create multiple AsyncTasks instances, one for each REST endpoint.
The back end would use:
RoomClient = new RoomClient();
roomClient.getID()
roomClient.getBookingForRoom()
In Android it looks like I'd need
class RoomFromId extends AsyncTask
...
call www.test.com/api/room/id
class BookingForRoom extends AsyncTask
..
call www.test.com/api/room/id/booking
What I'd ideally like in the Android app is the idiom of writing a rest client that can call all REST endpoints in the background, without having to do each one in its own AsyncTask. I'd prefer to use what Android has, rather than a 3rd party library.
Create a generic Class extends from AsyncTask that return response in a generic type that extends from YourBaseModel (I called it M)
public class HttpRequest<M extends BaseModel> extends AsyncTask<Object, Integer, M> {
public enum RequestMethod {
GET("GET"), POST("POST");
private final String requestMethod;
RequestMethod(String requestMethod) {
this.requestMethod = requestMethod;
}
public String getValue() {
return requestMethod;
}
}
private Context context = null;
private String url;
private OnResponseCallback onResponseCallback;
private OnFailureCallback onFailureCallback;
private RequestMethod method;
private int statusCode;
private String message;
private Class<M> responseModel;
private Object body = null;
private String token;
private HttpRequest() {
}
#Override
protected M doInBackground(Object... voids) {
try {
HttpURLConnection connection = getHttpConnection();
connection.connect();
int statusCode = connection.getResponseCode();
if (connection.getResponseCode() / 100 != 2) {
this.statusCode = statusCode;
this.message = connection.getResponseMessage();
return JsonParser.getErrorBodyAs(responseModel, statusCode,
message);
}
InputStreamReader streamReader = new
InputStreamReader(connection.getInputStream());
return JsonParser.getErrorBodyAs(responseModel, statusCode,
convertInputStreamToString(streamReader));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private HttpURLConnection getHttpConnection() throws IOException {
URL url = new URL(this.url);
HttpURLConnection connection = (HttpURLConnection)
url.openConnection();
connection.setRequestMethod(method.getValue());
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Authorization", "Bearer " + token);
connection.setReadTimeout(30000);
connection.setConnectTimeout(30000);
if (method == RequestMethod.POST) {
connection.setDoInput(true);
connection.setDoOutput(true);
if (body != null) {
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(new Gson().toJson(body));
writer.flush();
}
}
return connection;
}
#Override
protected void onPostExecute(M m) {
if (m == null) {
if ((message != null && !message.equals("") && statusCode != 0)) {
HttpException httpException = new HttpException(statusCode, message);
onFailureCallback.onFailure(httpException);
} else {
onFailureCallback.onFailure("unknown error");
}
} else {
onResponseCallback.onResponse(m);
}
}
public static String convertInputStreamToString(InputStreamReader inputStreamReader) throws IOException {
if (inputStreamReader == null) {
return "";
}
BufferedReader reader = new BufferedReader(inputStreamReader);
StringBuilder stringBuilder = new StringBuilder();
String inputLine;
String result;
while ((inputLine = reader.readLine()) != null) {
stringBuilder.append(inputLine);
}
reader.close();
inputStreamReader.close();
return stringBuilder.toString();
}
static public class Builder {
HttpRequest t = new HttpRequest();
public Builder setContext(Context context) {
t.context = context;
return this;
}
public Builder setUrl(String url) {
t.url = url;
return this;
}
public Builder setRequestMethod(RequestMethod method) {
t.method = method;
return this;
}
public Builder setBody(Object body) {
t.body = body;
return this;
}
public Builder setToken(String token) {
t.token = token;
return this;
}
public HttpRequest get() {
return t;
}
public HttpRequest run(Class<?> responseTypeClass,
OnResponseCallback onResponseCallback,
OnFailureCallback onFailureCallback) {
t.responseModel = responseTypeClass;
t.onResponseCallback = onResponseCallback;
t.onFailureCallback = onFailureCallback;
t.execute();
return t;
}
public Builder() {
}
}
}
You can use it like this:
HttpRequest.Builder builder = new HttpRequest.Builder();
builder.setContext(context)
.setRequestMethod(HttpRequest.RequestMethod.POST)
.setBody(body)
.setUrl("http://url")
.run(YourResponeModel.class, new OnResponseCallback() {
#Override
public void onResponse(Object response) {
},
new OnFailureCallback() {
#Override
public void onFailure(Object throwable) {
}
});
In the class you create that extends AsyncTask you can create a constructor and pass whatever you want/need.
In this case you can define a class ApiManager that extends AsyncTask and pass a constant that defines the method to call.
In that constructor you can save the variable to your ApiManager object and then check it in the doInBackground method.
So, to call the room/id you could do something like:
new ApiManager(ROOM_FROM_ID).execute(...
And to call the room/id/booking:
new ApiManager(BOOKING_FOR_ROOM).execute(...
And the ApiManager class should be something like:
class ApiManager extends AsyncTask... {
private int method;
public ApiManager(int method) {
this.method = method;
}
...
}

Android AsynchTask data return to Activity

I have a MainActivity class from which I am calling an instance of my JsonObj class.
JsonObj extends AsynchTask. It's pasted below:
public class JsonObj extends AsyncTask<String, Void, JSONObject>{
int tid;
String term;
#Override
protected JSONObject doInBackground(String... url) {
// TODO Auto-generated method stub
DefaultHttpClient httpclient = new DefaultHttpClient(new BasicHttpParams());
HttpPost httppost = new HttpPost(url[0]);
JSONObject jsonObject = null;
// Depends on your web service
httppost.setHeader("Content-type", "application/json");
InputStream inputStream = null;
String result = null;
try {
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
inputStream = entity.getContent();
// json is UTF-8 by default
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null){
sb.append(line + "\n");
}
result = sb.toString();
// Log.e("JSON-Test [RESULT]: ", result);
jsonObject = new JSONObject(result);
} catch (Exception e) {
Log.e("JSON-Test [exception]: ", e.toString());
}
finally {
try{if(inputStream != null)inputStream.close();}catch(Exception squish){}
}
return jsonObject;
}
#Override
protected void onPostExecute(JSONObject result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
}
What I am trying to achieve is an AsyncTask class which I can call from any activity that will fetch and return JSON from my server.
My problem is my AsyncTask class successfully fetches my JSON but I am struggling to pass it back to my Activity.
I know the OnPostExecute function returns data on completion of my AsyncTask but all my attempts to access the data in my Activity class are not working.
I'm using new JsonObj().execute("http://myserver/json"); to call my JsonObj class.
How do I call OnpostExecute to directly access data my AsynchTask fetches from my server OR what is a better solution to this problem?
Create an Interface that has a method called something like "onServiceFinished" which your activities that make this call implement. Then when you call your AsyncTask, pass it (this) and in your onPostExectue, call FinishedListener.onServiceFinished(var1, var2) or whatever data your passing.
Code Example: This example uses an AsyncTask to retrieve a message from the server, and when that is finished, pass back that message to the class that requested it
Interface:
public interface ServiceHandler {
public abstract void onServiceFinished(JSONObject jsonObject);
}
Activity:
public class YourActivity extends Activity implements ServiceHandler {
#Override
public void onCreate(Bundle savedInstanceState) {
//Activity stuff
new JsonObj(this).execute("http://myserver/json");
}
#Override
public void onServiceFinished(JSONObject jsonObject) {
// Do something with the message
}
}
Asynctask:
public class JsonObj extends AsyncTask<String, Void, JSONObject> {
ServiceHandler handler;
public ChatAsync(ServiceHandler serviceHandler) {
handler = serviceHandler;
}
#Override
protected JSONObject doInBackground(String...params) {
// fetch messages from service and parse JSON
}
#Override
protected void onPostExecute(JSONObject json) {
handler.onServiceFinished(json);
}
}
The basic model is there, you'll have to modify it to fit what you're doing! Hope this helps
If you doing all network operation in a separate class and you want the result on the calling Activity class.
Create a method onTaskComplete(JsonObject result) on the calling Activity class.
public void onTaskComplete(JsonObject result) {
//Do You Operation
}
And now you need to call this method on OnPostExecute()
public class JsonObj extends AsyncTask<String, Void, JSONObject>{
Activity _context;
public JsonObj(Activity _context) {
this._context = _context;
}
#Override
protected void onPostExecute(JSONObject result) {
super.onPostExecute(result);
this._context.onTaskComplete(result);
}
}
At last call the AsyncTask
new JsonObj(this).execute("YOUR URL");
The best way to do this is with a Callback. With this pattern, you create an interface that you implement in your Activity. You then set a method in this interface callback to be run in your onPostExecute() method. For example, this basic AsyncTask will call the callback function when complete:
public class JsonObj extends AsyncTask<Void, Void, JSONObject> {
private OnSuccessListener listener;
private String url;
public JsonObj(String url, OnSuccessListener listener) {
this.url = url;
this.listener = listener;
}
#Override
protected JSONObject doInBackground(Void... params) {
//handle url getter. returning the JSONObject here will send it to the onPostExecute method.
}
#Override
protected void onPostExecute(JSONObject json) {
if (listener != null)
listener.onSuccess(json);
}
public interface OnSuccessListener {
public void onSuccess(JSONObject json);
}
}
Then to use this properly, just do this in your Activity:
new JsonObj("http://myserver/json", new OnSuccessListener() {
#Override
public void onSuccess(JSONObject json) {
//TODO: handle new JSONObject.
}
}).execute();
If you want a simpler, already coded solution, consider the droidQuery library that I wrote. Apart from simplifying animations and common UI tasks, it can simplify this sort of network call immensely. The above code can be compressed down to this in your Activity:
$.ajax(new AjaxOptions().url("http://myserver/json")
.type("POST")
.dataType("JSON")
.headers(new Headers().content_type("application/json"))
.success(new Function() {
#Override
public void invoke($ d, Object... args) {
JSONObject json = (JSONObject) args[0];
//TODO: handle json
}
});

Android, can I put AsyncTask in a separate class and have a callback?

I'm just learning about AsyncTask and want to use it as a separate class, rather then a subclass.
For example,
class inetloader extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(urls[0]);
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(
new InputStreamReader(content));
String s = "";
while ((s = buffer.readLine()) != null) {
response += s;
}
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
#Override
protected void onPostExecute(String result) {
Log.e("xx",result);
// how do I pass this result back to the thread, that created me?
}
}
and the main(ui) thread:
inetloader il = new inetloader();
il.execute("http://www.google.com");
//il.onResult()
//{
///do something...
//}
Thanks!
Use a interface. Something like:
interface CallBackListener{
public void callback();
}
Then do this in your UI thread:
inetloader il = new inetloader();
li.setListener(this);
il.execute("http://www.google.com");
In inetloader, add:
CallBackListener mListener;
public void setListener(CallBackListener listener){
mListener = listener;
}
then In postExecute(), do:
mListener.callback();
you can pass the activity instance to constructor and call activity function from there...
Like use interface :
public interface ResultUpdatable {
public void setResult(Object obj);
}
Implement this in the Activity and pass in the constructor of Async task and update the result from onPostExecute using setResult function.
inetloader il = new inetloader();
il.execute("http://www.google.com");
String result = il.get();//put it in try-catch
^^^^^^^^
here you get result which is in onPostExecute(String result)

Categories

Resources