I have a MainActivity in which I instantiate a class. This class contains basic data and two important methods: GetRequestAccessUrl(params) and getToken(string) which returns an AuthResponse.
The first method runs fine, the string is generated and processed in the application. However, the getToken-method involves networking and is therefore prohibited to run on the main thread and AsyncTask is recommended. The implementation of the latter method is as following:
public AuthResponse getToken(String code) {
if (secrete == null) {
throw new IllegalStateException("Application secrete is not set");
}
try {
URI uri = new URI(TOKEN_URL);
URL url = uri.toURL();
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
try {
StringBuilder sb = new StringBuilder();
sb.append("client_id=" + clientId);
sb.append("&client_secret=" + secrete);
sb.append("&code=" + code);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(sb.toString().getBytes("UTF-8"));
if (conn.getResponseCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ conn.getResponseCode());
}
Reader br = new InputStreamReader((conn.getInputStream()));
Gson gson = new Gson();
return gson.fromJson(br, AuthResponse.class);
} finally {
conn.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
In the MainActivity the entire class is created, the first method is called, some actions are executed and the the getToken-method should run. However I seem to be completely stuck on how to do this, or how to create a (working) AsyncTask regarding this method. Any help is appreciated.
new YourAsyncTask ().execute(code);
private class YourAsyncTask extends AsyncTask<String, Integer, Integer> {
protected Long doInBackground(String... codes) {
AuthResponse res = getToken(codes[0]);
doSthWithRes(res);
}
protected void onProgressUpdate(Integer... progress) {}
protected void onPostExecute(Integer result) {}
}
This will probably work. Depending on what you wanna do with the AuthResponse.
As you can see the ASyncTask is more of a batch processing in the background. I prefer just using a Standard Thread. Additionally you may want to process the AuthResponse in the UIThread.
Here the Quick and dirty version:
/* It would be better to create a subclass of Runnable and pass the Code in the constructor*/
final String code = "testcode";
//Create the new Thread
Thread t = new Thread(new Runnable() {
#Override
public void run() {
final AuthResponse res = getToken(code);
//RunOnUiThread is a method of the Activity
runOnUiThread(new Runnable() {
#Override
public void run() {
doSomethingWithResponse(res);
}
});
}
});
t.start()
Try something like this
new AsyncTask<String, void, AuthResponse>() {
#Override
protected String doInBackground(String... params) {
String id = params[0];
String secret = params[1];
String code = params[2];
//do your stuff
return myAuthResponse;
}
#Override
protected void onPostExecute(AuthReponse result) {
//do stuff with AuthResponse
}
}.execute(clientId, clientSecret, code);
In onPostExecute you can handle the AuthResponse on the UIThread.
I think that I answered this in the following thread :
Java AsyncTask passing variable to main thread
A Http request is done in background in an Asyntask and the result is sent to the main activity thanks to a Callback. I gave a sample code on the answer.
Related
I can download data using the downloadJSON class for 1 URL using the below code no problem, but I want to get another set of data so it can be displayed with the other. I've tried several different method but to no avail.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_json);
TextView textView = (TextView)findViewById(R.id.JSONTextView);
textView.setText("Downloading JSON!");
new downloadJSON().execute("www.exampleURL.com/data1");
//new downloadJSON().execute(url2??);
}
private class downloadJSON extends AsyncTask<String, String, String>
{
protected String doInBackground(String... args) {
String result = "";
String formattedResult = "";
try {
InputStream stream = (InputStream)new URL(args[0]).getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line = "";
while (line != null) {
result += line;
line = reader.readLine();
}
JSONObject json = new JSONObject(result);
formattedResult = "Downloadable Puzzles\r\n--------------\r\n";
JSONArray puzzles = json.getJSONArray("PuzzleIndex");
for (int i = 0;i < puzzles.length(); ++i) {
formattedResult += puzzles.get(i) + "\r\n";
}
} catch (Exception e) {
e.printStackTrace();
}
return formattedResult;
}
protected void onPostExecute(String pResult) {
TextView textView = (TextView)findViewById(R.id.JSONTextView);
textView.setText(pResult);
}
}
Edit: my question is not a duplicate of the link posted below as my question has much more weight to it with the fact that JSON and URL's are involved. The link is in no way specific to my problem and doesn't help the question.
The Async task doesn't return the control to the calling method. It only runs the onPostExecute() in the main thread after completing the doInBackground() on a background thread.
One approach to transfer control back into calling method is to use interface.
public class DownloadJSON extends AsyncTask<String, String, String> {
private AsyncCallback mCallback;
public DownloadJSON(AsyncCallback callback) {
mCallback = callback;
}
protected String doInBackground(String... args) {
// process background task
}
protected void onPostExecute(String result) {
if (mCallback != null)
mCallback.onComplete(result);
}
public interface AsyncCallback {
void onComplete(String result);
}
}
And then start asynctask using
new DownloadJSON(new DownloadJSON.AsyncCallback() {
#Override
public void onComplete(String result) {
textView.setText(result);
}
}).execute("www.exampleURL.com/data1");
I can suggest you to abandon this old way of picking a thread from thread pool and execute this heavy operation at background thread
check out this awesome lib. RxJava
The code below will make it more clear:
public static String TCMResponse(String params, final Context c) {
final String url = "https://115.248.161.106/ois/API/android/" + params;
new Thread(new Runnable() {
#Override
public void run() {
String response="";
try {
Document doc = Jsoup.connect(url).validateTLSCertificates(false).timeout(6000).get();
response = doc.text();
}
catch (IOException e) {
Log.d("Err","External OIS not reachable!");
}
// I want to return 'response' here, for the TCMResponse()
}
}).start();
}
So as you can see from the code, there is a function, TCMResponse() which takes the parameters of the url which i pass, and it does web scraping, i know all these can be done using volley/ JSONParser easily. But i am just experimenting, how to parse using web scraping.
So after the page is scraped, i need that function to return the response of the scraped page,
I've used Callable with executor service, but it again freezes the thread..
Have a look on what i've done:
public static String TCMResponse(String params, final Activity act) {
StrictMode.ThreadPolicy policy = new
StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);
final String url = "https://115.248.161.106/ois/API/android/" + params;
response="";
class MyBgThread implements Callable<String>
{
#Override
public String call() throws Exception {
try{
Document doc = Jsoup.connect(url).validateTLSCertificates(false).timeout(6000).get();
return doc.text();
}catch (Exception e)
{
Log.d("Exception",e.toString());
Snackbar.with(act, null)
.type(Type.ERROR)
.message("Something got wrong!")
.duration(Duration.LONG)
.show();
return "{'auth':'false'}";
}
}
}
Callable<String> worker = new MyBgThread();
ExecutorService ex = Executors.newSingleThreadExecutor();
Future<String> future = ex.submit(worker);
try{
response = future.get();
}catch(Exception e)
{
Log.d("Thread Ex",e+"");
}
ex.shutdown();
return response;
}
The main thread gets blocked because of your call to Future::get().
From the docs:
Waits if necessary for the computation to complete, and then retrieves its result.
which means; if the task Thread has not yet finished, the current Thread will wait until it returns a result.
I can see another problem in your code: you are showing a Snackbar, which is a UI component, in a Thread that is not the UI Thread.
Since you are working on Android, I would definitely use an AsyncTask, perform the expensive call in doInBackground(), then update the UI in onPostExecute().
I am trying to send a POST request with a JSON BODY in CodeName one.
It reaches the server with an empty Json String.
Here is the code that makes the connection and sends the message:
JSONObject json = new JSONObject();
class MyConnection extends ConnectionRequest {
public Map<String, Object> results;
#Override
protected void readResponse(InputStream input) throws IOException {
JSONParser jp = new JSONParser();
results = jp.parseJSON(new InputStreamReader(input, "UTF-8"));
}
#Override
protected void handleErrorResponseCode(int code, String message) {
showError("The server returned the error code: " + code);
}
#Override
protected void handleException(Exception err) {
showError("There was a connection error: " + err);
}
#Override
protected void postResponse() {
try {
json.put("AAA", "AAA");
json.put("BBB", "BBB");
json.put("CCC", "CCC");
} catch (JSONException ex) {
ex.printStackTrace();
}
}
#Override
protected void buildRequestBody(OutputStream os) throws IOException {
os.write(json.toString().getBytes("UTF-8"));
}
}
---
MyConnection connec = new MyConnection ();
connec.setUrl("http://testServoce/addUser");
connec.setPost(true);
connec.setContentType("application/json");
InfiniteProgress prog = new InfiniteProgress();
Dialog dlg = prog.showInifiniteBlocking();
connec.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueueAndWait(connec);
I'm not sure what your intention was but it looks like you misunderstood the goal of the postResponse method. It's unrelated to the POST web method and is just called after the response completed on the EDT. So changing the JSON value there is irrelevant.
Also it looks like you are using two separate JSON parsers for some reason. The builtin one and the org.json one from one of the cn1libs.
currently I'm doing something for my project wherein I created a separate Class that will only handle the Asynctask and get the value of the webservices I passed and that class should return the JSON response as a String. Now I already achieved it using taskName.execute().get(); wherein it will wait for the task to complete but the problem is it is also waiting for the task to complete before displaying the screen layout. Making my progressDialog useless and cause a delay on switching screens. Here's my code for now:
For the Class with AsyncTask:
public class UtilGetResponse {
Context context;
Map hash_values = new HashMap();
int DialogType;
String response;
/*
PLAN FOR DialogTypes:
* 0 - Standard Please wait dialog
* 1 - Progress dialog
* 2 - Camera upload dialog
* */
InputStream is = null;
StringBuilder string_builder = null;
public UtilGetResponse(Map values, Context baseContext, int type){
/*initialize class and pass the hash values for parameters*/
context = baseContext;
hash_values.putAll(values);
DialogType = type;
}
public String startTask(){
//TODO CASE WHEN BASED ON THE DIALOG TYPE SPECIFIED
Utilities util = new Utilities();
if(util.isOnline(context)){
try {
new UploaderTaskStandard().execute().get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return response; //THE RESPONSE ONLY SHOW ONCE THE WHOLE TASK IS COMPLETED
}
public class UploaderTaskStandard extends AsyncTask<Map, Void, Void> {
ProgressDialog simpleDialog;
#Override
protected void onPreExecute() {
/*Do something before the async task starts*/
simpleDialog = new ProgressDialog(context);
simpleDialog.setMessage("Please wait");
simpleDialog.show();
}
#Override
protected Void doInBackground(Map... maps) {
uploadData();
return null;
}
protected void onPostExecute(Void v) {
/*Do something after the task is complete*/
simpleDialog.dismiss();
}
}
private void uploadData() {
response = "null";
String url = hash_values.get("url").toString().replace(" ", "%20"); //get the URL replacing the space with %20
//If the user is trying to upload a file use this part
try {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
MultipartEntity mpEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
/*This will convert the hashMap sent into individual part per key per value*/
Set set = hash_values.entrySet();
Iterator iterator = set.iterator();
/*do a loop passing all the data on a string*/
while(iterator.hasNext()) {
Map.Entry mapEntry = (Map.Entry)iterator.next();
String keyword = String.valueOf(mapEntry.getKey());
String value = String.valueOf(mapEntry.getValue());
/*this will check if the passed data is a URL, file or a simple value*/
if(!keyword.equals("url")){
if(value.matches("(.*)/(.*)")){
File file = new File(value);
Log.v("Does this exists?",String.valueOf(file.exists()));
if(file.exists()){
FileBody upload_file;
upload_file = new FileBody(file);
/*not url but file*/
mpEntity.addPart(keyword, upload_file);
}else{
/*not url and not file*/
mpEntity.addPart(keyword, new StringBody(value));
}
}else{
/*not URL and not file*/
mpEntity.addPart(keyword, new StringBody(value));
}
}
}
post.setEntity(mpEntity);
HttpResponse response = client.execute(post);
HttpEntity resEntity = response.getEntity();
is = resEntity.getContent();
} catch (Exception e) {
e.printStackTrace();
response = "null";
}
/*convert JSON to string*/
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
string_builder = new StringBuilder();
String line = "0";
while ((line = reader.readLine()) != null) {
string_builder.append(line + "\n");
}
is.close();
response = string_builder.toString();
}catch(Exception e){
e.printStackTrace();
}
}
}
And to call this:
Map hash_values = new HashMap();
try{
HashMap params = new HashMap<String,String>();
params.put("param1", "YOUR_PARAM");
params.put("url", "YOUR_WEBSERVICE_URL");
//pass parameters
hash_values.putAll(params);
//start async task
UtilGetResponse util = new UtilGetResponse(hash_values, getActivity(), 0);
String result = util.startTask();
Log.v("The result string",result);
}catch (Exception e){
e.printStackTrace();
e.getCause();
Toast.makeText(getActivity(), "Oops problem", Toast.LENGTH_SHORT).show();
}
Is there's a way for me to do this properly without really waiting for the whole task to finish before moving to the next screen? I'm thinking of using a Handler but I'm not really familiar on how to use it anyway.
Your issue is with usage of this
new UploaderTaskStandard().execute().get();
Although you using AsynTask, but still making system wait until result which is against your requirement, what you need is a delivery mechanism, which will notify you back once results are ready. You can take either of two approaches.
change to this, and implement one of below mechanism.
new UploaderTaskStandard().execute();
Implementing handler, and posting result back once result available.
Implementing observer design pattern, where you create an interface with methods such as onResultReady, and passing an object of class implementing above interface to your method startTask, and posting result back from AsyncTask onPostExecute once it is available via interface mechanism.
Going via interface will be very easy and in this way your code will be independent of your network logic, sample code below
// Observer listener interface design
interface ResultListener{
// You can overload this method with data type you want to return
public void onResultReceived();
// Use them in a proper way for sending error message back to your program
public void onTaskCancelled();
public void onError();
}
// This will be your new method signature
public String startTask(ResultListener listener){
// Call it liske this, passing listener reference
new UploaderTaskStandard().execute(listener);
}
// This is your AsyncTask model
public class UploaderTaskStandard extends AsyncTask<ResultListener, Void, Void> {
ResultListener listener;
#Override
protected Void doInBackground(ResultListener... maps) {
this.listener = maps[0];
uploadData();
return null;
}
protected void onPostExecute(Void v) {
/*Do something after the task is complete*/
simpleDialog.dismiss();
// Notify back to calling program
listener.onResultReceived();
}
}
I am developing an app where in a number of the activities I need to retrieve data via Http.
Once I have the data I process it in the onPostExecute() callback method.
This works fine if I define the async task as an inline class, but as I want to do the same processing in a number of activities I have defined it as an external class.
So the question is, using an external class how do I signal an "event" back to the calling class as a means of passing the data back. I know how to do this in C# but I am new to Java and can not see how to achieve this.
While it is true that a Listener technically correct, I would claim that it is either too complicated or not complicated enough.
Here's an easier solution:
class Foo {
class Bar extends AsyncTask<Void, Void, Result> {
public void onPostExecute(Result res) {
doSomething(res);
}
}
public void doSomething(Result res) {
/// process result
}
public void startTask() {
new Bar().execute();
}
}
Is equivalent to:
class Bar extends AsyncTask<Void, Void, Result> {
private final Foo foo;
public Bar(Foo foo) { this.foo = foo; }
public void onPostExecute(Result res) {
foo.doSomething(res);
}
}
class Foo {
public void doSomething(Result res) {
/// process result
}
public void startTask() {
new Bar(this).execute();
}
}
... which is what you asked: when you pull the inner class out, you lose the implicit pointer. Just make it explicit and pass it in.
The bad news is that there are all sorts of memory leak and lifecycle issues that arise from this solution. I would very much recommend that, before your program gets even bigger, you looking into using an IntentService instead of an AsyncTask and use a Handler, or Activity.runOnUiThread to get the results back onto the UI thread.
One approach:
Define a parent abstract class called ParentActivity (extend Activity of course). Have that contain an abstract method called onAsyncCallback();
Have each class that uses the task to extend that class and implement the method.
In your AsyncTask constructor, have it accept in a ParentActivity.
Eg
ParentActivity activity;
public MyTask (ParentActivity myActivity)
{
this.activity = myActivity;
}
When done in onPostExecute(), simply do
activity.onAsyncCallback(data);
This would also work with an interface, it does the same thing, except intead you have the Constructor accept in the Listener instance.
If you want to do it in a clean way try following approach
First create an enum which contains all your async call names
public enum TaskType {
USER_LOGIN(1), GET_PRODUCTS(2), GET_EMPLOYEE(3);
int value;
private TaskType(int value) {
this.value = value;
}
}
Then create an interface
public interface AsyncTaskListener {
public void onTaskCompleted(String result, TaskType taskType);
}
Now implement this interface in the activity which you are going to call the GetAsyncTask
eg:-
public class LoginActivity extends Activity implements AsyncTaskListener {
protected void onCreate(Bundle savedInstanceState) {
String url = ".....";
new GetAsyncTask(LoginActivity.this, LoginActivity.this, TaskType.USER_LOGIN).execute(url);
}
...
public void onTaskCompleted(String result, TaskType taskType) {
if(taskType == TaskType.USER_LOGIN){
//your login result handling here
}
}
Lastly, this is your AsyncTask
public class GetAsyncTask extends AsyncTask<String, Void, String> {
String outputStr;
ProgressDialog dialog;
Context context;
AsyncTaskListener taskListener;
TaskType taskType;
public GetAsyncTask(Context context, AsyncTaskListener taskListener, TaskType taskType){
this.context = context;
this.taskListener = taskListener;
this.taskType = taskType;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = ProgressDialog.show(context, "Loading", "Please wait...", true);
}
#Override
protected String doInBackground(String... params) {
String urlString = params[0];
try {
URL url = new URL(urlString);
HttpURLConnection conn
= (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
if (conn.getResponseCode() != 200) {
throw new IOException(conn.getResponseMessage());
}
// Buffer the result into a string
BufferedReader rd = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = rd.readLine()) != null) {
sb.append(line);
}
rd.close();
conn.disconnect();
String jsonStr = sb.toString();
outputStr = jsonStr;
} catch (SocketTimeoutException e) {
outputStr = "timeout";
}catch(Exception e){
e.printStackTrace();
outputStr = "error";
}
return outputStr;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
taskListener.onTaskCompleted(result, taskType);
dialog.dismiss();
}
}
Use Listener pattern. Take a look here TaskListener, Task that uses this listener and fragment that calls task
Try making your own http requesting class that extends AsyncTask and than put your code in "doInBackground" call. Thyt way you only have to instantiate your class with url and params and read response. That worked for me.
Here is example:
public class HttpRequest extends AsyncTask<String, Void, String> {
public String url;
public List<NameValuePair> nameValuePairs;
public String httpResponse =null;
public HttpRequest(String _url, List<NameValuePair> _nameValuePairs) {
url = _url;
nameValuePairs=_nameValuePairs;
}
#Override
protected String doInBackground(String... params) {
httpResponse = getWebPageWithFormData(url, nameValuePairs);
return "";
}
public static String getWebPageWithFormData( String url, List<NameValuePair> nameValuePairs ){
String html = "";
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
if (nameValuePairs!=null){
try {httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));}
catch (Exception e){}
}
HttpResponse response = null;
try {
response = httpclient.execute(httppost);
}
catch (ClientProtocolException e) { return ""; }
catch (IOException e) { return ""; }
int responseCode = response.getStatusLine().getStatusCode();
switch(responseCode) {
case 200:
HttpEntity entity = response.getEntity();
if(entity != null) {
try{ html = EntityUtils.toString(entity);}
catch (Exception e) {}
}
break;
}
return html;
}
}