In my Activity I send EventBus
#Override
protected void onResume() {
super.onResume();
EventBus.getDefault().post(new String("We are the champions"));
}
In my background service I register EventBus and try to get sent message from Activity like this
#Override
public void onCreate() {
super.onCreate();
EventBus.getDefault().register(this);
}
#Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
public void onEventBackgroundThread(String s){
Log.d(TAG, "onEventBackgroundThread: " + s);
}
But nothing happens. I tried to search internet but couldn't get answer.
Errors I get
No subscribers registered for event class java.lang.String
No subscribers registered for event class de.greenrobot.event.NoSubscriberEvent
1.Edit
I already have EventBus communication from Service to Activity. But now I want to have it also to work in reverse direction(From Activity to Service). So is it possible that it conflicts?
First you need to define a custom message event class:
public class MyMessageEvent {
String s;
}
Then you should add a subscriber method into your service. Key thing is to include your MyMessageEvent as the parameter:
public void onEventBackgroundThread(MyMeasageEvent myEvent){
Log.d(TAG, "onEventBackgroundThread: " + myEvent.s);
}
Plus make sure your service registers with EventBus.
Finally, create a MyMessageEvent in your activity and post it on the eventbus:
MyMessageEvent myEvent = new MyMessageEvent() ;
myEvent.s = "hello" ;
EventBus.getDefault().post(myEvent) ;
If it still doesn't get received, try splitting the post onto a seperate line from the get default. Sometimes chaining doesn't work.
I have not used greenrobot before but I think it should be similar to guava's eventbus. I think you should create an event class instead of using String. You can have a look at https://github.com/greenrobot/EventBus.
e.g.
public class MessageEvent {
String s;
}
Related
I have a rather general question.
Assuming I have a RecyclerView in some kind of a MainActivity. The content of this RecyclerView is being updated in multiple places in other activities.
For example there could be the possibility to make new entries by starting a new Activity and saving it there.In that case I would intuitively start that activity with startActivityForResult() and receive the changes in the onActivityResult() method in the MainActivity.
But lets say deeper inside the application, there is the possibility to delete all entries. Now how do we notify the MainActivity about this change in the dataset? I thought about setting a flag of some kind and clearing it after the content has been updated in the MainActivity. But somehow using global variables does not really follow the principle of proper encapsulation, does it?
Sorry for this vague question, but I find it quite hard to properly handle information flow in Android in a elegant manner, so here we are.
How about a local broadcast? You can find the idea of broadcast in this document. You need local broadcast and it is preferred if you want to pass data within your app only.
Android apps can send or receive broadcast messages from the Android system and other Android apps, similar to the publish-subscribe design pattern. These broadcasts are sent when an event of interest occurs. For example, the Android system sends broadcasts when various system events occur, such as when the system boots up or the device starts charging. Apps can also send custom broadcasts, for example, to notify other apps of something that they might be interested in (for example, some new data has been downloaded).
You can use Handler to pass the Message in Activity and then You have to update RecyclerView. Like,
1) In Activity.
public static Handler mHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
if(msg.what == 1223){
//update RecyclerView
}
return false;
}
});
2) pass message When you want to update RecyclerView
Message msg = new Message();
msg.what = 1223;
Activity1.mHandler.sendMessage(msg);
You can use EventBus to handle it.
Define a class for your event
public static class MyEvent {
int event;
/* define your fields */
}
And prepare your subscriber in main activity
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMyEvent(MyEvent myEvent) {
switch(myEvent.event) {
/* Do what you need */
}
};
Now where you need to make change, call your subscriber like this:
MyEvent myEvent = new MyEvent();
myEvent.event = 1;
EventBus.getDefault().post(myEvent);
You can read more about EventBus in here
If you were using RxJava2, RxAndroid. Then you could try this.
Create a Bus:
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject = BehaviorSubject.create();
public static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
In your WaitingActivity where you want to receive data(where you want not to use onActivityResult in your case)
Disposable disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof DataObject) {
//((DataObject) o).getValue();
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
});
In your activity where you want to send data
RxBus.getSubject().onNext(dataObject);
startActivity(new Intent(CurrentActivity.class, WaitingActivity.class));
Finally don't forget to dispose your disposable to avoid memory leaks in your WaitingActivity
#Override
protected void onDestroy() {
super.onDestroy();
disposable.dispose();
}
Your data should be separate from view, in model. If some other activity changes data ideally recycler view must be updated from there. So no matter which activity does what, when you refresh data on load or resume of your recycler view you will always get correct results.
How to update the RecyclerView Dataset from the background service.
The service maintains a socket connection with the server and when the server responds with data, the service has to update that in the recyclerview (that is in the MainActivity).
There is many way to send event from Serivce to Activity.
I recommend you the following way.
Bind and Callbacks
I think that Bind and Callbacks is official way.
Communication between Activity and Service
Example: Communication between Activity and Service using Messaging
EventBus
I think that EventBus is easy way.
https://github.com/greenrobot/EventBus
In Activity (or any where) :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
#Override
protected void onResume() {
super.onResume();
BusHolder.getInstnace().register(this);
}
#Override
protected void onPause() {
super.onPause();
BusHolder.getInstnace().unregister(this);
}
#Subscribe
public void onDatasetUpdated(DataSetUpdatedEvent event) {
//Update RecyclerView
}
}
BusHolder holds BusEvent instance:
public class BusHolder {
private static EventBus eventBus;
public static EventBus getInstnace() {
if (eventBus == null) {
eventBus = new EventBus();
}
return eventBus;
}
private BusHolder() {
}
}
The event posted:
public class DataSetUpdatedEvent {
//It is better to use database and share the key of record of database.
//But for simplicity, I share the dataset directly.
List<Data> dataset;
public DataSetUpdatedEvent(List<Data> dataset) {
this.dataset = dataset;
}
}
Send message from your Service.
BusHolder.getInstnace().post(new DataSetUpdatedEvent(dataset));
I hope this helps.
May be you should use some database like thing to store temporary data because I don't think it's a good thing to store data in an object on behalf of service component. It would be redundant to store whole list data into object as whether user comes back to app or not your object is going to cover memory which we should avoid throughout the development process. Best of luck.
In my app I use SyncAdapter(AbstractThreadedSyncAdapter) for synchronisation with server. Basically in background service I insert data to sql table, then on finish I want to inform UI to update ListView with new data. For this matter I tried to use GreenRobot EventBus, but no success.
my Event
public class SyncResultMsg {
public String message="";
public SyncResultMsg() {}
public SyncResultMsg(String value) {
this.message = value;
}
}
After insert data to database I call EventBus like this
SyncResultMsg event = new SyncResultMsg();
event.message = "groupsFetched";
EventBus.getDefault().post(event);
In my Fragment where I show ListView I try to receive EventBus like this:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
public void onEventMainThread(SyncResultMsg event) {
String msg = event.message;
if (msg.equals("groupsFetched")){
showNewData();
}
}
Try creating a custom EventBus with your own threadpool. Had a similiar issue and it solved it in my case.
I am using a TextView inside of a Fragment.
I wish to update this TextView outside of the fragment (but also outside of an activity) from a callback class.
For example the user scrolls, the callback is called somewhere in my package, and I want the fragment view to be updated.
Can anybody explain how to do this? I did use a Local Broadcast Receiver but it wasn't fast enough in its updating.
Eventually looked at Otto but as we had Guava I implemented a singleton eventbus and used Guava publish/subscribe model to pass stuff around.
Otto however looks very similar.
Use Otto: http://square.github.io/otto/
public class UpdateEvent {
private String string;
public UpdateListEvent(String string) {
this.string = string;
}
public String getString() {
return string;
}
}
...
...
public void update() {
SingletonBus.INSTANCE.getBus().post(new UpdateListEvent(editText.getText().toString()));
}
...
public class FragmentA extends Fragment {
#Override
public void onResume() {
super.onResume();
SingletonBus.INSTANCE.getBus().register(this);
}
#Override
public void onPause() {
SingletonBus.INSTANCE.getBus().unregister(this);
super.onPause();
}
#Subscribe
public void onUpdateEvent(UpdateEvent e) {
//do something
}
}
public enum SingletonBus {
INSTANCE;
private Bus bus;
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public Bus getBus() {
return bus;
}
}
EventBus is a nice and elegant way for communication between modules in Android apps. In this way you should register your fragment as a event subscriber, and post a this specific event from other part of your code. Keep in mind that only UI thread can work with Views.
I don't exactly understand what you want to achieve and why BroadcastReceiver does not work for you, but you may either:
1) try using callbacks (if it is possible in your app design);
2) try using this or that event bus implementation;
Both would work pretty fast without much overhead, compared to broadcasting.
In case 2 you won't have to maintain callback dependencies/references.
I have an android application that is connected to the computer via USB cable. I use a TCPServer Class to send messages and listen. For example:
When I send a message like: request:x
I get the response: response:x:55
I need to make changes on my activity according to the response I get. At the moment I temporarily solved the problem by passing activity and activity class object to the TCPServer's constructor
public TCPServer(int portNum, Activity activity, IntroActivity ia) {
super();
port = portNum;
this.activity = activity;
this.ia = ia;
}
Then after I receive the response:
void updateButton(final int color, final String txt) {
activity.runOnUiThread(new Runnable() {
public void run() {
ia.getConnectionButton().setBackgroundColor(color);
ia.getConnectionButton().setText(txt);
}
});
}
As you see, this is not effective at all. I need to somehow notify the activity whenever a relevant variable is received. I use a Class for GlobalVariables and change those static variables after listen(), however I am having troubles notifying the activity.
First of all, it is almost always bad practice to pass Activity instances around. This is a time when it's bad.
Define an interface and use a callback to let the activity know that a response has been received.
public interface ResponseReceivedListener {
void onResponseReceived(int arg1, string arg2); // <- add arguments you want to pass back
}
In your TCPServer class:
ArrayList<ResponseReceivedListener> listeners = new ArrayList<>();
// ...
public void setResponseReceivedListener(ResponseReceivedListener listener) {
if (!listeners.contains(listener) {
listeners.add(listener);
}
}
public void removeResponseReceivedListener(ResponseReceivedListener listener) {
if (listeners.contains(listener) {
listeners.remove(listener);
}
}
When you receive a response:
for (ResponseReceivedListener listener : listeners) {
listener.onResponseReceived(arg1, arg2);
}
In your Activity:
public class MainActivity extends Activity implements ResponseReceivedListener {
// ...
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
tcpServer.setResponseReceivedListener(this);
// ...
}
public void onResponseReceived(int arg1, string arg2) {
// do whatever you need to do
}
// ...
}
All from memory so please excuse typos.
This approach decouples the classes. The TCP Server has no knowledge of the activities. It simply calls back to any listeners registered. Those listeners might be Activities, they might be services. They might be instances of MySparklyUnicorn. The server neither knows nor cares. It simply says "if anyone's interested, I've received a response and here are the details".