Is there any app events on Xamarin.Android? - android

I have a Xamarin.Android app with several activities and fragments. The app uses SignalR, connected with a .net core backend web app. There are several activities that may require visual modifications depending on the events called by the server. Is there any kind of in-app events that activities may subscribe to on creation that handles those required visual changes?
For example:
I am on an activity that shows 5 images related to a publication, and then the server sends a notification that the publication has been edited so the images have changed. In this case i would want that the SignalR client triggered some in-app event that updates the changed images on created activities of this kind.

I have came up with some kind of solution. I created a class called EventSubscriber that acts exactly how i wanted to.
public class EventSubscriber<T> : IEventSubscriber
{
#region Private members
private List<Tuple<T, Func<T, bool>>> Subscribtions { get; set; }
private ConcurrentDictionary<int, Func<T, bool>> WaitingObjectSubscribtions { get; set; }
#endregion
public EventSubscriber()
{
Subscribtions = new List<Tuple<T, Func<T, bool>>>();
WaitingObjectSubscribtions = new ConcurrentDictionary<int, Func<T, bool>>();
}
#region Subscribe
public void Subscribe(T adapter, Func<T, bool> function)
{
lock (Subscribtions)
{
Subscribtions.Add(new Tuple<T, Func<T, bool>>(adapter, function));
}
}
public int Subscribe(Func<T, bool> function)
{
lock (WaitingObjectSubscribtions)
{
int id = WaitingObjectSubscribtions.Count;
WaitingObjectSubscribtions.TryAdd(id, function);
return id;
}
}
#endregion
#region Unsubscribe
public void UnSubscribe(Tuple<T, Func<T, bool>> item)
{
lock (Subscribtions)
{
Subscribtions.Remove(item);
}
}
public void UnSubscribe(int id)
{
lock (WaitingObjectSubscribtions)
{
Func<T, bool> func;
WaitingObjectSubscribtions.TryRemove(id, out func);
}
}
public void UnSubscribeAll()
{
lock (Subscribtions)
{
Subscribtions.Clear();
}
WaitingObjectSubscribtions.Clear();
}
#endregion
#region Call Subscribed
public void CallSubscribed()
{
lock (Subscribtions)
{
foreach (var item in Subscribtions)
{
(var adapter, var function) = item;
if (!function(adapter))
{
Log.Debug("[EventSubscriber]", "Failed to notify adapter, will be automatically unsubscribed from this event");
UnSubscribe(item);
}
}
}
}
public void CallSubscribedWith(T adapter)
{
foreach (var id in WaitingObjectSubscribtions.Keys)
{
var function = WaitingObjectSubscribtions[id];
try
{
if (!function(adapter))
{
Log.Debug("[EventSubscriber]", "Failed to execute function");
UnSubscribe(id);
}
}
catch (Exception)
{
Log.Debug("[EventSubscriber]", "Failed to execute function");
UnSubscribe(id);
}
}
}
#endregion
}
To manage several EventSubscribed used in-app i created a static class Accessible to every Activity or Fragment which contains all the needed events:
public static class EventBoard
{
#region Products and Favorites
public static EventSubscriber<SwipeRefreshLayout> FinishedLoadingProducts = new EventSubscriber<SwipeRefreshLayout>();
public static EventSubscriber<List<Product>> SuccessfullyLoadedProducts = new EventSubscriber<List<Product>>();
public static EventSubscriber<RecyclerView.Adapter> UnsuccessfullyLoadedProducts = new EventSubscriber<RecyclerView.Adapter>();
//Search
public static EventSubscriber<SwipeRefreshLayout> FinishedLoadingProductsForSearch = new EventSubscriber<SwipeRefreshLayout>();
public static EventSubscriber<RecyclerView.Adapter> SuccessfullyLoadedProductsForSearch = new EventSubscriber<RecyclerView.Adapter>();
public static EventSubscriber<RecyclerView.Adapter> UnsuccessfullyLoadedProductsForSearch = new EventSubscriber<RecyclerView.Adapter>();
public static EventSubscriber<RecyclerView.Adapter> SearchNotFound = new EventSubscriber<RecyclerView.Adapter>();
//favorites
public static EventSubscriber<LocalProduct> ConfirmNewFavoriteEvent = new EventSubscriber<LocalProduct>();
public static EventSubscriber<LocalProduct> ConfirmRemoveFavoriteEvent = new EventSubscriber<LocalProduct>();
public static EventSubscriber<LocalProduct> NewFavoriteEvent = new EventSubscriber<LocalProduct>();
public static EventSubscriber<LocalProduct> RemoveFavoriteEvent = new EventSubscriber<LocalProduct>();
public static EventSubscriber<List<LocalProduct>> SetFavoritesEvent = new EventSubscriber<List<LocalProduct>>();
#endregion
}
So basically if a new activity is created it can subscribe to an EventSubscriber of its preference. Moreover, it can provide a function that receives any kind of object so it can be as flexible as it can.
The only thing that raise my concern, be sure you unsubscribe your events when the activity or the fragment View is destroyed because they can be a good source of memory leaks.

