I have a thread which get some data from Internet. It seams that it is executed correctly and data is retrieved. However if I call a method which should return data it leaves me with null. From that I drew a conclusion that thread is somehow stopped just before finning.
Here is the code:
private class getHash extends AsyncTask<String, Void, String>{
#Override
protected String doInBackground(String... params) {
String str = null;
try {
// Create a URL for the desired page
URL url = new URL(params[0]);
// Read all the text returned by the server
InputStream is = url.openStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader in = new BufferedReader(isr);
str = in.readLine();
is.close();
isr.close();
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
hash = str; //If I set a global variable here it gets passed without a hitch
return str;
}
#Override
protected void onPostExecute(String result) {
hash = result; // If I comment the line above and live this one I left with a null
}
}
EDIT:
As requested adding code where the thread was called:
getHash hashThread = new getHash();
hashThread.execute(new String[] {"http://www.full.path/to/the/file.hash"});
if(hash != null && !hash.equals(localHash)){
....
Whatever launched the AsyncTask
{
....
getHash hashThread = new getHash(this);
hashThread.execute(new String[] {"http://www.full.path/to/the/file.hash"});
return; // ok now we just have to wait for it to finish ... can't read it until then
}
// Separate callback method
public void onHashComplete(String hash) {
if(hash != null && !hash.equals(localHash)) {
....
}
....
}
Now in your GetHash class
public String doInBackground(String[] params) {
.... // don't set hash here ... it will work but you will probably read it at the wrong time.
return str;
}
public void onPostExecute(String str) {
onHashComplete(str); // or just do all the work in here since it is a private inner class
}
....
Hopefully that helps. Remember doInBackground() happens on the AsyncTask thread, onPostExecute() executes on the main Thread. Whatever thread called execute() is should also be the main thread. Because of the way the main thread works, you can't expect the onPostCreate() to occur until whatever callback that it was using to call execute() in the first place, finishes. So that is why I add the return.
Related
Every tutorial I find seems to use AsyncTask (depreciated) instead of ExecutorService. I took a java course on Udemy and they used AsyncTask for everything as well. Here is one class I'm working with:
public class FetchURL extends AsyncTask<String, Void, String> {
Context mContext;
String directionMode = "driving";
public FetchURL(Context mContext) {
this.mContext = mContext;
}
#Override
protected String doInBackground(String... strings) {
// For storing data from web service
String data = "";
directionMode = strings[1];
try {
// Fetching the data from web service
data = downloadUrl(strings[0]);
Log.d("mylog", "Background task data " + data.toString());
} catch (Exception e) {
Log.d("Background Task", e.toString());
}
return data;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
PointsParser parserTask = new PointsParser(mContext, directionMode);
// Invokes the thread for parsing the JSON data
parserTask.execute(s);
}
private String downloadUrl(String strUrl) throws IOException {
String data = "";
InputStream iStream = null;
HttpURLConnection urlConnection = null;
try {
URL url = new URL(strUrl);
// Creating an http connection to communicate with url
urlConnection = (HttpURLConnection) url.openConnection();
// Connecting to url
urlConnection.connect();
// Reading data from url
iStream = urlConnection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
StringBuffer sb = new StringBuffer();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line);
}
data = sb.toString();
Log.d("mylog", "Downloaded URL: " + data.toString());
br.close();
} catch (Exception e) {
Log.d("mylog", "Exception downloading URL: " + e.toString());
} finally {
iStream.close();
urlConnection.disconnect();
}
return data;
}
}
and I'd really like to use ExecutorService like here instead of AsyncTask. I'm beating my head against the wall and I can't seem to get the proper arguments in and this thing working.
Replace your AsyncTask with a Runnable:
public class FetchUrl implements Runnable {
public interface Callback {
void onSuccess(String data);
void onFailure(Exception e);
}
private String url;
private WeakReference<Callback> callbackWeakReference;
public FetchUrl(String url, Callback callback) {
this.url = url;
this.callbackWeakReference = new WeakReference<>(callback);
}
#Override
public void run() {
try {
String data = downloadUrl(url);
Callback callback = callbackWeakReference.get();
if (callback != null) {
callback.onSuccess(data);
}
} catch (Exception e) {
Callback callback = callbackWeakReference.get();
if (callback != null) {
callback.onFailure(e);
}
}
}
... // include your downloadUrl function
}
Then create and submit it to the ExecutorService:
FetchUrl.Callback callback = new FetchUrl.Callback() {
#Override
public void onSuccess(String data) {
// handle your data
}
#Override
public void onFailure(Exception e) {
// handle the exception
}
};
Runnable job = new FetchUrl(url, callback);
ExecutorService executorService = Executors.newFixedThreadPool(4);
executorService.submit(job);
Notice I used a WeakReference<Callback>, because code in your callback is holding a reference to Context and would cause Context leaks.
The submit() function returns a Future to control your submitted job. It's handy if you want to cancel the job or want to wait for its completion (blocking the current thread). The latter usecase would perhaps favor using Callable<Result> instead of Runnable, because the calling thread can handle the exception and there would be no use for a callback making your code more concise.
Also don't forget to call shutdown() on your ExecutorService when you no longer need it.
I am trying to access the variable ArrayofTaxiDrivers in the class DownloadTask from the onCreate method of my activity file. However the variable is blank when accessed this way. The content does show when accessed from within the onPostExecute method of DownloadTask though.
Any help is greatly appreciated!
public class DownloadTask extends AsyncTask<String,Void,String> {
ArrayList<TaxiDriver> ArrayofTaxiDrivers = new ArrayList<TaxiDriver>();
#Override
protected String doInBackground(String... urls) {
String result = "";
URL url;
HttpURLConnection urlConnection = null;
try {
url = new URL(urls[0]);
urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = urlConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
int data = reader.read();
while (data != -1) {
char current = (char) data;
result += current;
data = reader.read();
}
return result;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
try {
JSONObject jsonObject = new JSONObject(s);
JSONArray items = jsonObject.getJSONArray("items");
DownloadTask task = new DownloadTask();
for(int i=0; i<items.length(); i++) {
JSONObject itemsObject = items.getJSONObject(i);
JSONObject fields = itemsObject.getJSONObject("fields");
task.addTaxiDriver(new TaxiDriver(fields.getString("name"), fields.getString("contact")));(asList(fields.getString("name"), fields.getString("contact"))));
}
Log.i("info",task.ArrayofTaxiDrivers.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_taxi_main);
DownloadTask task = new DownloadTask();
task.execute("https://test.com/json");
Log.i("info",task.ArrayofTaxiDrivers.toString());
}
When you call
task.execute("https://test.com/json");
the task will be executed based on its priority. For this reason the onPostExecute will be called after some time and probably after the onCreate
Log.i("info",task.ArrayofTaxiDrivers.toString());
I suggest you to think that "execute" is an instruction that exits immediatly and only after a while it will be executed.
I am trying to access the variable ArrayofTaxiDrivers in the class
DownloadTask from the onCreate method of my Activity.
That is because when you created DownloadTask, the List ArryofTaxiDrivers is initialized and at that time the size of the List is zero.
If you have the data in the onPostExecute() method, that means it was probably added by AsyncTask's doInBackground() method. Could be somewhere else entirely depending on your code.
Now the AsyncTask works asynchronously: it means that while it has been kicked to start executing, it doesn't start right away. However it will eventually start executing.
So when you access the list in onCreate() of the Activity, the list doesn't have any data because you haven't added to it.
What you should do
Instead implement an interface/callback to notify the Activity that the data is loaded and ready to be used. This callback can come from onPostExecute().
I have to do a lot of different things that AsyncTask doesn't allow (or maybe I don't know how to do). I have a class that call AsyncTask method (AsyncTask is another class) and I need to pass a String and an Integer, my AsyncTask has to open an online file and catch items and return a String[]. I was thinking to have solved my problem but it doesn't work with some android OS returning android.os.NetworkOnMainThreadException so I don't know other way to solve and I don't know what is the problem. My code:
public class Methods extends Activity{
static String url = "http://www.MYADDRESS.com/";
static String ConfPath = new String();
static public String[] Read(String path, int LinesNumber) throws InterruptedException{
try {
return new AsyncTask<Object, Integer, String[]>(){
#Override
public String[] doInBackground(Object... params) {
try {
String[] menus = new String [(int) params[1]];
BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(url+params[0]).openStream()));
String line = reader.readLine();
for (int k = 0; k < (int) params[1]; k++) {
menus[k] = line;
line = reader.readLine();
}
return menus;
}
catch (IOException e){
e.printStackTrace();
}
return null;
}
}.execute(path,LinesNumber).get();
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
}
the problem is
.execute(path,LinesNumber).get();
that makes the UI Thread wait the execution of your AsyncTask, since get is a blocking call. You should use just .execute(path,LinesNumber);
I'm new im Java, and im trying to return with a defined string variable at the end of the function, but eclipse keeps saying that it's cannot be resolved to a variable, and wants me to define it. Probably it's because im define the variable within the Try{} brackets, but how else could i do it?
public class readtextfile extends AsyncTask<String, Integer, String>{
private TextView description;
public readtextfile(TextView descriptionTextView){
this.description = descriptionTextView;
}
#Override
protected String doInBackground(String... params) {
try {
URL url = new URL("http://example.com/description1.txt");
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String line = null;
String result = "";
while ((line = in.readLine()) != null) {
//get lines
result+=line;
}
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
protected void onProgressUpdate() {
//called when the background task makes any progress
}
protected void onPreExecute() {
//called before doInBackground() is started
}
#Override
protected void onPostExecute(String result) {
this.description.setText(result);
}
}
Move the local variable declaration
String result = "";
to before the try block. If you define a variable within a block it's not available outside that block.
Alternatively you could move return result; to the end of the try block, but then you'd have to add another return statement at the end of the method, for the cases where an exception was thrown and got caught.
Or you could get rid of the try-block, move the exception-handling to elsewhere, and let any exceptions get thrown.
URL url = null;
String result = "";
Then inside your try, catch block.
try {
url = ....;
.
.
result = ....;
Declare the variable outside the try block.
Here's a section of my onCreate, which sometimes is causing exception:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tilisting);
_context = getApplicationContext();
SDName = Environment.getExternalStorageDirectory();
//listview = (ListView)findViewById(R.id.TIlistview);
String TIdir = new File(SDName, "/TitaniumBackup/").toString();
final ArrayList<String> apps = new ArrayList<String>();
final StringBuffer done = new StringBuffer();
Command command = new Command(0,"ls -a "+TIdir+"/*.properties") {
#Override
public void output(int arg0, String arg1) {
synchronized(apps) {
apps.add(arg1);
if (!done.toString().equals("")) {
done.append("done");//oh no
}
}
}
};
try {
RootTools.getShell(true).add(command).waitForFinish();
String attrLine = "";
int ind;
backups = new ArrayList<TIBackup>();
synchronized(apps) {
for (String app : apps) {
try {
TIBackup bkup = new TIBackup(app);
FileInputStream fstream = new FileInputStream(app);
BufferedReader atts = new BufferedReader(new InputStreamReader(fstream));
while ((attrLine = atts.readLine()) != null) {
ind = attrLine.indexOf('=');
if (ind !=-1 && !attrLine.substring(0,1).equals("#"))
bkup.prop.put(attrLine.substring(0,ind), attrLine.substring(ind+1));
}
backups.add(bkup);
atts.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
done.append("done");
}
setListAdapter( new StableArrayAdapter(this,backups));
} catch (InterruptedException e) {
//TODO:errors
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
The for (String app : apps) { is causing the exception, despite the waitforfinish() before it.
This updated code should fix it, adding data from the output, and waiting for any stragglers with the synchronized in the main code, but if you set a breakpoint on the //oh no line above, it is still getting to this point where it tries to add an item after the UI main code ran. So waitforfinish() is not waiting? How do I prevent this race condition?
I also tried the RootTask code below, but it seems to stop at the last readline?
RootTask getProfile = new RootTask() {
#Override
public void onPostExecute(ArrayList<String> result) {
super.onPostExecute(result);
for (String r : result) {
System.out.println(r);
}
}
};
getProfile.execute("ls /data/data/org.mozilla.firefox/files/mozilla/" );
onPostExecute never runs.
This was partially caused by a design flaw in RootTools. I believe the crux of the issue is that the operation that you are performing on the shell is taking longer than the default timeout that is set for shell commands. When the timeout occurs it simply returns the command as completed which is where the design flaw lies.
I have provided a new jar to use as well as some more information on this. I have also deprecated waitForFinish() as I agree that it was, and is, a poor solution.
https://code.google.com/p/roottools/issues/detail?id=35
Please let me know if you have any questions or problems :)
Output() is to be called during waitForFinish() waits. Something is wrong in the code implementing Command execution.
Most likely: the command executor (RootTools ?) runs the command on shell, gets a bunch of output lines, notifies the calling thread from waiting, and then calls output() of command for each line it got as output. I think it should notify the command thread after output() has been called on command object, for all output lines.
Still you can wrap the list modifying code and list iterating code in synchronized(<some common object>){}.
Update:
So waitForFinish() is not waiting? How do I prevent this race condition?
It does wait, but not for your code. Synchronized keyword merely made sure that output() of Command object is not called at the same time when you are iterating the apps collection. It does not schedule the two threads to run in a particular sequence.
IMHO, waitForFinish() is not a good pattern, making calling thread waiting defeats the point of a separate executor. It better be formulated like an AsyncTask or accept an event listener for each Command object.
Just a rough example, this class:
public class RootTask extends AsyncTask<String,Void,List<String>> {
private boolean mSuccess;
public boolean isSuccess() {
return mSuccess;
}
#Override
protected List<String> doInBackground(String... strings) {
List<String> lines = new ArrayList<String>();
try {
Process p = Runtime.getRuntime().exec("su");
InputStream is = p.getInputStream();
OutputStream os = p.getOutputStream();
os.write((strings[0] + "\n").getBytes());
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = rd.readLine()) != null){
lines.add(line);
}
mSuccess = true;
os.write(("exit\n").getBytes());
p.destroy();
} catch (IOException e) {
mSuccess = false;
e.printStackTrace();
}
return lines;
}
}
can be used as:
RootTask listTask = new RootTask{
#Override
public void onPostExecute(List<String> result){
super.onPostExecute();
apps.addAll(result);
//-- or process the results strings--
}
};
listTask.execute("ls -a "+TIdir+"/*.properties");