How to trigger UI update on Activity when underlying datahandler receives data - java

I am creating an application which relies on data backend over Internet connection. This data is common between activities. Also, data will be kept refresh e.g. by updating it every two or three minutes. Therefore, I have created a data handler class, which should handle everything related to data downloading, parsing and such. Sometimes, it will receive command from Activity to refresh the data.
The problem is, how to refresh the UI in the activity when new data becomes available in the datahandler. If the datahandler was in activity, I could just post a handler for UI thread to update it. However, I don't know how to do it from this underlying class. Any advice?
Here's the rough code for three classes:
/**
* The data handler class itself
*/
public class DataHandler {
Object data;
public void getData () {
// starts a thread (HTTP query) to get data
}
private class dataReceived (Object data) {
// receives data
this.data = data;
// TRIGGER updateUI() in MyActivity! HOW?
}
}
/**
* Application class. I'm planning to keep data handler here, and reference it from activities when required.
*/
public class MyApplication extends Application {
private DataHandler dataHandler;
public DataHandler getDataHandler() {
return dataHandler;
}
public void onCreate() {
DataHandler dataHandler = new DataHandler();
}
}
/**
* Activity
*/
public class MyActivity extends Activity {
DataHandler dataHandler;
public void onCreate(Bundle o) {
dataHandler = ((MyApplication) getApplication()).getDataHandler();
}
public void onResume() {
// trigger data refresh (OBS! This could also be by a button press etc.)
dataHandler.getData();
}
public void updateUI() {
// UPDATE UI
// should be called on DataHandler when data is received. HOW?
}
}

Register your activity as a listener to the DataHandler and when the thread recieves data invoke any listeners.
How are you presenting the data? If it's a ListView with an ArrayAdapter, then simply invoke the notifyDataSetChanged() on it as you get an update.
You'll have to make sure you update the UI in an appropriate UI thread, of which there are many ways. One that springs to mind is you could create a blocking AsyncTask that receives the listener events, unblocks it's background thread and runs its publishProgress() to ensure the update happens on the UI.

Related

Activity collides with AsyncTask? How to implement AsyncTask into GUI?

I am sorry for my bad english skills. I'm new to programming/stackoverflow and try to create a little android quiz app. This app has to connect to a php server and login/getquestion...
The simplest example is the login. The user has to type in his data and then i have to connect.
To provide that the Gui doesnt freeze i have to use asynchronous tasks.
Here the activity's code:
public void login(final String username, final String password) {
final Activity a = this;
FutureTask t = new FutureTask(new Callable() {
public Object call() {
Connection.GetInstance(a).login(username,password);
afterLoginTry(username,password);
return null;
}
});
t.run();
}
This calls a method in another class, which calls another FutureTask which calls an AsyncTask. At the end there is always an public synchronized method such as afterlogintry(). This works but it's a bit slow and i think dirty code.
My main problem is that i don't know how to give results back through different layers of classes and especially to the activity without using hotfixes all the time.
Is there any good explanation or tutorial, which describes how to design such a construct?
Thx for help
The way you can pass AsyncTask results back to other classes, is by declaring callbacks for the task, that will then report the result to a listener. Here is how it works.
First, you must declare an interface in your AsyncTask which contains a method that will send out the result of the task. So in my example task below, my result is a String. The String gets passed to onPostExecute() when the task finishes its work. I then call my callback method on a registered listener, and pass that return value on to whoever is listening for it. You register a listener by passing in an instance of your callbacks from whichever class is creating the task.
public class MyTask extends AsyncTask<String, Void, String> {
MyTaskCallback listener;
public MyTask(MyTaskCallback listener) {
this.listener = listener;
}
protected String doInBackground(String... params) {
String input = params[0];
//do work
input += "did some work on this String";
return input;
}
//When the thread finishes its work, this gets
//called on the main UI thread
protected void onPostExecute(String result) {
listener.onResultReceived(result);
}
public interface MyTaskCallback {
void onResultReceived(String result);
}
}
So next we need to register a listener for these callbacks, so when the result comes in from the task, it will get reported directly to our class. So let's say we have a simple Activity. The way we register the callbacks is to use the implements keyword on our class declaration, and then to actually implement the callback method in the class itself. We then create our task, and we pass in this which is our Activity that implements the callbacks. A simple example Activity that does this looks like this:
public class TaskActivity extends AppCompatActivity implements MyTask.MyTaskCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
//we pass in "this" because our Activity itself
//implements the callbacks below.
MyTask myTask = new MyTask(this);
myTask.execute();
}
//Here we implement our callback method, so the task
//can send its results straight through here
public void onResultReceived(String theResult) {
Log.d("TASK RESULT", "Here is our result String: "+theResult);
}
}
Now, our task has our Activity connected to it, through the callbacks we passed into it. So now when our task gets a result, we can send it directly to our listener, which is our Activity, and the result will come right through to our implemented onResultReceived method.
Callbacks are a great way to pass information around between classes while also keeping everything very separated. Hope this helps!

