Android User Interface Problems - android

I'm trying to get my user interface to react to events (like button presses, thread completions, etc.) in real time in Android (obviously).
I have a button layout, and one of the buttons is used to copy an unknown number of files from a remote computer using FTP. The FTP part of all this is working very well, but I just cannot find a way to let the user know the state of things:
The states, as I see them are:
1) Selected "Download Files" from "normal" menu.
2) Pressed Confirm (the download process may be quite lengthy and perhaps I don't want to select it by mistake -- however now it's a separate thread so may need to re-think that.
3) Downloading
4) Download complete, restore normal menu
One of the things I hoped would work would be to run the FTP code in a separate thread, and by using the thread.isAlive() construction, wait for the thread to complete and change the display accordingly.
The only thing I haven't been able to do is display that files are downloading. Regardless of what I try, the display jumps from the "Confirm Download" view to the "Normal Menu" view. (Please note, these are not Views as Android defines them in any way.)
Code follows:
Btn.setOnClickListener (new View.OnClickListener()
{
#Override
public void onClick (View v)
{
hideTempWidgets();
Btn01.setVisibility (View.GONE);
Btn02.setVisibility (View.GONE);
Btn03.setVisibility (View.GONE);
verfBtn.setVisibility (View.VISIBLE);
verfBtn.setText ("Press to Verify");
verfBtn.setOnClickListener (null);
verfBtn.setOnClickListener (new View.OnClickListener()
{
#Override
public void onClick (View v)
{
runOnUiThread (new Runnable()
{
public void run()
{
verfBtn.setText ("Downloading...");
}
});
Thread temp = new Thread (new Runnable()
{
public void run()
{
try
{
FileTransfer.getFiles (getAddr().trim());
}
catch (SQLException e)
{ }
}
}, "ftp");
temp.start();
while (temp.isAlive());
verfBtn.setVisibility (View.GONE);
Btn01.setVisibility (View.VISIBLE);
Btn02.setVisibility (View.VISIBLE);
Btn03.setVisibility (View.VISIBLE);
alert (true, VIBE_BLIP);
}
});
}
});
Has anybody faced this and come up with a decent solution??
What's not happening is the setText to "Downloading...", or at least, if it does, not when I want it to and too fast to see... Finally, what's especially frustrating is that that command does work properly when I remove the code to run the FTP thread.
Thanks,
R.

This task seems to be perfect for Async Task. Basically Async Task is a Thread with a built in, thread safe component to allow you to publish updates to the UI and notify when the task is complete.
A popular idea is to show a spinner or progress bar, until the AsyncTask is complete, then dismiss it.
For more information about Async Task see: http://developer.android.com/reference/android/os/AsyncTask.html

I think your mistake is here:
temp.start();
while (temp.isAlive());
The code inside onClick() is already running on graphical Thread, so there is no need for a runOnUIThread() method and you should not block it with the while() statement, which in fact is blocking the UI thread till the Thread completes.
My advice: you can start an AsyncTask instead and update the UI accordingly.

Since it is a very long file transfer, I'd suggest you consider a service to do the work. If you use thread, user will be bound to keep your application on top of others. Otherwise your thread can be just killed. Worst of all, there is not much you can do to prevent user from going to home screen or other application (some tricks are possible but user will be pissed).
If you use service, you can always use notification to show progress and go back to your application.

Related

Toast inside thread during heavy workload task

