This is a piece of code from the docs of android:
https://developer.android.com/guide/components/processes-and-threads.html.
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap bitmap =
loadImageFromNetwork("http://example.com/image.png");
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();
}
My question is why it is ok to use mImageView inside the Runnable, I mean when we run this piece of code the mImageView might not be existed so this is a memory leak, am i right? If so, why people do that kind of thinks like reference a View inside a Runnable.
Related
I thought that it is not possible to access main thread views in a new thread!
But why below codes runs without any problem?!
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
new Thread(new Runnable() {
#Override
public void run() {
try {
textView.append(InetAddress.getByName("192.168.1.252").getHostName() + "\n\n");
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}).start();
}
As it stands here:
For example, below is some code for a click listener that downloads an
image from a separate thread and displays it in an ImageView:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();
}
At first, this seems to work fine, because it creates a new thread to
handle the network operation. However, it violates the second rule of
the single-threaded model: do not access the Android UI toolkit from
outside the UI thread—this sample modifies the ImageView from the
worker thread instead of the UI thread. This can result in undefined
and unexpected behavior, which can be difficult and time-consuming to
track down.
So it works, but not recommended.
There are some recommended way to do this instead:
To fix this problem, Android offers several ways to access the UI
thread from other threads. Here is a list of methods that can help:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
try this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
new Thread(new Runnable() {
#Override
public void run() {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
textView.append(InetAddress.getByName("192.168.1.252").getHostName() + "\n\n");
}
});
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}).start();
}
runOnUiThread - is method from activity. if you work inside fragment you can call getActivity().runOnUiThread
The solution is to run the UI thread inside your new thread.
Here is an example using anko.
btn_login.text = "LOGING IN"
doAsync {
authenticate(email, password)
uiThread { btn_login.text = "LOGIN" }
}
From my generic fragment, I have this method:
protected void loadDataListWithDelay() {
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadDataList();
}
}, DELAY_START_LOADING);
}
This method allows to start a new loading data from server (with Volley networking).
In my loadDataList() method there are some views visibility setting:
protected void loadDataList(String url, ArrayList<BaseFilters> filters,
String query, boolean byPassSearchMode) {
...
mLoadingDataListView.setVisibility(View.INVISIBLE);
mListContainer.setVisibility(View.VISIBLE);
...
This code runs perfectly, but I have this crash this morning on mobile with Android 6.0.1.
Could you help me guys?
I will give you a good advice, if you want to do postDelayed, take a view in your fragment (or any view), and do postDelayed with it. i.e. textView.postDelayed(.. This way you can be sure you are on the ui thread.
Why not use runOnUiThread instead of creating a new handler ?
runOnUiThread(new Runnable() {
#Override
public void run() {
loadDataList();
}
});
If you need a delay, then you can use a handler inside the runOnUiThread
runOnUiThread(new Runnable() {
#Override
public void run() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadDataList();
}
}, DELAY);
}
});
public class MainActivity extends Activity {
ImageView img;
MediaPlayer failure;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bButton= (Button)findViewById(R.id.blueBtn);
img = (ImageView)findViewById(R.id.image);
img.setVisibility(View.VISIBLE);
failure= MediaPlayer.create(this,R.raw.failure_sound);
bButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
img.setVisibility(View.VISIBLE);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
failure.start();
}
});
}
well thats my code.
I just want to make that when I press the button it'll show the image
then wait 1000 miliseconds, and then make a sound.
BUT(!) unfortunaltly when I press that button: the process waits 1000 ms, and then make the sound and shows the img.
help plz!!!
Replace your onClick handler:
public void onClick(View v){
img.setVisibility(View.VISIBLE);
v.postDelayed(new Runnable() {
public void run() { failure.start(); }
},1000);
}
With Thread.sleep(10000); you are causing the main thread to freeze and thus the UI cannot be updated for that amount of time.
If you want to wait for a certain amount of time to do something, you may use a Handler instead, so the UI can be updated in the meantime:
public void onClick(View v){
img.setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable() {
public void run() {
failure.start();
}
}, 1000);
}
You're putting your UI thread to sleep, which causes your app to freeze and the image not showing up.
A better approach for this is to use a handler, which will execute code after a delay and will not freeze your UI.
A quick adaption of your clickListener would be:
bButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
img.setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
failure.start();
}
}, 1000);
});
}
I want to show other image in ImageView within 3 second, after that rollover old image. The code:
OnClickListener oc = new OnClickListener() {
#Override
public void onClick(View v) {
ImageView iv = (ImageView)v;
iv.setImageResource(img2_id);
SystemClock.sleep(3000);
iv.setImageResource(img1_id);
}
}
myImageView.setOnClickListener(oc);
But it doesn't work? So, am I doing something wrong?
You are blocking the UI thread. Thus during the sleep command, the screen won't refresh. What you need is to schedule a non-blocking delayed call to a function which changes image resource. Here is a modified code that would do such a thing:
Handler mHandler = new Handler(); /*handler declared in your Activity thread, I assume*/
OnClickListener oc = new OnClickListener() {
#Override
public void onClick(View v) {
ImageView iv = (ImageView)v;
iv.setImageResource(img2_id);
mHandler.postDelayed(new Runnable(){
public void Run(){
iv.setImageResource(img1_id);
}
},3000);
}
}
myImageView.setOnClickListener(oc);
I want to make a view disappear (to be gone) when the user pushes a button.
I can make it inside the onCreate() method (main UI thread) by doing:
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
However, I want to be able to do the same thing inside another thread (out of the main UI thread). I tried to put the above live in my thread and it didn't work.
Thank you in advance!
## EDIT ####
To make myself more clear, I want to do something like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.teste_aba_3);
botao_tab_musica.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
...
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
}
});
}
However, that DOESN'T work! How can I fix this?
theres a neat method called runOnUiThread
runOnUiThread(new Runnable() {
#Override
public void run() {
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
}
});
edit: with your code
botao_tab_musica.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
runOnUiThread(new Runnable() {
#Override
public void run() {
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
}
});
}
});
I'd suggest either using View.post(Runnable), View.postDelayed(Runnable, long), a Handler, or an AsyncTask to do this for you.
There are some good examples on how to post to the UI thread in these scenarios:
http://developer.android.com/resources/articles/painless-threading.html
I think one possibility is to pass a reference to your activity to that other thread so that your thread can access the findViewById method.
try this
findViewById(R.id.llLoadingGallery).post(new Runnable()
{
#Override
public void run()
{
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
}
});
or create AsyncTask