5 Common Android Anti-Patterns That Make Your Code a Mess

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys welcome back to a new video in this video i have five anti-patterns for android for you so an anti-pattern is basically the opposite of a design pattern so just a pattern just a solution people typically use for their problems that is totally wrong don't do that don't use what i show you here and in case that you use these patterns that i show you here i will also show you how you can actually avoid using these and actually solve these specific problems in a much better way number one are base classes i see that all the time that people use base classes and i have used these myself in the past so much because i thought they are actually helpful they help you to remove duplicate code yes they do help you to remove duplicate code but they are not that good let's take a look at this main activity here you can see it inherits from a base activity and now i want you to tell me what's the purpose of base activity can you tell me that what does it do which functions does it bring well you don't know because you can't know it's called base activity let's say you just look into another project and you are actually trying to understand what it does and then you just see that base activity with a class name you have no idea what that actually does because usually you name your classes in a way that actually tells you what they do the name base activity is not really clear you have no idea what's in there so if we take a look here in our base activity then you can see that just contains a function to show a toast and yeah because it's a base activity and our main activity inherits from that we can now simply call the show toast function here with a message uh oops hello world and sure this this is cool this eliminates a lot of duplicate code you don't always need to write toast that make text and that stuff but you don't want to put this in a base activity another disadvantage of base activities is that they create a very very strong coupling between your base activity and all your other activities that inherit from that base activity so what that means is if you go to your base activity and you change this function that means you change the code in all your activities because you can't really distinguish that you just say okay i only want to make this change for my register and login activity i only make want to make this for my chat activity no if you change this it will apply to all activities in your whole project and you really want to avoid such a strong coupling so let's actually see how we can solve this how can we still eliminate the duplicate code that we have this toast text here all the time this is just an example this is still very quick to write but people often use base activities to put in a lot of code a lot more than this here but in that case what you can simply do is you take this code and simply make that an extension function in kotlin so just say function activity dot show toast pass in the message and paste this text and then you just take this and throw it in the thrash can you go back to a main activity replace this with the normal good old app combat activity and there you go you can use it just the way as before but you're not forcing your activity to actually have this function and if you want some other activities to use another function for example to show a short toast then you can just create an overload for that and then use the overloaded version of that and that's just much cleaner in your code that you just have a file for some activity extension functions than just having a file for base activity in which you put all the code that your activities actually share let's get to anti-pattern number two and that is putting all your dependencies into one huge app module i see that all the time well i don't have any dependencies in here but i see that all the time that people just create one app module singleton component everything is a singleton in there and they just put in everything here seriously don't do that we have these modules to actually have multiple of these to to just separate our dependencies we don't need every dependency throughout the whole lifetime of our application not every dependency needs to be a singleton you can create other modules for activities for views for fragments use that do that that's not only better in terms of memory efficiency and memory management but also that it's just much clearer which module actually contains which dependencies and also if you have multiple singletons it can make sense to create multiple modules for that you don't need to put all your singletons in app module another disadvantage if you use one big module is that it's it's much harder to actually use that module for testing because if you have ever written test cases with dagger hill for example then this looks for example like this so we just have our field android test annotation here and then we can just inject dependencies in here with this annotation well and which dependencies will this then use it will use the dependencies it can find in our modules so in this case from a wrap module but what actually if you need specific test dependencies so dependencies that are only for your test cases well if you actually want that dagger uses these test dependencies instead of these dependencies from your actual app module what you need to do is you need to annotate this with uninstall modules and you pass the app module so this will just say okay we uninstall the app module and then it will take a look at other modules if it can find the dependencies so then you will just have a test up module in your test directory but maybe you only want to actually uninstall some dependencies of your app module that's not possible with this annotation you just delete all these dependencies for this test class so you you can't inject any of these anymore if you have different modules and you just separate your dependencies between different modules you can just have a much more fine-grained control which modules you actually uninstall here and which you actually like to keep anti-pattern number three is using an activity for every screen i see that all the time that people do that you can see i have an activities package here and literally for everything i have an activity in there we have login registration profile gender forgot password activity chat activity birth date activity so many activities you don't need that android has the concept of fragments so these are basically just portions of the screen portions of an activity but you can also just use fragments to display a layout for your whole screen that's how they are supposed to be used the reason why you want to use fragments is because they are just much much lighter than activities activities are very heavy you know you need to send an intent to actually launch them um the transition usually takes a little bit longer fragments are basically just layout containers or layouts that can be replaced in such a container so there's just a container hey in in this area of this screen we can have a fragment and it can just say okay now i show this fragment you click on a button and then it just replaces the content of this container with another fragment so that is how you actually navigate with fragments without changing the activity oftentimes just using a single activity for your whole app is enough um i usually like to create one activity per entry point and per main component of my app basically so if you take a look at these here then i would take these two make this an auth activity that contains a login and a register fragment i would take actually this as well um the forget password thing then i would take the birth date and gender activity which i would also make fragments and put into an onboarding activity and the chat activity and that's it and and other other screens i would then maybe make a main activity so for all main main components then let's get to anti-pattern number four and that is in our view model which is hard coding dispatchers so you can see we have a shared flow here we have a long running task and yeah that just uses the default dispatcher which is totally the right thing for long running tasks that are just very cpu heavy you can see it just delays the crew team and then if it's a finished string in our shared flow doesn't make any sense here not important but i don't recommend actually doing this actually hard coding the dispatcher here you force this crew routine to use the default dispatcher and let's say you do that even more often here in this in this view model with more long-running tasks and at some point you realize okay maybe i should i should use another dispatcher maybe i need some i o operations or whatever then you need to go through it manually and replace every single dispatcher but that's rather the the minor issue of that the bigger issue is if you want to test that for test cases if you want to test curotenes we have a special test croutine dispatcher so if you want to test your your viewmodel what what you actually always want to do because that is what you use to actually um run your unit tests in if you want to do that then you actually want that the curtains in the in the view model use this test dispatcher you have full control of in your test cases if this uses the default dispatcher then you don't really have the control you want so how can we solve this problem now the answer is by simply injecting these dispatchers and because that might not be clear right now i want to show you an example here what i usually do is i will go here and say interface put that in a separate file but for simplicity i'll just put it in the main view model interface dispatcher provided and in here we just define the different dispatchers we have so that is main of type creatine dispatcher we have io and default and unconfined so io default unconfined and then we take such an interface and actually inject it here in our review model so we say private val dispatches typed this patcher provider and instead of this we now say this patches dot default so this now looks very similar how does that actually solve our problem so let's actually create an implementation of this interface so we say class standard dispatchers for example and that just implements that interface then we just have our four dispatchers we need to override here for the main one we say dispatchers dot main dispatchers.i o default and finally unconfined so now we can actually take this standard dispatches object provided here in our app module and or in whichever module we want to provide that so let's say singleton add provides we say provide this patcher provider and in here we obviously return such a dispatcher provider interface and we just create our standard dispatches like this come on like this so now this will work as before we create our standard dispatches here which is our implementation that will be injected for our normal app into our viewmodel here for that and then with that we just use the default dispatcher here which refers to the actual default dispatcher but what we can now do is for our test cases where we actually want test dispatchers for every routine we can create another implementation of that interface which i would normally do in the in the test package of of course but yeah just for simplicity and clarity i'll do it here so test this patches for example that also implements this patcher provider and here we can say main as i would test uh curity in this patcher which i don't have the dependency for but there is an object that looks like that and we just do that for every single dispatcher so for every dispatcher we just return the same test security in dispatcher so default and finally unconfined so for test cases we can then have a module we can uninstall this app module so it doesn't use the standard dispatchers and instead uses this testless patches so when we create a view model in our test package and it uses the test module it will also inject this test dispatch's dispatcher provider and no matter which dispatcher from that we actually want to use it will always make sure that we use the test routine dispatcher for our test cases so this will for our test cases just refer to the test current in dispatcher and that is a much cleaner solution here especially if you have really big project and you need that testability which you usually want to have and let's actually get to the last anti-pattern anti-pattern number five that is also related to coroutines and i have an example here in the main activity let's revert this like this and also make this app compat activity again so we just inject the review model here we get an instance of that and then we use global stop to launch and we just collect our shared flow that we have in our view model so far so good but you can already see that we get a warning global scope will always give you that warning unless you annotate this with delicate core routines api which you don't want to do i mean if you use global scope you want to do that but you shouldn't actually use global scope in android or at least be very careful with it and fully know what you're actually doing but the reason why people like to use global scope so much is because it's just the easiest way to launch a coroutine but it's definitely not the best way to launch a curtain in android so what does globalscope actually do globalscope limits the lifetime of this curtin to the lifetime of our actual application of the process but since in android we deal with lifecycles a lot so for example the lifecycle of this activity what would happen here is if this activity gets destroyed this global scope does not get destroyed and the creatine in it does not get cancelled this curotin will run as long as it needs until it finishes or gets cancelled in some other way but well what can happen here is main activity gets destroyed global scope will persist and global scope will use resources of this main activity in it and because of that the garbage collector won't actually collect these resources from main activity as trash because they are not used anymore if the activity is destroyed the garbage collector won't collect that because they are still used in global scope and that is what we call a memory lake so this is very very vulnerable here to memory leaks in android and there are actually super easy fixes for that and that is just using the typical curtain scopes that there actually exists for android for activities that is lifecycle scope and that will automatically cancel this creatine if the cycle of this component here in this case the activity actually gets to the undestroyed state if we have a view model then we use viewmodelscope as here and if you have any other type of component for example a service then you need to write your own curating scope and just manually cancel the curtain of that scope when your service gets to the undestroyed state or whatever kind of component you deal with but that was it for this video for five anti patterns in android i hope you don't do these and if you do these i hope you now know why you shouldn't do this anymore and how you can fix that in your projects and by the way if you like my videos then you can now become a channel member down here on this join button there you will get an option at least for tier 2 that you receive all my live streaming recordings from twitch so if you missed these recordings where i just really build an app live and you can also see me struggle see me fix my problems and stuff like that if you want to see these and if you don't want to miss these live streams click on join and for 4.99 a month you can actually become a channel member and also help to actually support this channel so i can keep maintaining that thanks for watching and i wish you an awesome day see you next video bye bye
Info
Channel: Philipp Lackner
Views: 8,772
Rating: undefined out of 5
Keywords: android, tutorial, philip, philipp, filipp, filip, fillip, fillipp, phillipp, phillip, lackener, leckener, leckner, lackner, kotlin, mobile
Id: skW4wSuXCe0
Channel Id: undefined
Length: 17min 6sec (1026 seconds)
Published: Mon Aug 16 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.