Update GUI when a boolean in a class changes - android

I'm trying to have my Activity update an ImageView whenever a boolean value changes in another class.
Therefore i have some sort of timer that starts on my first onCreate()
private void startTimer() {
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 2000); // first run after 2 secs
}
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
connectionControl.checkNetworkState();
System.out.println("online?: " + connectionControl.isOnline());
mHandler.postDelayed(this, 15000); // repeat every x/1000 secs
}
};
My class ConnectionControl has a boolean, that gets set to either true or false whether my app can reach a specific http-host.
Now my question is: how can i achieve a automatic change on the ImageView in my Activity to display the boolean's value?
I already looked at Observer-Pattern, EventListener, BroadcastReciever but I'm stuck at finding the right solution.
Also, i want to use the listening in other Activities.
What has worked so far was starting/stopping the timer-thing for each activity and have the ImageView update inside the run() method. But my guess is, there has to be a way around the redundancy.

I think you are in search of AsyncTask , by using it you can made a call to webservice asynchronously and after the successful call to the webservice, we can process the fetched data.
Just go through this article: http://developer.android.com/resources/articles/painless-threading.html.
I think you needs this: (from android.com)
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
For more info about AsyncTask , here is link: http://developer.android.com/reference/android/os/AsyncTask.html

Here is how I did it.
I made a layout file for the custom titlebar thing, that has an ImageView.
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<ImageView android:id="#+id/title_statusimg"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="#drawable/connected" android:layout_centerVertical="true"
android:layout_alignParentRight="true" />
</RelativeLayout>
</merge>
I include this in every activities layout file i want to use it.
<include layout="#layout/titlebar" />
Also, i made a StatusActivity that extends Activity. It receives a Broadcast that is send from whenever an background thread detects a change in the connectivity to the webserver.
public class StatusActivity extends Activity {
private String CONNECTION_STATUS = "app.CONNECTION_STATUS";
private ImageView conn;
#Override
protected void onResume() {
super.onResume();
IntentFilter iFilter = new IntentFilter(CONNECTION_STATUS);
registerReceiver(mBroadcastReceiver, iFilter);
conn = (ImageView) findViewById(R.id.title_statusimg);
if (ConnectionControl.isOnline()) {
conn.setImageResource(R.drawable.connected);
} else {
conn.setImageResource(R.drawable.nconnected);
}
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mBroadcastReceiver);
}
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (ConnectionControl.isOnline()) {
conn.setImageResource(R.drawable.connected);
} else {
conn.setImageResource(R.drawable.nconnected);
}
}
};
}
Every Activity that shall use this Status-Image extends StatusActivity instead of Activity.
Hope i covered everything, atleast for me it's working.

Related

Android change button text takes a long time

