I used synthetic property in my code.But wondering for how and when it actually initialise each view in android.
We simply provide import and access each view by its id. When it allocate memory for view object?
This is easy enough to investigate by decompiling a Kotlin file where you use Kotlin Android Extensions. (You can do this by going to Tools -> Kotlin -> Show Kotlin Bytecode and then choosing Decompile in the pane that appears.) In short, it's nothing magical, it just uses findViewById and then casts the View to the concrete type for you.
If you use it inside an Activity or a Fragment, these get cached in a Map so that the lookup only occurs once. After that, you're only paying the costs of fetching a map entry by the ID as the key.
You can also use it on a ViewGroup to find a child with a given ID in it, in these cases, there's no caching, these calls are replaced by simple findViewById calls that will happen every time that line is reached. This second syntax looks something like this:
val view = inflater.inflate(...)
view.btnLogin.text = "Login"
And it will translate to something similar to this in the bytecode:
View view = inflater.inflate(...);
Button btnLogin = (Button) view.findViewById(R.id.btnLogin);
btnLogin.setText("Login");
Note that the actual View instances are still created when your layout is inflated. Kotlin Android Extensions is only syntactic sugar over findViewById calls.
Related
When I add a view to xml (layout) of my activity, it takes an id, so I can use that id in code of activity to recognize it and change my view if I like. Android make an R.class as an intermediate between my activity code an my layout(xml), so I use R.class for example:
findViewById(R.id.textView);
But how does android find it is a TextView? For example, if I write:
Button b = findViewById(R.id.textView);
I get an error. From where does it find I write it wrong?
I mean in R.class it define my id so android know a new id is defined in my xml, and I use it to return an object of View class or class extend View. But how does it find I am writing wrong and it is not a button, before I run the program (in compile time)?
Android Studio runs Android lint checks on your code, and there's the WrongViewCast check to detect such issues and report them as errors:
WrongViewCast
-------------
Summary: Mismatched view type
Priority: 9 / 10
Severity: Error
Category: Correctness
Keeps track of the view types associated with ids and if it finds a usage of
the id in the Java code it ensures that it is treated as the same type.
Source: http://tools.android.com/tips/lint-checks
If your view, that is returned by findViewById(R.id.ID), doesn't cast to the field you're assigning the view to, your code won't even compile! Android Studio checks your assigned variables and tries to cast them in real-time, but if that fails, because you made a mistake, your Activity.java can't compile! This is just a way of how this IDE operates.
Some time ago, you needed to cast the view manually, like this:
(TextView) findViewById(R.id.text_view)
But this is not necessary anymore.
Recently after getting back into Android development, as I was reviewing some code, I noticed that code would reference a control directly by its id without the need to use findViewById. So if I had a textview with an id of tvUsername, I could just call:
tvUsername.setText("john");
In the past I was always using findViewById but am now wondering whether the ability to use a control directly without calling findViewById always existed, or did Google start supporting directly referencing it after some version.
Is findViewById still needed
Is findViewById still needed if you are using JAVA than it is needed,
But if you use Data Binding Library then no need to do findViewById
Also in KOTLIN the findViewById is not needed
You can directly use it like this
tvUsername.text ="john"
for more information read
Kotlin Android Extensions
Goodbye findViewById, say hello to Synthetic Binding
Whenever we want to inflate a view or get a resource we have to cast it in run-time. views, for example, are used like so:
In the past, we would have needed to cast it locally
(RelativeLayout) findViewById(R.id.my_relative_layout_view)
Now, we use generics
findViewById<RelativeLayout>(R.id.my_relative_layout_view)
my question is why doesn't the compiler(or whoever generates the R class) doesn't also keep some kind of a reference to the type of the element(doesn't matter if it's a string or an int or any other type) that way casting problems should not occur
We cannot really speculate on that, that would be a design choice.
It might be that they wanted to avoid bloating the APK. Every ID would need a full package name to the class. So would each ID in android.R too. Since R is packaged in every APK.
Solutions
However, if you are using Kotlin, you can even do away with the generics check. Kotlin will determine it automatically.
val view = findViewById(R.id.my_relative_layout_view)
view.method()
Or event simpler, if you use synthetics:
my_relative_layout_view.method()
Also, if you are using data bindings, you can just access it like this:
binding.my_relative_layout_view.method()
Before Kotlin, Android developers supposed to save reference to the Activity's Views in a variable like this:
Button fooBtn = (Button) findViewById(R.id.btn_foo)
to reduce the amount of the boiler-plate code and the number of findViewById calls.
With the introduction of the Kotlin's Android Extensions we can reference the same Button by simply using:
btn_foo
Questions:
Does the btn_foo have a reference to the Button saved, or does it call findViewById every time?
Do developers still suppose to use variables to store btn_foo to improve app's performance, or just use it directly in the code?
Edit: there is an explanation how Extensions work, however it is still a bit unclear.
It's cached, so findViewById isn't called every time you need it. Storing the variable won't definitely improve the app's performance
One of the Kotlin Android Extension (KAE) developers Ihor Kucherenko confirmed that:
KAE will keep a reference to the view after the first call, instead of using findViewById all the time. That works only for Activities and Fragments.
KAE will not cache data and will use findViewById every time for any other element (except for an Activity/Fragment).
So in case you are going to init a ViewHolder:
class FooViewHolder(view: View): RecyclerView.ViewHolder(view) {
fun bind(day: FooItem.Day) {
btn_foo.text = day.title
}
}
Decompile into Java call will look like:
((Button)this.itemView.findViewById(R.id.btn_foo)).setText((CharSequence)day.getTitle());
which is exactly what you want to avoid.
The developers might be aware of this.
Conclusion: fill free to use KAE without any additional variables, but only for your Activitiies/Fragments.
Is there a way that I can replace all findviewbyid formulas by Butterknife #Inject(id) annotation automatically or I have to do it manually? I use Android Studio IDE
I have written some regular expressions to handle the search and replace task. It may not be worth it for you though, depending on how many statements you have.
I used SublimeText to run the queries, it supports newline which I need. IntelliJ didn't match newlines in the query I tried. I think sed could also be used (check sed manual for 'advanced' switch or something).
Search Pattern for Strings:
private (String) (\w+);((?:.|\n)*)\1 = \w*.getString\(R.string.(\w*)\);
Replace Pattern for Strings:
#BindString(R.string.$4)\n $1 $2;$3
Search Pattern for View ids
(?:private |public |protected |)(\w*) (\w+)(?:\s*=\s*null|);((?:.|\n)*)\s\2 = .*findViewById\(R.id.(\w*)\);
In the above I added the search for fields declared with other modifiers.
Replace Pattern for View ids
#Bind(R.id.$4)\n $1 $2;$3
You may wish to replace spaces with \s*, but it makes expressions more difficult to read, so I didn't do it in every location.
Example of what it does
private View map_center;
someMethod() {
...
map_center = activityOrView.findViewById(R.id.centerview);
...
}
becomes
#Bind(R.id.centerview)
View map_center;
someMethod() {
...
...
}
The regular expression uses the back reference \1 to locate the assignment of the field. If the field is not there but is a local variable, it'll wrap the local variable with #Bind and you'll manually have to make it a field. It won't match local variables with declaration in same line as assignment.
According to this tutorial I should say yes!:
The Butter Knife library, developer and maintained by Jake Wharton
(Square Inc.), has annotations that help developers to instantiate the
views from our activity or fragment. It also has annotations to handle
events like onClick(), onLongClick(), etc.
In every activity or fragment, you have to remove, or comment out,
every call of the findViewById() method and add the #InjectView
annotation before the declaration of the variable, indicating the
identifier of the view.
#InjectView(R.id.sample_textview)
TextView sample_textview;