In one of my adapters for a custom ListView, I have a piece of code to insert a Bitmap into an ImageView that looks something like this :
Handler handler = new Handler(context.getMainLooper());
handler.post(new Runnable()
{
#Override
public void run()
{
imageView.setImageBitmap(bitmap);
}
});
Except that I'm passing on the Handler along with a callback (that is later wrapped in the Runnable), to a different thread which computes the Bitmap to be displayed and then posts the results on to the provided handler.
And every time the ListView is updated, the contents flicker a couple of times.
I'm aware that AsyncTasks are primarily for this purpose. But I'd still like to know what is causing the screen to flicker.
Related
I have a TextView on my splash screen and I'm trying to emulate Discord's randomized loading text.
I have the following code, pulled from here:
private void LoadText() {
Runnable runnable = new Runnable() {
#Override
public void run() {
final TextView loadingTextView = findViewById(R.id.loading);
loadingTextView.setText(FetchRandomLoadingText());
}
};
ScheduledExecutorService ex = Executors.newScheduledThreadPool(1);
ex.scheduleAtFixedRate(runnable, 0, 5, TimeUnit.SECONDS);
}
FetchRandomLoadingText() returns a string from an ArrayList by index, works fine (tested with Toast to make sure it wasn't the problem).
This is working fine for one or two updates, but if the splash takes longer (slow internet connection for example) to do its thing, the text stops updating after 2 iterations.
I've looked at the equivalent of setInterval in JavaScript for Java but the solutions seems to iterate once and then stop all together.
Am I missing something obvious in getting this to endlessly run until I flag for a new Activity to be loaded?
I'd use a handler instead of a scheduler.
long delay = 5000; //5 seconds
final TextView loadingTextView = findViewById(R.id.loading);
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
loadingTextView.setText(FetchRandomLoadingText());
handler.postDelayed(this, delay);
}
};
handler.post(runnable);
I would also test to make sure FetchRandomLoadingText() runs like you expect it to. Maybe run it 20 times and print the results in a dummy activity
Andrew's code is what I've been using in production but with a slight change. I did some profiling on the code during execution and noticed a slight memory leak if my splash screen didn't change to the main activity (by design when I found the bug). I left the above code running for around 20 minutes by mistake, and found that the app began running 10s of mb over what should be realistic for a simple Activity with a TextView.
The altered, memory leak free code should have handler and runnable declared at a class level and instantiated in the onCreate function, rather than inside the local execution block. Doing so not only prevents the memory leak, but also executes faster.
I'm using JFace to write a simple file-explorer application. The application's logic can be simplified as:
Display contents of a folder in a TableViewer.
Whenever a folder item gets double-clicked, async-load (to keep UI responsive) its contents and display it.
So in my opnion, there are at least 2 threads get involved: a) the UI thread and b) the background thread that fetches contents of a folder.
What really bothers me here is how does the two threads communicate and do I have to 'invent the wheel'? To be more specific:
How to tell the background thread when an item gets double-clicked? I suppose I need a task queue shared between the two threads or does JFace already provides some async-task mechanism?
How to tell the UI thread that the data have arrived and repaint the table? Which one to choose, asyncexec or syncexec?
What I would usually do is something like this:
// On double-click, start a new thread
new Thread(new Runnable()
{
#Override
public void run()
{
// Get your new data in this thread
final MyFancyDataObject data = SomeOtherClass.goAndGetMyData();
// Update the GUI, this is the safe way to do it from a non-gui-thread
Display.getCurrent().asyncExec(new Runnable()
{
public void run()
{
GuiClass.updateContent(data);
}
});
}
}).start();
I've been trying to fix an error causing an intermittent ConcurrentModificationException. What's happening is that I have tons of geopoints being displayed with an ItemizedOverlay. When the map is moved, however, I'm trying to clear out all current overlay items (the geopoints) and replace them with new points appropriate to the new view window.
I therefore have a callback function that clears out the old overlays and replaces them with new ones. I think my bug stems from multiple threads trying to do this simultaneously. The relevant sections are below. I have a very limited understanding of how the overlays and such work on a low level, so I was wondering if anyone could confirm (or refute) that this could be causing issues.
//first clear out the old overlays
List<Overlay> mapOverlays = mapView.getOverlays();
mapOverlays.clear();
//create the new overlays, each initialized with appropriate Drawables
MenuOverlay lowOverlay = new MenuOverlay(this, lowRisk);//(all valid Drawables)
MenuOverlay medOverlay = new MenuOverlay(this, medRisk);
MenuOverlay highOverlay = new MenuOverlay(this, highRisk);
//populate the overlays
//add the new overlays into the list of overlays
mapOverlays.add(lowOverlay);
mapOverlays.add(medOverlay);
mapOverlays.add(highOverlay);
//make the map refresh; this operation has to be pushed to another thread
Runnable runnable = new Runnable() {
public void run() {
mapView.invalidate();
}
};
runOnUiThread(runnable);
I tried making this method synchronized, but the error still occurred. Could this arise from the new runnable being pushed to the UI thread before the previous runnable terminates maybe? I've seen mention that populate is a better way than invalidate, although I'm not entirely sure how they're different. Any ideas how to resolve this?
Modifying the set of overlays should always be done on the UI thread. The List that getOverlays() returns is owned by the MapView, and it can decide to view or manipulate the list at any time.
Since you're working with a lot of geopoints, it's likely that your background thread is clearing (or adding) overlays while the MapView is iterating over them on the UI thread. That would trigger a ConcurrentModificationException because the iterator is invalidated when its underlying set of overlays changes. Sometimes the changes are not immediately visible to the UI thread, so the crash is intermittent.
Setting up the overlays is usually the slow part of this type of workflow. To avoid the concurrent modification, you could set up your overlays in the background thread and then make all of your calls to clear() and add() inside of the Runnable. (Another option is to use an AsyncTask.)
For example:
// Slow setup steps
final MenuOverlay lowOverlay = new MenuOverlay(this, lowRisk);
final MenuOverlay medOverlay = new MenuOverlay(this, medRisk);
final MenuOverlay highOverlay = new MenuOverlay(this, highRisk);
Runnable runnable = new Runnable() {
public void run() {
// Anything that touches UI widgets (map, overlay set, views, etc.)
List<Overlay> mapOverlays = mapView.getOverlays();
mapOverlays.clear();
mapOverlays.add(lowOverlay);
mapOverlays.add(medOverlay);
mapOverlays.add(highOverlay);
mapView.invalidate();
}
};
runOnUiThread(runnable);
ImageView image = (ImageView) findViewById(R.id.imageview);
image.setImageResource(drawable.image1);
SystemClock.sleep(1000);
image.setImageResource(drawable.image2);
I am trying to change the image for a second, my code above doesn't work but not sure why?
Should I be using a thread? or does anyone have any better ideas?
EDIT
To clarify on the problem:
The image being displayed as "drawable.image2"
I want "drawable.image1" to be shown for one second then change to "drawable.image2".
EDIT2:
This code is used in the onClick. When a user clicks the image it needs to change for one second
I'd recommend using a TimerTask with a Timer. You can set it up like this:
protected void showDelayedImages() {
mImageView.setImageResource(resId1);
Timer timer = new Timer();
timer.schedule( new MyTimerTask(), 1000 );
}
private class MyTimerTask extends TimerTask {
#Override
public void run() {
runOnUiThread( new Runnable() {
#Override
public void run() {
mImageView.setImageResource(resId2);
}
} );
}
}
Thread.sleep(1000);
should do it. Although there are better ways too.
Use debug mode and set breakpoints on each call to setImageResource. Step through and see if each is getting called to see if your image is changing properly.
In real world cases, you probably want to change the image based on some user action, or for example change an icon while a thread is processing, then change it bad when complete. For this example check out AsyncTask.
use R.drawable.image1 instead of drawable.image1
It looks like you're performing the "switch" in an onCreate() method, the sleep will probably just make your Activity load slower since at this stage there isn't actually anything written to the page.
To have your image change you need to perform the switch on the UI thread and you need to perform it after the image has been inflated and added to the page.
Try adding this code in an "onClick" event.
Hi i'm using Rotating Progress Bar in my Android Music Plyer Application....I'm not able to stop it. While working with horizontal Progress bar i used handler to stop and start it.
But while working with Rotating One, The progress bar goes into Infinite Loop.....
Can you please suggest method to stop the indefinite loop. Thanks in advance.
How about using ProgressBar#dismiss() method?
EDIT: dismiss() is only for ProgressDialog. For ProgressBar you should toggle the Visibilty of the View.
If mHandler is a Handler bound to your UI thread and mProgress is your ProgressBar, you can have something like the following from inside the run method of your background thread:
mHandler.post(new Runnable() {
public void run() {
mProgress.setVisibility(View.INVISIBLE);
}
});
You can dismiss a ProgressDialog. A progressBar is just a view you can make set its visibility as visible or invisible based on your requirement
Drawable d = yourActivity.this.getResources().getDrawable(android.R.drawable.ic_dialog_alert);
d.setBounds(progressbar.getIndeterminateDrawable().getBounds());
progressbar.setIndeterminateDrawable(d);