For my app I want to disable/change a specific button that is pressed.
I have an onclick method called btnClicked which simplified looks like this:
Public class MainActivity extends Activity{
Button myBytton;
#Override
protected void onCreate(Bundle savedInstanceState) {
myBytton = (Button)findViewById(R.id.buttonCall);
}
public void btnClicked(View view)
{
myBytton.setText("loading");
myBytton.setEnabled(false);
myBytton.setClickable(false);
// Do a call to an external api
callApi();
}
public void callApi(){
// run querys
if(succesullyCalledApi){
vibrator.vibrate(500);
// I tried commenting out the below part,
// it is than visible that the phone vibrates before it
// has changed the text (atleast a quarter of a second).
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
}
}
}
In the callApi method is a vibrate method which vibrates after the function gets a result.
Also if there is a result in the callApi myButton will be enabled and the text changed to search.
What happens is the following:
I click on the button, the phone vibrates first and afterwards it changes its text.
my question.
Why did callApi / vibrate run before myBytton.setText ?
what NigelK said is true.
When you arrive in the btnClicked method all the instructions are made on the UI thread. Therefore when you ask the System to vibrate, it will be blocked for XX time depending on the time you passed to the method vibrator.vibrate(XX);.
In order to avoid this "freeze" you need to make the vibration on another Thread.
Here is what it will look like :
Public class MainActivity extends Activity
{
Button myBytton;
#Override
protected void onCreate(Bundle savedInstanceState)
{
myBytton = (Button)findViewById(R.id.buttonCall);
}
public void btnClicked(View view)
{
myBytton.setText("loading");
myBytton.setEnabled(false);
myBytton.setClickable(false);
// Do a call to an external api
callApi();
}
public void callApi()
{
// run querys
if(succesullyCalledApi)
{
// here you create and run the Thread.
// put anything you want to do inside the run method
new Thread(
new Runnable()
{
public void run()
{
// here you start the vibration
vibrator.vibrate(500);
}
}
).start();
// I tried commenting out the below part,
// it is than visible that the phone vibrates before it
// has changed the text (atleast a quarter of a second).
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
}
}
}
And that's it. It will launch another Thread that will handle the vibration and not freeze your UI thread.
EDIT
Here is the AsyncTask version :
The three elements asked when you extend AsyncTask are :
The type of the parameters you pass to the doInBackground() method
The Type of the elements that are passed in the onProgressUpdate() method.
The Type of the element returned by the doInBackground() method that is also the parameter of the onPostExecute() method.
This is what it looks like :
public class MyTask extends AsyncTask<Void, Integer, Boolean>
{
private Button mButton;
public MyTask(Button button)
{
mButton = button;
}
// Here everything will run on a background Thread
protected Boolean doInBackground(Void... voids)
{
boolean succesullyCalledApi = false;
// do your long querys here
// ...
return succesullyCalledApi;
}
// Here everything will run on the UI Thread
protected void onProgressUpdate(Integer... progress) {
// here you can make some update to the UI like updating a
// progress bar
}
// Here everything will run on the UI Thread
protected void onPostExecute(Boolean succesullyCalledApi)
{
if(succesullyCalledApi)
{
mButton.setText("search");
mButton.setEnabled(true);
mButton.setClickable(true);
// here you start the vibration
vibrator.vibrate(500);
}
}
}
And in your callApi() method you only have to to this :
public void callApi()
{
new MyTask(myButton).execute();
}
EDIT 2
In order to retrieve the query back to your main Thread (or UI Thread) all you have to do is ... nothing.
You are in the UI Thread when the onPostExecute() method is called.
But I assume that you want to retrieve the query back to your MainActivity. To do so :
Pass MainActivity in parameter of MyTask constructor,
Create a method in MainActivity named processQuery() (or whatever you want),
Finally call this method in the onPostExecute() method.
Here are some snippets :
Public class MainActivity extends Activity
{
Button myBytton;
...
public void callApi()
{
// add this to the constructor
new MyTask(this, myButton).execute();
}
// I put String here but adapt it to your query Type.
public void processQuery(String query)
{
// process your query here.
}
}
public class MyTask extends AsyncTask<Void, Integer, Boolean>
{
private Button mButton;
private MainActivity mMainActivity;
public MyTask(MainActivity mainActivity, Button button)
{
mButton = button;
mMainActivity = mainActivity;
}
...
// Here everything will run on the UI Thread
protected void onPostExecute(Boolean succesullyCalledApi)
{
if(succesullyCalledApi)
{
// process your query
mMainActivity.processQuery("THE QUERY YOUR WANT TO PROCESS");
mButton.setText("search");
mButton.setEnabled(true);
mButton.setClickable(true);
// here you start the vibration
vibrator.vibrate(500);
}
}
}
There probably is a better way to do this but this one is simple and work :)
Hope it helps.
Cheers
This is because your call to the API is being done on the UI thread. Even though you have made changes to the UI, the screen won't refresh until the processing invoked from the button clicked event completes. Call your API on a new thread or via an Async Task to get the behaviour you want.
Because you are doing all stuff at the UI Thread. You must use an AsyncTask for your long running operations.
Try below implementation:
public void callApi() {
MyTask myTask = new MyTask();
myTask.execute();
}
private class MyTask extends AsyncTask<Void, Void, Boolean> {
protected void doInBackground(Void... params) {
// This runs on a separate background thread
boolean succesullyCalledApi = false;
// run querys
// do your long running query here and return its result.
return succesullyCalledApi;
}
protected void onPostExecute(Boolean succesullyCalledApi) {
// this runs on UI Thread
if(succesullyCalledApi){
vibrator.vibrate(500);
myBytton.setText("search");
myBytton.setEnabled(true);
myBytton.setClickable(true);
} else {
// You should better think this part also. what will happen if result is false?
}
}
}

How can i know when the async task has returned

