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.
Related
I need to pass some data between two activities MainActivity and ChildActivity. Button click on MainActivity should open ChildActivity and send event with data. I have singleton:
Subject<Object, Object> subject = new SerializedSubject<>(PublishSubject.create());
and in MainActivity I have the following button click handler:
public void onClick(){
startActivity(new Intent(MainActivity.this, ChildActivity.class));
subject.onNext(new SomeEvent(data));
}
and event listener subscription in ChildActivity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addEventListeners();
}
private void addEventListeners() {
subject.ofType(SomeEvent.class)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribe(
event -> {
loadData(event.getData());
});
}
When I send event after starting activity and call addEventListeners in ChildActivity onCreate is still not subscribed to this event and loadData() is not called.
What is proper way to pass data between activities using RxJava (if it's possible)?
if anybody needs a complete solution to send data between activities using RxJava2
1- Create the bus:
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject
= BehaviorSubject.create();
public static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
2- the sender activity
//the data to be based
MyData data =getMyData();
RxBus.getSubject().onNext(data) ;
startActivity(new Intent(MainActivity.this, AnotherAct.class));
3-the receiver activity
disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof MyData) {
Log.d("tag", (MyData)o.getData();
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
});
4-unSubscribe to avoid memory leacks:
#Override
protected void onDestroy() {
super.onDestroy();
disposable.dispose();
}
Reason:
Problem is that you are using PublishSubject. As per documentation of PublishSubject emits all the subsequent items of the source Observable at the time of the subscription. So in your case it will emit event only if it is subscribed.
Fix for your problem
Instead of using PublishSubject use BehaviorSubject which emits the most recently emitted item and all the subsequent items of the source Observable when a observer subscribe to it.
Browse following link for more details.
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 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;
}
I am trying to create a handler to make my api calls using retrofit.
My call work and I get a response, but I would like to know what would be a best practice to return the response.
Here is a simplified version of my code:
public class ApiHandler{
private static String username = "myUsername";
private static String sessionID = "mySessionID";
public static ObjectResponse getObjects(String id){
ClientApi.getApiClient().getObjects(new ObjectRequest(username, sessionID, id), new Callback<ObjectResponse>() {
#Override
public void success(ObjectResponse ObjectResponse, Response response) {
//Request succes
/*I would like to pass the ObjectResponse from the success method to the getObjects method to be able to return them*/
}
#Override
public void failure(RetrofitError error) {
//Request failure
///TODO
System.out.println(error.getResponse().getStatus());
}
});
//I would like to return the ObjectResponse here
return null;
}
}
Then I just call from my fragment: myObjectResponse = ApiHandler.getObjects(id);
My problem is that when the success() method is called in the callback, the getObject() method already ended and I'm not able to return the response.
I saw that I could use an eventbus (otto) to achieve that but I'm not sure how that works or if it even is the best way to do that.
So how could I achieve that?
I finally found my answer.
Using otto I just need to send the objectResponse to the bus as followed:
public class ApiHandler{
private static String username = myUsername;
private static String sessionID = mySessionID;
public static ObjectResponse getObjects(String id){
ClientApi.getApiClient().getObjects(new ObjectRequest(username, sessionID, id), new Callback<ObjectResponse>() {
#Override
public void success(ObjectResponse objectResponse, Response response) {
//Request success
BusProvider.getInstance().post(objectResponse);
/*I would like to pass the ObjectResponse from the success method to the getObjects method to be able to return them*/
}
#Override
public void failure(RetrofitError error) {
//Request failure
///TODO
System.out.println(error.getResponse().getStatus());
}
});
//I would like to return the ObjectResponse here
return null;
}
}
And then in my fragment, register as a receiver:
#Override
public void onPause(){
super.onPause();
//Unregister the fragment from the bus provider
BusProvider.getInstance().unregister(this);
}
#Override
public void onResume(){
super.onResume();
//Register the fragment to be able to receive events through the bus provider
BusProvider.getInstance().register(this);
}
#Subscribe
public void onObjectResponse(ObjectResponse objectResponse){
System.out.println("We did it!!!");
//Do what you want with your object!
}
Before EventBus, an interface was required as a private member of the ApiHandler class (in your case). Now on success, you call your unimplemented method of your interface.
You implement this method somewhere in your UI, and you call getObject() to run in the background. Once the method is successful, it will trigger your interface method which you have implemented in your UI along with the objectResponse to play around with.
EventBus has quite similar concept except that it is a central (or singular) and works in a channel/subscribe manner. Try to use Eventbus + Retrofit + any dependency injector to ease your life.
I'm using SQLite database in android and want to listening for any database changes. How can I do this?
Thanks for all future help!
In fact, SQLite offers such functionality: SQLite Data Change Notification Callbacks
How it can be used in Android is another story though..
SQLite doesn't offer any change listener functionality; you have to monitor it yourself. The simplest way to achieve this would be to send a Broadcast (or even better, a LocalBroadcast) anytime you modify the database. Some of the database libraries already offer functionality that is similar to this - check out GreenDAO.
a simple implementation of changeListener for the database on Android
suppose that you have a class to handle your queries in your android app, we need to make the database methods observable.
and also we need some listeners to observe the abovementioned observable. let's make the database handler observable:
let's make the observable interface:
public interface DatabaseObservable {
//register the observer with this method
void registerDbObserver(DatabaseObserver databaseObserver);
//unregister the observer with this method
void removeDbObserver(DatabaseObserver databaseObserver);
//call this method upon database change
void notifyDbChanged();
}
now implement the observable in your database class
public class LocalStorageDb extends SQLiteOpenHelper implements DatabaseObservable {
LocalStorageDb lDb;
//make it Singleton
public static synchronized LocalStorageDB getInstance(Context context) {
if (mlLocalQuickChatDB == null) {
mlLocalQuickChatDB = new LocalStorageDB(context.getApplicationContext());
}
return mlLocalQuickChatDB;
}
//there are some methods to do some queries
public void createContact(Foo foo, Bar bar){
//some queries here
//call the Observable Method to let know the observers that it has changed
onDatabaseChanged();
}
//now override the DatabaseObservable method which is responsible to notify the listeners
#Override
public void onDatabaseChanged() {
for (DatabaseObserver databaseObserver:observerArrayList){
if (databaseObserver!= null){
databaseObserver.onDatabaseChanged();
}}
}
//also you need functions to **register** or **unregister** the observers:
#Override
public void registerDbObserver(DatabaseObserver databaseObserver) {
if (!observerArrayList.contains(databaseObserver)){
observerArrayList.add(databaseObserver);
}
#Override
public void removeDbObserver(DatabaseObserver databaseObserver) {
observerArrayList.remove(databaseObserver);
}
then we need an observer to observe the changes:
public interface DatabaseObserver {
void onDatabaseChanged();
}
now in your activity or fragment, there is a function to fetch the changes, like getLocalContact. implement the observer on the fragment for example:
public class ExampleFragment extends Fragment implements DatabaseObserver {
LocalStorageDB localStorageDB;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
localStorageDB = LocalStorageDB.getInstance();
}
#Override
public void onPause() {
super.onPause();
localStorageDB.removeObserver(this);
}
#Override
public void onResume() {
localStorageDB.registerObserver(this);
super.onResume();
}
public ExampleFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_example, container, false);
}
#Override
public void onDatabaseChanged() {
getLocalContact();
}
private void getLocalContact(){
//function to fetch contacts from database
}
}
Hi I'd like to add a new suggestion Incase of firebase usage. You can make a new json node for users messages and use On Data Change to detect the new number of unread messages then update your ui in the onDatachange. Is that smart or far away from the main idea?