Is it possible to place Transformations.switchMap in a repository? - java

I'm attempting to implement a LiveData reference to data collected via gps and bluetooth, that is also grouped by a foreign key. I don't understand why the Transformations.switchMap doesn't trigger once I create a new foreignkey.
I've moved both, the observer and foreignkey creation around, the LiveData always returns null.
Both, the Database and the Repository are Singletons.
Activity.java
public class Activity extends AppCompatActivity {
...
private ViewModel mViewModel;
#Override
onCreate {
...
mViewModel = ViewModelProviders.of( this ).get( ViewModel.class );
init();
}
private class ObserverManager {
private List<Observer> observers = new ArrayList<>();
private List<LiveData> livedata = new ArrayList<>();
public void registerObserver( TextView view, int hid, int uid ) {
Observer observer = null;
LiveData ld = null;
ld = mViewModel.getLatestData();
observer = ( Observer<Float> ) ( #Nullable final Float data ) -> {
// String formatting...
};
observers.add( observer );
livedata.add( ld );
ld.observeForever( observer );
}
public void logValue( int index ) {
Log.d( "OBSERVERMANAGER", String.valueOf( livedata.get( index ).getValue() ) );
}
}
final ObserverManager mObserverManager = new ObserverManager();
}
During init() The foreignkey is inserted and updated, then the observer is attached dynamically.
The service logs the correct foreignkey and inserts values to eData entity, but the Transformations.swapMap never updates, or shows a value other than null.
ViewModel.java
...
private LiveData<Integer> mLiveActivityId;
private LiveData<Float> mLatestData;
ViewModel( Application application ) {
...
mLiveActivityId = mRepository.getLiveActivityId();
mLatestData = mRepository.getLatestData();
}
public LiveData<Float> getLatestData() {
return mLatestData;
}
Repository.java
...
private LiveData<Integer> mLiveActivityId;
private LiveData<Float> mLatestData;
Repository( Application application ) {
...
mLiveActivityId = mDataDao.getLiveActivityId();
mLatestData = Transformations.switchMap( mLiveActivityId, aid -> mDataDao.getLatestData( aid, 0 ) );
}
...
LiveData<Float> getSpeedGPSLatest() {
return mSpeedGPSLatest;
}
DataDao.java
#Transaction
#Query( "SELECT id FROM eActivity WHERE active = 1" )
LiveData<Integer> getLiveActivityId();
#Transaction
#Query( "SELECT data FROM eData WHERE aid = :aid AND source = :source AND time = " +
"( SELECT MAX(time) time FROM eData WHERE aid = :aid AND source = :source )" )
LiveData<Float> getLatestData( int aid, int source );
Is it even possible apply Transformations in the repository? So far I have only seen examples with them applied in the ViewModel. Unfortunately, due to some data sharing the same entity with a type field, that would mean I have to pass the LiveData objects back from a function in the repository, which I thought is wrong.
I have also read that switchMap creates a new LiveData object, would this somehow affect how ViewModel cannot read the new object in the repository?
If more code is required to understand the problem, just ask. Am totally stumped on this.

Simply put, yes you can. The Transformations.switchMap() was not the issue with LiveData not updating. Still cannot figure out how to communicate to the room db from a service, ie the follow up question Insert to room in service not updating LiveData in activity, but also solved that by doing things differently.

Related

How to sort LiveData in room database by clicking a sort button?