I have an async task that loads image urls from server.After loading urls than i load the images one by one through another asynctask.
On the click of a button i start the first asynctask
public void getit(View v)
{
new getdata().execute("http://10.0.2.2/geturls.php");
// line no 2
}
After i get the urls i use another async task to load images.
How can i find out when the image urls have been loaded and i can call the second async task at line no 2.
if i use a boolean variable which i toggle in the onpostexecute
#Override
protected void onPostExecute() {
urlgot=true;
}
then i shall have to use some repeating loop inside getit method at line no 2 to check the status of this variable urlgot. but it may take more time than allowed for ui thread.
Can there be a more cleaner method to do this check.
thanks
There are two solutions I can think of:
1) You create one AsyncTask that does everything (getting the urls, and downloading all images). Than you know exactly when to start downloading the images.
2) You start the next AsyncTask from the onPostExecute() of the first AsyncTask.
You won't be able to do your next piece of work in //line no 2 without defeating the purpose of AsyncTask. If you're doing network activity, you need to be doing it asynchronously, so that's not an option.
Instead, in onPostExecute() you can call another method in your activity that does what you would have done in //line no 2. This is safe to do, because onPostExecute() happens on the UI thread.
But depending on your design, it might make more sense to do all the //line no 2 stuff in your original AysncTask in onPostExecute, so you only have one task doing all of the work.
Use a Handler. In the method onPostExecute of your AsyncTask you can send a message informing the Handler to start another AsyncTask.
Something like this:
#Override
protected void onPostExecute(Void res) {
MyHandlerHandler handler = new MyHandlerHandler();
Message msg = new Message();
msg.what = MyHandler.TASK_FINISHED;
handler.sendMessage(msg);
}
And in your Handler class:
public class MyHandlerHandler extends Handler {
public static final int TASK_FINISHED = 2;
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TASK_FINISHED:
new MyAsyncTask().execute();
break;
}
}
}
instead of putting line 2 in getIt, put it in onPostExecute like below :
public void getit(View v)
{
new getdata().execute("http://10.0.2.2/geturls.php");
}
#Override
protected void onPostExecute() {
// line 2
}
I use a custom interface to do stuff after execution.
public Interface OnDataReceived
{
public void onReceive( Object result);
}
and on MyASyncTask
public class MyAsyncTask extends ASyncTask<Object,Object,Object>
{
OnDataReceived receiver;
public MyAsyncTask( OnDataReceived receiver )
{
this.receiver = receiver;
}
...
protected void onPostExecute( Object result)
{
receiver.onreceive( result );
}
}
and let my main class implement OnDataReceived
public class Main implements OnDataReceived
{
....
public void getit(View v)
{
new MyAsyncTask(this).execute("http://10.0.2.2/geturls.php");
}
#override
public void onReceive( Object result)
{
// do whatever
}
}
EDIT
Even for more control you can add onFailed and rename your interface to OnResponse
public Interface OnResponse
{
public void onReceive( Object result);
public void onFailed( Object errcode);
}

android asynchronous get foreground activity

