So I have an activity with an ImageView. My goal is to have the activity start and asynchronously fetch the image I need in there. While the image is being fetched, I'd like to display some sort of spinning loading symbol in place of the image. I've tried using AsyncTask as a separate class, but ran into issues relating to modifying the views of another activity. I tried an anonymous Runnable class inside the activity, but it doesn't seem to be yielding the effects I expect. Can anyone advise?
SOLVED: Simply needed a handler in the activity
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
ImageView qrCode = (ImageView)findViewById(R.id.qr);
qrCode.setImageBitmap((Bitmap)msg.obj);
}
};
new Thread(new Runnable(){
public void run() {
BitmapFactory.Options bmOptions;
bmOptions = new BitmapFactory.Options();
bmOptions.inSampleSize = 1;
int retry = 0;
Bitmap bm = null;
while(retry < 50 && bm == null){
bm = LoadImage(image_URL, bmOptions);
if(bm == null){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
retry++;
}
Message msg = new Message();
msg.obj = bm;
handler.sendMessage(msg);
return;
}
}).start();
`
Place a progress dialog inside of the view with the imageview.
set the imageview ImageView.visibility(View.INVISIBLE);
In your preExecute() in your AsyncTask
Show the spinning dialog, and then do the fetching in the doInBackground() of the AsyncTask
In onPostExecute() show your imageview by changing the visibility and dismiss the dialog or sets its visibility to View.GONE
Android's developer guide for threading should help you out. It has AsyncTask examples that pretty much do exactly what you're asking for:
Android Threading Guide
If you don't implement the publishProgress() method, and only implement doInBackground to get the data, and onPostExecute to update the UI, then it should by default give you the spinning icon to indicate that it's loading. Keep in mind that onPostExecute runs on the main UI thread so you can't make any network calls from there
I did something similar and did according to this tutorial:
http://evancharlton.com/thoughts/lazy-loading-images-in-a-listview
Related
I am trying to process some files and it takes some time, so I would like to use a progressBar while the operation is being performed. In my activity, I have a button which starts the processing, when I press on it, the progressBar does not update and after a while the operation is complete and I see the result of the processing (so the function is properly called and act as expected).
The progressBar is visible, it just does not update. I have displayed getProgress() in the log and the value increases, only the actual progressBar does not update.
The function which should update the progressBar:
private byte[] processFiles() throws ExecutionException, InterruptedException {
//Init things
final ArrayList<String> fileNames = getFileList();
progressBar.setVisibility(View.VISIBLE);
progressBar.setMax(fileNames.size());
progressBar.setIndeterminate(false);
for (int i = 0 ; i<fileNames.size(); i++) {
//Do stuff on files
progressBar.setProgress(i+1);
Log.d("CurrentActivity", String.valueOf(progressBar.getProgress()));
}
return bos.toByteArray();
}
In the onCreate():
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setIndeterminate(false);
I have also tried as displayed in the android developer tutorial using a Thread but with no success (though I did not use a while loop but a for loop, but figured it shouldn't matter).
Can anyone help me figure out the problem?
Note I have also tried to use this before each update as suggested in some other answers:
progressBar.setProgress(0);
progressBar.setMax(fileNames.size());
Edit, here is my onClickListener:
fileButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
// Init things
try{
new Thread(new Runnable() {
public void run() {
try {
outStream = new FileOutputStream(path);
outStream.write(processFiles());
outStream.close();
runOnUiThread(new Runnable() {
#Override
public void run() {
//stuff that updates ui
}
});
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}catch(Exception e){
e.printStackTrace();
}
}
});
Now the progressBar works but using processedFile fails, it is an image that I display with Glide, it worked well before but now it doesn't display anything.
Edit 2: using runOnUiThread for all UI update worked like a charm and fixed my new issue.
You need to use two threads:
Main Thread (aka UI Thread) (aka WhereYouWorkAlways Thread)
Find and store the ProgressBar, create a Handler and then create the new Thread (see below)
Work Thread (aka just a new thread)
Inside the Thread Runnable, after each step of the work (= after each file operation is done) post a Runnable at the handler which updates the progress bar
handler.post(new Runnable() {
public void run() {
progressBar.setProgress(i);
}
})
Why two Threads are needed
The main thread is actually the UI Thread which means that every operations is placed in the "Update" step of the following cycle:
-> Update -> (Re)Draw -> Wait ->
Which means that if the Update lasts 10s then the screen will update after 10s. (Most UIs are single thread based)
Because on each iteration of cycle you have progressBar.setProgress(0);
I'm trying to implement a slide show for my App and I seem to be getting stuck on what seems like a small detail.
I'm new to Android and haven't been programming long so I'm not great with threading yet.
Anyways I have an ArrayList which is great. When I try to loop through the ArrayList replacing the images in my ImageView with images in the ArrayList however, I only end up seeing the final image in the List.
I can see in my LogCat that the images are being set (at least that section of the code is running. It is sleeping as I asked (I can notice the 1000ms in the LogCat entries.
I'm doing this in an AsyncTask and trying to set the results from the onPostExecute(). Here is that code...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slide_show);
screenSize = MediaHelper.getScreenSize(this);
Bundle extras = getIntent().getExtras();
Long projectId;
imageList = new ArrayList<Bitmap>();
ivSlide = (ImageView)findViewById(R.id.iv_slide);
tvLoading = (TextView)findViewById(R.id.tv_loading);
tvLoading.setVisibility(View.INVISIBLE);
if(extras != null){
projectId = extras.getLong("projectId");
new GetImagesTask().execute(projectId);
}
}
class GetImagesTask extends AsyncTask<Long, Void, ArrayList<Bitmap>>{
#Override
protected ArrayList<Bitmap> doInBackground(Long... id) {
long projectId = id[0];
Project project = getProject(projectId);
ArrayList<Bitmap> i = new ArrayList<Bitmap>();
File[] files = MediaHelper.getProjectFilenames(project.getImagePath());
if(files != null && files.length > 0){
for(File aFile : files){
Bitmap bitmap = MediaHelper.getScaledImage(aFile, screenSize.y / 2, screenSize.x / 2);
i.add(bitmap);
Log.d(TAG, "image added...size = " + bitmap.getByteCount());
}
}
return i;
}
#Override
protected void onPostExecute(ArrayList<Bitmap> result) {
if(result != null && result.size() > 0){
imageList = result;
if(imageList.size() > 0){
ivSlide.post(new Runnable(){
#Override
public void run(){
try{
for(Bitmap bm : imageList){
ivSlide.setImageBitmap(bm);
Thread.sleep(1000);
Log.d(TAG, "Setting ImageView image");
}
}catch (InterruptedException e){
Log.d(TAG, "Thread interrupted unexpectedly.");
}
}
});
}
}
Any help is much appreciated!!
I haven't used View.post(Runnable) before so I'm not sure about its behaviour but if it's not running on the UI Thread that "setImageBitmap" is not applied instantly, I think it's only applied when some other thread asks your view to reload its layout.
If it runs on the UI Thread, the Thread.sleep(1000) is asking the UI to stop working for a whole second, making the setImageBitmap effect impossible to notice to the user.
While facing a similar problem some time ago I used a Thread with a while in it that calls to runOnUiThread(Runnable) each time it needed to swipe the pictures. That way, the loading task was ran on the UI Thread and the waiting on a background one.
Hope it helps.
EDIT:
Could you try replacing your AsyncTask with this one, as suggested on my comment? gist.github.com/Arasthel/9788601
Your problem is likely Thread.sleep(). This will sleep the main thread and likely prevent the ui from updating till the last image when you don't sleep the thread.
Use Handler.postDelayed(Runnable, long) instead of sleeping the thread.
Obviously only the last item will be updated, Because you are running the loop and updating the same variable "ivSlide". For each iteration the same variable is getting updated with the different Bitmap at last it stops with the last Bitmap updated.
To overcome this you have to update each ivslide view variable with the arraylist index.
Please update full code so I can answer better.
In my android application, on a certain activity I need to create screenshots of views without actually displaying them. I have been successful in achieving this by inflating the views and saving them as bitmaps.
But in some scenarios the number of these bitmaps is large enough and it takes a lot of time to create them. As such the UI on the phone becomes non responsive. Is there any way I can do this whole process in the background? I have already tried implementing it in an Async Task but that does not work because its not allowed to inflate views in an Async task.
Any suggestions are highly appreciated.
AsyncTask doBackground method works on another Thread, That's the reason you are not able to inflate the views.
First whether u have one layout or many. If you have many then try below.
I have not test this. Just a sample for you.
public class Task extends AsyncTask<Void, Integer, Void>
{
private ArrayList<Integer> layoutIds;
private View currentView;
private LayoutInflater inflater;
private Object lock = new Object();
public Task(Context context) {
super();
inflater = LayoutInflater.from(context);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
Bitmap temp;
for (int i = 0; i < layoutIds.size(); i++) {
temp = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
Canvas canvas = new Canvas(temp);
synchronized (lock) {
publishProgress(i);
try {
// Wait for the UI Thread to inflate the layout.
lock.wait();
}
catch (InterruptedException e) {
}
}
currentView.draw(canvas);
// Now save this bitmap
try {
FileOutputStream stream = new FileOutputStream(new File(Environment.getExternalStorageDirectory(), "File_" + i + ".png"));
temp.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.flush();
stream.close();
} catch (Exception e) {
}
finally
{
if(temp != null)
{
temp.recycle();
temp = null;
}
}
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
synchronized (lock) {
currentView = inflater.inflate(layoutIds.get(values[0]), null);
// Notify async thread that inflate is done.
lock.notifyAll();
}
}
}
EDITED
Here we have two thread one is AsyncTask which is a Thread Pool and another is UI Thread.
With synchronized block we could make sure that only one thread could use the object lock as long as it is not in sleeping or waiting for another thread.
So if one thread is executing the block inside synchronize then it will start monitoring that object and make sure no other thread which also has a synchronize block for that object will be executed. i.e., another thread has to wait for as long as the active thread goes to sleep or completed its execution inside synchronized block.
For more explanation, See this
Here, we used the synchronize block to wait for UI thread to complete.
So as it execute lock.wait(), the AsyncThread will wait till another thread calls notify on the same object. And when lock.notifyAll() is called all the thread (AsyncThread) which are waiting will be resumed.
AsyncTask is divided to onPreExecute(), onProgressUpdate() and onPostExecute(), all happens in the main UI thread allowing you to inflate the view. Only doInBackground() is where things actually happen in the other thread. If you can do al your calculation on this method and only inflate onProgressUpdate() or onPostExecute() it might help.
Also if you can give us your code it might help us to find a way to make it more CPU efficient.
Anyhow, android will try to force close your app if the UI thread isn't responding for more than 5 seconds, and theres isn't much to do about it (As far as I know).
My question concerns this thread-
How to refresh imageview in android
Im too trying to get an image via socket, decode it, and setting it on an imageview.
The socket need to stay open in case another image will be sent and then ill update the imageview again.
I didnt got the part when in the other thread they response not to do it in the main thread.
From the main activity I should open a new thread which will be 'stuck' on "while (true)"
so it can keep listening to up coming images, and when ever a full image had sent, it needs to update the imageview.
But the imageview is in the main thread (main thread equals UI thread?) so its starting to be a bit problematic for me...
Any one?
// ADDED -
this is the asyncTask I had -
public class AsyncTask extends AsyncTask<Void, Void, Void> {
private DataInputStream dataInputStream = null;
private ImageView iv = null;
public RobotMapAsyncTask(DataInputStream dataStream, ImageView imageView) {
dataInputStream = dataStream;
iv = imageView;
}
#Override
protected Void doInBackground(Void... params) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] byteChunk = new byte[1024];
Bitmap bitmap = null;
int c;
if (dataInputStream != null) {
while (true) {
try {
byteChunk = new byte[1024];
while ((c = dataInputStream.read(byteChunk)) != -1){
buffer.write(byteChunk, 0, c);
}
} catch (IOException e) {
e.printStackTrace();
}
bitmap = BitmapFactory.decodeByteArray(byteChunk, 0, byteChunk.length);
if (bitmap != null) {
//runOnUiThread(new Runnable() {
//public void run() {
//iv.setImageBitmap(bitmap);
//}
// });
}
}
}
return null;
}
but then I obvious cant call runOnUiThread because this is not an activity...
After you get a new image, call this:
runOnUiThread(new Runnable() {
public void run() {
imView.setBackgroundResource(yourResource);
}
});
Just make the obvious changes to fit your code
Change the image of your ImageView in a Runnable launched by Activity.runOnUIThread(Runnable) or Handler.post(Runnable)
You can do this in two ways:
a) one way is invoke a worker thread using handler and runnable from main thread which does the fetching and decoding of images. Once you have the decoded image, just set the imageview from your worker thread
b) Alternatively, you can use the async task feature of android to fetch images and update the imageview on main thread
I have a View that recieves swipe gestures.
this should trigger the creation and setContentView(layout) for the main activity.
(the View is within a Layout on the Main activity)
When i try to do this within an asynctask it leaves out all the images for the layout?
as if it hasn't finished yet .
There is prolly something i'm not fully understanding .
this the partial code from the view.java
Main.mHandler.post(new Runnable()
{
public void run()
{
new Main.GetNavigationLayout().execute(url);
}
});
the url is a location of an .xml file I use to create a new layout.
GetNavigationLayout is the AsyncTask
code that is the AsyncTask in Main.java :
public static class GetNavigationLayout extends AsyncTask<String, Void, CustomLayout> {
boolean isDone = false;
#Override
protected EvadoLayout doInBackground(String... url)
{
CustomLayout layout = new CustomLayout(Main);
Looper.prepare();
try
{
String newpage = url[0];
Document doc = XmlImporter.GetNewPlayer(newpage);
if (doc == null)
{
layout = null;
}
layout.LoadXml(doc); // cause drawing of objects etc.
}
catch (Exception e)
{
e.printStackTrace();
}
isDone = true;
//Looper.loop(); // causes it to never return...
return layout;
}
#Override
protected void onPostExecute(CustomLayout layout)
{
if (isDone)
Main.setContentView(layout);
}
}
now this shows everythig besides images , whereas if i run this without AsyncTask it displays everything in the layout.
what am i missing?
layout.LoadXml(doc); // cause drawing of objects etc.
I reckon you are drawing the images here? I think this is the problem. Trying to draw from the AsyncTask's doInBackground() is wrong, since it is not the UI thread. You should do the drawing in onPostExecute() which runs in the UI thread
You can post to the UI in the onProgressUpdate(). Using this method will allow you to stay inside the doInBackground and post an update to the UI when you get a layout and continue inside the doInBackground. Use publishProgress() to send information from the doInBackground to the UI thread.