I am updating a database (SQLite) with a lot of data, which takes several minutes. I am doing this on a Thread. At the same time I want to show the progression of the updates (25%, 50%, etc.) through a Toast. Since I am within a thread, I need to use the runOnUiThread() function to run the Toast. Like this:
getActivity().runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getActivity(),"Updating the database...", Toast.LENGTH_SHORT).show();
}
});
It was working fine when the workload was not to big. Now that it is, no toast is being displayed at all.
I have been looking for ways to set a very high priority to the toast's thread, without success. Maybe I can bypass using a thread for the toast in the first place?
Thank you for the insights!
EDIT: In fact, the toast is working, but is displayed after all the work has been done. I want to notify the percentage of the complete update to the user, so I need to have the toast displayed during the update and not after.
I tried removing the workload (no database update, only a Log.d inside the for loop of things to add to the database). And the toast are displayed after the loop is finished although the runOnUiThread() method is called inside the loop.
EDIT 2: I managed to do what I wanted after cleaning up the code and starting fresh. I posted the code I used as the answer below.
After having cleaned up the initial thread, I managed to have something working. I post the code here since it can be used as a template to do a specific task:
Run a initial thread that does tasks periodically for a given number of times (here collect some data). After this given number of times, the collected data is pushed to a database and the user is notified of the advancement of the process (which takes a long time).
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
long t0 = System.currentTimeMillis();
#Override
public void run() {
if(System.currentTimeMillis() - t0 > EXPERIMENT_DURATION ){
processData(dataArrayList); // custom tasks - see below
cancel();
}else {
dataArrayList = collectData(); // custom tasks
}
}
}, 0, INTERVAL);
With:
void processData(Arraylist<Data> dataArrayList){
for(Data data : dataArrayList){
// show progression to the user
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
int percent = Math.round(((float)dataArrayList.indexOf(data))/((float)dataArrayList.size())*100)
Toast.makeText(getActivity(),"Update: " + Integer.toString(percent) + "%", Toast.LENGTH_LONG).show();
}
});
updateDataBase(data);
}
}
I highly recommend you use an AsyncTask.
AsyncTask performs in another working Thread, but it offers a method that is executed in the UI Thread, publishProgress(String ...). that you then customize by overwriting onProgressUpdate(String ...).
EDIT: As read in the comments, AsyncTask is not that good of an option because:
All AsyncTask share a Thread (they don't run on workers as I expected)
For long running Tasks, all other AsyncTasks (system's and your's) will be on hold
This includes AsyncTasks from libraries

android while loop alternative

This is my first Android application and I am finding troubles with while loop, I am trying to use a while loop on my Android application but the application freezes.
What I'm trying to do is track the user location (using onlocationChanged) and keep querying on the location until the query returns a result. It's a GIS application so I am going to describe the application behavior:
the application keeps tracking the user position using a listener "onLocationChangedListener" and store it in a variable "myPosition". I am using a boolean"noResults=true". I will use a method "query(myPosition)" in the while loop, this method has a callback that when a result is found, and changes a boolean "noResults" to false. the loop will keep on until "noResults" is false (that means query's callback changed the boolean's value)
, here's what I did:
while(noResults)
{
//myPosition keeps changing
query(myPosition);
//query has a callback that when a result is found it changes noResults to false
}
I resolved the problem using a "Handler" that query the Feature Layer every 5 seconds, this stops the main thread from generating application not responding error:
Handler m_handler=new Handler();
Runnable m_runnable;
m_runnable = new Runnable(){
public void run() {
//query code here
m_handler.postDelayed(m_runnable, 5000);
}
};
m_handler.postDelayed(m_runnable, 0);
running while loop codes on the main thread freezes the UI, and makes all other processes pause making your app unresponsive use
Threads..
also note that the while loop you are running is running on a default Thread termed as the ui thread so in short run while loops on separate threads..
eg..
new Thread(new Runnable() {
#Override
public void run() {
// Your hard while loop here
//get whatever you want and update your ui with ui communication methods.
}
).start();
for ui communicating methods
View.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Toast.makeText(getActivity(), "updated ui", Toast.LENGTH_LONG).show();
}
});
the view could be any views you are updating..
also like #TehCoder said you could use asynctask but asynctask is not meant for long workaflow work there are 3 of them but i can't recall the last one
Maybe you should use an AsyncTask? I'm not quite sure what your problem is tho.
Loop is not a problem in android (or any language).
There are two scenario might be reason for your freezing,
If you run network call in api, android throw error and crashes. You have to do network related calls in Aysnc Task ot threading
Use try throw catch and exception cases to avoid app crashing and better coding skill.

