LibGDX architecture design guidelines - android

I deal with loads of architecture design patterns and guidelines to choose from and to follow as an android client-server app developer.
The most popular ones are:
MVP or MVVM
Clean architecture
Repository pattern
Dependency injection technique
and so on...
Due to the strict rules of patterns, a developer has to admit the fact that patterns are just recommendations themselves, and are not required by Android SDK at all.
Same is true for LibGDX. There are no strict rules or requirements provided by LibGDX library, so the developer is free to decide how to write the game.
So the question is:
Are there some recommendations, design guidelines or even standards for LibGDX game developers to follow? How should I write the code (with usage of LibGDX) in a way that other developer can easily understand?

From my experiences, there is no standard everyone is following. libGDX developers come from different backgrounds. Some are backend developers in their day life, some are just hobbyist devs and learn their first development skills.
I see a lot of libGDX open-sourced projects with typical static SomeManager.getInstance() calls, while I prefer to pass-through references (as a backend developer, you will know about the advantages - testability and so on).
Even the libGDX backend itself does not follow one single approach. There are some parts getting references to managers by reflection (which is not good, because you must exclude such classes from obfusciation) and some using static getInstances().
If you also HTML5, you also must respect some GWT-based restrictions, so you are sometimes forced to go a way you would never do when developing Spring Boot applications.

Now that 3 years passed I can answer this question myself.
I found that using ECS (Entity-Component-System) is the best approach for creating games.
With this approach you'll have 3 different purpose objects. As name suggests they are:
Entity - is just a general purpose object which only contains set of component objects, and usually an ID.
Component - is a bag of data. No logic just plain POJO. The data it contains will define the behaviour of Entity which contains the component.
System - is where you put your logic. Every system should process an entity if and only if it contains a very specific set of components.
Engine - is a representation of a scene where all entities live, and are processed by systems.
The easies way of implementing such approach is by using Ashley lib. It's a lightweight implementation of ECS. Although as my experience has shown this works best in a case of real gameplay. If you want to have very specific animations (even of game objects) then use scene2d.
Scene2d represents a more familiar (to vast majority of developers) approach. It's a graph of UI elements which are placed in a hierarchy. There are very useful Actions which can be used to implement your animations. It also works best for any UI that you want to display in your game.
So to sum it all up I have worked out that you should have a couple of classes per screen to have a distinct division of labour.
Screen - I see it as a branch of running code. It contains an Engine (if we need an ECS) and a Stage (if we need an UI). Handles all game events, user input and routing.
Engine - ecs container which knows how to manage its systems, entities and components.
Stage - UI container which knows what UI elements should be present, how they should look and behave.
Simple example in kotlin (NOTE in my real project I use koin DI to bind all together):
class MyGameScreen : Screen, MyGameEngine.Callback, MyGameStage.Callback {
val engine = MyGameEngine(callback = this)
val stage = MyGameStage(callback = this)
override fun create() {
engine.create()
stage.create()
}
override fun render(delta: Float) {
enigne.update(delta)
stage.act(delta)
stage.draw()
}
override fun dispose() {
enigne.dispose()
stage.dispose()
}
override fun buttonPress() {
//handle button press
}
override fun onGameEvent(event: MyGameEngine.Event) {
//handle some game event
}
}
class MyGameEngine(
private val callback: Callback
) : com.badlogic.ashley.core.PooledEngine() {
fun create() {
// create and add all your systems and entities
}
sealed class Event {
// ...
}
interface Callback {
fun onGameEvent(event: Event)
}
}
class MyGameStage(
private val callback: Callback
) : com.badlogic.gdx.scenes.scene2d.Stage() {
val button1 = TextButton(...).apply {
addClickListener { callback.buttonPress() }
}
val label1 = Label(...)
// and so on
fun create() {
val root = Table().apply {
// add all your actors to the root table
}
val rootContainer = Container(root).apply {
setFillParent(true)
fill()
top()
}
addActor(rootContainer)
}
fun startSomeAnimation() {
val action = Actions.sequence(
// your animation
)
addAction(action)
}
interface Callback {
fun buttonPress()
}
}

