I implemented a messaging system for my android application. Here is how it works :
The Users sends a message
The message is add in the Database and then add to the listview Adapter
To get the messages from the other users, I implement a background thread that "poll" the server. If an older message is found, it is then add to the listview adapter if the message is not already in the adapter.
The problem that I'm facing is that sometimes the message send by the user is displayed twice. I think that I understand the problem : the polling gets the new added message as not already in the listview and add it to the listview adapter. Is there a way to avoid that behaviour?
Here is how I checked if the polled message is already in the adapter :
public class MessageListViewAdapter : BaseAdapter
{
List<Model.Message> messages = new List<Model.Message>();
Context context;
public MessageListViewAdapter(Context context,List<Model.Message> messages)
{
this.context = context;
this.messages = messages;
}
public void add(Model.Message message)
{
if (!messages.Contains(message))
{
this.messages.Add(message);
NotifyDataSetChanged(); // to render the list we need to notify
}
}
public override int Count =>messages.Count;
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return position;
}
Let me know if you need other informations, maybe my explanation is not clear or not complete enough.
Thanks in advance,
Lio
What you should do is separate the listview from the storage of the messages.
Instead of putting items into a listview, you should maintain a list of messages in an array. When you type a message you can add it to the array, along with a date time stamp or GUID. When you poll, you also add entries to the array if they don't exist. Create a method called AddItemToListView() or similar to do this
Then keep population of the list view separate - create a method called UpdateListView() and call it after polling and after you type a message and add it.
Inside AddItemToListView() you can put logic to check if the item is already in the list, and if it is, don't add it. By either comparing the date time, or the GUID.
I would definitely check the Date or a GUID to compare messages because anything else is not reliable, especially object comparisons which is what it looks like you're doing.
Related
I'm currently writing a custom logging method for my Web API where users can purchase items. This log method will log all the steps the users takes while following a purchase process so support can later track the steps. There are multiple steps like:
Creating a order
Updating a order
Purchasing a item
Receiving status
etc..
This method will return one 'Event' object where all the necessary log information is combined, for example the LogLevel, Message, UserId and more, and write this to a database.
Before i can reach this point, i have to create the very useful log message. The message is based on these two enums (explanation is a bit simplified):
ActionName - At which step in my process is this log event called
ActionOrigin - Is the recorded log event from my front end or backend system...
It is also based on a object where the necessary log values, like order id for example, are provided.
The log class where the log event method is defined is a scoped injected class so i can log events every where critical in my code.
The first thing that came into my mind was creating a switch statement and create the messages based on the correct case. But this would combine 2 switch statements and quickly started to look like a mess.
I did some research and found the strategy pattern. I'm not completely sure if this can help me? Are there any other ideas or examples?
Whenever you are working on an object model and find yourself writing a ton of switch statements, it usually means you've put the class-specific logic in the wrong place. You should put it with the class itself, not the class that consumes it.
To put it another way, your logger should not know how to log each and every type of event. That would be a maintenance nightmare. Instead, it should know how to log a common object (e.g. a string), and each event should itself know how to create that common object, via a common method that the logger knows about. That is the only thing it needs to know.
Here is a simple example. In this case, the logger accepts any type of LoggableEvent and calls its Serialize() method to figure out how it gets added to the common log. The event itself is responsible for knowing how to serialize itself.
abstract class LoggableEventBase
{
public string ActionName { get; }
public string ActionOrigin { get; }
public LoggableEventBase(string actionName, string actionOrigin)
{
ActionName = actionName;
ActionOrigin = actionOrigin;
}
public virtual string Serialize()
{
return string.Format("{0} {1}", ActionName, ActionOrigin);
}
}
class CreateOrderEvent : LoggableEventBase
{
protected readonly List<Item> _items;
protected readonly int _orderId;
public CreateOrderEvent(string origin, int orderID, List<Item> items) : base("CreateOrder", origin)
{
_orderId = orderID;
_items = items;
}
public override string Serialize()
{
return base.Serialize() + string.Format(" {0} {1}", _orderId, string.Join(",", _items.Select(item => item.SKU)));
}
}
Now the actual logging logic is rather simple-- no switch statements or anything else that needs to know what the event is:
class Logger : ILogger
{
public void Log(LoggableEventBase eventToLog)
{
Write(eventToLog.Serialize());
}
protected virtual void Write(string message)
{
//Write the message to a log file
}
}
To add additional event types, you just need to define the new class (and override Serialize()). You never have to go back and modify the Logger class. This is more consistent with the Open-Closed Principle than your existing solution.
This is a design pattern question. You might want to read on different patterns used for the language/framework you are using. It seems like you are trying to avoid writing your logs in line. One way of doing it would be to define the format for your different messages in a constant and use string interpolation (or simple concatenation) to build the message with a log() method.
Example (I'll do my best to write proper C#, please edit any mistakes or inadequacies):
class Logger {
// If you want personalized messages for different actions or origins, define their template constants and create different methods for building them.
public const string ORDER_PROGRESS_MSG_TMPL = "Action:{0}, Origin:{1}, OrderId:{3}";
void log_order_progress(string actionName, sting actionOrigin, string orderId){
Console.WriteLine(
ORDER_PROGRESS_MSG_TMPL, actionName, actionOrigin, orderId
);
}
}
Order
class Order {
...
void create(int orederId){
Logger.log_order_progress(ActionEnum.CREATING, OriginEnum.BACK_END, orderId)
// Do some stuff here to create order
Logger.log_order_progress(ActionEnum.UPDATING, OriginEnum.BACK_END, orderId)
// etc
}
}
This is a way of doing it, you could modularize it more by having templates in their own class. Also you could create (or better: use an existing logging framework) to differentiate level of logging (debug, info, error) as #Sandeep Sharma described.
You can create multiple methods in your Logger class, each for specific scenario.
The methods can be :
info() = for logging some information.
debug() = for debugging.
error() = for logging an error event.
Let's say you want to log an event of purchasing an item , and when user does buy action, you can pass information to the logger.info() method.
If you encounter an error, or a certain action or condition was not fulfilled , you can pass data to the method error() , which will log error in your case.
For messages :
1. Action Name - You can pass the method name or route path that was called by action of an user.
2. Action Origin - Provide details like user name , full path , action type etc.
You can also maintain fields like 'timestamp' and some 'unique-identifier' for better logging of events.
I am working on an app that plays music. Now, I have a function in my main class that chooses a random new song and plays said song:
private void ChooseRandomNewSongAndPlay(bool songHasCompleted)
{
Random rnd = new Random();
int rndValue = rnd.Next(0, Mp3ObjectSmall.Count());
int currentPos = 0;
if (!songHasCompleted)
{
currentPos = mediaPlayer.CurrentPosition; // if song infact has completed, reset position to else save current position (when next has been pressed)
}
WriteSeekingToDataBase(currentPos, CurrentSongObject);
mediaPlayer.Stop();
if (Android.Net.Uri.Parse(CurrentSongObject.Mp3Uri) != null)
{
PhotoAlbumAdapter.OldSongUri = Android.Net.Uri.Parse(CurrentSongObject.Mp3Uri);
}
PhotoAlbumAdapter.NewSongUri = (Android.Net.Uri.Parse(Mp3ObjectSmall[rndValue].Mp3Uri));
PlayMusic((Android.Net.Uri.Parse(Mp3ObjectSmall[rndValue].Mp3Uri)));
}
But I also set up a broadcast receiver, so when the user is in his car and clicks on next song from the car stereo, I also want the above function to be played.
But here is the problem:
I cannot make this above function public static since it calls other non static functions. I would have to make those static too, but that would cause many, many other errors and is not a good solution at all I believe.
Also, I cannot create a new object of my main class in within the broadcast receiver as such: class xy = new class(). I cannot do that, because that would also create another object of my mediaplayer object, but this object needs to be the same to skip to a next some. If it isnt, just anoither song is played on top of the first song which of course is also not good.
Lastly, I cannot just hand over the class as a parameter to the constructor of the broadcast receiver. I am getting told then that the braodcast receiver needs to have a "standart constructor" so I cannot alter the parameters.
Unfortunately, these 3 optiones are all I believe I have and neither seems to work. What I really, really do not want to do is to copy paste all functions from my main class into the broadcast receiver for obvious reasons.
Can you guys help me out here?
Thank you!
You didn't specified if you broadcast receiver is an activity-local broadcast receiver or application-wide broadcast receiver.
LocalBroadcastReceivers can be created as activity's property and let you broadcast messages within the app, while classic BroadcastReceivers can let you respond to an action coming from outside your app even if your app is closed.
If you have a BroadcastReceiver you can, when it receives a message from, make it republish the message through LocalBroacastManager.
Also your activity must setup a LocalBroadcastReceiver that handles that particular internal broadcast message.
Doing so, you don't worry about Activity state. When the global BroadcastReceiver is invoked, it broadcast the message inside the application, and if there's some receiver listening to they will be invoked all, otherwise the message is simply ignored with no bugs, retain cycles or crashes.
I suppose you are using Xamarin Android.
You can get the full documentation about receivers here
https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/broadcast-receivers
The easiest way is to just create a single static instance from your class and fire the functions:
public static Activity_Player Instance;
then in the broadcast recevier:
Instance.MYFUNCTION
How about creating a property on the broadcast receiver and using that to pass a reference to the main class?
public class MyBroadcastReceiver
{
// Constructor
public MyBroadcastReceiver() {}
// property
public MyMainClass {get;set;}
}
public MyMainClass
{
private void CreateBroadcastReceiver()
{
var br = new MyBroadcastReceiver();
br.MyMainClass = this;
}
}
I have a MvxViewController and in the ViewDidLoad i bind the button click to the viewmodel. When the button is clicked I open another view in which I will need to return a string back to my first view
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand);
set.Apply();
}
public ICommand MyButtonCommand
{
get
{
_myButtonCommand = _myButtonCommand ?? new MvxCommand(MyButtonCommandClick);
return _myButtonCommand;
}
}
private void MyButtonCommandClick()
{
ShowViewModel<ViewModelNumber2>();
}
After some logic is ran in my second view I want to return the string
private void SomeMethodInViewModelNumber2()
{
//Raise event that will get pickup up in MyView
//Or somehow get "SomeString"
if (OnMyResult != null)
OnMyResult ("SomeString");
}
The problem is that I don't want to send the string back using the messenger. I have my reasons but basically because ViewModelNumber2 can be opened from many different places and works slightly different and managing the different messages that would need to be sent back and where to subscribe to these messages would be a mess
Is there any way that I can do something like the below?
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand).OnMyResult((myString) => {Process(myString)});
set.Apply();
}
Or perhaps when I create ViewModelNumber2 I should pass a callBack into the constructor and use that to send the string back from ViewModelNumber2 to MyView1ViewModel
ShowViewModel<ViewModelNumber2>(OnMyResult);
What is the best way to do this?
In short: I don't know what "the best way to do this" is.
The area of ChildViewModel-ParentViewModel messages is complicated - especially because on platforms like Android using Activities and WindowsPhone using Pages you have no guarantee that the ParentViewModel will be in memory when the Child is shown. (Note: this isn't a problem on iOS as its "app suspension" model is simpler)
When I do need one ViewModel returning data to another, then:
Often I try to implement the data collection views as "popup dialogs" rather than as "whole pages" - this makes the parent-child ViewModel relationship more correct - and ensures the parent ViewModel will be in memory when the child closes.
Often I recommend people use a Messenger-based technique like Greg describes in: http://www.gregshackles.com/2012/11/returning-results-from-view-models-in-mvvmcross/
often I've done this messaging via background services rather than via ViewModel-ViewModel messaging (a bit like the way screens are updated in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/tree/master/N-17-CollectABull-Part6)
Another solution I've used is to:
implement a IDropBoxService singleton - with an API like void Deposit(key, value) and bool TryCollect(key, out value)
allow the closing "child" ViewModels to leave "values" when they close
implement IVisible functionality in my "parent" ViewModel - like in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-42-Lifecycles/Lifecycle.Core/ViewModels/FirstViewModel.cs#L10
use the IVisible method to check for messages
To implement anything perfectly, you really should add serialisation code to make sure this all works during "tombstoning" on all platforms... but often this is overkill - for a simple data collection dialog users often don't need "perfect" tombstoning support.
I have a C# Windows Service that routes incoming emails and twitter comments to available Agents (I don't really - but the app does something very similar). So, I have a list of Agents, and logic to route these emails and tweets to available agents.
How does my code look?
I have an AgentManager class that keeps track of which agents are available, their skill levels, etc.
I have an EmailManager class that routes Emails to Agents.
I have a TwitterManager class that routes Tweets to Agents.
So - logic pertaining to Agents, such as the list of Agents, who is available, who has capacity for a Tweet, who has capacity for an Email, etc is all in the AgentManager.
Now, when EmailManager detects that there is a new email and needs to assign it to an Agent, I want
to get my list of agents from AgentManager(rather than going back to the database or keeping a separate list of Agents in EmailManager).
My initial thought is to pass AgentManager to EmailManager by reference. The reason I want to do this is so as Agents change state, are added/removed, etc - EmailManager will always be working wit
h the latest AgentManager (and therefore the latest Agent list). Somehow - this feels dirty. I know it is out of fashion to pass by ref, but it seems to be a good way to handle this. Am I doing it wrong?
EDIT:
I am passing the AgentManager by reference for storage, not to change it in the EmailManager class.
From your descriptions seems more soud to go the other way.
An AgentManager process EMails and Tweets and knows everything of its Agents -
So it should have a method that receive a list of EMails/Tweets and process them.
Because we are speaking of reference-types the question about passing by ref is a bit unclear.
For example:
AgentManager ag = new AgentManager();
EMailManager eg = new EMailManager();
TweetManaget tg = new TweetManager();
eg.LoadEMail();
List<EMails> mails = eg.GetMailsList();
tg.LoadTweet();
List<Tweets> tws = tg.GetTweetsList();
ag.ProcessData(mails, tws);
EDIT: Looking at the comment from OP I have thought of another strategy
Let the EMailManager and TweetManager declare an Event to which the AgentManager subscribe-
eg.EmailReceived += ag.NotifyEmail;
tg.TweetPolled += ag.NotifyTweet;
public class EventManager
{
public delegate void OnMailReceived(EMails m);
public event MailReceived;
........
private void GetMail()
{
EMails m;
.....
if(MailReceived != null)
MailReceived(m);
}
}
public class AgentManager()
{
public void NotifyEMail(EMails m)
{
.....
}
}
I have tabs application, each Tab contain many views... And in each Tab the save mode is global.
If I leave current tab containing changes, a pop-up appear asking user confirm with or without save or cancel. After save, others opened tabs is reloaded.
How would you do, to detect the changes? To save only the views changed? And then to propagate the changes to the other tabs?
My first think is, to implement a IsModified property and ReloadTab method in each ViewModel, but is not really viable solution, each setter must change this property... Any idea ?
class MyViewMode
{
...
public bool IsModified { get { return MyViewModel1.IsModified || MyViewModel2.IsModified ... || _isModified }
...
}
[EDIT]
I hesitate between EventAggregator approach and Messenger (MVVM Light Toolkit implementation)...
I think I am going to create Events/Messages Domain representing each command generates a tab reloading, and create a Handler (Service receive all messages/events and send specific messages/events for each View to be reloaded). Any advices ?
I've done this using a global event broker. The idea is that events such as save which have global scope will pass through this broker class.
The event broker is a singleton, where each class will register it's handlers. The handler would be registered using attributes:
[EventSubscribe(EventNames.Save)]
private void OnSaved(GlobalEventArgs args)
{
// do something on saved
}
And each object that wishes to push itself to the broker would do it like this:
EventBroker.Instance.Register(this);
How does this relate to your tabs issue? Fairly simply, when one tab saves, then that should raise the save event via the EventBroker:
EventBroker.Instance.Publish(
EventNames.Save,
new SavedGlobalEventArgs(typeof(YourModel)));
And all your other tabs would handle the event such as this:
[EventSubscribe(EventNames.Save)]
private void OnSaved(GlobalEventArgs args)
{
var savedArgs = (SavedGlobalEventArgs)args;
if(savedArgs.SavedType == typeof(YourModel)
{
this.Model.Refresh();
}
}
You'll still have to handle the saved event on each tab that might require a refresh when another tab has done something, but this keeps the code relatively nice and simple without having to put all kinds of crap in. Can also extend it outside of a save event, make some other global events that may be useful:
UserCreated
UserLoggedIn
SearchInitiated
whatever; i don't know the context of your app - but the broker is a really nice way to deal with sharing knowledge in a tabbed environment.
Please let me know if you want me to send some code :)
The best way to do this is by modifying each setter. If not, you cannot know exactly if your model data has changed and I wouldn't suggest tracking modified changes in the UI. Something like this should give you a good head start.
public class Person : IModifiable
{
private bool _markDirty;
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (value != _Name)
_markDirty = true;
_Name = value;
}
}
public bool IsDirty()
{
return _markDirty;
}
}
public interface IModifiable
{
public bool IsDirty();
}