How to get user feedback (e.g. from AlertDialog) inside AsyncTask/Background-Thread?

an Android 4+ app should perform a long running operation. This could be copying a million files from A to B for example. To not block the UI this operation runs in the background using an AsyncTask.
Assume that the operation needs some user feedback in the middle of the process to continue its work, e.g. "File XY already exists. Override, Irgnore or Rename?"
What is the best way to get this feedback from the user? Since the operation is running in a background thread one could not just present an AlertDialog (or something similar) since UI interaction is only possible in the main thread...
So for I came across these solution:
Ask for feeback before background threads starts, e.g. ask how to handle conflicts before starting to copy/move the files in the
background.
Do not handle conflicts but note them to ask the user
how to handle them after the operation is complete in a new
operation.
End the background operation on the first conflict, ask the user for feedback and continue a new background operation
I do not like any of these solutions. In the first case the user is asked for feedback even if there will be no conflict at all. The second solutions is not possible if the steps have to be processed in a specific order. The third solution would result in code that is very difficult to read/understand/maintain.
A good solution would be:
Stop the background thread
Marshal to the UI thread and get feedback from the user
Resume background thread and use feedback to continue the operation
Using GCD in Objectiv-C/iOS or async/await in C# this is not a big problem. But how can this be done in Android using AsyncTask?
Meanwhile I thought, that I found an answer here: Simply run myActivity.runOnUiThread(...) within doInBackground(...) and wait for it. Sounds good, but it does not work. The AsyncTask/background thread does NOT wait for the Runnable to finish:
private void copyFiles() {
CopyTask copyTask = new CopyTask (this);
copyTask.execute();
}
private class CopyTask extends CustomAsyncTask<Void, Void, Void> {
private doCopy;
#Override
protected Boolean doInBackground(Void... params) {
// Custom code, e.g. copy files from A to B and check for conflict
for (File file : allFiles) {
doCopy = true;
if (isConflict(file)) {
// Stop current thread and ask for user feedback on UI Thread
Runnable uiRunnable = new Runnable() {
public void run() {
// Pos 1. --> Execute custom code, e.g. use AlertDialog to ask user if file should be replaced...
doCopy = false;
synchronized (this) {
this.notify();
}
}
});
synchronized(uiRunnable) {
// Execute code on UI thread
activity.runOnUiThread(uiRunnable);
// Wait until runnable finished
try {
uiRunnable.wait();
}
catch (InterruptedException e ) {
e.printStackTrace();
}
}
}
// Pos 2. --> Continue work
if (doCopy)
copyFromAToB(File);
}
return null;
}
}
I would expect, that when a conflict is detected the Runnable is executed and and Pos 1 (code inside Runnable to resolve conflict) is executed BEVOR Pos 2 is reached. This is not the case. The Runnable is executed correctly but the AsyncTask does not wait for it to finish. The execution of doInBackground is continued without any interruption. It seems that doInBackground and the Runnable are executed in parallel (not suprising since they are executed on different threads) but why does doInBackground not wait?

How can I keep UI elements responsive while my program is working?