To communicate with the real-time server, you would have to use methods from the server's package. From researching, you can use the HubConnection.On method. Signalr registers the send and receive methods when you built the hub:
public class MyHub : Hub{
public async Task Send(string user, string message){
// Receive is the name of the listener method
await Clients.All.SendAsync("Receive", user, message);
}
}
Then, in your Android activities, you can register to the HubConnection (assuming you've sent the message about the edited image) of your listener method using the On method:
myHubConnection.On<string, string>("Receive", (user, message) =>
{
/* change UI here */
});

Related

Differentiate between several events on the same Eventbus

I created an application using MVP pattern, I found this tutorial link and decided to implement it in my application in order for the fragments to communicate with their activities. I moved the implementation of the Eventbus to the correspond activity presenter and fragment presenter in order to still use the MVP pattern. Now I'm facing a new problem, one of my fragments need to change two things in the activity parameters (toolbar related and ImageView drawable). Can I somehow differentiate which callback is from in the accept function?
RxBus class
public final class RxBus {
private static SparseArray<PublishSubject<Object>> sSubjectMap = new SparseArray<>();
private static Map<Object, CompositeDisposable> sSubscriptionsMap = new HashMap<>();
public static final int CHANGE_APP_BAR_LAYOUT = 0;
public static final int CHANGE_POSTER_IMAGE = 1;
#IntDef({CHANGE_APP_BAR_LAYOUT, CHANGE_POSTER_IMAGE})
#interface Subject {
}
private RxBus() {
// hidden constructor
}
/**
* Get the subject or create it if it's not already in memory.
*/
#NonNull
private static PublishSubject<Object> getSubject(#Subject int subjectCode) {
PublishSubject<Object> subject = sSubjectMap.get(subjectCode);
if (subject == null) {
subject = PublishSubject.create();
subject.subscribeOn(AndroidSchedulers.mainThread());
sSubjectMap.put(subjectCode, subject);
}
return subject;
}
/**
* Get the CompositeDisposable or create it if it's not already in memory.
*/
#NonNull
private static CompositeDisposable getCompositeDisposable(#NonNull Object object) {
CompositeDisposable compositeDisposable = sSubscriptionsMap.get(object);
if (compositeDisposable == null) {
compositeDisposable = new CompositeDisposable();
sSubscriptionsMap.put(object, compositeDisposable);
}
return compositeDisposable;
}
/**
* Subscribe to the specified subject and listen for updates on that subject. Pass in an object to associate
* your registration with, so that you can unsubscribe later.
* <br/><br/>
* <b>Note:</b> Make sure to call {#link RxBus#unregister(Object)} to avoid memory leaks.
*/
public static void subscribe(#Subject int subject, #NonNull Object lifecycle, #NonNull Consumer<Object> action) {
Disposable disposable = getSubject(subject).subscribe(action);
getCompositeDisposable(lifecycle).add(disposable);
}
/**
* Unregisters this object from the bus, removing all subscriptions.
* This should be called when the object is going to go out of memory.
*/
public static void unSubscribe(#NonNull Object lifecycle) {
//We have to remove the composition from the map, because once you dispose it can't be used anymore
CompositeDisposable compositeDisposable = sSubscriptionsMap.remove(lifecycle);
if (compositeDisposable != null) {
compositeDisposable.dispose();
}
}
/**
* Publish an object to the specified subject for all subscribers of that subject.
*/
public static void publish(#Subject int subject, #NonNull Object message) {
getSubject(subject).onNext(message);
}
}
MainPresenter class
public class MainPresenter extends BasePresenter<MainView> implements Observer<ConfigurationResponse>,Consumer<Object>
{
...
#Override
public void accept(Object o) throws Exception {
//here is the problem how can I know if I should call to changeAppBar or change Image url?
}
ClientPresenter class
public class ClientPresenter extends BasePresenter<SeriesSpecsView>
{
...
//I'm calling to those function withing the fragment when the user click on the ui
public void setPosterUrl(String posterUrl)
{
RxBus.publish(RxBus.CHANGE_POSTER_IMAGE,posterUrl);
}
public void setAppBarLayoutParams(boolean collapse)
{
RxBus.publish(RxBus.CHANGE_APP_BAR_LAYOUT,collapse);
}
}
I found a two solutions for this problem:
1) to check the object by calling instanceof function, not very effective and if I will need to send the same type of information between the two events?
2) Add another evenbus but I don't think it's logical to have separate eventbus for every event you want to have callback to your activity.
Thanks for your help
UPDATE
I encountered another problem(or at least potentially problem). I added a SwipeRefreshLayout to wrap my content(which is the framelayout, each fragment that I will have will be displayed in this container). My main reason to do it was to implement a single interface between the activity and all the fragments. Let's say you don't have a network connection I will display a message to the user to swipe down in order to try to refresh the current fragment. So far I have done this by adding SwipeRefreshLayout to each of the fragments that I have. It's basically the same code and I thought to merge all the code in one place in the activity. I would love to use the EventBus but from what I understand I would need to subscribe all the fragments to the "event" onRefresh.
How can I send the event to the appropriate fragment?
I use RxBus to transmit global events. You can also use this your way.
class RxBus {
private val busSubject: Subject<ActionEvent<out Any>> =
PublishSubject.create()
fun register( onNext:
Consumer<ActionEvent<out Any>>):Disposable{
return busSubject
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onNext)
}
fun post(event: ActionEvent<out Any>) {
busSubject.onNext(event)
}
}
open class ActionEvent<T>(val action: ActionEnum
, val event: T) {
}
You can use String in place of ActionEnum, which is just an enum class
When you post something,
getRxBus()?.post(ActionEvent(ActionEnum.CHANGE_APP_BAR_LAYOUT,collapse))
When you want to subscribe,
val disposable = rxBus.subscribe(Consumer{...})
Remember to dispose the disposale on destroy.

