Creating Multiple AsyncTask - java

I am using an external library for my android project. With that library i am getting the ID3 tags from stream Mp3 url. The code uses URLConnection to reach the link and then gets the tag informations.But the problem is its a very slow procedure.
while doing it on a main thread it takes more than 2 minutes to get ID3 tags of 25 songs from their URLs. So i decided to use multiple AsyncTasks to make the process faster and i used this code below. It became faster a bit, but still few seconds below 2 minute mark. When i checked the Threads tab from DDMS and i saw that during the runtime, there is only 6 AsyncTask is created and running.
My question is , how can i increase the number of AsyncTask running , in this loop.
counter=0;
for (final SongDetail e : songs) {
new AsyncTask<Void , Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
MP3 mp3 = new MP3(e.getLink());
e.setTitle(mp3.getTitle());
e.setArtist(mp3.getBand());
} catch (MalformedURLException e1) {
e1.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
counter++;
if(counter==songs.size()) {
Log.d("KA","loop finished");
}
}
}.execute();
}

The Android OS will decide how many parallel threads can be running at any given time. Just because you start 20 of them doesn't mean they'll all run. Android OS will queue them up and will run some of them at a time.
See this answer for more details:
Running multiple AsyncTasks at the same time -- not possible?

Related

Android permission denied when reading /proc/self/exe from non-main thread

