I've got a thread in a for loop to download some files from a http server reading the file names in an array list.
I need to launch several times the thread to get all the files, it seems that some threads don't achieve but with no rules at all.
I would like to launch the threads in order to see if each task works fine and optionally do something if not.
here's my code
for(String object:stringArrayList_dwlfromex){
try {
//String result = stringArrayList_dwlfromex.get(k);
String result = String.valueOf(object);
String[]row=result.split(";");
imei = row[4].toString();
dir = row[1].toString();
filename = row[2].toString();
compteur++;
//Toast.makeText(getApplicationContext(),dir+"&"+filename,Toast.LENGTH_SHORT).show();
Toast.makeText(getApplicationContext(),compteur+" "+result,Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
public void run() {
DownloadFiles(imei,dir,filename);
}
}).start();
}catch (Exception e) {
e.printStackTrace();
}
}
any idea?
I never see any workaround like you did.
You will have to make a service with AsyncTask. You will not use for loop.
Solution is to download another file when one is downloaded. You have to set a interface callback when a file is downloaded. and execute next download after this. Also destroy service when executed files size is equal to your list.
Related
I'm working on an app that goes through all of the phone directories, and collecting all the songs.
When i run it normally it works fine, just takes about 6 seconds to go through everything, and causes the app to skip a lot of frames.
I changed it so every time a file is found, a different thread reads the metadata and saves it.
I'm also waiting for all of them in the end, because after that I'm trying to use that list.
All of a sudden several of the song are null, even though they're not initialized as such anywhere.
What can cause that? an app that works fine, but not with threads..?
the constructor that calls the search:
phoneSongsList = new Playlist();
findSongs(Environment.getExternalStorageDirectory().getAbsolutePath()); //.concat("/Music")
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
The function that recursively looks for songs:
public void findSongs(String path) {
File home = new File(path);
for (final File file : home.listFiles()) {
if (file.isDirectory())
findSongs(path.concat("/" + file.getName()));
else if (isAcceptableExtension(file.getName())) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
phoneSongsList.add(fileToSong(file));
}
});
t.start();
threads.add(t);
}
}
}
The function that converts the file to a song object:
private Song fileToSong(File file) {
final Album album = new Album();
Song song = new Song();
song.setName(file.getName().substring(0, (file.getName().length() - 4))); // remove suffix
song.setPath(file.getPath());
final MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever();
metaRetriever.setDataSource(file.getPath());
song.setArtists(metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST));
album.setName(metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM));
album.setYear(metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR));
album.setCover(metaRetriever.getEmbeddedPicture(), context);
song.setAlbum(album);
song.setDuration(Long.parseLong(metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)));
song.setGenre(metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE));
metaRetriever.release();
return song;
}
The Playlist.add function:
public void add(Song song) {
add(list.size(), song);
}
public void add(int index, Song song) {
if(song==null)
return;
if (index > list.size())
index = list.size();
if (list.contains(song))
list.remove(song);
list.add(index, song);
}
Even when i explicitly specify that no null objects would be added to the list, it runs fine when it saves the songs, but give a null error when trying to read.
Each time i run different songs and a different number of songs are set to null.
Please help.
You're dynamically trying to add new Threads to the list of threads on other threads, but reading that thread on one thread. That means you'll finish the loop on some subset of those threads before all of them are added. This entire approach is one big race condition.
This isn't something threads are going to speed up much, and you're doing your threading all wrong anyway. Throw this out and just do it on a single background thread, and do not join on that thread (or you may as well do it sequentially)- have it post back to the main thread when done.
I am new to threading and i went through many post in stack overflow and find many solution for my problem but i am not sure which one is best for which condition.
First thing first, my problem is that i want to update one JSON file
when all threads are done with the bitmap generation at a specific path so
that i can get that all those image and update JSON file. So in
simple word my i want to run some code when all thread are done with it
execution and major requirement is that i don't want my main to be blocked because of this.
What i have found out
thread. join
excutorServive
android-priority-jobQueue (link)
Mutex in threadpool ( also let me know if any other is there)
I am confused which one is the best way to tackle my problem. if any
android expert out there can summarise that for following the two
scenerio what is the best available in android.
wait till when all thread completes
don't wait and get informed when all completes
You can have counter for your threads, after each thread is complete check how many have already completed, if not all completed, increment the number of completed threads and the last thread to complete will then run the piece of code.
You can do it like this.
In your thread:
private Runnable runnableThread= new Runnable() {
#Override
public void run() {
try {
if (lastThreadDone){
handler.sendEmptyMessage("SUCCESS");
}
}
catch (Exception ex) {
throws ex;
}
}
};
lastThreadDone is boolean which will become true if the process is done, this is base on how you implement it.
then in you handler:
#SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
try {
switch (msg.what) {
case "SUCCESS": {
// your code here
break;
}
case "FAIL":
break;
default:
break;
}
}
catch (Exception ex) {
throw ex;
}
super.handleMessage(msg);
}
};
I would use a completion service and then poll until all tasks are finished. When they are done, the json file gets updated. The problem is that you need to do this async or you risk to block the ui. Therefore I would encapsulate the work with the completion service inside an intent service. If you need to update the ui you then can post local broadcasts from the intent service.
Furthermore for you cases
wait till when all thread completes
only do this when you are already on a background thread like intent service or async task
don't wait and get informed when all completes
implies the case above. Do the work async and notify the ui or some listening component with broadcasts, content observers, handlers or the 'onPostExecute' if you are using async task.
I have created an AsyncTsak class in my project which downloads some information from web server. I am sure that it works well because when it is called by onCreate() , I can see the result . But unfortunately when I call it again via a button it doesn't work.
I am not sure but i think i have read somewhere about this problem . It said , we are permitted to use AsyncTask class only once.
AsyncTask class
class DownloadAdvertismentLevel2 extends AsyncTask<String,String,String>{
String [] ImageInformation=null;
protected void onPreExecute(){
// do nothing !
}
protected String doInBackground(String...Urls){
String Data="";
HttpURLConnection urlConnection = null;
try{
URL myUrl=new URL("http://10.0.2.2:80/Urgence/ads.aspx?Action=DownloadIds&TB="+TopBoundry+"&LB="+LowBoundry);
urlConnection = (HttpURLConnection)myUrl.openConnection();
BufferedReader in = new BufferedReader (new InputStreamReader(urlConnection.getInputStream()));
String temp="";
// Data is used to store Server's Response
while((temp=in.readLine())!=null)
{
Data=Data+temp;
}
}
catch(Exception ex){
Log.d("Er>doInBackground", ex.toString());
urlConnection.disconnect();
}
finally{
urlConnection.disconnect();
}
return Data;// it sends Result to onPostExcute
}
protected void onPostExecute(String Data){
createPhotoAlbum();
pb.closedProg();
}
}
onCreate
Here I don't have any problem . It works fine
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ads);
new DownloadAdvertismentLevel2().execute();
}
Via Button
ButtonSeeMore.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
Counting();
}});
Counting
public void Counting(){
if(TotalRows-6>0){
TopBoundry=TotalRows;
LowBoundry=TotalRows-6;
TotalRows=LowBoundry;
}
new DownloadAdvertismentLevel2().execute();
}
Please consider that I need to use this class till it shows the information. What would you suggest ?
To expand on what JVN said about AsyncTask
Each instance of Async task can only be executed once.
The task can be executed only once (an exception will be thrown if a second execution is attempted.)
http://developer.android.com/reference/android/os/AsyncTask.html (Under 'Threading Rules')
This doesn't stop you making a new instance -
public void Counting(){
if(TotalRows-6>0){
TopBoundry=TotalRows;
LowBoundry=TotalRows-6;
TotalRows=LowBoundry;
}
new DownloadAdvertismentLevel2().execute();
new DownloadAdvertismentLevel2().execute();
}
^ The code above will run your task twice.
Your code looks fine.
I would guess that the problem is (in order of likelihood)
1) On click isn't working
2) Post Execute isn't working as expected
3) The server response isn't being read correctly
4) Your Server isn't handling the request properly
But this would be obvious if you run your debugger or add some extra log outputs
1) I think you might be able to use the Async task only once in the class. But definitely it can be called multiple times.
2) please check if your button onclicklistener() function is really getting called on button click. try some logs in that.
because the code seems to be fine.
To allow multiple asycnh tasks run at the same time you need to use the 'executeOnExceuter mechanism:
See this note from the Android doucmentation:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
An example invocation would look like (this particular example is from a video manipulation app - the 'distributionTask' is an instance of a class that extends AsynchTask):
//Now execute synch task - to allow multiple AsynchTasks execute in parallel the
//'executeOnExecutor' call is required. It needs to be used with caution to avoid the usual synchronization issues and also
//to avoid too many threads being created
distributionTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Environment.getExternalStorageDirectory() + "/videoChunk_"+i+".mp4");
I'm trying to figure out a good way to give my services a preference file, like the SavedInstanceState of an activity. This service starts, sets an alarm, and calls stopSelf();, because it may be days before it runs again, I want it to start, do its task, then be done. I'm just going to same some key/value pairs in a comma separated text file.
OK, so I want to open the FileInputStream provided by Context, but I have to handle the exception. I'd rather check to be sure the file exists first to avoid the error, and in case a large list of files is returned I want to do this work off the UI thread. Here is my code:
public void setNewAlarm() {
Log.d("alarmServ", "inside setNewAlarm()");
FileInputStream fis;
Time time = new Time();
Boolean prefsAvail = false;
String[] fileList = fileList();
Runnable fileCheck = new Runnable() {
public void run() {
int i = 0;
while (i<fileList.length) {
if (fileList[i] == "preferences") {
prefsAvail = true;
break;
}
i++;
}
if (prefsAvail) {
try {
fis = openFileInput("preferences");
} catch (FileNotFoundException ioe) {
Log.d("alarmServ", "i/o error: output file fail");
ioe.printStackTrace();
}
}
}
};
//do something with the InputStream - read prefs and build alarm based on the criteria
}
The problem then, obviously, is the FileInputStream, boolean, and String[] are not available inside the runnable. How can I work around this, my understanding was that nested classes had access to their container's variables. Is this not true because its declared inside a method?
I tried googling, and discovered that one way to do this would be to declare the data as final, but this doesn't seem appropriate for a boolean or the stream.
If you need just to save key/value pairs I would suggest you using SharedPreferences, this is a preferred Android way.
Also if you intended to use files I would suggest you just to run it on the worker thread, that way you won't need to share your FileInputStream, boolean, and String[] between threads.
P.S. to use reference inside anonymous inner class, you have to define it as final.
I am creating an app that involves reading data from text files that are in the Assets folder. For each file, it stores the data in a separate ArrayList. The files are read in one after another in the onCreate() method of the activity. All of the text files combined total 1.8 MB and on the emulator it currently takes 12 seconds for the activity to load. The app does not crash on the emulator (it just takes approx 12 seconds).
I have read about asynchronous threads, but I have never had a need for them in the past. I was planning on having some sort of message or progress bar to notify the user that the activity is in fact loading and has not crashed.
So my question is: even though the app does not crash when loading the activity, should I still put the reading of the files on an asynchronous or different thread? If so, how would I go about doing it properly? (I have never done it before.)
Here is sample code with the reading of the text files:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_name);
populateArrayLists();
}
public void populateArrayLists() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(getAssets().open(
"text1.txt")));
String text;
while ((word = br.readLine()) != null) {
ArrayList1.add(text);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close(); // stop reading
} catch (IOException ex) {
ex.printStackTrace();
}
}
br = null;
try {
br = new BufferedReader(new InputStreamReader(getAssets().open(
"text2.txt")));
// the same process is duplicated for 1-15
// it may not be the best or most efficient way but it works
Any help would be appreciated.
Yes, you'll need a background thread for this. The Loader api may be your easiest bet.
This will allow you at least display a notice and offer some content while the files load. Maybe even load them and incrementally displaying the data, if that's what you're doing.
Edit: Loaders are a 3.0 feature available in the compatibility library. If you're not willing to add the dependency, you can always fall back to AsyncTask, in which case you could take a look at this.