Where do I put a websocket chat in MVP architecture

I have a question regarding MVP architecture, this is not a technical question.
I need to implement a chat (like facebook back and fourth chat) in my Android app. My question is how do I fit this to MVP.
I have these (simplified) classes:
public class ChatFragment extends Fragment {
private final ChatFragmentPresenter presenter;
#Override
public void onResume() {
super.onResume();
List<ChatMessage> chatHistory = repository.load(); //dont think fragment should actually interact with repository
displayChatHistory(chatHistory);
}
private void displayChatHistory(List<ChatMessage> chatHistory) {
//displays chat history..
}
}
public class ChatFragmentPresenter {
private final ChatFragment fragment;
}
public class ChatClient {
public interface ChatClientCallback {
void onMessageReceived();
void onMessageSentConfirmed();
}
public void start(ChatClientCallback callback) {
//starts the chat..
}
public void send(String message) {
//sends chat message..
//if successfully sent then onMessageSentConfirmed() is called
}
}
public class ChatRepository {
public interface LoadChatCallback {
List<ChatMessage> onLoadChatSuccess();
void onLoadChatFailed();
}
public void load(GetChatCallback callback) {
// loads saved chat messages..
}
public void save(ChatMessage message) {
//saves chat message..
}
public void clear() {
//deletes all saved chat messages..
}
}
public class ChatMessage {
public ChatMessage(String text, Type type) {
this.text = text;
this.type = type;
}
public final String text;
public final Type type;
public enum Type {FROM_OTHER, FROM_USER}
}
So my questions are:
Who is going to start the chat by calling ChatClient#start(callback)?
Who is responsible for receiving messages from ChatClient and sending messages to ChatClient?
Who is responsible for saving received chat messages to the repository?
Who is responsible for loading chat history from repository during onResume()?
Should the presenter do all of this? I would prefer if there was some kind of data layer that handled all retrieving / saving / getting / setting up and closing the ChatClient, and give presenter just the data in usable form
Who is going to start the chat by calling ChatClient#start(callback)?
-- it is the Application / Service / syncadapter
Who is responsible for receiving messages from ChatClient and sending messages to ChatClient? -- you can use application / service / syncadapter
Who is responsible for saving received chat messages to the repository? the application can send it to the content provider (content provider provide safe mechanism to access sqlite)
Who is responsible for loading chat history from repository during onResume()? the flow is Activity / Fragment -> Presenter -> Repository -> Data Provider