I'm trying to get the canonical path of /proc/self/exe. When I do this on the Main Thread it works, when I do this on an different thread it crashes with an IOException: "Permission denied":
DBG E Thread: main
E Path: /system/bin/app_process32
E Thread: Thread-21656
System.err W java.io.IOException: Permission denied
W at java.io.File.canonicalizePath(Native Method)
W at java.io.File.getCanonicalPath(File.java:414)
W at java.io.File.getCanonicalFile(File.java:428)
W at com.quanturium.testbugprocselfexe.MyActivity.getPathOfExecutable(MyActivity.java:36)
W at com.quanturium.testbugprocselfexe.MyActivity.access$000(MyActivity.java:12)
W at com.quanturium.testbugprocselfexe.MyActivity$1.run(MyActivity.java:26)
W at java.lang.Thread.run(Thread.java:818)
Code:
#Override
protected void onCreate (Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getPathOfExecutable(); // Works as expected
new Thread(new Runnable() {
#Override
public void run ()
{
getPathOfExecutable(); // Trigger the IOException: Permission denied
}
}).start();
}
private void getPathOfExecutable()
{
try
{
Log.e("DBG", "Thread: " + Thread.currentThread().getName());
Log.e("DBG", "Path: " + new File("/proc/self/exe").getCanonicalFile().getPath());
}
catch (IOException e)
{
e.printStackTrace();
}
}
This error only happens when debuggable is configured to false in the build.gradle file
Code to try it out: https://github.com/quanturium/TestBugProcSelfExe
Is this a bug or an intended behavior? What would be a workaround to get the path of the current executable?
Does the code block? If it doesn't, there should be no ramifications of running it in the main thread. You can, however, do that from another thread, with:
Context.runOnUiThread(new Runnable() {
getPathOfExecutable();
});
This is the cleanest work around I can think of, short of editing the permissions of your file (that you can't get the path of without running your code on the main thread anyways) because you have r/w privileges on /proc/self/exe.
This is very weird, and I am still researching the permission differences in different threads on android.
If you can get it working in the main thread, my opinion would be to just do it in the main thread, and not worry much about optimization, as the performance is no different on different threads.
What would be a workaround to get the path of the current executable?
Since every Android app is forked from Zygote, which is the first Java vm process when the virtual machine created by /system/bin/app_process at system booting.
If you try to read the /proc/self/exe from your Android app, the actual executable will be /system/bin/app_process. Even if you read this outside of your app's main thread, the result is the same and it wouldn't have the permission error in theory.
The question you asked is a kind of weird problem, I have tested with the following code on Android 2.3.3 and worked fine.
new Thread() {
/* (non-Javadoc)
* #see java.lang.Thread#run()
*/
#Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
Log.d(TAG, new File("/proc/self/exe").getCanonicalFile().getPath());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();

Execute multiple threads each one for a certain amount of time in java

I'm sending files to my local server that creates a file back. My problem occurs when the user perform multiple actions one after another and I need to show an error message if one of the requests don't get a feedback file in 5 min.
How can I handle all these requests? I used newSingleThreadScheduledExecutor to check if the feedback file is there every minute but I don't know how to handle multiple ones and keep the countdown to each request for the 5 min case.
My try:
ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(listPrinter.size()));
for(int i=0;i<list.size();i++){
try {
final File retrievedFile = new File("/home/"+list.get(i)+".csv");
ListenableFuture<File> future = executor.submit(new Callable<File>() {
public File call() {
// Actually send the file to your local server
// and retrieve a file back
if(retrievedFile.exists())
{
new Notification("file exits").show(Page.getCurrent());
}
else{
new Notification("file no exits").show(Page.getCurrent());
}
return retrievedFile;
}
});
future.get(5, TimeUnit.MINUTES);
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
} catch (ExecutionException ex) {
Exceptions.printStackTrace(ex);
} catch (TimeoutException ex) {
Exceptions.printStackTrace(ex);
new Notification("Time out").show(Page.getCurrent());
}
}
But it just get executed at the beginning and that's it but when the file is added nothing happens.
Is it possible to do this with watchService? It works pretty well for me but I didn't know about the 5 min case
Take a look to the Future interface:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html
should fit perfectly to your problem.
When you run a thread, the result could be a Future, it is the result of a asyncronous task, and you can have one Future per asyncronous task that you are launching.
Future<File> sendReceiveFile(File inputFile) {
final Future<File> future = new YourFuture<File>(...);
new Thread() {
#Override
public void run() {
File outputFile = null;
try {
outputFile = SendFileToServer(inputFile);
} catch (final Exception e) {
// do something
} finally {
future.setValue(fileOutput);
}
}
}.start();
return future;
}
And in your main:
Future<File> future = sendReceiveFile(myFile);
File outputFile = null;
try {
outputFile = future.get(1, TimeUnit.MINUTE);
} catch(TimeOutException e) {
// do something
}
You could do this manually, but using Guava ListenableFuture would be much better:
// Here we create a fixed thread pool with 10 threads and an inifinite-capacity queue
ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
final File fileToSend = ...; //
ListenableFuture<File> future = executor.submit(new Callable<File>() {
public File call() {
// Actually send the file to your local server
// and retrieve a file back
File retrievedFile = YourLocalServer.sendAndRetrieve(fileToSend);
return retrievedFile;
}
});
Futures.addCallback(future, new FutureCallback<File>() {
public void onSuccess(File retrievedFile) {
// Handle the successfully retrieved file when it returns
}
public void onFailure(Throwable thrown) {
// Handle the error
}
});
By sending the file asynchronously, you can send and retrieve many files at any given time. Then, when the server responds (either with a retrieved file or with an error), you can handle the response (retrieved file or exception) just when it comes back, without needing to wait for it. This means that the onSuccess() or onFailure() methods will be automatically executed when there's a response available from your local server.
I solved the problem by using a Timer that is executed every 5 minutes getting all the db transactions that happened for the last 5 minutes and didn't get any response and show my error code. It works pretty good. Thanks everyone for the help

Android Concurrency, App dying after about 3 uses

I'm developing an app with different views populated with data acquired from a Server API. As of right now, I've come up with a 'solution' as to how to make the most out of the multicore Android devices that are currently available. I thought that a Producer/Consumer strategy was the right choice for this problem. However I'm using as many consumers as the Phone has cores and when executing the application everything works fine. But in some cases the application dies and doesn't do anything when I launch it, if I clear the RAM on my device I can launch the application again.
Consumer run method:
while(true) {
lock.lock();
while(queuedOperations.isEmpty()) {
ma.debug("Slave " + slaveId + " is waiting for requests.");
try {
slaveCondition.await();
if(doTerminate) { return; }
} catch (InterruptedException e) {
e.printStackTrace();
}
}
ma.debug("Executing Request... " + slaveId);
JSONRequestParam[] params = queuedOperations.poll();
ma.debug("Polled an item. " + slaveId);
lock.unlock();
ma.debug(request.execute(params)); // returns a String.
}
Using the following shutdown method:
public void destroy() {
ma.debug("Terminating all external resources...");
lock.lock();
this.doTerminate = true;
slaveCondition.signalAll();
lock.unlock();
try {
service.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
service.shutdown();
}
The way I trigger the Consumers is by a signal from the Condition class. Here's the code:
public void submitRequest(JSONRequestParam... params) {
lock.lock();
queuedOperations.add(params);
slaveCondition.signal();
lock.unlock();
}
I overrided the onBackPressed method in the main activity as well;
#Override
public void onBackPressed() {
try {
JSONRequestManager.getInstance(this).destroy();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
super.onBackPressed();
}
but to no avail. The application is very buggy and unstable. I have tried to find a replicate of this problem here and on numerous of different sites, couldn't find anything that helped. I'm almost sure there's no deadlocks in this application, I have a reasonable amount of experience working with concurrent applications, however Android is a tad bit different when it comes to threading...
Is there any hints or practices that I'm missing out? Why is the application working when the RAM is cleared and on first launch? Is there something jamming in the background? I really do not know. I hope you can tell!
Any help is greatly appreciated!

Google Drive for Android SDK Doesn't List Files

I've got a really odd problem with the Google Drive Android SDK. I've been using it for several months now, and until last week it performed perfectly. However, there is now a really odd error, which doesn't occur all the time but does 9 out of 10 times.
I'm trying to list the user's files and folders stored in a particular Google Drive folder. When I'm trying to use the method Drive.files().list().execute(), 9 out of 10 times literally nothing happens. The method just hangs, and even if I leave it for an hour, it just remains doing... nothing.
The code I'm using is below - all of this being run within the doInBackground of an AsyncTask. I've checked credentials - they are all fine, as is the app's certificate's SHA1 hash. No exceptions are thrown. Google searches have yielded nothing. Here is the particular bit of code that's bothering me:
try {
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(
SettingsActivity.this, Arrays.asList(DriveScopes.DRIVE));
if (googleAccountName != null && googleAccountName.length() > 0) {
credential.setSelectedAccountName(googleAccountName);
Drive service = new Drive.Builder(AndroidHttp.newCompatibleTransport(),
new GsonFactory(), credential).build();
service.files().list().execute(); // Google Drive fails here
} else {
// ...
}
} catch (final UserRecoverableAuthIOException e) {
// Authorisation Needed
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
startActivityForResult(e.getIntent(), REQUEST_AUTHORISE_GDRIVE);
} catch (Exception e) {
Log.e("SettingsActivity: Google Drive", "Unable to add Google Drive account due to Exception after trying to show the Google Drive authroise request intent, as the UserRecoverableIOException was originally thrown. Error message:\n" + e.getMessage());
}
}
});
Log.d("SettingsActivity: Google Drive", "UserRecoverableAuthIOException when trying to add Google Drive account. This is normal if this is the first time the user has tried to use Google Drive. Error message:\n" + e.getMessage());
return;
} catch (Exception e) {
Log.e("SettingsActivity: Google Drive", "Unable to add Google Drive account. Error message:\n" + e.getMessage());
return;
}
I'm using Drive API v2. Thanks everyone!
Edit
Having played around a bit more, it turns out this isn't for just listing files. Trying to interact with any file on Google Drive behaves the same way - deleting, downloading, creating... Anything! I have also noticed that putting the device in aeroplane mode so it has not internet access makes no difference either: Google Drive doesn't throw an exception, or even return, it just freezes the thread it's on.
I've updated to the very latest Drive API lib but that hasn't helped. I remembered that the error happened soon after I added the JSch SSH library to the project, so I removed that, but it made no difference. Removing and re-adding the Drive API v2 has made no difference either, and nor has cleaning the project.
Edit 2
I've found something which may be significant. On the Google Developer console, I had some Drive errors recorded as follows:
TOP ERRORS:
Requests % Requests Methods Error codes
18 38.30% drive.files.list 400
14 29.79% drive.files.insert 500
11 23.40% drive.files.update 500
4 8.51% drive.files.get 400
Do you reckon these are the errors? How could I fix them? Thanks
This is my code and it's work
new AsyncTask<Void, Void, List<File>>() {
#Override
protected List<File> doInBackground(Void... params) {
List<File> result = new ArrayList<File>();
try {
com.google.api.services.drive.Drive.Files.List list = service.files().list();
list.setQ("'" + sourcePath + "' in parents");
FileList fileList = list.execute();
result = fileList.getItems();
if(result != null) {
return result;
}
} catch (UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(List<File> result) {
//This is List file from Google Drive
};
}.execute();
I've come up with a solution which does work, and thought I'd post it so others could see it if they happen to come across the problem.
Luckily, I had backed up all of the previous versions of the app. So I restored the whole project to how it was two weeks ago, copied and pasted all changes from the newer version which had been made since then, and it worked. I don't see why this should work, since the end result is the same project, but it does!
Google Drive List Files
This might help you.. Try to display it in ListView u will see all fetched folders
public void if_db_updated(Drive service)
{
try {
Files.List request = service.files().list().setQ("mimeType = 'application/vnd.google-apps.folder'");
FileList files = request.execute();
for(File file : files.getItems())
{
String title = file.getTitle();
showToast(title);
}
} catch (UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (IOException e) {
e.printStackTrace();
}
}
public void showToast(final String toast) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), toast, Toast.LENGTH_SHORT).show();
}
});

