My application processes some images in a thread and meanwhile it shows a dialog with a progressbar.
progressDialog = new ProgressDialog(Activity.this);
progressDialog.setProgressStyle(size);
progressDialog.show();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
for(int i = 0; i < size; i++{
//process images
runOnUiThread(new Runnable() {
#Override
public void run() {
progressDialog.setProgress(i);
}
});
}
}
});
thread.start();
Now, if a rotation occurs I do
#Override
protected void onDestroy() {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
super.onDestroy();
}
and in onCreate I recreate the dialog. But the thread don't changes the progressbar of this new dialog.
After rotation the Activity (or the Fragment) in which this dialog is shown is discarded and a new one is created.
You re-create the dialog in the new Activity, but the thread you started updates the dialog in the old one (the fact that this thread has a reference to the old dialog also constitutes a memory leak).
I'd suggest that you do the following:
Implement some ImageProcessor class and put an instance of it in Application object.
Make ImageProcessor class observable and notify the listeners about the progress.
Get a reference to this object in Activity and subscribe to notifications in onStart() (and unsubscribe in onStop()).
When status update notifications arrive - update the progress indication
Using this approach you'll have the processing logic encapsulate in a special object that survives rotation, and different components can invoke its methods and subscribe to notifications from it.
Related
I used HandlerThread and then used its looper to create a new Handler so that it can run operations on a non-UI thread. In the runnable which is posted to the handler, I added Toast messages to be displayed. I expected that to cause a problem as you can't touch UI components from the non-UI thread, but it still works and that toast is still being shown. Can anyone explain why toast is being displayed from the non-UI thread?
//Inside a Fragment class
private Handler handler;
private HandlerThread mHandlerThread = null;
public void startHandlerThread() {
mHandlerThread = new HandlerThread("HandlerThread");
mHandlerThread.start();
handler = new Handler(mHandlerThread.getLooper());
}
private Runnable submitRunnable = new Runnable() {
#Override
public void run() {
//do some long running operations here
//Thread.sleep(2000);
//Check whether currentLooper is the Main thread looper
boolean isUiThread = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
? Looper.getMainLooper().isCurrentThread()
: Thread.currentThread() == Looper.getMainLooper().getThread();
if (isUiThread) {
// You are on the UI thread
Log.d("Thread", "Main thread");
} else {
// You are on the non-UI thread
Log.d("Thread", "Not Main thread"); //This will be printed
}
Toast.makeText(getContext(), "toast is shown", Toast.LENGTH_SHORT).show();
}
};
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
handler.post(submitRunnable);
}
});
I checked Toast.java and saw that the looper initializes itself with Looper.myLooper().
if (looper == null) {
// Use Looper.myLooper() if looper is not specified.
looper = Looper.myLooper();
}
From the doc:
myLooper(): Return the Looper object associated with the current thread.
And the currentThread is the HandlerThread, not the main thread.
Hence, I am unable to understand how the toast is being displayed from the non-UI thread, or if it is something plain simple I am missing to see.
For showing Toast you used getContext() as a context.
getContext() - Returns the context the view is currently running in. Usually the currently active Activity.
While you are using fragment so it will take activity context where fragment will resides in activity.
That's why Toast shown.
I have an activity which runs a thread and also on that activity in the onBackPressed() void I have an AlertDialog which closes the activity.
The problem is that the thread is running after the activity was closed.
new Thread((new Runnable() {
#Override
public void run() {
//...
}
})).start();
#Override
public void onBackPressed(){
new AlertDialog.Builder(this)
.setTitle("Quit")
.setMessage("Are you sure you want to quit?")
.setNegativeButton("No", null)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent(rest.this, MainActivity.class);
finish();
Thread.currentThread().interrupt();
}
}).create().show();
}
This is a trick , which i used also
Solution 1:-
private boolean isActivityRunning=false;
then in onCreate() set it true
isActivityRunning=true;
then in onStop()
isActivityRunning=false;
And in the thread method use it like
new Thread((new Runnable() {
#Override
public void run() {
if(isActivityRunning){
//do your stuff
}
//...
}
})).start();
Solution 2:- this is better solution according to the usage
Use Handler
private Handler mHandler;
private Runnable mRunnable;
in call this where u want to use
mHandler = new Handler();
mRunnable = new Runnable() {
#Override
public void run() {
// do your stuff here
}
};
Also Override
#Override
protected void onResume() {
super.onResume();
if (mHandler != null) {
if(mHandler!=null){
mHandler.postDelayed(mRunnable, 1000);//time in miliseconds
/
}
}
}
#Override
protected void onPause() {
super.onPause();
if (mHandler != null) {
if(mHandler!=null){
mHandler.removeCallbacks(mRunnable);//time in miliseconds
}
}
}
And you should go through this why we prefer worker thread instead of thread
Difference between UI Thread and Worker Thread in Android?
Make a Boolean flag and run the thread while (flag).
Thread {while (flag) { Your thread code goes here. } }
Change the flag to False in the OnClick function.
Having threads in activities is not a very good idea. The activity lifecycle does not mention that explicitly, but in my experience, the process might not finish and activity instance might not always be garbage collected once you call finish. Also, there are scenarios in which you might want to stop your background operation that are not related to you pressing "back".
Also note, that Thread.currentThread().interrupt() does not signal interrupt to the thread you started, but the to the current thread. Interrupt also does not guarantee that the thread function will finish. In order to interrupt() a thread, you need a reference to it.
To be clear: I advise against such approach. Use services for such processing or at AsyncTasks. Without knowing what you want to achieve, it is hard to say what is the best approach.
If you must stick with your thread-inside-activity idea, I suggest you signal to the thread function that it should exit. For thread signalling, you might want to check the following resources:
https://docs.oracle.com/javase/tutorial/essential/concurrency/
http://tutorials.jenkov.com/java-concurrency/thread-signaling.html
https://stackoverflow.com/a/289463/22099
https://stackoverflow.com/a/6859855/22099
i am writing here to ask you a litlle question about the progress dialog.
In fact, i call web service in my application, the progress dialog works very well.
But when i change the date of my object to do the call of my web service,
the progress dialog appear, the results are receiveid. AND automatically, there is a new call of the web service and the progress dialog didn't dissmiss :-/
On my server JBoss, i can see the calls from my application.
At the first create of my activity i have one call.
But at the second call, when i changed the date, there are two call. i don't know why because when i change one of my spinner its works fine, the web service is call and returns the results.
I call the web service in a asynctask. I do nothing with the progress dialog in the asynctask.
Here is the method where i call the progress dialog
public void callWebService()
{
myProgressDialog = ProgressDialog.show(OverviewMoney.this,"", "Récupération liste des positions cash",true);
final Runnable runInUIThread = new Runnable() {
public void run() {
setListView();
}
};
new Thread() {
#Override public void run()
{
loadListMoney();
myProgressDialog.dismiss();
uiThreadCallback.post(runInUIThread);
myProgressDialog= null;
}
}.start();
}
The method loadListMoney call my web service in a asynctask and the method setListView put my resultList in an adapter to print the list on a list view.
Thanks in advance :-) (And sorry for my english)
Try this
public void callWebService()
{
if (myProgressDialog != null) myProgressDialog.dismiss();
myProgressDialog = ProgressDialog.show(OverviewMoney.this,"", "Récupération liste des positions cash",true);
final Runnable runInUIThread = new Runnable() {
public void run() {
setListView();
}
};
new Thread() {
#Override public void run()
{
loadListMoney();
myProgressDialog.dismiss();
uiThreadCallback.post(runInUIThread);
myProgressDialog= null;
}
}.start();
}
According to my understanding, when this function is called twice 1st time it will start progress dialog and quickly another function call is placed so without dismissing previous progress dialog you were creating one more progress dialog. So that was the main cause of it.
i am using a custom listview with images in my app
and loading all data from a json url.
i have created a onscrollistener()
which automatically add data below the current data when user scrolls to the bottom of the listview.
But when my data is loading whole listview freezes for 2-3 sec.
I dont know whats wrong??
here is my code for AsyncTask
private class BackgroundLoadMore extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
// Showing progress dialog before sending http request
}
protected Void doInBackground(Void... unused) {
runOnUiThread(new Runnable() {
public void run() {
LoadData();
list.setOnScrollListener(new EndlessScrollListener());
}
});
return (null);
}
protected void onPostExecute(Void unused) {
// On completing background task
// closing progress dialog etc,.
}
You must be fetching the json data from url in main UI thread. This blocks the UI from being updated by system, and hence the freeze. Use AsyncTask to do such network tasks in background.
LoadData() should be called in a background thread, which is asynctask's doInBackground(). Your call runOnUIThread puts it back on the UI thread, and that you dont want. remove the runOnUIThread call from asynctask.
protected Void doInBackground(Void... unused) {
LoadData();
return (null);
}
protected void onPostExecute(Void unused) {
// On completing background task
// closing progress dialog etc,.
list.setOnScrollListener(new EndlessScrollListener());
}
Move LoadData(); out of
runOnUiThread(new Runnable() {
public void run() {}
};
doInBackground is performing in the worker thread, but in it you use runOnUiThread wich start a UI therad operations.
You have to load all the data from net in background thread and then in postExecute update your listview
You are probably loading your data inside the UI thread.
Since operations such as loading JSON data from the internet are slow (from 0.5-10 seconds is typical) then if you do that inside the main thread of your app, the user interface will stop responding for this time. You should use a Thread or AsyncTask to load the JSON data asynchronously and so keep the UI thread free to respond to user input (such as scrolling the list).
My suggestion is that you use an AsyncTask to load the data.
Here are some links:
AsyncTask documentation
AsyncTask example
See the accepted answer for this question to see an implementation
Edit
Put list.setOnScrollListener(new EndlessScrollListener()); and list.notifyDatasetChanged(); inside onPostExcecute();
I have a long running method that is called during onCreate, this method populates textviews so interacts with the UI, and updates maybe 70 labels (about 3-20 seconds depending on device).
I want to display a progressdialog as this method executes.
Ideally I want to fire my method on the UI thread once the Activity has been displayed and the progress is displayed, this I cannot do, the Activity won't paint until the method has finished.
I hoped to find an event which was fired after the activity was displayed, and I found the one below, but it still leaves the screen black until the method has finished.
#Override
public void onPostCreate(Bundle savedInstanceState)
I am normally a WP7 developer, and in .NET you add an event handler for onLoadComplete which is fired after the ui is displayed, but before the user has a chance to interact withthe UI, how do I do this in Android JAVA?
Thanks
Put a ProgressBar in the View.
Then in the onCreate() or onResume() method do this:
new Thread() {
public void run() {
yourLargeMethod();
}
}.start();
Now you can do this inside your method to update the progressBar
public void yourLargeMethod() {
// doSomething
...
runOnUiThread(new Runnable() {
public void run() {
// update the progress from the thread
progressBar.setProgress(x); // x is your progress, 0 <= x <= progressBar.getMax()
}
});
}