Android NPE while second calling interface

I have some class application class extending Application. I use it as a controller for filling the database and perfoming REST calls. There is an interface:
private OnDBfilled onFilledListener;
...
public void setOnFilledListener(OnDBfilled onFilledListener) {
this.onFilledListener = onFilledListener;
}
public interface OnDBfilled {
void onFilledCallback();
}
...
When I've got required data call it to get known the Fragment, that data was successfully load from the internet and filled to database:
#Override
public void onResponse(Call call, Response response) throws IOException {
...//working with data
onFilledListener.onFilledCallback();
}
For the first time, when the call is done and database is filled I can see the result in the log:
private Application controller;
private Application.OnDBfilled listener;
...
controller = new Application();
listener = new Application.OnDBfilled() {
#Override
public void onFilledCallback() {
System.out.println("bd is filled. replacing fragments initiated editianal");
replaceFragment();
}
};
controller.setOnFilledListener(listener);
This works fine and I can see the logging message. But when I'm trying to do in the another fragment, I've got Null Pointer Exception in this line:
onFilledListener.onFilledCallback();
Here's code of another fragment, but it's almost equal:
private Application controller;
private Application.OnDBfilled filledDBListener;
...
//onCreateView
controller = new Application();
filledDBListener = new Application.OnDBfilled() {
#Override
public void onFilledCallback() {
swipeRefreshLayout.setRefreshing(false);
// fillEventListFromBD(UserData.getCity(getActivity()));
}
};
controller.setOnFilledListener(filledDBListener);
Any suggestions? Thanks!
OnDBfilled flid static
private static OnDBfilled onFilledListener;
or
fragment & viewpager use:
viewpager.setoffscreenpagelimit(9)

RxJava as event bus?