Solution for downloading hundreds of files asynchronously

I have an app in which the user may need to download up to 760 files, totaling around 350MB. It is not possible to zip these files, they must be downloaded as loose files!
I'm currently using Android Asynchronous Http Client to download individual files, and AsyncTask to run the entire process.
Here's an example of a DownloadThread object which handles downloading hundreds of files in the background:
public class DownloadThread extends AsyncTask<String,String,String> {
ArrayList<String> list;
AsyncHttpClient client;
String[] allowedContentTypes = new String[] { "audio/mpeg" };
BufferedOutputStream bos;
FileOutputStream fos;
#Override
protected String doInBackground(String... params) {
DownloadTask task;
for (String file : list) {
//the "list" variable has already been populated with hundreds of strings
task = new DownloadTask(file);
task.execute("");
while (!task.isdone)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
class DownloadTask extends AsyncTask<String, String, String> {
String character, filename;
boolean isdone = false;
public DownloadTask(String file) {
//file = something like "Whale/sadwhale.mp3"
character = file.split("/")[0];
filename = file.split("/")[1];
}
#Override
protected void onPreExecute() {
}
#Override
protected void onPostExecute(String result) {
if (!result.equals("Error")) {
//Do something on success
}
isdone = true;
}
#Override
protected String doInBackground(String... str) {
client = new AsyncHttpClient();
client.get("http://some-site.com/sounds/" + character + "/"
+ filename, new BinaryHttpResponseHandler(
allowedContentTypes) {
#Override
public void onSuccess(byte[] fileData) {
try {
// Make file/folder and create stream
File folder = new File(Environment
.getExternalStorageDirectory()
+ CharSelect.directory + character);
folder.mkdirs();
File dest = new File(folder, filename);
fos = new FileOutputStream(dest);
bos = new BufferedOutputStream(fos);
// Transfer data to file
bos.write(fileData);
bos.flush();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
});
return "Success";
}
}
}
DownloadThread runs in the background, and also calls hundreds of it's own AsyncTasks. It waits until the task is done downloading, then continues the for loop for each download.
This works, kinda. Some downloads appear to not finish properly or not start at all. Out of a list of 760 downloads, an average of 100 downloads complete properly, and I have to restart the process to download another additional 100 downloads until that one fails as well. I have a feeling this is due to timing issues, as the Thread.sleep(10) line seems a little "hackish".
Surely, calling hundreds of AsyncTasks from another AsyncTask is not the most efficient way to do this. How can I alter this code or implement a 3rd party solution to fit this task?
Try out DownloadManager API. This should be what you need.
Here is the thing you need to keep in mind:
Computers have limited resources; network bandwidth, CPU, memory, disk, etc
The time it takes to download 1 file at a time vs. 760 files simultaneous can never logically take any longer than simultaneous download.
However, by spawning a whole lot of background tasks/threads you are incurring a lot of thread thrashing/overhead as each one needs to be context switched in and out. CPU bandwidth will be consumed in the switching instead of actually moving data in and off of the network interface. In addition, each thread will consume it's own memory and potentially need creating if not part of a pool.
Basically the reason your app isn't working reliably/at all is almost certainly because it's running out of CPU/DISK-IO/memory resources well before it finishes the downloads or fully utilizes the network.
Solution: find a library to do this or make use of the Executor suite of classes and use a limited pool of threads (then only download a few at a time).
Here is some good evidence in the wild that what you're trying to do is not advised:
Google play updates are all serialized
Amazon MP3 file downloader is totally serialized
default scp client in Linux is serialized file transfer
Windows update downloads serially
Getting the picture? Spewing all those threads is a recipe for problems in return for perceived speed improvement.

Categories