I have two activity A and B.
In activity A I have a AsyncHttpClient fetching some data from a website. Once the data is received, I refresh the RecyclerView in activity A. But while the data is being fetched if someone switches to activity B, i want the RecyclerView of activity B to be refreshed on completion of data fetch.
Currently I am able to update in the same activity A, but can't do that in B.
Here is what I have done -
public void loadFromWeb(){
RequestParams params = new RequestParams();
AsyncHttpClient client = new AsyncHttpClient();
params.put("id", id);
client.post("http://example.com/process.php", params, new JsonHttpResponseHandler() {
#Override
public void onStart() {
}
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
try {
//process the response
adapterInActivityA.notifyDataSetChanged();
//This works for Activity A
//How to implement the function updateInActivityB()?
updateInActivityB();
} catch (Exception e) {
//catch exception
}
}
#Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
// Process failure
}
});
}
The problem is how to implement function updateInActivityB()
Solution (As suggested by Alok)
Following are the details of what I did for others who want to do the same -
This uses greenrobot's EventBus library
Compile the library by adding it to the app's build.gradle file
Compile the library
compile 'de.greenrobot:eventbus:2.4.0'
Create your event class, this will store the data which you are communicating.
//In FirstActivity
public class myEvent {
private final String data;
//Constructor
public myEvent(String data){
this.data = data;
}
public String getData(){
return data;
}
}
Whenever you want to send an update the data has been downloaded (i.e. in updateInSecondActivity() function)
post the update -
//In FirstActivity
public void updateInSecondActivity(){
EventBus.getDefault().post(new myEvent("Updated Data");
}
Now, In the SecondActivity, you have to listen for this event. First Register in the SecondActivity to subscribe to the events -
Register for the events
//In SecondActivity
EventBus.getDefault().register(this);
You can register in onCreate(), onStart(), onResume(). Also don't forget to unregister in onPause(), onDestroy(), onStop() -
Unregister the event listener
//In SecondActivity
EventBus.getDefault().unregister(this);
Once the registration and unregistration is in place, you have to listen if the update has been posted by FirstActivity or not. You can do this by creating an onEvent() function which listens for posting of myEvent class -
Listen to the event
//In SecondActivity
public void onEvent(myEvent mE){
String updatedData = me.getData();
// Process/update using the updateData
}
You can try to make a broadcast receiver that can receive a broadcast
Message fired by your asynchronous thread.
Or
2.you can use event bus to limitless communication.
Or
3.you can register an observer for content received .
Logic is pretty simple
You try to fetch the data if data fetched before user switch to next activity than everything is fine.
In second case when user switches earlier, than in this case you can check for content while resuming next activity. If content not received yet than you can implement any of above 3 method observer ultimately when content received your observer will be notified.
Related
I have a class in a service
MyClass m = new MyClass();
and inside my class I check if I have permission to overlay the view; if so, it's ok, otherwise I must start an activity
if (Settings.canDrawOverlays(mContext)) {
// draw over app
} else {
// start the activity
Intent i = new Intent(context,Calls.class);
context.startActivity(i);
}
When I start the activity I have a problem communicating between the class and the activity. I know how to use the interface but how can I register it in activity.
Some time I want to pass an object or data from the class to the activity or from the activity to the class... how can I do that?
I saw many examples in Stack Overflow about how to communicate between service and activity; they suggest to start the class from the activity but this does not work in my app because my class must be running all the time.
Perhaps you could use an event bus like mechanism where you can send or receive events through out your app, Though there are several libraries out there, I would recommend using Otto library for android.
Usage is pretty simple just register in your activity onCreate
Bus bus = new Bus();
bus.register(this);
For sending events
// example data to post
public class TestData {
public String message;
}
// post this data
bus.post(new TestData().message="Hello from the activity");
And subscribe to events like this
#Subscribe public void getMessage(TestData data) {
// TODO: React to the event somehow!
}
More info here
If you want to implement a communication pattern between a Service and an Activity, you should use a LocalBroadcastManager.
It will turn handy because, in case your Service is still on but your Activity
has been destroyed (very common situation), then the 'messagging' between the two will simply have no effect (no NPE or whatsoever will be thrown).
Step 1
Create a BroadcastReceiver in your Activity and define an ID / Filter
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do what you have to do here if you receive data from the Service / Background Task
}
}
public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL")
Step 2
In your Activity register the broadcast in onResume() and unregister it in onPause().
#Override
protected void onResume() {
// Listen if a Service send me some data
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}
#Override
protected void onPause() {
// I'm going to the background / or being destroyed: no need to listen to anything anymore...
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}
Your Activity is now ready to receive data from any other component in your Application.
If it's in the background, then there is no need to update the UI: in fact the Activity will not respond if in the background.
In the same way, if it's being garbage collected, the Receiver will be unregistered and the Activity will just not respond to anything.
If the Activity is resumed / restarted, onResume() will be triggered and the Receiver will be registered again.
Step 3
All you need to do right now, is send data from the Service.
Simply call
final Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER);
// put your data in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
and your Activity will accordingly respond to the signal.
It's surprising how few people know about the LocalBroadcastManager and instead use some self-implemented callback / singleton pattern, which increases complexity and non-readability.
This pattern is built-in in Android, so you don't need external libraries. As for security, this ensures that your signals stay internal to your application: no data can therefore be read by other apps.
I similarly answered to another question here.
I am working on datasync service, i'm getting the json response from webserver. my question is how to pass that response to the activity from which i'm calling the service.
You can use EventBus and send the data to the desired Activity by Subscribing to the Event.
For Eg:
compile 'org.greenrobot:eventbus:3.0.0'
Eventbus Class
public class DataSyn {
public final List<YourModel> YourModel;
public DataSyn(List<YourModel> YourModel) {
this.YourModel = YourModel;
}
}
Send Data from your response :
EventBus.getDefault().post(new DataSyn(yourdataList));
Subscribe and Receive the Data wherever you need:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onDataRecevied(DataSyn event) {
if (event.YourModel != null) {
populateData(event.YourModel);
}
}
Above is the most easiest way to share data
You can subscribe to a broadcast receiver in your activity with receiving a message, or you may look in push notification field...
I am building an android library.
The main class includes methods like connect,getUserSession .Workflow includes few steps-
Step 1 :
In getUserSession method. I need to send a Http POST request to external api and
recieve the response data(sessionKey,id,name etc). It is like an authentication method.
Step 2 :
In connect method. It uses the response data from Step 1 then I need to connect to websocket server recieve the response data(userdetails,profilepic etc).
Step3 :
Pass the response data from Step 2 in Activity.
I want to be step 1 and step 2 to be synchrounous because step 2 won't work without step 1. And i dont want to block the app while processing.After doing some research i found this can be done using AsyncTask class. But it seems i cannot return values from doInBackground and onPostExecute methods. How to do this task?
You have to set listener (interface) for first AsyncTask Please try below code
interface AsyncTaskListener{
public void onTaskCompleted(Object<Type> value);
}
public class DemoAsyncTask extends AsyncTask<String,Void,Void>{
AsyncTaskListener listener;
public DemoAsyncTask(AsyncTaskListener listener){
this.listener = listener;
}
#Override
protected String doInBackground(Void... params) {
///do some task
return someResult;
}
#Override
protected void onPostExecute(String result) {
listener.onTaskCompleted(result);
}
//Calling Async task from activity or fragment
DemoAsyncTask task = new DemoAsyncTask(new AsyncTaskListener(){
#Override
public void on onTaskCompleted(Object<Type> value);{
//do your second step here
})
};
task .execute();
I have three java files in my Android project. Two are activities (MainActivity and GeoActivity) and one is a plain java file (PostHttp -> sends data to server via the HTTP POST)
I switch over to GeoActivity via a simple button on-click method. GeoActivity returns the co-ordinates of the current location in a TextView AND sends them to a remote server via the HTTP POST.
I have a Handler.class which executes sends the Post Message after a delay of 50s. Something like this below. The problem i have is that when i click the back button and switch over to MainActivity i can still see in LogCat the echoes receiving from the server that the data is still being sent. How can i stop that?
GeoActivity.class
public class GeoActivity extends Activity {
Location location;
private Handler mHandler = new Handler();
protected void onCreate(Bundle savedInstanceState) {
....
if(location != null){
mHandler.postDelayed(updateTask,0);
}
...
}
...
public Runnable updateTask = new Runnable(){
public void run(){
mlocListener.onLocationChanged(location);
//send coordinates with a delay of 50s
new PostHttp(getUDID(),latitude,longitude).execute();
mHandler.postDelayed(updateTask, 50000);
}
Try acting on the activity's life cycle.
For example:
#Override
protected void onStop() {
super.onStop(); // Always call the superclass method first
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
ContentValues values = new ContentValues();
values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
getContentResolver().update(
mUri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
);
}
This doesn't destroy the activity, it will reside in memory. However, you can always resume when needed.
Source:
Stopping and Restarting Android Activities
I've already bound an activity to my service following this tutorial.
http://developer.android.com/guide/components/bound-services.html
I'm able to call service functions, but what if I want to for example, change some of my textviews or disable some of the toggle buttons because of work done on the service (and from the service). Would there be an easy to way to do this?
You can use messages to send information between activities and services. This is an easy way to send simple data, but may not be the best option if you need to send data very frequently, or send complicated data. This is an example of some code I have in one of my apps with a service and an activity which communicate:
Code in the activity:
//this is where you set what you want to happen
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
//this switch reads the information in the message (usually just
//an integer) and will do something depending on which integer is sent
case 1: do_something();
case 2: do_something_2(); //etc.
default:
super.handleMessage(msg);
}
}
}
final Messenger myMessenger = new Messenger(new IncomingHandler());
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
myService = new Messenger(service);
myCallbackText = (TextView)findViewById(R.id.tv01); //This is a text view which will display status information as needed
myCallbackText.setText("Attached.");
try {
Message msg = Message.obtain(null,
1);
msg.replyTo = mMessenger; //here we send an instance of our messenger implementation as the replyTo address
mService.send(msg);
msg = Message.obtain(null,
3, this.hashCode(), 0);
mService.send(msg); //send a message with the value "3"
} catch (RemoteException e) {
//nothing you can do if the server isn't active
}
Toast.makeText(Service_testActivity.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();//confirmation that the connection happened successfully
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mCallbackText = (TextView)findViewById(R.id.tv01);//same textview as before
mCallbackText.setText("Disconnected.");
Toast.makeText(Service_testActivity.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
Code in the service:
In the service, you will want to have code (very similar to the code in the activity) to receive a message and save the msg.replyTo field as a Messenger object. There is an example somewhere which will have you make an object and then use an IncomingHandler like this:
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
default:
super.handleMessage(msg);
}
}
}
This can allow your service to keep track of multiple clients at once and send messages to specified clients. To send a message simply use something like this:
mClients.get(1).send(Message.obtain(null, 3, new Random().nextInt(), 0));
//sends a message to the first client saved in the list