I'm start learning RxJava and I like it so far. I have a fragment that communicate with an activity on button click (to replace the current fragment with a new fragment). Google recommends interface for fragments to communicate up to the activity but it's too verbose, I tried to use broadcast receiver which works generally but it had drawbacks.
Since I'm learning RxJava I wonder if it's a good option to communicate from fragments to activities (or fragment to fragment)?. If so, whats the best way to use RxJava for this type of communication?. Do I need to make event bus like this one and if that's the case should I make a single instance of the bus and use it globally (with subjects)?
Yes and it's pretty amazing after you learn how to do it. Consider the following singleton class:
public class UsernameModel {
private static UsernameModel instance;
private PublishSubject<String> subject = PublishSubject.create();
public static UsernameModel instanceOf() {
if (instance == null) {
instance = new UsernameModel();
}
return instance;
}
/**
* Pass a String down to event listeners.
*/
public void setString(String string) {
subject.onNext(string);
}
/**
* Subscribe to this Observable. On event, do something e.g. replace a fragment
*/
public Observable<String> getStringObservable() {
return subject;
}
}
In your Activity be ready to receive events (e.g. have it in the onCreate):
UsernameModel usernameModel = UsernameModel.instanceOf();
//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy
subscription = usernameModel.getStringObservable()
.subscribe(s -> {
// Do on new string event e.g. replace fragment here
}, throwable -> {
// Normally no error will happen here based on this example.
});
In you Fragment pass down the event when it occurs:
UsernameModel.instanceOf().setString("Nick");
Your activity then will do something.
Tip 1: Change the String with any object type you like.
Tip 2: It works also great if you have Dependency injection.
Update:
I wrote a more lengthy article
Currently I think my preferred approach to this question is this to:
1.) Instead of one global bus that handles everything throughout the app (and consequently gets quite unwieldy) use "local" buses for clearly defined purposes and only plug them in where you need them.
For example you might have:
One bus for sending data between your Activitys and your ApiService.
One bus for communicating between several Fragments in an Activity.
One bus that sends the currently selected app theme color to all Activitys so that they can tint all icons accordingly.
2.) Use Dagger (or maybe AndroidAnnotations if you prefer that) to make the wiring-everything-together a bit less painful (and to also avoid lots of static instances). This also makes it easier to, e. g. have a single component that deals only with storing and reading the login status in the SharedPreferences - this component could then also be wired directly to your ApiService to provide the session token for all requests.
3.) Feel free to use Subjects internally but "cast" them to Observable before handing them out to the public by calling return subject.asObservable(). This prevents other classes from pushing values into the Subject where they shouldn't be allowed to.
Define events
public class Trigger {
public Trigger() {
}
public static class Increment {
}
public static class Decrement {
}
public static class Reset {
}
}
Event controller
public class RxTrigger {
private PublishSubject<Object> mRxTrigger = PublishSubject.create();
public RxTrigger() {
// required
}
public void send(Object o) {
mRxTrigger.onNext(o);
}
public Observable<Object> toObservable() {
return mRxTrigger;
}
// check for available events
public boolean hasObservers() {
return mRxTrigger.hasObservers();
}
}
Application.class
public class App extends Application {
private RxTrigger rxTrigger;
public App getApp() {
return (App) getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
rxTrigger = new RxTrigger();
}
public RxTrigger reactiveTrigger() {
return rxTrigger;
}
}
Register event listener wherever required
MyApplication mApp = (App) getApplicationContext();
mApp
.reactiveTrigger() // singleton object of trigger
.toObservable()
.subscribeOn(Schedulers.io()) // push to io thread
.observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread
.subscribe(object -> { //receive events here
if (object instanceof Trigger.Increment) {
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1));
} else if (object instanceof Trigger.Decrement) {
if (Integer.parseInt(fabCounter.getText().toString()) != 0)
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1));
} else if (object instanceof Trigger.Reset) {
fabCounter.setText("0");
}
});
Send/Fire event
MyApplication mApp = (App) getApplicationContext();
//increment
mApp
.reactiveTrigger()
.send(new Trigger.Increment());
//decrement
mApp
.reactiveTrigger()
.send(new Trigger.Decrement());
Full implementation for above library with example -> RxTrigger

Converting class that creates threads into Service

I have this class that creates threads, but I want to convert that class into Service hoping it wont be destroyed on orientation change.
Here is the class:
public class Getter {
private final String ip;
private final int amount, poolSize;
private Vector<Integer> results = new Vector<Integer>();
private final ExecutorService es;
private Collection<Future<?>> futures = new LinkedList<Future<?>>();
public Getter(String ip, int amount, int poolSize) {
this.ip = ip;
this.amount = amount;
this.poolSize = poolSize;
es = Executors.newFixedThreadPool(this.poolSize);
}
public boolean working() {
boolean work = false;
for (Future<?> future : futures) {
if (!future.isDone()) {
work = true;
}
}
return work;
}
public Vector<Integer> getResults() {
Collections.sort(results);
return results;
}
public int threads(){
return poolSize;
}
public void start() {
for (int i = 0; i <= amount; i++) {
futures.add(es.submit(new Get(ip)));
}
es.shutdown();
}
public void stop(){
for (Future<?> future : futures) {
future.cancel(true);
}
}
private class Get implements Runnable {
private String ip;
private Get(String ip) {
this.ip = ip;
}
public void run() {
try {
// network stuff
// adds result to results Vector.
} catch (Exception ex) {
}
}
}
}
so is this class possible to convert into Service so it would run on background no matter what, once its started?
you can add this to a manifest file that stops your app getting destroyed on orientation change:
android:configChanges="orientation"
but if you want to make a service then just copy this example:
http://developer.android.com/reference/android/app/Service.html
you could add the class to the service and then just add an accessor to your service connection.
You can also add something to the manifest file to let your service start when the device is turned on
see here
Trying to start a service on boot on Android
If you want a Service which runs "no matter what", you might want to set it as foreground.
Yes. I think this would make a good IntentService. Your next-best choice is probably AsyncTask.
More:
Generally, I would say, if the behavior of the background task is closely tied to whatever starts it (e.g., an Activity, Fragment, Service, whatever), then make it an AsyncTask. If it is self-contained and meant to serve several different components in a modular fashion, make it a Service. In either case, an AsyncTask or IntentService is very easy to make.

Categories

Resources