Receive object from handler in other activity

I'm writing a simple chat application but i don't know how to use my main result handler from another activity. I'm setting resultHandler in main activity like this:
ResultHandler handler = new ResultHandler();
MGey.setUpdatesHandler(handler);
My ResultHandler.java has this code
public class ResultHandler {
#Override
public void onResult(ChatObject object)
{
//receive message object from server
}
}
Now in my other activity (Dialogs.java) I want to use this handler to get updates.
For example: if my onResult method receive new message object (ChatObject.NewMessage) I want to use this object in my Dialogs.java for new message notification etc.
if (object instanceof ChatObject.NewMessage)
//pass this object to Dialogs activity

Android how to update (UI thread) from other classes (really?)

you may know about Google Cloud Messaging
The problem is that when a gcm message triggers by the server, my application receives a bundle from google play services, this happen at GcmBroadcastReceiver.java. Here i can send this data to other classes in order to append some info from the server.. well. I got stuck when i try to update, for example, some views in the UI thread.
HOW I CAN DO THIS?
Imagine that MainActivity.java is the UI thread when i declare the views, etc.
I tried to create here a public static method which can be called directly by GcmBroadcastReceiver.java by this way: MainActivity.*updateUI*(args..), but it throws this exception:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Can anyone try to explain me this? i also know about asyncTask but i cant imagine how it works. I also find some pages explaining events that are fired by the UI thread it self like runnables that do some task in background. Im searching something like this:
MainActivity extends Activity{
...
protected void onCreate(Bundle blabla)..{
setContentView(R.layout.blabla);
registerSomeEvent(this);
}
private void handleEvent(Bundle ...){
... do stuff with the data provided in the UI thread
}
}
And here at GcmBroadcastReceiver, when gcm push some data, trigger that magic event in order to perform updates at the UI thread with some views like ListViews or TextView
One way is to use use LocalBroacastManager. For how to implement is, there is a great example on how to use LocalBroadcastManager?.
LocalBroadcast Manager is a helper to register for and send broadcasts of Intents to local objects within your process. The data you are broadcasting won't leave your app, so don't need to worry about leaking private data.`
Your activity can register for this local broadcast. From the GCMBroadcastReceiver, you send a local broadcast when you receive something in GcmBroadcastReceiver. Inside your Activity you can listen to the broadcast. This way if the activity is in the forefront/is active, it will receive the broadcast otherwise it won't. So, whenever you receive that local broadcast, you may do the desired action if activity is open. This is like saying to the activity that "Hey Activity, I've received a message. Do whatever you want with it".
If you want to do for the whole app, then you can make all your activities extend an abstract activity. And inside this abstract activity class you can register it for this 'LocalBroadcast'. Other way is to register for LocalBroadcast inside all your activities (but then you'll have to manage how you'll show the message only once).
You can use Handlers in your MainActivity in order to comunicate with UI Thread.
Communicating with the UI Thread
public class MainActivity extends Activity{
public static final int NEW_DATA_AVAILABLE = 0;
public static final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MainActivity.NEW_DATA_AVAILABLE:
String newData = msg.getData().getString(MyClass.DATA);
//Do some stuff with newData
break;
}
}
};
}
and in your non Activity class
public class MyClass implements Runnable{
Thread thread;
public final static String DATA = "new_data";
public MyClass(){
thread = new Thread(this);
thread.start();
}
#Override
public void run() {
while(true){
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
Message msg = mHandler.obtainMessage(MainActivity.NEW_DATA_AVAILABLE);
Bundle bundle = new Bundle();
bundle.putString(DATA, "We have received new data");
msg.setData(bundle);
MainActivity.handler.sendMessage(msg);
}
}
}

android waiting for response from server

say I want to perform an Http request from the server, this process takes time.
now because of this, the http request needs to be run on a different thread (AsyncTask, Runnable, etc.)
but sometimes I just need the response when I ask for it, in order to update the UI
using Thread.sleep in a loop to wait for the response is not good performance wise
example: I want the user's name, I ask the server for it, and I have to wait for it
now the activity calls the UserManager that calls the serverHandler that performs the operation and returns the result back
maybe an event system is in order, but I'm not sure how to do this in my scenerio
and quite frankly I am really confused on this issue
please help someone?
This can most definitely be done w/ AsyncTask... Handle the network request in doInBackground() and once doInBackground() is finished, onPostExecute() is triggered on the UI thread and that's where you can execute any code that will update UI elements.
If you need something a bit more generic and re-usable, you would probably want to implement a callback... I'll refer to the UI thread as the client and the AsyncTask as the server.
Create a new interface and create some method stubs.
public interface MyEventListener {
public void onEventCompleted();
public void onEventFailed();
}
Have your client pass instance of MyEventListener to the server. A typical way of doing this is to have your client implement the interface (MyEventListener) and pass itself to the server.
public class MyActivity implement MyEventListener {
public void startEvent() {
new MyAsyncTask(this).execute();
}
#Override
public void onEventCompleted() {
// TODO
}
#Override
public void onEventFailed() {
// TODO
}
}
On the onPostExecute of the server, check if the callback is null and call the appropriate method.
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private MyEventListener callback;
public MyAsyncTask(MyEventListener cb) {
callback = cb;
}
[...]
#Override
protected void onPostExecute(Void aVoid) {
if(callback != null) {
callback.onEventCompleted();
}
}
}
You can read more about callbacks here: http://www.javaworld.com/javaworld/javatips/jw-javatip10.html

Synchronization on methods executing methods on other threads

Hey m a newbie to android programming and I was working on this project.
This question is pretty long so here's the deal.
I have this GCMIntentService class extending GCMBaseIntentService and whenever a message arrives from the server, the GCMBroadcastReceiver automatically recognizes it and calls the overriden onMessage() method in the GCMIntentService class. Now in the onMessage body, I am doing some operations on the SQLiteDatabase and I am notifying my adapter for list view by calling the adapter.notifyDataSetChanged() in the ui thread inside the onMessage body.
Now, if more than 2 or 3 gcm messages come simultaneously to the device the app crashes since more than one thread is calling the same onMessage() method and is messing up with my database and adapter as well. I figured I needed to use synchronized keyword on the method that should be used by only one thread at a time.
But since my onMessage method is an overriden method, I decided to make another method and put synchronized modifier on it but once again I need to call the runOnUiThread() method from inside it since i need to notify changes to my list view's adapter.
I just want to ask if doing this is the right way or is it possible to use a much simpler solution to my problem?
Here is the sample code to what m doing:
#Override
protected void onMessage(Context arg0, Intent intent) {
// called when a new cloud message has been received
Log.w("Service ", "Started");
dbh = new DatabaseHandler(this);
sld = dbh.getWritableDatabase();
who = this;
// processing json object
putDataFromJSON();
//other stuff
}
synchronized private void putDataFromJSON(){
//do some work on JSON Object
//complete work on JSON by putting in database
dbh.saveInDB();
//notify the adapter
((MainActivity) MainActivity.con).runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
//do other stuffs as well
}
}
}
I'm writing a dummy code here that I think can demonstrate you an abstract architecture..
public class GCMIntentService extends GCMBaseIntentService{
private static ArrayList<Message> messageQueue = new ArrayList<Message>();
private static boolean isProcessingMessage = false;
onMessage(Context context, Intent intent)
{
if(isProcessingMessage)
{
Message currentMsg = new Message();//Create a instance of message and put it in arrayList
}
else{
isProcessingMessage = true;
for(int i = 0; i < messageQueue.size(); i++)
{// Process all your messages in the queue here
messageQueue.remove(i);
}
isProcessingMessage = false;
}
}
private class Message{
//In this class you can configure your message that you are going to queue.
}
}
Firstly, the onMessage() method gets executed every single time a new GCM message arrives(even when you are not into your app, because we register this receiver inside the manifest file.) So, getting the context of your activity my cause your app to crash (NullPointerException).
Now, as far as your question is concerned, you can maintain a queue that keeps track of incoming GCM messages. And, upon processing a message you can check for the entries in the queue and process them. For this purpose, you can use a boolean that flags if any message is currently being processed (flag == true). And when (flag == false), you can take the next entry from the queue and process that..
I hope it was useful.

Categories