Related

Map multiple suspend functions to single LiveData

The company I just started working at uses a so called Navigator, which I for now interpreted as a stateless ViewModel. My Navigator receives some usecases, with each contains 1 suspend function. The result of any of those usecases could end up in a single LiveData. The Navigator has no coroutine scope, so I pass the responsibility of scoping suspending to the Fragment using fetchValue().
Most current code in project has LiveData in the data layer, which I tried not to. Because of that, their livedata is linked from view to dao.
My simplified classes:
class MyFeatureNavigator(
getUrl1: getUrl1UseCase,
getUrl1: getUrl1UseCase
) {
val url = MediatorLiveData<String>()
fun goToUrl1() {
url.fetchValue { getUrl1() }
}
fun goToUrl2() {
url.fetchValue { getUrl2() }
}
fun <T> MediatorLiveData<T>.fetchValue(provideValue: suspend () -> T) {
val liveData = liveData { emit(provideValue()) }
addSource(liveData) {
removeSource(liveData)
value = it
}
}
}
class MyFeatureFragment : Fragment {
val viewModel: MyFeatureViewModel by viewModel()
val navigator: MyFeatureNavigator by inject()
fun onViewCreated() {
button.setOnClickListener { navigator.goToUrl1() }
navigator.url.observe(viewLifecycleOwner, Observer { url ->
openUrl(url)
})
}
}
My two questions:
Is fetchValue() a good way to link a suspend function to LiveData? Could it leak? Any other concerns?
My main reason to only use coroutines (and flow) in the data layer, is 'because Google said so'. What's a better reason for this? And: what's the best trade off in being consistent with the project and current good coding practices?
Is fetchValue() a good way to link a suspend function to LiveData?
Could it leak? Any other concerns?
Generally it should work. You probably should remove the previous source of the MediatorLiveData before adding new one, otherwise if you get two calls to fetchValue in a row, the first url can be slower to fetch, so it will come later and win.
I don't see any other correctness concerns, but this code is pretty complicated, creates a couple of intermediate objects and generally difficult to read.
My main reason to only use coroutines (and flow) in the data layer,
is 'because Google said so'. What's a better reason for this?
Google has provided a lot of useful extensions to use coroutines in the UI layer, e.g. take a look at this page. So obviously they encourage people to use it.
Probably you mean the recommendation to use LiveData instead of the Flow in the UI layer. That's not a strict rule and it has one reason: LiveData is a value holder, it keeps its value and provides it immediately to new subscribers without doing any work. That's particularly useful in the UI/ViewModel layer - when a configuration change happens and activity/fragment is recreated, the newly created activity/fragment uses the same view model, subscribes to the same LiveData and receives the value at no cost.
At the same time Flow is 'cold' and if you expose a flow from your view model, each reconfiguration will trigger a new flow collection and the flow will be to execute from scratch.
So e.g. if you fetch data from db or network, LiveData will just provide the last value to new subscriber and Flow will execute the costly db/network operation again.
So as I said there is no strict rule, it depends on the particular use-case. Also I find it very useful to use Flow in view models - it provides a lot of operators and makes the code clean and concise. But than I convert it to a LiveData with help of extensions like asLiveData() and expose this LiveData to the UI. This way I get best from both words - LiveData catches value between reconfigurations and Flow makes the code of view models nice and clean.
Also you can use latest StateFlow and SharedFlow often they also can help to overcome the mentioned Flow issue in the UI layer.
Back to your code, I would implement it like this:
class MyFeatureNavigator(
getUrl1: getUrl1UseCase,
getUrl1: getUrl1UseCase
) {
private val currentUseCase = MutableStateFlow<UseCase?>(null)
val url = currentUseCase.filterNotNull().mapLatest { source -> source.getData()}.asLiveData()
fun goToUrl1() {
currentUseCase.value = getUrl1
}
fun goToUrl2() {
currentUseCase.value = getUrl2
}
}
This way there are no race conditions to care about and code is clean.
And: what's the best trade off in being consistent with the project
and current good coding practices?
That's an arguable question and it should be primarily team decision. In most projects I participated we adopted this rule: when fixing bugs, doing maintenance of existing code, one should follow the same style. When doing big refactoring/implementing new features one should use latest practices adopted by the team.