The UI I am creating includes a button, which once pressed, is supposed to find and display values repetitively until the user presses the button again.
This is my button's initialization with the listener:
pollOn = false;
pollButton = (Button) findViewById( R.id.pollButton );
pollButton.setOnClickListener( new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
Log.i( "POLL BUTTON", "onClick" );
if( !pollOn ) {
Log.i( "POLL BUTTON", "!pollOn" );
pollOn = true;
methodToReadAndDisplayData();
} else {
Log.i( "POLL BUTTON", "else pollOn" );
pollOn = false;
}
}
});
It is not shown here to make things easier to read, but the way I currently have it working is to call the methodToReadAndDisplayData() 5 times in a for loop and then stop.
So when I press the pollButton initially, the UI freezes and Log.i("POLL BUTTON", "onClick") is displayed at the top of LogCat. I then press the pollButton while it appears to be frozen, and after all of the work is done and the UI unfreezes, Log.i("POLL BUTTON", "onClick") is shown at the very end of my LogCat. The way I am interpretting this is that the button click is entered into a queue, and the onClick method is called after all 5 iterations of the for loop have been completed.
Another large part of this problem is that the work being done is mainly in a native library, and is called using the NDK. I am unsure if this has anything to do with the UI freezing, but it definitely increases the processing time needed.
If anything is unclear or confusing please let me know and I will try to clarify things.
Any information on this situation would be greatly appreciated.
Doing some stuff while keeping the UI responsive can achieved by many methods, two of them are very common. Async Task and threads.
AsyncTask:
It's a class which have a few methods which help you to do time consuming work while updating the UI at the same time. For Example: If you want to search a specific word in a big document, do the reading and searching stuff in doInBackground() (a method of AsyncTask class) and you can display a progress bar and notify the user with your progress with onProgressUpdate(). This gives your app more professional feel and at the same time helps you executing time consuming task in background.
Threads:
They are simple runnable threads. You can execute non UI stuff with these threads but you can't perform UI tasks in them (as UI is not thread safe). For dealing with UI in these threads, you will have to use Handlers, which is quite cumbersome for beginners to get a grip and understanding of them.
So depending on your needs and app you can choose the one best suits you.
Because you're running on the main thread the system UI freezes, you'd be better off running in a new thread which would leave your UI running as normal. Below is an example of how to run your method in a new thread.
new Thread(new Runnable() {
#Override
public void run() {
methodToReadAndDisplayData();
}
}).start();
Note that if your method interacts with the UI in any way, you need to do it via the following:
When you want to update your UI:
Handler threadHandler = new Handler();
threadHandler.post(updateRunnable);
Update your UI here:
Runnable updateRunnable = new Runnable() {
public void run() {
example.setText(newValue);
}
};
Try that and see if there's any improvement. I've not worked with the NDK, just the standard SDK so there may be difference I'm unaware of.
Do it on another thread. Use an AsyncTask to call methodToReadAndDisplayData.

How to implement the javascript setTimeout function in Java

I am currently trying to integrate a live search functionality in android. I use a customized Autocomplete widget. The widget itself provides me with a threshold to only start a query after a certain amount of characters have been typed in. But what I also want is that a query only starts, say if a user has stopped typing for 2 seconds.
As I load my contents with a AsyncTask I tried blocking the doInBackground function by calling Thread.sleep() right at the beginning. If the user would then continue typing the program would look after an existing task, cancel it and start a new one. This was my idea. But it doesn't quite work the way I expected. Sometimes it sends out queries even if the user is still typing, sometimes queries are canceled after the users stopped typing.
Do you have any ideas on this or maybe a better way to solve this?
Here are the code snippets:
1. The function that is called on text changes:
public void afterTextChanged(Editable s) {
if(mWidget.enoughToFilter()) {
if(mTask != null && mTask.getStatus() != Status.FINISHED) {
mTask.cancel(true);
}
mTask = new KeywordSearchLoader(mActivity,
mItems);
mTask.execute(s.toString());
}
}
2. doInBrackground
try {
Thread.sleep(Properties.ASYNC_SEARCH_DELAY);
} catch (InterruptedException e) {
Log.e(TAG, "the process was interrupted while sleeping");
Log.w(TAG, e);
}
Thanks
Phil
Create a Handler and use .postDelayed(..) to run a background task after some delay.
If user presses the key then call handler.removeCallback(..) and then again .postDelayed(..) to add a new delayed callback.

Categories

Resources