I'm trying to figure out how to sort my recyclerview's data by click of a button. So far I've got it sorted initially as I start the application and add new items on the list. But I'd like to be able to sort it by different parameters as I'm using the app, by clicking a button "Sort by distance".
I'm using the MVVM architecture and am working with LiveData.
Here's my MainActivity (Not completely, but irrelevant parts are omitted)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
final RunListAdapter adapter = new RunListAdapter(new RunListAdapter.RunDiff());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
mRunViewModel = new ViewModelProvider(this).get(RunViewModel.class);
mRunViewModel.getAllRuns().observe(this, runs -> {
adapter.submitList(runs);
});
sortByDistanceButton = findViewById(R.id.sortByDistance);
sortByDistanceButton.setOnClickListener(view -> {
//DO SOMETHING
});
Here's my RunViewModel
public class RunViewModel extends AndroidViewModel {
private RunRepository mRepository;
private final LiveData<List<Run>> mAllRuns;
public RunViewModel(Application application) {
super(application);
mRepository = new RunRepository(application);
mAllRuns = mRepository.getAllRuns();
}
LiveData<List<Run>> getAllRuns() {
return mRepository.getAllRuns();
}
public void insert(Run run) {
mRepository.insert(run);
}
public void delete(Run run) {
mRepository.delete(run);
}
}
Here's my RunRepository
public class RunRepository {
private RunDao mRunDao;
private LiveData<List<Run>> mAllRuns;
RunRepository(Application application) {
RunRoomDatabase db = RunRoomDatabase.getDatabase(application);
mRunDao = db.runDao();
mAllRuns = mRunDao.getRunsByDate();
}
LiveData<List<Run>> getAllRuns() {
return mRunDao.getRunsByDate();
}
void insert(Run run) {
RunRoomDatabase.databaseWriteExecutor.execute(() ->
mRunDao.insert(run));
}
public void delete(Run run) {
RunRoomDatabase.databaseWriteExecutor.execute(() -> {
mRunDao.deleteRun(run);
});
}
}
And finally here's my RunDao
public interface RunDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
void insert(Run run);
#Query("DELETE FROM run_table")
void deleteAll();
#Query("SELECT * FROM run_table ORDER BY date")
LiveData<List<Run>> getRunsByDate();
#Query("SELECT * FROM run_table ORDER BY distance ASC")
LiveData<List<Run>> getRunsByDistance();
#Query("SELECT * FROM run_table ORDER BY duration ASC")
LiveData<List<Run>> getRunsByDuration();
#Delete
void deleteRun(Run run);
}
I've tried to pass on a "choice" argument and setting the choice to a different one, but to no avail. I've tried to read about other's having similar problems, but I haven't been able to figure how to apply any of them to my situation. I'm completely stumped.
Here are some SO questions I've read and tried to wrap my head around.
This one actually is very similar to mine and the original question asked figured out a solution, but never shared the details of the implementation =)
How to sort LiveData from Room Database? Doing a button to switch recyclerview's item's order
https://www.reddit.com/r/androiddev/comments/n9nraa/i_want_to_sort_a_livedatalistplayers_from_room_in/
how to properly sort a list using repository pattern and live data in an MVVM android application?
Moving from top to bottom,
You are observing mRunViewModel.getAllRuns()
Your mRunViewModel.getAllRuns() is mRepository.getAllRuns();
Your mRepository.getAllRuns(); is mRunDao.getRunsByDate();
So you are so far just unconditionally routing LiveData from Room to your view.
Simplest way to achieve switching could be this:
Change your RunDao to have single parameterized method like this example
#Query("SELECT * FROM Persons ORDER BY
CASE WHEN :isAsc = 1 THEN first_name END ASC,
CASE WHEN :isAsc = 0 THEN first_name END DESC")
List<Person> getPersonsAlphabetically(boolean isAsc);
(your implementation will be different, including return type and parameter type, but the above should give the idea)
Route the above dao method into repo and use updated repo inside vm. Every time view decides it's time to show some different sorting order, it will talk to view model, viewmodel will pull repo's method and repo will pull dao with proper parameter, dao will give you back livedata with properly sorted content.

How to collect data from MongoDB in Android Studio?