What is the proper way to provide Context to a DataSource with Clean Architecture, MVVM and Koin?

The bounty expires in 2 days. Answers to this question are eligible for a +100 reputation bounty.
Ethansocal is looking for an answer from a reputable source:
I have this same problem, and would like to know what the best method is.
I am developing an Android application with Kotlin in which I need to get the current location of the mobile device. I've already found a way to do it in various examples, but I don't know how to integrate this logic according to Clean Architecture with MVVM.
In my architecture I have the following layers: Presentation, UseCase, Data, Domain and Framework. I have the presentation layer organized with the MVVM pattern. Also I use Koin for dependency injection.
I get all the data that my application needs from the DataSources that are in the Framework layer. For example, data obtained remotely or from a database, or data provided by the device (location).
Here is an example of the files involved in obtaining the location from the ViewModel:
ConfigurationViewModel (Presentation layer):
class ConfigurationViewModel(private val useCase: GetLocationUseCase) : ViewModel() {
fun onSearchLocationButtonClicked() = liveData<Resource<Location>>(Dispatchers.IO) {
emit(Resource.loading())
try {
emit(Resource.success(data = useCase.invoke(UseCase.None())))
} catch (exception: Exception) {
emit(Resource.error(message = exception.message))
}
}
GetLocationUseCase (Usecase layer):
class GetLocationUseCase(private val locationRepository: LocationRepository) :
UseCase<Location, UseCase.None>() {
override suspend fun invoke(params: None): Location = locationRepository.getLocation()
}
LocationRepositoryImpl (Data layer):
class LocationRepositoryImpl(private val locationDeviceDataSource: LocationDeviceDataSource) :
LocationRepository {
override suspend fun getLocation(): Location = locationDeviceDataSource.getLocation()
}
LocationDeviceDataSourceImpl (Framework layer):
class LocationDeviceDataSourceImpl(private val context: Context) : LocationDeviceDataSource {
override suspend fun getLocation(): Location =
LocationServices.getFusedLocationProviderClient(context).lastLocation.await()
}
As you can see, in LocationDeviceDataSourceImpl I need the context to get the last location. I don't know what is the best way to provide the context to the DataSource. I have seen several examples but I want to understand what is the best way to do it.
I have seen the following options:
Use AndroidViewModel, to provide the context of the application to the UseCase, to provide it to the Repository to finally provide it to the DataSource. But I am not sure if it is a suitable way, if it is safe and if it maintains the sense of architecture. Based on Alex's answer
The other option that I have seen is to inject the androidContext through Koin to the DataSource, which is another way to provide the context of the application. In this way it would not be necessary for the context to go through the ViewModel, the UseCase or the Repository. Based on maslick's answer
What would be the appropriate way to integrate this logic according to my architecture and why? Or is this problem because of my architecture?
Thanks a lot for your time.
I would simply inject the Context in the Framework layer!
and the reason for that is quite simple, Context class itself is a part of the Android framework, and your framework layer should autonomously be able to access such classes.
Also, note that you don't even need to have a wrapper class around Context when you inject it in your framework layer. Why? because you aren't going to unit test your framework layer classes. Why? because it would use a mix of Android framework classes or some 3rd party libraries(including other Android libs), that are a black box to us and as a software development principle:
We don't mock dependencies we don't own.

Where to do Arrow.io IO.runUnsafeSync() ? ViewModel or Activity/Fragment?

I'm trying to learn the Arrow library and improve my functional programming by transitioning some of my Android Kotlin code from more imperative style to functional style. I've been doing a type of MVI programming in the application to make testing simpler.
"Traditional" Method
ViewModel
My view model has a LiveData of the view's state plus a public method to pass user interactions from the view to the viewmodel so the view model can update state in whatever way is appropriate.
class MyViewModel: ViewModel() {
val state = MutableLiveData(MyViewState()) // MyViewState is a data class with relevant data
fun instruct(intent: MyIntent) { // MyIntent is a sealed class of data classes representing user interactions
return when(intent) {
is FirstIntent -> return viewModelScope.launch(Dispatchers.IO) {
val result = myRoomRepository.suspendFunctionManipulatingDatabase(intent.myVal)
updateStateWithResult(result)
}.run { Unit }
is SecondIntent -> return updateStateWithResult(intent.myVal)
}
}
}
Activity
The Activity subscribes to the LiveData and, on changes to state, it runs a render function using the state. The activity also passes user interactions to the view model as intents (not to be confused with Android's Intent class).
class MyActivity: AppCompatActivity() {
private val viewModel = MyViewModel()
override fun onCreateView() {
viewModel.state.observe(this, Observer { render(it) })
myWidget.onClickObserver = {
viewModel.instruct(someIntent)
}
}
private fun render(state: MyViewState) { /* update view with state */ }
}
Arrow.IO Functional Programming
I'm having trouble finding examples that aren't way over my head using Arrow's IO monad to make impure functions with side effects obvious and unit-testable.
View Model
So far I have turned my view model into:
class MyViewModel: ViewModel() {
// ...
fun instruct(intent: MyIntent): IO<Unit> {
return when(intent) {
is FirstIntent -> IO.fx {
val (result) = effect { myRoomRepository.suspendFunctionManipulatingDatabase(intent.myVal) }
updateStateWithResult(result)
}
is SecondIntent -> IO { updateStateWithResult(intent.myVal) }
}
}
}
I do not know how I am supposed to make this IO stuff run in Dispatcher.IO like I've been doing with viewModelScope.launch. I can't find an example for how to do this with Arrow. The ones that make API calls all seem to be something other than Android apps, so there is no guidance about Android UI vs IO threads.
View model unit test
Now, because one benefit I'm seeing to this is that when I write my view model's unit tests, I can have a test. If I mock the repository in order to check whether suspendFunctionManipulatingDatabase is called with the expected parameter.
#Test
fun myTest() {
val result: IO<Unit> = viewModel.instruct(someIntent)
result.unsafeRunSync()
// verify suspendFunctionManipulatingDatabase argument was as expected
}
Activity
I do not know how to incorporate the above into my Activity.
class MyActivity: AppCompatActivity() {
private val viewModel = MyViewModel()
override fun onCreateView() {
viewModel.state.observe(this, Observer { render(it) })
myWidget.onClickObserver = {
viewModel.instruct(someIntent).unsafeRunSync() // Is this how I should do it?
}
}
// ...
}
My understanding is anything in an IO block does not run right away (i.e., it's lazy). You have to call attempt() or unsafeRunSync() to get the contents to be evaluated.
Calling viewModel.instruct from Activity means I need to create some scope and invoke in Dispatchers.IO right? Is this Bad(TM)? I was able to confine coroutines completely to the view model using the "traditional" method.
Where do I incorporate Dispatchers.IO to replicate what I did with viewModelScope.launch(Dispatchers.IO)?
Is this the way you're supposed to structure a unit test when using Arrow's IO?
That's a really good post to read indeed. I'd also recommend digging into this sample app I wrote that is using ArrowFx also.
https://github.com/JorgeCastilloPrz/ArrowAndroidSamples
Note how we build the complete program using fx and returning Kind at all levels in our architecture. That makes the code polymorphic to the type F, so you can run it using different runtime data types for F at will, depending on the environment. In this case we end up running it using IO at the edges. That's the activity in this case, but could also be the application class or a fragment. Think about this as what'd be the entry points to your apps. If we were talking about jvm programs the equivalent would be main(). This is just an example of how to write polymorphic programs, but you could use IO.fx instead and return IO everywhere, if you want to stay simpler.
Note how we use continueOn() in the data source inside the fx block to leave and come back to the main thread. Coroutine context changes are explicit in ArrowFx, so the computation jumps to the passed thread right after the continueOn until you deliberately switch again to a different one. That intentionally makes thread changes explicit.
You could inject those dispatchers to use different ones in tests. Hopefully I can provide examples of this soon in the repo, but you can probably imagine how this would look.
For the syntax on how to write tests note that your program will return Kind (if you go polymorphic) or IO, so you would unsafeRunSync it from tests (vs unsafeRunAsync or unsafeRunAsyncCancellable in production code since Android needs it to be asynchronous). That is because we want our test to be synchronous and also blocking (for the latter we need to inject the proper dispatchers).
Current caveats: The solution proposed in the repo still doesn't care of cancellation, lifecycle or surviving config changes. That's something I'd like to address soon. Using ViewModels with a hybrid style might have a chance. This is Android so I'd not fear hybrid styles if that brings better productivity. Another alternative I've got in mind would maybe be something a bit more functional. ViewModels end up retaining themselves using the retain config state existing APIs under the hood by using the ViewModelStore. That ultimately sounds like a simple cache that is definitely a side effect and could be implemented wrapped into IO. I want to give a thought to this.
I would definitely also recommend reading the complete ArrowFx docs for better understanding: https://arrow-kt.io/docs/fx/ I think it would be helpful.
For more thoughts on approaches using Functional Programming and Arrow to Android you can take a look to my blog https://jorgecastillo.dev/ my plan is to write deep content around this starting 2020, since there's a lot of people interested.
In the other hand, you can find me or any other Arrow team maintainers in the Kotlinlang JetBrains Slack, where we could have more detailed conversations or try to resolve any doubts you can have https://kotlinlang.slack.com/
As a final clarification: Functional Programming is just a paradigm that resolves generic concerns like asynchrony, threading, concurrency, dependency injection, error handling, etc. Those problems can be found on any program, regardless of the platform. Even within an Android app. That is why FP is an option as valid for mobile as any other one, but we are still into explorations to provide the best APIs to fulfill the usual Android needs in a more ergonomic way. We are in the process of exploration in this sense, and 2020 is going to be a very promising year.
Hopefully this helped! Your thoughts seem to be well aligned with how things should work in this approach overall.

Why should we use interface in MVP pattern for Android?

I'm making an Android app using Kotlin for the first time using MVP pattern. My questions is, why do I need interfaces for View and Presenter as Kotlin provides higher order functions? Can't we just communicate using those higher order functions? Is the use of pattern without interfaces bad?
I have looked and read lots of article and tutorials but non answered my question. Is what I am doing in the code below a wrong practice? Can someone explain it to me?
In my Activity
override fun init() {
btn_login.setOnClickListener {
LoginPresenter.userLogin(et_emailAddress.text.toString(),et_password.text.toString()){
if (it){
//do something
}else{
//do something
}
}
}
}
My Presenter
object LoginPresenter {
fun userLogin(emailId: String, password: String, completion: (Boolean) -> Unit) {
//do something
completion(true)
}
}
Higher-order function costs
Kotlin official documentation on the cost of higher order functions
Using higher-order functions imposes certain runtime penalties: each
function is an object, and it captures a closure, i.e. those variables
that are accessed in the body of the function. Memory allocations
(both for function objects and classes) and virtual calls introduce
runtime overhead.
and if you're replacing all your interfaces with higher-order functions, you may end up with a bad performance.
2.
Interfaces can hold multiple functions, for which you'll need individual function params when using higher-order functions.
Consider the following case,
interface UserLoginInterface {
fun onLoginSuccess(loggedInUser: User)
fun onLoginFailure(error: ErrorResponse)
fun onRedirect(someOtherObjectWithDirectives: SomeDataClass)
}
To translate this to higher-order functions usage, You'll have to use three Function params
why do I need interfaces for View and Presenter as Kotlin provides higher order functions?
This is rather a common practice in software development. And while you may not use interfaces, there is a number of key points why interfaces are preferable. Off the top of my head:
with interface you can have multiple implementations of it without actually caring about the concrete type of the implementation. This is what you're missing with the higher order functions - you're restricted with the only type, LoginPresenter, when using the LoginPresenter.userLogin() method.
most of the design patterns is based on the separation of interfaces from their implementations. So programming into implementation rather than abstraction won't let you make use of those.
you won't be able to properly unit test classes that depend on other implementations as no mocking is possible in this case.
code maintenance and extension becomes much harder with concrete implementation.

Is this method of data binding in Android suitable for production use? Does it contain hidden problems?

I'm working on an Android app and I want to implement the MVVM pattern, which is pretty much the standard pushed by Google, however, I'd like to avoid using Android Data Bindings library if possible, since I hate autogenerated XML magic.
I've tried to implement something essentially akin to databinding in RxJava (Kotlin) using Jake Wharton's data binding library, plus some helpful extension methods.
My question is, is this the right way to go about things? Is this good enough to use in production? Are there potential problems I'm not seeing with this approach that will pop up later?
Essentially, I've implemented it like this:
I have a MvvmFragment (there is a similar class for activities) which takes care of setting up and managing the lifecycle of a CompositeDisposable object.
Then, in my ViewModel (part of the android Arch ViewModel package) I have all of the fields that will be bound to declared like this:
var displayName = BindableVar("")
var email = BindableVar("")
var signInProvider = BindableVar<AuthProvider>(defaultValue = AuthProvider.PASSWORD)
(Side note - Since Rx doesn't allow null values, I'm not sure how to handle the case of defaults for objects where the concept of a default doesn't really make sense, such as the AuthProvider above)
The BindableVar class is implemented like this:
class BindableVar<T>(defaultValue: T) {
var value: T = defaultValue
set(value) {
field = value
observable.onNext(value)
}
var observable = BehaviorSubject.createDefault(value)!!
}
Using Jake Wharton's RxBindings library, I have created some helpful extension methods on top of that, such as:
fun Disposable.addTo(compositeDisposable: CompositeDisposable): Disposable {
compositeDisposable.add(this)
return this
}
fun TextView.bindTextTo(string: BindableVar<String>): Disposable {
return string.observable.subscribe(this.text())
}
fun View.bindVisibilityTo(visibility: Int) {
// ... not shown
}
fun ImageView.bindImageUriTo(
src: BindableVar<Uri>, #DrawableRes placeholder: Int? = null
): Disposable {
return if (placeholder == null) {
src.observable.subscribe {
GlideApp.with(context).load(it).into(this)
}
} else {
src.observable.subscribe {
GlideApp.with(context).load(it).placeholder(placeholder).into(this)
}
}
}
Using these extension methods, I then obtain the ViewModel instance on Fragment initialization, and call a method initBindings(), which looks something like this:
item_display_name_value.bindTextTo(viewModel.displayName).addTo(bindings)
item_email_address_value.bindTextTo(viewModel.email).addTo(bindings)
item_profile_picture_view.bindImageUrlTo(viewModel.avatarUrl).addTo(bindings)
I want to avoid getting a week into fleshing out this architecture and then suddenly realizing there is some critical problem that can't be solved easily, or some other hidden gotcha. Should I just go with XML based data binding? I've heard a lot of complaints about the difficulty of unit-testing it, and the difficulty of reusing code with it.

Categories

Resources