My app needs frequently needs to download images. So i'm implementing an IntentService in order to serve the download petitions.
The first problem is that the IntentService serves the download in order and sequentially and I'd like to have a way of prioritizing the downloads and making multiple downloads at the same time.
By the way I'm considering to use (inside the IntentService) a ThreadPoolExecutor with an PriorityBlockingQueue or combine a PriorityBlockingQueue and AsyncTasks.
What is the best approach for this problem? Is there some other reasonable posibilities?
Thanks in advance
You use AsyncTask.... the following snippet does the AsyncTask for your reference:
private class doSomethingDelayed extends AsyncTask {
private int num_runs = 0;
#Override
protected Void doInBackground(Void... gurk) {
Log.v(TAG, "going into postData");
long ms_before = SystemClock.uptimeMillis();
try {
Log.v(TAG, "Time Now is " + ms_before);
postData();
Log.v(TAG, "coming out of postData");
publishProgress(num_runs);
} catch (Exception e) {
System.out.println("--DoSomeThing--" + e.getMessage());
}
return null;
}
#Override
protected void onCancelled() {
Context context = getApplicationContext();
int duration = Toast.LENGTH_LONG;
}
#Override
protected void onProgressUpdate(Integer... num_runs) {
Context context = getApplicationContext();
}
}
}
}
Indeed a IntentService handles everything in sequence and shuts down where there's no more Intents available. I guess this will go wrong when you have a seperate thread to do your stuff.
What you can do is use a 'normal' Service and use the AsyncTask etc. When your tasks are done you can call stopSelf().
If it's downloading small stuff in background, android.os.AsyncTask is a way to approach.
Check http://code.google.com/p/minemessagevibrator/source/browse/trunk/src/com/mine/oauth/MineOAuthAccessActivity.java for an example.
Related
I'm starting a new thread from my activity, this thread does a 10 second operation and then reports back to the UI with runOnUiThread()
During the 10 second operation, the UI becomes unresponsive and does not respond to any user interaction. In this case I am attempting to close the activity using a button in the toolbar. An ANR error is thrown but the button click is processed after the worker thread has finished.
Although, while the thread is working, the app is still able to display a spinning ProgressBar which wouldn't happen if the work was being done on the UI thread.
The Profiler shows that the UI thread is sleeping during this work, so to my understanding it should be responsive?. I've tried using AsyncTask instead but that also doesn't work. Anyway here is some code:
The new Thread is started when the window comes into focus:
Activity:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus && !recyclerSetup){
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.VISIBLE);
WorkThread thread = new WorkThread();
thread.start();
}
}
Thread:
private class WorkThread extends Thread {
#Override
public void run() {
getViewModelAndWords();
runOnUiThread(() -> setupRecycler());
}
}
private void getViewModelAndWords() {
viewModel = ViewModelProviders.of(this).get(WordViewModel.class);
adapter = new WordDetailedAdapter(this, viewModel, this, this, !favGroup.equals(ANY_WORD_PARAM));
allWords = viewModel.getAllWords();
}
I'm not sure if the viewModel has anything to do with the issue or not, but it's the viewModel.getAllWords() method which performs a heavy 10 second Room db operation.
Here's a snapshot of the Profiler showing the sleeping UI thread and worker Thread (AsyncTask #6):
EDIT:
Okay, so I think the issue lies within the room DB operation / viewModel. Replacing the contents of getAllWords() with Thread.sleep(10000); free'd up the UI thread for user interaction, therefore it's the following code which is (for some reason) preventing user input:
EDIT 2:
As suggested, I now use onPostExecute() along with an interface to retrieve the words:
public static class GetAllWordsWithCallBackTask extends AsyncTask<Void, Void, List<Word>>{
WordViewModel.iGetWords listener;
WordDao wordDao;
public GetAllWordsWithCallBackTask(WordDao wordDao, WordViewModel.iGetWords listener) {
this.listener = listener;
this.wordDao = wordDao;
}
#Override
protected List<Word> doInBackground(Void... voids) {
return wordDao.getAllWords();
}
#Override
protected void onPostExecute(List<Word> words) {
listener.gotWords(words);
}
}
get() has been removed and I simply execute the task, passing in listener to handle the call back:
public void getAllWordsWithCallBack(WordViewModel.iGetWords listener) {
try {
new GetAllWordsWithCallBackTask(wordDao, listener).execute();
} catch (Exception e) {
Crashlytics.log("Getting all words exception: "+e.getMessage());
e.printStackTrace();
}
}
This works well and the words are returned to my activity successfully, but the UI is still unresponsive while the operation is being executed.
Edit 1
You call .get() on a AsyncTask. The calling thread waits for the AsyncTask to complete. You could implement interface callbacks to fix this problem.
Here is a solution for you're problem
Edit 2:
I took a closer look at your code, and again, there is no error in the code you posted here.
Using AsyncTask with callbacks is a possible solution. Your code runs in the background thread and the result is passed to the main thread without blocking it.
I think that your error lies in transferring the data from the callback to ViewModel or MainActivity.
The best solution to get around this is using LiveData.
I tried to rebuild your code as closely as possible. Maybe it will help you to find the mistake.
WordDb
#Database(entities = {Word.class}, version = 3)
#TypeConverters(DateConverter.class)
public abstract class WordDb extends RoomDatabase {
private static WordDb INSTANCE;
public abstract WordDao wordDao();
static synchronized WordDb getInstance(Context contextPassed){
if(INSTANCE == null){
INSTANCE = Room.databaseBuilder(contextPassed.getApplicationContext(),WordDb.class,"word_db")
.fallbackToDestructiveMigration()
.build();
}
return INSTANCE;
}
}
WordRepo
class WordRepo {
private WordDao wordDao;
WordRepo(Context applicationContext) {
WordDb wordDb = WordDb.getInstance(applicationContext);
wordDao = wordDb.wordDao();
}
void getAllWords(WordRepo.iGetWords listener) {
try {
Log.i("WordRepo", String.format("getAllWords() called from %s", Thread.currentThread().getName()));
new GetAllWordsWithCallBackTask(wordDao, listener).execute();
} catch (Exception e) {
e.printStackTrace();
}
}
public static class GetAllWordsWithCallBackTask extends AsyncTask<Void, Void, List<Word>> {
WordRepo.iGetWords listener;
WordDao wordDao;
GetAllWordsWithCallBackTask(WordDao wordDao, WordRepo.iGetWords listener) {
this.listener = listener;
this.wordDao = wordDao;
}
#Override
protected List<Word> doInBackground(Void... voids) {
Log.i("WordRepo", String.format("GetAllWordsWithCallBackTask.doInBackground() called from %s", Thread.currentThread().getName()));
return wordDao.getAll();
}
#Override
protected void onPostExecute(List<Word> words) {
Log.i("WordRepo", String.format("GetAllWordsWithCallBackTask.onPostExecute() called from %s", Thread.currentThread().getName()));
listener.gotWords(words);
}
}
public interface iGetWords {
void gotWords(List<Word> words);
}
}
MainViewModel
public class MainViewModel extends AndroidViewModel {
MutableLiveData<List<Word>> wordList = new MutableLiveData<>();
private static final String TAG = "MainViewModel";
public MainViewModel(#NonNull Application application) {
super(application);
}
void getAllWords() {
Log.i(TAG, String.format("getAllWords() called from %s", Thread.currentThread().getName()));
WordRepo repo = new WordRepo(getApplication());
repo.getAllWords(new WordRepo.iGetWords() {
#Override
public void gotWords(List<Word> words) {
wordList.setValue(words);
}
});
}
}
getViewModelAndWords() in MainActivity
private void getViewModelAndWords() {
Log.i(TAG, String.format("getViewModelAndWords() called from %s", Thread.currentThread().getName()));
viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
viewModel.wordList.observe(this, new Observer<List<Word>>() {
#Override
public void onChanged(List<Word> words) {
//Do something with youre result
Log.i(TAG, String.format("viewModel.wordList livedata returned %d results", words != null ? words.size() : -1));
}
});
viewModel.getAllWords();
Log.i(TAG, "viewModel.getAllWords() done");
}
If you find out what is going wrong with youre code, please leave a comment
As #mayokun already mentioned i would recommend to use RxJava or migrating your project to Kotlin + Coroutines to keep your code nice an clean.
Here you can find more:
Medium - Coroutines on Android (part I): Getting the background
CodeLabs - Using Kotlin Coroutines in your Android App
Medium - RxAndroid Basics: Part 1
Medium - RxJava VS. Coroutines In Two Use Cases
I have successfully tested this code with about 300,000 records. Running this operation has blocked the Async Task on my emulator for about 30 sec. The main thread was accessible during this process.
I hope this works for you this time as well
return new GetAllWordAsyncTask(wordDao).execute().get();
By calling get(), you are forcing the current invoking thread to synchronously wait for the result to come back, which makes your background query block the main thread while it executes.
The solution is to use a callback and onPostExecute rather than blocking the main thread to obtain your query results.
I'm new to Android and Java. I'm trying to download 1000 plus images. I don't want to do that serially in a UI thread as that will be slow. Hence, I implemented multi-threading using thread and runnable in the below manner.
The for-loop will be called 1000 plus times. So is it an efficient way of achieving it? Will the OS manage the thread pool by its own?
private void syncS3Data() {
tStart = System.currentTimeMillis();
try {
for (final AWSSyncFile f : awsSyncData.getFiles()) {
new Thread(new Runnable() {
#Override
public void run() {
beginDownload(f);
}
}).start();
}
} catch (Exception ex) {
progressDialog.dismiss();
showMessage("Error:" + ex.getStackTrace().toString());
}
}
for Sure you can't do that in MainThread (UI Thread) because if you did, the application will not be responding.. and then it will be killed by system, you can use AsyncTask class in order to do what do you need but i prefer to use intentservice
However you have to use Intentservice it's a worker thread (long operation) but be noted intentservice will not execute anything until it finish the current task, if you need to download it in parallel then you have to use Service it works with UI Thread so you need asyncTask in order to perform the action but make sure of calling stopSelf() unlike intentService it will be stopped once it finish
Instead of creating threads for each download, create one thread and use that for downloading all images.
You can use AsyncTask Refer: https://developer.android.com/reference/android/os/AsyncTask.html
private class DownloadFilesTask extends AsyncTask<SomeObject, Integer, Long> {
protected Long doInBackground(SomeObject... objs) {
for (final AWSSyncFile f : obj.getFiles()) {
beginDownload(f);
}
}
protected void onPostExecute(Long result) {
//Task Completed
}
new DownloadFilesTask().execute(someObj);
I had developed an e-commerce app before and have encountered a similar problem in which I had to download some 200+ images for each category.The way I did it was using a loop within an AsyncTask and after each download was completed the image was displayed at the relevant place using the onProgessUpdate() function.I can't share the actual code,so i will give a skeleton example.
public class DownloadImages extends AsyncTask<String,String,String>
{
File image;
protected String doInBackground(String... params)
{
//download the image here and lets say its stored in the variable file
//call publishProgress() to run onProgressUpdate()
}
protected void onProgressUpdate(String... values)
{
//use the image in variable file to update the UI
}
}
So I needed a while loop that would auto-refresh the text on the layout and I finally found a way like this (I only put the important part so you get the idea) :
public void restart() {
GetRate asyncRate = new GetRate();
asyncRate.execute();
}
private class GetRate extends AsyncTask<Void, Integer, String> {
#Override
protected String doInBackground(Void... params) {
try {
String p = urlfind();
return p;
}
catch (IOException e) {
return "0";
}
}
#Override
protected void onPostExecute(String p) {
price_text.setText(p);
restart();
}
now my question is, is this a good way of getting this done or could this cause problems ? the app seems to be working fine and I am not getting any errors for the moment, if there is a better or simpler way to do this please give an answer. thanks
Edit : this turned out to be a really bad idea even when I added sleep intervals the app would work for a 15min then crash so I dont advise anyone to use this.
Instead of calling the thread manually, AlarmManager or ScheduledExecutorService could be used to fire the task at certain intervals or at certain time.
AlarmManager is generally used for large intervals and the later one for short intervals.
Using these classes, you can fire your task and get your UI updated.
This could be a duplicate question but I did not find what I was looking for.
I am calling an AsyncTask in the UI activity new LoadData().execute(); and in doInBackground I call a method which takes time. I want to interrupt this thread if the data is not return after some time.
Below is the code how I tried to do this.
class LoadData extends AsyncTask<String, String, String>
{
#Override
protected void onPreExecute() {
super.onPreExecute();
startTime = System.currentTimeMillis();
}
protected String doInBackground(String... args)
{
DataCollector dc = new DataCollector();
data = dc.collectData(query);
//Here I check if the time is greater than 30 seconds then cancel
if(((System.currentTimeMillis()-startTime)/1000)>30)
{
cancel(true);
}
return null;
}
}
But this does not stop the task after 30 seconds, in fact it is taking more time.
I have tried get(long timeout, TimeUnit unit); as well but that does not work either.
Can anyone show me how can I do it or how do I use isCancelled() in doInBackground.
Thanks.
You need a thread that cancels your task after a certain amount of time. That Thread could look like this:
public class TaskCanceler implements Runnable{
private AsyncTask task;
public TaskCanceler(AsyncTask task) {
this.task = task;
}
#Override
public void run() {
if (task.getStatus() == AsyncTask.Status.RUNNING )
task.cancel(true);
}
}
And when you call your AsyncTask, you need to run the cancle task after a certain amount of time (=the timeout, in this case 20 sec)
private Handler handler = new Handler();
private TaskCanceler taskCanceler;
...
LoadData task = new LoadData();
taskCanceler = new TaskCanceler(task);
handler.postDelayed(taskCanceler, 20*1000);
task.execute(...)
It's a good idea if you clean this up on cancel or finish with
if(taskCanceler != null && handler != null) {
handler.removeCallbacks(taskCanceler);
}
You can of course wrap this in an custom implementation of AsyncTask. I've used this pattern many times and it works like a charm. One thing to note, in rare cases the handler would not start, I suspect if you create it in the wrong context it will not survive in certain instances, so I forced the handler to be an the UI Thread with handler= new Handler(Looper.getMainLooper());
You have to do the Time check on a different thread.
What you currently do is: executing the dc.collectData(query) (in background) and once it is ready you check if you should cancel. So if the query takes 1 minute, you will do the cancel check after 1 minute, which is already too late.
What you could do is schedule a TimerTask that should run 30 seconds after the LoadData().execute() and if the timer Task is run, you can cancel the AsyncTask (if it is still running)
I would translate this into an async/await problem making all the expensive methods as async methods.
First, Modify DataCollector's collectData(query) to collectDataAsync(query). (If you can't modify DataCollector, there are work arounds to wrap it in a lambda function or something similar).
Second, change doInBackground as an async task, something like this:
protected async Task<String> doInBackgroundAsync(String... args)
{
DataCollector dc = new DataCollector();
int timeout = 1000;
var task = dc.collectDataAsync(query);
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
data = task.Result;
} else {
// timeout logic
}
}
Basically, you have two tasks inside doInBackgroundAsync: collectDataAsync and a delay task.
Your code waits for the faster one. Then you know which one was and you can react accordingly.
If you also need to cancel collectDataAsync task, then you want to used a cancellationToken.
I use this to solve your problem https://stackoverflow.com/a/11191070/3307066.
Note that now doInBackgroundAsync is a async, so it changes a bit the way of using it.
Hope it helps.
Short answer is you CAN'T cancel an AsyncTask once its started. What you can do, is insert a loop inside doInBackGround() which will check for isCancelled() and if it is set to true sometime in the future - return a value from the function (which will in turn call onPostExecute() if you have defined it);
Note that just because you can't stop an AsyncTask doesn't mean that the OS won't cancel it if it's low on memory. You should have this in mind if you are doing essential tasks in the AsyncTask (ones that you want executed 100%). If so, it is better to use a Service - a component that is automatically killed and restarted by the OS as need.
try this :
public class MyTask extends AsyncTask<Void, Void, Void> {
private volatile boolean running = true;
private final ProgressDialog progressDialog;
public MyTask(Context ctx) {
progressDialog = gimmeOne(ctx);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
// actually could set running = false; right here, but I'll
// stick to contract.
cancel(true);
}
});
}
#Override
protected void onPreExecute() {
progressDialog.show();
}
#Override
protected void onCancelled() {
running = false;
}
#Override
protected Void doInBackground(Void... params) {
while (running) {
// does the hard work
}
return null;
}
// ...
}
Courtesy and for more details see this answer.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can’t create handler inside thread that has not called Looper.prepare() inside AsyncTask for ProgressDialog
I'm developing an Android service that try to obtain the device IP address every x time and comunicate it to a server.
I'm using:
Netbeans 7.2Android SDKAndroid Google-Api 8SQLite
I know there are a few questions related to this same issue, but none of them gave me a solution to my problem. As you can see in my code below, I'm not trying to access to the UI of the service main thread (well, I tried, but after I commented the line, the error remains the same). On the other hand, I'm using AsyncTask, which I think is the appropriate way to do it.
This is the main part of my service:
public class ArsosService extends Service {
private NotificationManager mNM;
private final Messenger mMessenger = new Messenger(new IncomingHandler());
protected DatabaseUtil dbu = null;
#Override
public void onCreate() {
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
try {
dbu = DatabaseUtility.getInstance(this);
} catch (IOException ex) {
Log.e("Service", ex);
}
Timer timer = new Timer();
timer.schedule(new Checks(), 0, 15000);
}
private class Checks extends TimerTask {
#Override
public void run() {
CheckIpAddress_Task checkIp = new CheckIpAddress_Task();
checkIp.execute();
}
}
// Other methods
private class CheckIpAddress_Task extends AsyncTask<Void, Void, Integer> {
#Override
protected Integer doInBackground(Void... arg0) {
String ipLocal = getLocalIpAddress();
String text = null;
// ipLocal==null means there is no available connection, so we do nothing.
if (ipLocal != null) {
String ipDb = dbu.getLastIP(); // we get the IP saved in the DB.
if (ipDb == null) {
dbu.addProperty("IP", ipLocal); // we save the IP in the DB.
} else if (!ipLocal.equals(ipDb)) {
dbu.setProperty("IP", ipLocal); // we update the IP in the DB.
}
}
if (text != null) {
//showNotification(1, text, ipLocal);
}
return 0;
}
private String getLocalIpAddress() {
String result = null;
// Irrelevant code
return result;
}
}
}
I think the problem may be related to the threads, but I can't see where. Any help will be appreciated.
EDITED: Although I have accepted one of the answers as correct, or maybe because of it, I've been searching for some more information regard to it. I've run into this page I want to share with all of you who someday need to know more about this issue. Its author,Tejas Lagvankar, explains everything about threads, loopers and handler in a very clear and understandable way.
Try this...
- First declare the Handler Object reference variable at class scope.
Handler h;
- Inside the onCreate() method create the instance of the Handler.
h = new Handler();
- Use it with thread like below:
new Thread(new Runnable(){
public void run(){
h.post(new Runnable(){
// Do the UI work here.
});
}
});
- You can very well use the AsyncTask, provided in android, its known as P*ainless threading.*
Handler always runs in the Looper thread context. When you declare a seperate thread, its context is different from the Looper. Hence the error.
Simple solution is always declare Handlers in onCreate(), onStart() and onResume(). If you use AsyncTasks, you can very well declare handlers in onPreExecute() and onPostExecute() as they too run in the Looper context.