I am experimenting with the integration of MongoDB on Android using Java as the language.
I followed the guide provided by MongoDB to configure the Atlas account and the Realm to communicate with.
After that I tried implementing CRUD methods, for insertions I did not encounter any problems, while for queries I did.
In particular to get all the objects of a certain class in a certain collection.
I used this method, as suggested by the wiki (https://www.mongodb.com/docs/realm/sdk/java/quick-start-local/)
RealmResults<Contact> contacts = backgroundThreadRealm.where(Contact.class).findAll();
inserted in a class made to handle background tasks:
public class BackgroundTasks implements Runnable {
#Override
public void run() {
String realmName = "MyApp";
RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build();
Realm backgroundThreadRealm = Realm.getInstance(config);
// all Tasks in the realm
RealmResults<Contact> contacts = backgroundThreadRealm.where(Contact.class).findAll();
Log.v("Contacts", String.valueOf(contacts.size()));
backgroundThreadRealm.close();
}
}
While in the MainActivity I inserted this (looks like a battlefield, maybe I inserted stuff I won't even need, but I was experimenting):
// initialize mongodb realm
realm.init(this);
// open realm
String realmName = "MyApp";
RealmConfiguration config = new RealmConfiguration.Builder().name(realmName).build();
backgroundThreadRealm = Realm.getInstance(config);
app = new App(new AppConfiguration.Builder(appId).build());
User user = app.currentUser();
mongoClient = user.getMongoClient("mongodb-atlas");
mongoDatabase = mongoClient.getDatabase("MyApp");
MongoCollection<Document> mongoCollection = mongoDatabase.getCollection("Contacts");
FutureTask<String> task = new FutureTask(new BackgroundTasks(), "test");
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(task);
#Override
protected void onDestroy() {
super.onDestroy();
// the ui thread realm uses asynchronous transactions, so we can only safely close the realm
// when the activity ends and we can safely assume that those transactions have completed
backgroundThreadRealm.close();
}
I get no exceptions but the Log:
Log.v("Contacts", String.valueOf(contacts.size()));
results in 0.
Yet I have these contacts in the DB (they have different IDs):
And the related model in java:
#RealmClass
public class Contact extends RealmObject implements Serializable {
#PrimaryKey
private String nameSurname;
private int age;
// Drawable resource ID
private int imageResourceId;
public Contact() {
}
public Contact(String name, String surname, int age, int imageResourceId) {
this.nameSurname = name+" "+surname;
this.age = age;
this.imageResourceId = imageResourceId;
}
// In addition all the getters and setters
Can you help me?
It would also help to understand when it's appropriate to make synchronous and asynchronous calls, because I guess I've confused the implementations in general.
I'd like to use synchronous calls to get all the objects in the DB and then display them on the app, but it seems ill-advised online so I tried asynchronous, although I'm sure I did something wrong..
Thanks

Binding of properties in custom JavaFX components

I am currently developing some custom JavaFX components in my application and stumbled upon an issue with the binding.
The following is a component which allows to rate an item with one to five stars (Rating is an enum with the possible ratings).
public final class RatingControl extends HBox {
private final ReadOnlyObjectWrapper<Rating> ratingProperty = new ReadOnlyObjectWrapper<>( Rating.NO_RATING );
public RatingControl( final Rating initialRating ) {
createVisibleStars( );
ratingProperty.set( initialRating );
ratingProperty.addListener( ( observable, oldValue, newValue ) -> updateVisibleStars( newValue ) );
updateVisibleStars( ratingProperty.get( ) );
}
private void createVisibleStars( ) {
// omitted
}
private void onStarClicked( final Rating rating ) {
if ( ratingProperty.get( ) == rating ) {
setRating( Rating.NO_RATING );
} else {
setRating( rating );
}
}
private void updateVisibleStars( final Rating newValue ) {
// omitted
}
public ReadOnlyObjectProperty<Rating> ratingProperty( ) {
return ratingProperty.getReadOnlyProperty( );
}
public void setRating( final Rating rating ) {
ratingProperty.set( rating );
}
}
So my idea is as following: A click on a star changes the internal state (ratingProperty) by calling setRating. This can also be triggered from the outside. An update of this property changes the visual representation and makes sure that the visible rating is updated as well. This, again, can also be read readonly from the outside (via ratingProperty( )).
This works, but I am not happy with it. The calling component has to do something like the following (assuming that model has a Property named rating):
final RatingControl ratingControl = new RatingControl( model.rating( ).getValue( ) );
ratingControl.ratingProperty( ).addListener( ( observable, oldValue, newValue ) -> model.rating( ).set( newValue ) );
Using "normal" JavaFX components one would assume that a binding like the following is possible:
final RatingControl ratingControl = new RatingControl( );
ratingControl.ratingProperty( ).bindBidirectional( model.rating( ) );
final RatingControl ratingControl = new RatingControl( );
ratingControl.ratingProperty( ).bind( model.rating( ) );
So I wonder how I would achieve this. If I simply allow access to my internal property, I would not be able to set it when clicking a star, as a bound property cannot be set ("A bound value cannot be set).
After thinking about this a little bit more, I realized that it is not the bindings per se that are a problem, but only the unidirectional binding, as this leads to a contradiction when changing the value from within the component.
I tested the same structure with a StringProperty and a default TextField and noticed something. When using a bidirectional binding between the property and the field's textProperty, everything works as expected. However, when using a unidirectional binding, the text field no longer allows the user to change the input (this can actually be seen in TextInputControl.replaceText).
So I think it is not a problem to expose the internal property for the rating in my case. I just have to make sure that my onStarClicked method checks whether the property is bound or not.
private final ObjectProperty<Rating> ratingProperty = new SimpleObjectProperty<>( Rating.NO_RATING );
...
private void onStarClicked( final Rating rating ) {
if ( !ratingProperty.isBound( ) ) {
if ( ratingProperty.get( ) == rating ) {
setRating( Rating.NO_RATING );
} else {
setRating( rating );
}
}
}
public ObjectProperty<Rating> ratingProperty( ) {
return ratingProperty;
}
This allows to bind a property both uni- and bidirectional without leading to an exception in any case.

How can I create row and children related by ForeignKey with RoomDB in a clean way?

This question is somehow related to my last question, because it is the same project but now I am trying to go one more step forward.
So, in my previous question I only had one table; this time I have two tables: the new second table is supposed to contain related attributes for the rows of the first table, in a OneToMany relationship. So, I store a ForeignKey in the second table that would store the Row ID of the first table's related row (obviously).
The problem is this: the intention is creating both registers (parent and child) at the same time, using the same form, and ParentTable uses AUTO_INCREMENT for his PrimaryKey (AKA ID).
Due to how RoomDb works, I do the creation using a POJO: but after insertion, this POJO won't give me the auto-generated ID as far as I know... so, the only workaround I am able to imagine is, when submitting the form, first make the INSERT for the parent, then using one of the form's fields that created the parent to make some kind of "SELECT * FROM parent_table WHERE field1 LIKE :field1", retrieving the ID, and then use that ID to create the child table's POJO and perform the next INSERT operation. However I feel something's not right about this approach, the last time I implemented something similar this way I ended up with a lot of Custom Listeners and a callback hell (I still have nightmares about that).
About the Custom Listeners thing, it is the solution I ended up choosing for a different problem for a different project (more details about it in this old question). Taking a look to that old question might help adding some context about how misguided I am in MVVM's architecture. However, please notice the current question has nothing to do with WebServices, because the Database is purely local in the phone's app, no external sources.
However, I am wondering: isn't this overkill (I mean the INSERT parent -> SELECT parentID -> INSERT child thing)? Is it inevitable having to do it this way, or there is rather a more clean way to do so?
The "create method" in my Repository class looks like this:
public void insertParent(Parent parent) {
new InsertParentAsyncTask(parent_dao).execute(parent);
}
private static class InsertParentAsyncTask extends AsyncTask<Parent, Void, Void> {
private final ParentDao parent_dao;
private InsertParentAsyncTask(ParentDao parent_dao) {
this.parent_dao = parent_dao;
}
#Override
protected Void doInBackground(Parent... parents) {
parent_dao.insert(parents[0]);
return null;
}
}
Trying to follow Mario's answer, I changed this method in my parent's DAO:
// OLD
#Insert
void insert(Parent parent);
// NEW (yes, I use short type for parent's ID)
#Insert
short insert(Parent parent);
EDIT2:
Now, I am trying to make changes to my Repository's insert AsyncTask, like this:
private static class InsertParentAsyncTask extends AsyncTask<Parent, Void, Short> {
private final ParentDao parent_dao;
private InsertParentAsyncTask(ParentDao parent_dao) {
this.parent_dao = parent_dao;
}
#Override
protected Short doInBackground(Parent... parents) {
short parent_id;
parent_id = parent_dao.insert(parents[0]);
return parent_id;
}
#Override
protected void onPostExecute(Short hanzi_id) {
// TODO ??? What now?
}
}
LONG STORY SHORT
It worked for me this way down here, but this ain't clean code (obviously):
// TODO I am aware that AsyncTask is deprecated
// My Repository class uses this
public void insertParentAndChildren(Parent parent, String[] children_list) {
new InsertParentAndChildrenAsyncTask(parent_dao, children_list).execute(parent);
}
private static class InsertParentAndChildrenAsyncTask extends AsyncTask<Parent, Void, Short> {
private final ParentDao parent_dao;
private String[] children_list;
private InsertParentAndChildrenAsyncTask(ParentDao parent_dao, String[] children_list) {
this.parent_dao = parent_dao;
this.children_list = children_list;
}
#Override
protected Short doInBackground(Parent... parents) {
short parent_id;
Long row_id = parent_dao.insert(parents[0]);
parent_id = parent_dao.getIdForRowId(row_id);
return parent_id;
}
#Override
protected void onPostExecute(Short parent_id) {
// Second "create method" for children
for (int n = 0; n < children_list.length; n++) {
Child child = new Child();
child.setParentId( parent_id );
child.setMeaning( children_list[n] );
// My Repository has this method as well
insertChildStaticMethod(child);
}
}
}
You are on the right track. A clean way would be to wrap it in a function like this:
fun saveParent(parent: Parent): Int {
val rowId = parentDao.insert(parent) // Returns Long rowId
val parentId = parentDao.getIdForRowId(rowId) // SELECT id FROM table_parent WHERE rowid = :rowId
return parentId
}
This function could be part of a repository class to make it even more clean.
Your functions in DAO can return the rowId and Parent.ID like this:
#Insert
fun insert(parent: Parent): Long
#Query("SELECT ID FROM table_parent WHERE rowid = :rowId")
fun getIdForRowId(rowId: Long): short
If you want to get basic functionality working first, you can call the Room database functions on the main thread when you build your database with allowMainThreadQueries():
MyApp.database = Room.databaseBuilder(this, AppDatabase::class.java, "MyDatabase").allowMainThreadQueries().build()
Like this, you can postpone background processing to later. If you have specific questions about that subject, it is better to ask a separate question.
I think you could try SELECT last_insert_rowid() as a query on room (you write it just like that no need to reference any table). This statement returns the rowid of the last insertion into your database. By default rowId is what most sql DBs use as primary keys when you define them as auto incremental integers. so I guess you would define the following method in your DAO
#Query("SELECT last_insert_rowid()")
public abstract int getLastId()
#Insert
void insert(Parent parent)
then you can use it together with your insert statement in a transaction. like so
#Transaction
public int insertAndGetPrimaryKey(Parent parent){
insert(parent);
return getLastId();
}
it is important to use transaction as else the id delivered could be the wrong one if in your app multiple threads could potentially modify the tables at the same time.
btw I would not use short for a primary key. not only is it short XD (only 32k capacity) but the satndard is really to use Integer (4 bn capacity). if these were the 80's id be all for it (those are 2 entire bytes that you are saving after all XD) but now a days memory is cheap and abundant and as i said integer is what DBs work with by default.

Efficient state machine pattern in java

I am writing a java simulation application which has a lot of entities to simulate. Each of these entities has a certain state at any time in the system. A possible and natural approach to model such an entity would be using the state (or state machine) pattern. The problem is that it creates a lot of objects during the runtime if there are a lot of state switches, what might cause bad system performance. What design alternatives do I have? I want performance to be the main criteria after maintainability.
Thanks
The below code will give you high performance (~10ns/event) zero runtime GC state machine implementation. Use explicit state machines whenever you have a concept of state in the system or component, this not only makes the code clean and scalable but also lets people (not even programmers) see immediately what the system does without having to dig in numerous callbacks:
abstract class Machine {
enum State {
ERROR,
INITIAL,
STATE_0,
STATE_1,
STATE_2;
}
enum Event {
EVENT_0,
EVENT_1,
EVENT_2;
}
public static final int[][] fsm;
static {
fsm = new int[State.values().length][];
for (State s: State.values()) {
fsm[s.ordinal()] = new int[Event.values().length];
}
}
protected State state = State.INITIAL;
// child class constructor example
// public Machine() {
// // specify allowed transitions
// fsm[State.INITIAL.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
// fsm[State.STATE_0.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
// fsm[State.STATE_0.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
// fsm[State.STATE_1.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
// fsm[State.STATE_1.ordinal()][Event.EVENT_2.ordinal()] = State.STATE_2.ordinal();
// fsm[State.STATE_1.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
// fsm[State.STATE_2.ordinal()][Event.EVENT_2.ordinal()] = State.STATE_2.ordinal();
// fsm[State.STATE_2.ordinal()][Event.EVENT_1.ordinal()] = State.STATE_1.ordinal();
// fsm[State.STATE_2.ordinal()][Event.EVENT_0.ordinal()] = State.STATE_0.ordinal();
// }
public final void onEvent(Event event) {
final State next = State.values()[ fsm[state.ordinal()][event.ordinal()] ];
if (next == State.ERROR) throw new RuntimeException("invalid state transition");
if (acceptEvent(event)) {
final State prev = state;
state = next;
handleEvent(prev, event);
}
}
public abstract boolean acceptEvent(Event event);
public abstract void handleEvent(State prev, Event event);
}
if fsm is replaced with a unidimentional array of size S*E it will also improve cache proximity characteristics of the state machine.
My suggestion:
Have you "transitions managment" be configurable (i.e - via XML).
Load the XML to a repository holding the states.
The internal data structure will be a Map:
Map<String,Map<String,Pair<String,StateChangeHandler>>> transitions;
The reason for my selection is that this will be a map from a state name
To a map of "inputs" and new states:
Each map defines a map between possible input and the new state it leads to which is defined by the state name and a StateChangeHandler I will elaborate on later
change state method at the repository would have a signature of:
void changeState(StateOwner owner, String input)
This way the repository is stateless in the sense of the state owner using it, you can copy one copy, and not worry about thread safety issues.
StateOwner will be an interface your Classes that need state changing should implement.
I think the interface should look like this:
public interace StateOwner {
String getState();
void String setState(String newState);
}
In addition, you will have a ChangeStateHandler interface:
public interface StateChangeHandler {
void onChangeState(StateOwner, String newState) {
}
}
When the repository's changeState method is called, it will
check at the data structure that the current state of the stateOwner has a map of "inputs".
If it has such a map, it will check if the input has a new State to change to, and invoke the onChangeState method.
I will suggest you have a default implementation of the StateChangeHandler, and of course sub classes that will define the state change behavior more explicitly.
As I previously mentioned, all this can be loaded from an XML configuration, and using reflection you can instantitate StateChangeHandler objects based on their name (as mentioned at the XML) and that will be held in the repository.
Efficiency and good performance rely and obtained using the following points:
a. The repository itself is stateless - no internal references of StateOwner should be kept.
b. You load the XML once , when the system starts, after that you should work with in memory data structure.
c. You will provide specific StateChangeHandler implementation only when needed, the default implementation should do basicaly nothing.
d. No need to instantiate new objects of Handlers (as they should be stateless)
This proposal isn't universal, it isn't UML compliant but for simple thing, it's a simple mean.
import java.util.HashMap;
import java.util.Map;
class Mobile1
{
enum State {
FIRST, SECOND, THIRD
}
enum Event {
FIRST, SECOND, THIRD
}
public Mobile1() { // initialization may be done by loading a file
Map< Event, State > tr;
tr = new HashMap<>();
tr.put( Event.FIRST, State.SECOND );
_fsm.put( State.FIRST, tr );
tr = new HashMap<>();
tr.put( Event.SECOND, State.THIRD );
_fsm.put( State.SECOND, tr );
tr = new HashMap<>();
tr.put( Event.THIRD, State.FIRST );
_fsm.put( State.THIRD, tr );
}
public void activity() { // May be a long process, generating events,
System.err.println( _state );// to opposite to "action()" see below
}
public void handleEvent( Event event ) {
Map< Event, State > trs = _fsm.get( _state );
if( trs != null ) {
State futur = trs.get( event );
if( futur != null ) {
_state = futur;
// here we may call "action()" a small piece of code executed
// once per transition
}
}
}
private final Map<
State, Map<
Event, State >> _fsm = new HashMap<>();
private /* */ State _state = State.FIRST;
}
public class FSM_Test {
public static void main( String[] args ) {
Mobile1 m1 = new Mobile1();
m1.activity();
m1.handleEvent( Mobile1.Event.FIRST );
m1.activity();
m1.handleEvent( Mobile1.Event.SECOND );
m1.activity();
m1.handleEvent( Mobile1.Event.FIRST ); // Event not handled
m1.activity();
m1.handleEvent( Mobile1.Event.THIRD );
m1.activity();
}
}
output:
FIRST
SECOND
THIRD
THIRD
FIRST

Categories