Is there any problem with code like this?
public class SomeClass extends View {
private final float someFieldVariable = getResources().getDimension(R.dimen.someVariableValue);
....
}
I think this is dangerous code.
The context gets wired the time on of the super-constructors is called. The initialization of someFieldVariable depends on the context and maybe is done before super is called.
So there is a chance the context is not wired because of the compiler not being smart enough and then your initialization will fail with an uncaught exeption. This will cause your app to crash.
Even if it works, I think it is bad style to rely on how the compiler does his work.
You should initialize it in your constructors instead to make sure the super-connstructor has been called before or just get the value from resources as you need it.
I also think there is no big advantage in defining a local variable for holding a resource value. It is like defining a variable to hold another variable, which is even final. It's just reasonable if you need the value very often and every processor-cycle counts.
Related
I'm a complete newbie to android development and I've been stuck on this problem for the past two days and I've never felt more frustrated in my life. A little backstory first,
I'm creating the most basic Book Library app and I was trying to add a Navigation Drawer to the app. Inside the kotlin file, When I declare all the variables (corresponding to the tags created in the layout file) using lateinit, it throws me a nullPointerException. For this reason I've taken to declaring my variables like this:
var drawerLayout: DrawerLayout? = null
which helps me avoid the exception.
Now coming to the real problem,
I was trying to create a click listener for my actionBarDrawerToggle inside the onCreate method this way:
val actionBarDrawerToggle = ActionBarDrawerToggle(this#MainActivity, drawerLayout, R.string.open_drawer, R.string.close_drawer)
drawerLayout.addDrawerListener(actionBarDrawerToggle)
actionBarDrawerToggle.syncState()
and for some reason, the drawerLayout part of the "drawerLayout.addDrawerListener(actionBarDrawerToggle)" line is underlined in red meaning there's an error. When I run it, this is the error it shows me in build window:
Smart cast to 'DrawerLayout!' is impossible, because 'drawerLayout' is a mutable property that could have been changed by this time
I have no ideo how to proceed with this. I've tried a lot of things and none of them is working. I think the error might have something to do with the declaration method I use that I described above. It would be great if someone could help me out
Tactically, change that line to:
drawerLayout?.addDrawerListener(actionBarDrawerToggle)
?. in Kotlin is the "safe call" operator. It says:
If drawerLayout is not null, call addDrawerListener()
If drawerLayout is null, do nothing (technically, it evaluates to null, but you are not using that here)
That will allow you to compile. Whether the code will work will depend on whether you have successfully set a non-null value on drawerLayout or not by the time you reach that line. The fact that your lateinit var declaration was failing suggests that you are not populating drawerLayout — with lateinit var, you would crash at runtime; with the nullable property and the safe call, the line will wind up being ignored. Neither is what you want.
Ideally, the books that you are reading (or courses that you are taking) on Kotlin and Android app development would cover things like safe calls, how to avoid having properties like drawerLayout, and so on. If you are not reading books or taking courses on Kotlin and Android app development, that may be something that you should consider.
You've defined drawerLayout as a nullable (DrawerLayout?) var, which means the value can change at any time, and that value can be null. So even if you do a null check to make sure it's not null, that could change at any moment! There's no way for the compiler to guarantee the call is safe.
Here's some options, in order from bad to good:
Explicitly mark the variable as non-null by referencing drawerLayout!!. This is you telling the compiler "I know what's up and it's fine, trust me". You should avoid ever doing this because it's a red flag, you ignore warnings, and you'll end up running into a situation where you're wrong
Copy the non-null value into a temporary variable, that you know won't change. This is the typical way to handle nullables in Kotlin, and you use one of the scope functions like let:
drawerLayout?.let { drawer -> // do stuff with drawer }
That's doing a null check (with the ?) and if it's not null, it calls the let block with that non-null value. And you know that value isn't going to change inside the block. I've called the variable drawer but you can omit that, it's called it by default. There's other scope functions like run too, slightly different ways of doing the same thing.
You can also just make a call on the checked variable itself, if that's all you need to do:
drawerLayout?.doAThingItsNotNullSoItsFine()
which is basically doing the same thing under the hood
Don't make drawerLayout nullable in the first place! It's never actually supposed to be null, right? It should always exist, and have a value when you try to access it? Then save yourself the aggro and the need to null check it all the time - which is what lateinit is for.
lateinit allows you to declare a variable without actually giving it a value. Usually you have to, which is what you've done here right - you've made it nullable, just so you can stick a temporary null in there. lateinit is a promise to the compiler that it's ok, you're not providing a value yet, but you will definitely have set one before anything tries to access it. (That's why it's called late init!)
So the problem you were running into was that you hadn't actually set that value before something tried to read it. You can check if it's been set with ::drawerLayout.isInitialized, but really you should understand your code flow enough to ensure that the reading is only possible after the intialisation. If you can't guarantee that, then you have a possible state where you don't have a value for a variable that other stuff might need to access - and that's a good candidate for making it nullable and using the null-handling features to deal with it smoothly
I have some code and when I run it produces an error, saying:
NoSuchMethod: the method 'XYZ' was called on null
What does that mean and how do I fix it?
Why do I get this error?
Example
As a real world comparison, what just happened is this conversation:
Hey, how much gas is left in the tank of the car?
What are you talking about, we don't have a car.
That is exactly what is happening in your program. You wanted to call a function like _car.getGasLevel(); but there is no car, the variable _car is null.
Obviously, in your program it might not be a car. It could be a list or a string or anything else really.
Technical explanation
You are trying to use a variable that is null. Either you have explicitly set it to null, or you just never set it at all, the default value is null.
Like any variable, it can be passed into other functions. The place where you get the error might not be the source. You will have to follow the leads from the actual null value to where it originally came from, to find what the problem is and what the solution might be.
null can have different meanings: variables not set to another value will be null, but sometimes null values are used by programmers intentionally to signal that there is no value. Databases have nullable fields, JSON has missing values. Missing information may indeed be the information itself. The variable bool userWantsPizzaForDinner; for example might be used for true when the user said yes, false when the user declined and it might still be null when the user has not yet picked something. That's not a mistake, it's intentionally used and needs to be handled accordingly.
How do I fix it?
Find it
Use the stack trace that came with the error message to find out exactly which line the error was on. Then set a breakpoint on that line. When the program hits the breakpoint, inspect all the values of the variables. One of them is null, find out which one.
Fix it
Once you know which variable it is, find out how it ended up being null. Where did it come from? Was the value never set in the first place? Was the value another variable? How did that variable got it's value. It's like a line of breadcrumbs you can follow until you arrive at a point where you find that some variable was never set, or maybe you arrive at a point where you find that a variable was intentionally set to null. If it was unintentional, just fix it. Set it to the value you want it to have. If it was intentional, then you need to handle it further down in the program. Maybe you need another if to do something special for this case. If in doubt, you can ask the person that intentionally set it to null what they wanted to achieve.
simply the variable/function you are trying to access from the class does not exist
someClass.xyz();
above will give the error
NoSuchMethod: the method 'xyz' was called on null
because the class someClass does not exist
The following will work fine
// SomeClass created
// SomeClass has a function xyz
class SomeClass {
SomeClass();
void xyz() {
print('xyz');
}
}
void main() {
// create an instance of the class
final someClass = SomeClass();
// access the xyz function
someClass.xyz();
}
why Google calls variables with the prefix "m" for example:
private int mSectionResourceId;
private int mTextResourceId;
I see it in all examples. But i not understand why they do it?
And now i have some example where it practic very good. If a called variabels without prefix i need write
public SimpleSectionedRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId,
RecyclerView.Adapter baseAdapter) {
this.sectionResourceId = sectionResourceId;
this.textResourceId = textResourceId;
but if i use prefix i can write
public SimpleSectionedRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId,
RecyclerView.Adapter baseAdapter) {
mSectionResourceId = sectionResourceId;
mTextResourceId = textResourceId;
I think it more readable. Who can explain to me the pros and cons of a prefix?
The variables starting with m are telling you they are variables in the scope of your class. Member of the class.
Link to Android Code Style Guide
The m just stands for 'Member'. It is simply declared that your Variable is a Class-Member.
It is more readable Code, because you know where Class Members got declared, so you can find it pretty fast. You don't need to write this, even if you don't prefix your Variables with an m.
In your Example, this only makes it more readable when there is no prefix-m. Another developer knows that it is a instance variable (member variable) and so declared on top or bottom of the class.
It is a prefix for class member variables. It's just a naming convention.
Mostly sure, taken from Hungarian Notation where similar prefix: m_ stands for exactly the same).
Referring to pros & cons:
Pros:
it allows to type fewer chars during programming,
programmers that are used to use Hungarian Notation may found it easier to follow the code.
Cons:
as the code changes very often, it is easy to forget about changing prefixes every time, when variable changes it's purpose (especially during prototyping),
it makes the code starts to smell bad,
Generally, it is some kind of reinventing the wheel. Java has this keyword that should be more than enough for accessing proper variable. If it's not, the code requires refactoring, maybe because of naming glitches or using too wide variable scopes.
Personally, I do not recommend to use Hungarian Notation (even the part of Android Code Style). We have great IDEs that increases the readability of the code.
There is an exception. The code, where Hungarian Notation (or more general, specific code style) was already been used. It is a matter of consistency.
The m is just a member variable. A class member if you will. Useable with constructors like WebView M WebView then later on you would use something like mWebView.loadurl("example.com"); it's just a placeholder for the variable you created. You don't have to add the member class variable as an m but it's more organized if you do
Is there a way to have an global settings variable for an android application, which is accessable as well from any help java classes without giving them context?!
I try to explain what I mean.
I have an application version as string value in strings.xml
I can get its value from every android activity, but not from help java classes withought giving context
What I do now, is saving it in a static variable of my first activity, but it seems, that sometimes it will be erased and set to null.
May be I do something wrong?!
Sorry for newbie question.
And thank you in advance,
Mur
P.s.
I wrote a small tutorial for this topic, to show the solution.
A variable declared as public, static, and final will be visible to all of your classes and never get erased.
public static final String VERSION = "1.2.3.4";
You could make a public static variable in your application class that you fill with the value from strings.xml in the onCreate method. The application class is a singleton and will be the last thing that is killed as part of your app so it will always be there and if you make it public static there will be only one instance.
I'm guessing that you have a JAVA class for some common utility functions. You get the value of your string using a context in your Activity/Service and then pass in that value to the JAVA class function as a parameter.
Just a random question. I'm learning a bit of Android right now, and in most examples, it seems a lot of common items (such as buttons, editboxes etc) get requested within each function using (cast) findViewById().
Is it considered good or bad practice to store the result of that in an Activity's member values? Simple example:
public class MyActivity extends Activity {
private EditText myText;
public void onCreate(blah blah) {
// blah
this.myText = (EditText) findViewById(R.id.mytext);
}
}
and use the myText field from there on. I think it'd be good for performance (depending on findViewById's inner workings, I'm quite sure it's already very fast), but I haven't seen it be encouraged yet. Also, it wouldn't be the first time I encountered a situation where 'caching' something like this leads to problems (had a case where database connections weren't released properly because I remembered a ConnectionManager or something in that fashion).
Secondly, somewhat related, if you want to remember something across methods in your Activity (and later on too, when the Activity is restarted later), is it wiser to keep up both a class field and a value in SharedPreferences, or would calling SharedPreferences each time for setting / getting the value where it's needing be a better solution? (better being 'cleaner, without impacting performance significantly)
That is completely normal practice and is exactly what you SHOULD be doing. If you're worried about memory leaks, or holding references or whatever, don't be concerned about that when dealing with views.
However, you SHOULD be careful about holding references to other contexts because that COULD cause a memory leak. That doesn't mean you shouldn't do it, just be careful about when you're doing it.
is it wiser to keep up both a class field and a value in SharedPreferences, or would calling SharedPreferences each time for setting / getting the value where it's needing be a better solution?
You should do both. You should keep a member variable for when you only need to read the data, just be sure that when you WRITE to the member variable, you also change the shared preference.
Also, it wouldn't be the first time I encountered a situation where 'caching' something like this leads to problems (had a case where database connections weren't released properly because I remembered a ConnectionManager or something in that fashion).
This is what I was saying first. It all depends on what you're storing. Views are fine to store, contexts can be dangerous, and database connections and registered listeners can cause really weird bugs. It all depends on the specific case.