Is there a (thread safe) method I can call from Java on Android which
will give me a handle to the foreground activity from a background task?
I would like to be able to do so in order to safely post toasts on top
of the top window.
Thanks.
You wouldn't necessarily have to get a handle to the foreground UI activity to show a toast message. You can do this from a background thread like this:
runOnUiThread(new Runnable() {
public void run() {
// make toast, show toast
}
});
Create your own class that extends AsyncTask and pass the Activity as one of the parameters:
public class MyAsyncTask extends AsyncTask<String, String, String>{
Activity mActivity;
public MyAsyncTask (Activity mActivity){
this.mActivity = mActivity;
}
#Override
protected String doInBackground(String... urls) {
return null;
}
#Override
protected void onPostExecute(String result) {
}
}
Doing that should allow you to access the Activity (assuming it's still the foreground throughout; which may not necessarily be the case).
If there's a chance that it's not the foreground Activity, you may want to just use the:
runOnUiThread(new Runnable() {
public void run() {
// Do UI stuff here
}
});
However, that isn't necessarily thread-safe

Best practices to handle multiple presses on buttons

I have found as a common issue in any of my apps that the user may accidentally perform several clicks on the same button causing multiple executions of the onClick listener logic. Typically, my business logic for those onClickListeners usually consists of launching of a heavy-process AsynTask that performs an HTTP request and later modifies the UI.
My way to prevent multiple executions of the asyntask was to unable the button at the beginning of the listener method and enable it again as a first statement of the onPostExecute. That has generally worked for me or at least I have not received any issue regarding to this situation.
Recently, a colleague has pointed me a potential problem of this unable-enable-button method. As shown in the below code that consists of two buttons '+' and '-', quick and alternative presses on those buttons causes a crash the application by an ArrayOutOfIndex exception.
That fact has made me think about my way of managing the concurrency of the onClickListener events and if it is really possible to have the situation in which a second asyntask may be launches prior to the finalization of the first asyntask using the aforementioned method.
What are your suggestions to handle this situation?
For those suggestions that recommend to apply some logic rejecting the second launches of the asyntask until the completion of the first asyntask, is it is really worth to generally apply that logic for a common used application in which the buttons perform an http request?.
CrashActivity.java
public class CrashActivity extends Activity {
private int mNumbers[] = { 1, 2, 3, 4, 5 };
private int position = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final TextView result = (TextView) findViewById(R.id.resultTextView);
final Button minusBtn = (Button) findViewById(R.id.minus_button);
final Button plusBtn = (Button) findViewById(R.id.plus_button);
minusBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
minusBtn.setEnabled(false);
plusBtn.setEnabled(true);
result.setText("" + mNumbers[--position]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
minusBtn.setEnabled((position > 0));
}
});
plusBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
plusBtn.setEnabled(false);
minusBtn.setEnabled(true);
result.setText("" + mNumbers[position++]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
plusBtn.setEnabled((position <= 4));
}
});
minusBtn.setEnabled(false);
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="#+id/minus_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="-" />
<Button
android:id="#+id/plus_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="+" />
<TextView
android:id="#+id/resultTextView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="hello stackoverflow!" />
</LinearLayout>
A solution that would guarantee that both methods do not try to update the UI at the same time would be to synchronize the UI update code. You could either create a synchronized method or an Object that acts as a lock for a block of code. Example:
public synchronized void updateUI() {
// ...
}
or
private Object mLock = new Object();
public void updateUI() {
// ...
synchronized (mLock) {
// Critical code here.
}
// ...
}
Expanding this to make sure that task 1 completes before task 2, completes before task 3, etc., you would need to somehow keep track of which started first. Note: synchronization occurs on the UI thread in this example.
private List<AsyncTask> mRunningTasks = new ArrayList<AsyncTask>();
public void onClick(View v) {
v.setEnabled(false);
if (v == task1View) {
Task1 task = new Task1();
mRunningTasks.add(task);
task.execute();
}
else if (v == task2View) {
Task2 task = new Task2();
mRunningTasks.add(task);
task.execute();
}
// ...
}
// Copy and paste for each necessary task.
private Task1 extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... v) {
// Run task 1 code here.
while (this != mRunningTasks.get(0)) {
wait(1000);
}
return v[0];
}
protected void onPostExecute(Void v) {
updateUI();
mRunningTasks.remove(0);
task1View.setEnabled(true);
}
}
public void updateUI() {
// UI update code here.
}

get notified when requestSync function completed

im trying to start the calendar sync programatically using this code
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_FORCE, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
ContentResolver.requestSync(accounts[0], "com.android.calendar", bundle);
i want a way so i can know when sync complete so i can read data from the calendar
i tried doing this
while (ContentResolver.isSyncActive(accounts[0], "com.android.calendar")) {
System.out.println("looping: " + i);
}
readLocalCalendar();
readLocalEvents();
but the system exit the loop before the sync ends and i can still see the sync sign at the status bar, so any help so i can read calendar events after sync completle done ??
thanks
Another option would be to register a broadcast receiver to tell you when the sync is finished like this:
public class UpdateableActivity extends Activity {
public static final String ACTION_FINISHED_SYNC = "your.package.ACTION_FINISHED_SYNC";
private static IntentFilter syncIntentFilter = new IntentFilter(ACTION_FINISHED_SYNC);
private BroadcastReceiver syncBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// update your views
}
};
#Override
protected void onResume() {
super.onResume();
// register for sync
registerReceiver(syncBroadcastReceiver, syncIntentFilter);
// do your resuming magic
}
#Override
protected void onPause() {
unregisterReceiver(syncBroadcastReceiver);
super.onPause();
}
}
Inside your SyncAdapter use this when done:
getContext().sendBroadcast(new Intent(UpdateableActivity.ACTION_FINISHED_SYNC));
You can also use this for when starting or updating the status of the sync ;)
Update: Updated the code to avoid leaks and making sure the activity is still active (onResume/onPause)
using the addStatusChangeListener actually worked for me .
here's a reference .
don't forget to add the needed permissions .
Use ContentResolver.addStatusChangeListener (int mask, SyncStatusObserver callback) to get notified of changes in sync status. docs
Please do not loop forever, its really bad design. Using the above method everything is asynchronous so you don't waste any cpu cycles.
You could also use ContentResolver.registerContentObserver (Uri uri, boolean notifyForDescendents, ContentObserver observer) docs to get notified in changes on a specific URI (like the calendar's URI)
try an AsyncTask :
private class CustomTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
// TODO sync your calendar
}
protected void onProgressUpdate(Void... progress) {
//TODO display a spinner or something else to show progress
}
protected void onPostExecute(Void t){
//TODO what you want when doInBackground has finished
}
}
Good luck !

Categories

Resources