Change Observable List to Hashmap Singleton - android

I have a singleton to cache objects, from here I create an observable from a List, this List is a response from the API which is filled with objects. (JSON)
private static BehaviorSubject<List<Model>> observableModelsList;
private static Observable<List<Model>> observable = myAPI.loadModelsRx();
private static Subscription subscription;
private PoiSingleton() {
}
public static PoiSingleton getInstance() {
return ourInstance;
}
public static void resetObservable() {
observablePoisList = BehaviorSubject.create();
if (subscription != null && !subscription.isUnsubscribed()) {
subscription.unsubscribe();
}
subscription = observable.subscribe(new Subscriber<List<Model>>() {
#Override
public void onCompleted() {
// do nothing
}
#Override
public void onError(Throwable e) {
observablePoisList.onError(e);
}
#Override
public void onNext(List<Model> models) {
observablePoisList.onNext(models);
}
});
}
public static Observable<List<Poi>> getPoisObservable() {
if (observablePoisList == null) {
resetObservable();
}
return observablePoisList;
}
What I want to achieve is create a HashMap from the List, the key should be the ID of the object and the value the object itself.
I am new to Android and Retrofit/RxJava so in what method/stage is it responsible to create the HashMap?

Related

Why retrofit returning Null 'LiveData' even after setting return value non-null correctly?

I got the response correctly, set value of LiveData correctly (got confirmed by printing value on console after setting value to live data). But When i tried to print same thing just before "return" it's giving me NullPointerException.
public class ProjectRepository {
private ProjectRepository instance;
Context context;
public ProjectRepository(Context context) {
this.context=context;
}
private MutableLiveData<List<PojoDivision>> data = new MutableLiveData<>();
public LiveData<List<PojoDivision>> getDivisionList() {
((RetrofitConfiguration)context).getDivisionRestApiWithAuthentication().getDivisionList().enqueue(new Callback<List<PojoDivision>>() {
#Override
public void onResponse(Call<List<PojoDivision>> call, Response<List<PojoDivision>> response) {
if (response.isSuccessful()) {
System.out.println(response.body().get(4).getName()); // this is printing
data.setValue(response.body());
System.out.println(data.getValue().get(4).getName()); // this is printing
}
}
#Override
public void onFailure(Call<List<PojoDivision>> call, Throwable t) {
Log.d(TAG, t.getMessage());
}
});
/*
following code is not printing with nullPointerException
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.List.get(int)' on a null object reference
*/
System.out.println(data.getValue().get(4).getName());
return data;
}
}
Since you're using a LiveData object at the class level for your repository class, your method has no need to return anything. Instead it's responsibility is for updating that LiveData object.
public class ProjectRepository {
private static final String TAG = "ProjectRepository";
private ProjectRepository instance;
Context context;
public ProjectRepository(Context context) {
this.context = context;
}
public MutableLiveData<List<PojoDivision>> data = new MutableLiveData<>();
public void getDivisionList() {
((RetrofitConfiguration) context).getDivisionRestApiWithAuthentication().getDivisionList().enqueue(new Callback<List<PojoDivision>>() {
#Override
public void onResponse(Call<List<PojoDivision>> call, Response<List<PojoDivision>> response) {
if (response.isSuccessful()) {
data.postValue(response.body());
}
}
#Override
public void onFailure(Call<List<PojoDivision>> call, Throwable t) {
Log.d(TAG, t.getMessage());
}
});
}
}
The clients of this class would need to 1) observe the LiveData object, and 2) call the getDivisionList() method to trigger an update:
class MyFragment extends Fragment {
private ProjectRepository repository;
private void setRepository() {
Context context = getContext();
if (context == null) return;
repository = new ProjectRepository(context);
}
public void observeData() {
repository.data.observe(this, new Observer<List<PojoDivision>>() {
#Override
public void onChanged(List<PojoDivision> pojoDivisions) {
// do something with updated data
}
});
}
public void triggerUpdate() {
repository.getDivisionList();
}
}

How to notify activity when repository class gets the data?

I'm new in android architecture component. this is my code , i'm at the point that I don't know how to notify my activity and get the results back
these are my codes:
Activity:
private void iniViewModels() {
Observer<List<User>>usersObserver=new Observer<List<User>>() {
#Override
public void onChanged(#Nullable List<User> users) {
Log.v("this","LiveData: ");
for (int i=0;i<users.size();i++){
Log.v("this",users.get(i).getName());
}
}
};
mViewModel = ViewModelProviders.of(this)//of-->name of act or fragment
.get(AcActivityViewModel.class);///get -->the name of viewModelClass
mViewModel.mUsers.observe(this,usersObserver);
}
this is my viewModel Class:
public class IpStaticViewModel extends AndroidViewModel {
public LiveData<List<Ipe>> ips;
private AppRepository repository;
public IpStaticViewModel(#NonNull Application application) {
super(application);
repository=AppRepository.getInstance(application.getApplicationContext());
}
public void getIpStatics() {
repository.getStaticIps();
}
}
this is my repository class:
public class AppRepository {
private static AppRepository ourInstance ;
private Context context;
private IpStaticInterface ipInterface;
public static AppRepository getInstance(Context context) {
if (ourInstance == null) {
ourInstance=new AppRepository(context);
}
return ourInstance;
}
private AppRepository(Context context) {
this.context=context;
}
public void getStaticIps() {
ipInterface= ApiConnection.getClient().create(IpStaticInterface.class);
ipInterface.getIpes()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new SingleObserver<IpStaticList>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(IpStaticList ipStaticList) {
List<Ipe>ips=ipStaticList.getIpes();
}
#Override
public void onError(Throwable e) {
Log.v("this","Eror "+ e.getMessage());
}
});
}
}
I'm using retrofit for fetching the data ,it fetch the data successfully but I don't know how to notify my activity
can you help me?
Have a MutableLiveData
final MutableLiveData<List<Ipe>> data = new MutableLiveData<>();
In onSucess
public MutableLiveData<List<Ipe>> getStaticIps() {
ipInterface= ApiConnection.getClient().create(IpStaticInterface.class);
ipInterface.getIpes()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new SingleObserver<IpStaticList>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(IpStaticList ipStaticList) {
List<Ipe>ips=ipStaticList.getIpes();
data.setValue(ips);
}
#Override
public void onError(Throwable e) {
Log.v("this","Eror "+ e.getMessage());
}
});
return data;
}
In repository expose this to viewmodel
public LiveData<List<Ipe>> getIpStatics() {
return repository.getStaticIps();
}
In Activity you observe the livedata
IpStaticViewModel viewmodel = ViewModelProviders.of(this
.get(IpStaticViewModel.class)
viewModel.getIpStatics().observe(this, new Observer<List<Ipe>>() {
#Override
public void onChanged(#Nullable List<Ipe> ipes) {
if (ipes != null) {
// dosomething
}
}
});
If you want to generalize your response in case you have a error or something have a look at https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/vo/Resource.kt

onNext not called for PublishSubject in android, rxjava?

I am using rxjava 2 and trying to use rxbus for passing a value
rxbus code
public class SeasonTabSelectorBus {
private static SeasonTabSelectorBus instance;
private PublishSubject<Object> subject = PublishSubject.create();
public static SeasonTabSelectorBus instanceOf() {
if (instance == null) {
instance = new SeasonTabSelectorBus();
}
return instance;
}
public void setTab(Object object) {
try {
subject.onNext(object);
subject.onComplete();
} catch (Exception e) {
e.printStackTrace();
}
}
public Observable<Object> getSelectedTab() {
return subject;
}
}
I am setting the value as
SeasonTabSelectorBus.instanceOf().setTab(20);
This is code of my subscription
SeasonTabSelectorBus.instanceOf().getSelectedTab().subscribe(new Observer<Object>(){
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Object o) {
if (o instanceof Integer) {
int seasonSelected =(int) o;
Log.e("season selected",seasonSelected+"");
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Now I am getting the value on the first call, but when I call again with different values, I do not get the callback.
SeasonTabSelectorBus.instanceOf().setTab(40);
SeasonTabSelectorBus.instanceOf().setTab(90);
SeasonTabSelectorBus.instanceOf().setTab(120);
SeasonTabSelectorBus.instanceOf().setTab(290);
You are receiving only the first one because, after publish (subject.onNext(object)), you are calling subject.onComplete(). Just remove that line.

Configuring RxJava to Send Data to activity from GCMListenerService

I am trying to send an update to my Activity from my GCMServiceListener so, I am using RxJava/RxAndroid And created a BusClass for handling sending and Observers
public class ClientBus {
//private final PublishSubject<Object> _bus = PublishSubject.create();
// If multiple threads are going to emit events to this
// then it must be made thread-safe like this instead
private final Subject<Object, Object> _bus = new SerializedSubject<>(PublishSubject.create());
public void send(Object o) {
_bus.onNext(o);
}
public Observable<Object> toObserverable() {
return _bus;
}
public boolean hasObservers() {
return _bus.hasObservers();
}
}
And in my Application Class I did this to initialize the BusClass
private ClientBus clientBus;
public ClientBus getRxBusSingleton() {
if (clientBus == null) {
clientBus = new ClientBus();
}
return clientBus;
}
In the activity I want to receive the message, I registered a CompositeSubscription and get a reference to my ClientBus class from the Application Class
clientBus = ((MyApplication) getApplicationContext()).getRxBusSingleton();
#Override
protected void onStart() {
super.onStart();
initSubscriptions();
}
#Override
protected void onStop() {
super.onStop();
_subscriptions.unsubscribe();
}
void initSubscriptions() {
_subscriptions = new CompositeSubscription();
_subscriptions.add(clientBus.toObserverable().subscribe(new Action1<Object>() {
#Override
public void call(Object event) {
Log.e("New Event", "Event Received");
if (event instanceof MyGcmListenerService.Message) {
String msg = ((MyGcmListenerService.Message) event).getMessage();
if (msg.equals("Update Available")) {
scheduleArrayList = getSchedules();
scheduleAdapter = new ScheduleAdapter(getApplicationContext(), scheduleArrayList, ScheduledUberActivity.this);
scheduledList.setAdapter(scheduleAdapter);
scheduleAdapter.notifyDataSetChanged();
} else if (msg.equals("Refresh")) {
fetchTrips();
}
}
}
}));
}
And from the MyGcmListenerService class I did this when I get a new notification
private void sendRefreshNotif() {
if (clientBus.hasObservers()) {<--It enters the if cause the Log prints. But, the activity doesn't get the message
Log.e("Obervers", "Observers aren't null");
clientBus.send(new Message("Refresh"));
}
}
What I don't understand is why isn't it working here? I use it to interact between activities and fragments. I closed my application to check if the notification comes in, It'll enter this block if (clientBus.hasObservers()) { but it didn't and starting the app and testing the Observer, it notices there's an active Observer. Any help? Thanks.
It seems like you used different instances of the ClientBus class in CompositeSubscription and MyApplication.
Try to make a singleton from ClientBus class, it works fine for me.
public class ClientBus {
public ClientBus(SingletonAccessor accessor) {}
private static ClientBus instance;
private static class SingletonAccessor{}
public static ClientBus getInstance() {
if (instance == null) instance = new ClientBus(new SingletonAccessor());
return instance;
}
private final Subject<Object, Object> mBus = new SerializedSubject<>(PublishSubject.create());
public void send(Object o) {
mBus.onNext(o);
}
public Observable<Object> toObserverable() {
return mBus;
}
public boolean hasObservers() {
return mBus.hasObservers();
}
}

Equivalent of iOS NSNotificationCenter in Android?

Is there an equivalent of the iOS class NSNotificationCenter in Android ? Are there any libraries or useful code available to me ?
In Android there is not a central notification center as in ios.
But you can basically use Observable and Observer objects to achieve your task.
You can define a class like something below, just modify it for singleton use and add synchronized for concurrent use but the idea is the same:
public class ObservingService {
HashMap<String, Observable> observables;
public ObservingService() {
observables = new HashMap<String, Observable>();
}
public void addObserver(String notification, Observer observer) {
Observable observable = observables.get(notification);
if (observable==null) {
observable = new Observable();
observables.put(notification, observable);
}
observable.addObserver(observer);
}
public void removeObserver(String notification, Observer observer) {
Observable observable = observables.get(notification);
if (observable!=null) {
observable.deleteObserver(observer);
}
}
public void postNotification(String notification, Object object) {
Observable observable = observables.get(notification);
if (observable!=null) {
observable.setChanged();
observable.notifyObservers(object);
}
}
}
Take a look at the Otto event bus from Square:
http://square.github.com/otto/
It has essentially the same features as NSNotificationCenter but thanks to annotations and static typing it is easier to follow the dependencies of components and paths that events follow. It's much simpler to use than the stock Android broadcast API, IMO.
i had the same wondrings.. so i wrote this:
public class NotificationCenter {
//static reference for singleton
private static NotificationCenter _instance;
private HashMap<String, ArrayList<Runnable>> registredObjects;
//default c'tor for singleton
private NotificationCenter(){
registredObjects = new HashMap<String, ArrayList<Runnable>>();
}
//returning the reference
public static synchronized NotificationCenter defaultCenter(){
if(_instance == null)
_instance = new NotificationCenter();
return _instance;
}
public synchronized void addFucntionForNotification(String notificationName, Runnable r){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list == null) {
list = new ArrayList<Runnable>();
registredObjects.put(notificationName, list);
}
list.add(r);
}
public synchronized void removeFucntionForNotification(String notificationName, Runnable r){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list != null) {
list.remove(r);
}
}
public synchronized void postNotification(String notificationName){
ArrayList<Runnable> list = registredObjects.get(notificationName);
if(list != null) {
for(Runnable r: list)
r.run();
}
}
}
and a usage for this will be:
NotificationCenter.defaultCenter().addFucntionForNotification("buttonClick", new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Hello There", Toast.LENGTH_LONG).show();
}
});
tried to make the interface as similar to IOS as possible, but simpler (no object registration needed).
hope that helps:)
If you don't want to use Observer - it can be problematic in cases you want a Fragment to be your Observer cause you can't extend more then one class-
You can use google's Guava Library (https://code.google.com/p/guava-libraries/)
for "Function" and "Multimap" - although you can use as well HashMap> for the subscibersCollection
and implement something like this:
import java.util.Collection;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.base.Function;
public class EventService {
ArrayListMultimap<String, Function<Object, Void>> subscibersCollection;
private static EventService instance = null;
private static final Object locker = new Object();
private EventService() {
subscibersCollection = ArrayListMultimap.create();
}
public static EventService getInstance() {
if (instance == null) {
synchronized (locker) {
if (instance == null) {
instance = new EventService();
}
}
}
return instance;
}
/**
* Subscribe to the notification, and provide the callback functions in case
* notification is raised.
*
* #param notification
* - notification name
* #param func
* - function to apply when notification is raised
*/
public void addSubscription(String notification, Function<Object, Void> func) {
synchronized (subscibersCollection) {
if (!subscibersCollection.containsEntry(notification, func)) {
subscibersCollection.put(notification, func);
}
}
}
/**
* Remove subscription from notification
*/
public void removeSubscription(String notification,
Function<Object, Void> func) {
synchronized (subscibersCollection) {
subscibersCollection.remove(notification, func);
}
}
/**
* raise notification for all its subscribers
*
* #param notification
* - notification name
* #param data
* - update data
*/
public void publish(String notification, Object data) {
Collection<Function<Object, Void>> observableList = subscibersCollection
.get(notification);
for (Function<Object, Void> func : observableList) {
func.apply(data);
}
}
}
On the basis of Behlül answer, I change the code to make it closer to iOS NSNotificationCenter.
Another thing: the notifications will fire on the main thread
package com.oxygen.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.os.Handler;
public class NotificationCenter {
//---------------- event type list ---------------------
public static enum NotificationID{
IMAGES_CACHE_READY
}
//---------------- singelton ---------------------------
private static NotificationCenter instance = null;
private NotificationCenter() { observables = new HashMap<NotificationID, MyObservable>(); }
public static synchronized NotificationCenter singelton() {
if (instance == null) {
instance = new NotificationCenter ();
}
return instance;
}
//-------------------------------------------
public class Notification {
private Object poster; // the object that post the event
private Object info; // event specific data
private NotificationID id; // event name
public Notification(Object poster, NotificationID id, Object info) {
super();
this.poster = poster;
this.info = info;
this.id = id;
}
public Object getPoster() {
return poster;
}
public Object getInfo() {
return info;
}
public NotificationID getId() {
return id;
}
}
//-------------------------------------------
public interface Notifiable {
public void onNotification(Notification notify);
}
//-------------------------------------------
protected class MyObservable {
List<Notifiable> observers = new ArrayList<Notifiable>();
public MyObservable() {
}
public void addObserver(Notifiable observer) {
if (observer == null) {
throw new NullPointerException("observer == null");
}
synchronized (this) {
if (!observers.contains(observer))
observers.add(observer);
}
}
public int countObservers() {
return observers.size();
}
public synchronized void deleteObserver(Notifiable observer) {
observers.remove(observer);
}
public synchronized void deleteObservers() {
observers.clear();
}
public void notifyObservers(Notification notify) {
int size = 0;
Notifiable[] arrays = null;
synchronized (this) {
size = observers.size();
arrays = new Notifiable[size];
observers.toArray(arrays);
}
if (arrays != null) {
for (Notifiable observer : arrays) {
observer.onNotification(notify);
}
}
}
}
//-------------------------------------------
HashMap<NotificationID, MyObservable > observables;
public void addObserver(NotificationID id, Notifiable observer) {
MyObservable observable = observables.get(id);
if (observable==null) {
observable = new MyObservable ();
observables.put(id, observable);
}
observable.addObserver(observer);
}
public void removeObserver(NotificationID id, Notifiable observer) {
MyObservable observable = observables.get(id);
if (observable!=null) {
observable.deleteObserver(observer);
}
}
public void removeObserver(Notifiable observer) {
for (MyObservable observable : observables.values()) {
if (observable!=null) {
observable.deleteObserver(observer);
}
}
}
public void postNotification(final Object notificationPoster, final NotificationID id, final Object notificationInfo) {
final MyObservable observable = observables.get(id);
if (observable!=null) {
// notification post to the maim (UI) thread
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(AppContext.get().getMainLooper());
Runnable myRunnable = new Runnable() {
#Override
public void run() {
observable.notifyObservers(new Notification(notificationPoster, id, notificationInfo) );
}
};
mainHandler.post(myRunnable);
}
}
}
Listener sample:
public class CustomGridViewAdapter extends ArrayAdapter<Category> implements Notifiable {
int layoutResourceId;
public CustomGridViewAdapter(Context context, int layoutResourceId) {
super(context, layoutResourceId);
this.layoutResourceId = layoutResourceId;
loadCategories(false);
NotificationCenter.singelton().addObserver(NotificationID.IMAGES_CACHE_READY, this);
}
public void onDestroy() {
NotificationCenter.singelton().removeObserver(this);
}
#Override
public void onNotification(Notification notify) {
switch (notify.getId()) {
case IMAGES_CACHE_READY:
loadCategories(true);
break;
}
}
...
}

Categories

Resources