I have a MVVM project where I have ViewModel classes extending BaseObservable. Now if put #Inject class in my ViewModel then compilation fails with many errors like: "error: package xxx.databinding does not exist"
Can I find the actual error that's causing this using some gradle technique? Also is #Inject really supported with databinding?
Edit:
Code is exactly the same as https://github.com/googlesamples/android-architecture/tree/todo-mvvm-databinding/
In that I have added dagger and I'm trying to #Inject a repository into a view model that extends BaseObservable. As soon as I add #Inject into the view model then I cant compile
The general approach to fixing this kind of problem is to find the errors that are not tied to databinding. Once those are fixed, your databinding errors will go away. Databinding just complains loudly because the build failed before it could do its thing. Unfortunately this often feels like finding the needle in the haystack.
If you have a lot of errors you may need to increase the maximum error count displayed, as otherwise the error output may end before it prints the actual root cause. See here: https://stackoverflow.com/a/35707088/436417
Dagger's #Inject is compatible with databinding in general.
Dagger works with data binding, you have something wrong in your setup.
When you get error: package xxx.databinding does not exist it means that code generation failed, and since both data binding and dagger use code generation problem might be in the setup of both components.
Based on your description it looks like you have not configured dagger properly, i.e. not set up how it should provide the object you are injecting.
Make sure that you did the actions under "satisfying dependencies" and "building the graph" from here https://google.github.io/dagger//users-guide.html
Like Uli mentioned, this is due to the number of displayed errors being limited by the compiler.
Do this:
1. Increase the displayed error limit by doing the following
Add this snippet in your submodule gradle file inside the android block.
kapt {
javacOptions {
// Increase the max count of errors from annotation processors.
// Default is 100.
option("-Xmaxerrs", 1000)
}
}
2. Find the errors which are not binding related and fix them.
i.e (Fix the errors from app/src/.. folders and ignore the ones from app/build/generated/.. which are binding related)
Check this thread and this comment for more info.
Related
We are a company with 10+ apps all using 10+ internal SDKs, all coded back at the glorious time of the kotlin-android-extensions gradle plugin. We heavily rely on both #Parcelize and synthetic binding (all our views are coded with it). With the newer versions of Kotlin, this plugin is now deprecated in favor of kotlin-parcelize for #Parcelize and View Binding as a replacement for synthetic binding.
We tried to upgrade our Kotlin version from 1.4.10 to 1.6.0 while still using the kotlin-android-extensions plugin. We suddenly had a #Parcelize error reported here, fixed in Kotlin 1.5.0. Except that the fix is not in Kotlin itself, it is in the kotlin-parcelize plugin. And of course as they deprecated kotlin-android-extensions, the later doesn't contain the fix. So in short, in order to fix the issue we have to use kotlin-parcelize.
Knowing that kotlin-parcelize can't be used along kotlin-android-extensions (build error), and that switching all our views from synthetic to view binding will be a hell of a work that will take a tremendous amount of time, what can be done here ? We really want to keep using synthetic binding while being able to upgrade Kotlin to its latest versions.
What looks like the obvious choice is that you need to move from synthetic views as soon as possible although it might not be possible due to lack of resources.
Old-school approach
A solution to get around the Parcelable problem is to figure out the classes that are not properly parcelized and serialize them in an old-school fashion.
Serializing to JSON
I suggest you take a look at the kotlinx-serialization package it's quite handy to read from and write to JSON.
For those that don't work you can provide the following parcelable implementation which would be generic and work all around the codebase:
Ensure that your classes are annotated with #Serializable important that such annotation comes from kotlinx.serialization package and not the java one.
With this generic function you can write any #Serializable object into a parcel.
inline fun <reified T> writeToParcel(out: Parcel, data: T) {
val jsonString = Json.encodeToString(data)
out.writeInt(jsonString.length)
out.writeByteArray(jsonString.toByteArray())
}
And with this other one you should be able to deserialize such object
inline fun <reified T> readFromParcel(input: Parcel): T {
val size = input.readInt()
val bytes = ByteArray(size) { input.readByte() }
val jsonString = bytes.toString()
return Json.decodeFromString<T>(jsonString)
}
You can make them extension function from Parcel to write even less code.
This should get you around the classes that are problematic for the Parcelize extension; it's not as fast as a proper Parcelable implementation but hey, it works.
Side notes
Note that the code has written directly as an answer of this post and hence untested and some parts might need adaptation. And of course, check out the kotlinx.serialization documentation to see how to create the Json encoder and decoder (it's fairly simple)
My ViewModel class implements LifecycleObserver.
When I call fragment.lifecycle.addObserver(this) it produces exception.
Caused by: java.lang.IllegalArgumentException: The observer class has some methods that use newer APIs which are not available in the current OS version. Lifecycles cannot access even other methods so you should make sure that your observer classes only access framework classes that are available in your min API level OR use lifecycle:compiler annotation processor.
Strange, that firstly it was working fine, but not long ago this exception has appeared. I've found, that audioFocusRequest is cause of this bug.
private val audioFocusRequest by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener(this)
.build() else throw RuntimeException("Can't be done for Android API lower than 26")
}
Does anybody know how it can be fixed?
UPD
Tried to use annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version", but got compilation error:
(decided to paste screenshot, because whole logs are quite big)
UPD 2
At the end I've decided to delete audioFocusRequest field and to use old deprecated method - requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) instead of recommended requestAudioFocus(#NonNull AudioFocusRequest focusRequest)
It helped me to make code working again, so it can be solution. But I didn't find answer - why this problem had appeared. It strange because code used to be working before.
So problem has been solved but question still stays unanswered
Try to use kapt "androidx.lifecycle:lifecycle-compiler:2.0.0"
The class which implements LifecycleObserver has some method, which has parameters with type that only exist for higher APIs.
Your variables (i guess) and function parameters must exist on all APIs even function is not called (maybe this is requirement for classes who implement LifecycleObserver).
A possible solution is to change function parameter type to Any (kotlin) or Object (Java) and cast it to appropriate type inside function.
I have to remove this set method on SpinnerView: lifecycleOwner = viewLifecycleOwner
I was able to fix this by moving the offending methods into another class, but still called from my LifecycleObserver. After reading the error message again:
Caused by: java.lang.IllegalArgumentException: The observer class has some methods that use newer APIs which are not available in the current OS version. Lifecycles cannot access even other methods so you should make sure that your observer classes only access framework classes that are available in your min API level OR use lifecycle:compiler annotation processor.
It seems as though no methods or objects are allowed in the class extending LifecycleObserver if they don't exist in the device's OS, even if they are wrapped in an SDK version check and never accessed.
In Java, we have the package protected (default) modifier for classes, which allows us to have many classes in a single package but exposes only a few and keeps the logic encapsulated.
With Kotlin this doesn't seem to be the case. If I want a few classes to be visible to each other but no further, I have to use a private modifier which limits visibility to a single file.
So if you want 10 classes in a package but only one of them to be public, you'd have to have one huge file with all the classes in it (and private all over the place).
Is this normal practice or there is a way to achieve some similar modularity in Kotlin?
I don't understand: if they have the notion of a package, why did they get rid of package protected access?
Update: We might have package protected visibility after all
see the discussion here
Update: If you read through the discussion and still think this is a must-have feature for the language, please vote here
Kotlin, compared to Java, seems to rely on packages model to a lesser degree (e.g. directories structure is not bound to packages). Instead, Kotlin offers internal visibility, which is designed for modular project architecture. Using it, you can encapsulate a part of your code inside a separate module.
So, on top level declarations you can use
private to restrict visibility to the file
internal to restrict visibility to the module
At this point, there is no other option for visibility restriction.
As a workaround for me on android I've created #PackagePrivate annotation and lint checks to control access. Here you can find the project.
Lint checks are obviously not that strict as compiler checks and some setup needed to fail the build on errors. But android studio picks up lint checks automatically and shows error immediately while typing. Unfortunately I don't know a way to exclude annotated members from autocomplete.
Also, as lint is a purely compile-time tool, no checks at runtime performed.
As #hotkeys points out, you can use the internal keyword in a module or you can put all classes that would otherwise belong in a package inside a single file, but sticking several classes in a file may be a questionable design decision.
For me, the package visibility is helpful for its documenting value. I want to know what public interface some package is presenting to the rest of the project, hide factory implementation classes and so on.
So even if it's possible to access package-private classes and methods in Java, I still choose to use the package modifier.
For this I created a project with a single annotation:
package com.mycompany.libraries.kotlinannotations;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Documented
#Retention(SOURCE)
#Target({ TYPE, METHOD, CONSTRUCTOR })
/**
* Use in Kotlin code for documentation purposes.
*
* Whenever a Kotlin class or method is intended to be accesible at package level only.
*
*/
public #interface PackagePrivate {
}
Then I can use this annotation in any Kotlin project.
The second step, which I haven't done yet, is creating a PMD rule to enforce this with maven (or any other build tool for that matter) and also be able to see violations of the rule in my IDE with the pmd plugin.
There no is full Kotlin support in pmd at this moment but it seems to be expected at some point.
A near-replacement for package private visibility is available using the opt-in requirements feature (credit to pdvrieze on Kotlin discussions). This is the annotation syntax typically used to flag experimental features in an API.
To use it, create an annotation denoting package private declarations:
#RequiresOptIn(message = "Only to be used in MyPackage")
#Retention(AnnotationRetention.BINARY)
annotation class MyPackagePrivate
Then annotate any methods you want to be package private with it:
#MyPackagePrivate
fun aPackagePrivateMethod() {
// do something private within a package
}
In this way a warning will be generated on any method that calls the annotated method unless the calling method is itself annotated with the corresponding #OptIn annotation, here shown at class level:
#OptIn(MyPackagePrivate::class)
class AClassInThePackage {
fun userOfPackagePrivateMethod() {
aPackagePrivateMethod()
}
}
This, then, produces a similar effect to Java's package private, except that calling methods need to explicitly opt in to using a package private declaration.
If it is desired to generate an error rather than a warning, the level parameter of #RequiresOptIn can be specified:
#RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "Only to be used in MyPackage")
// annotation declaration as before
Package-based protection is pointless in Kotlin because packages themselves are unprotected
In Java, package was tied to directory structure. So if you put your classes in com\example\yoursecretengine, any attempt (deliberate or accidental) to add a rogue class there would be easily noticeable. This is the kind of security we've depended on.
Kotlin removes the ties between directory and package, so I can put my class in "my" directory (eg.src\java\pl\agent_l\illegalaccess) yet declare its package as com.example.yoursecretengine - and gain access to all the properties you've meant as package private.
In fact, a Kotlin project works perfectly without ANY package declarations. This only highlights that packages are "more what you'd call guidelines than actual rules". They're a convenience feature, useful only to unclutter namespace and nothing more.
Relevant quotes from kotlinlang:
unlike many other languages, Kotlin packages do not require files to have any specific locations w.r.t. itself; the connection between a file and its package is established only via a package header.
And:
an absence of a package header in a file means it belongs to the special root package.
So I'm trying to follow the simple read.me for Kotshi and get it set up for my project but I seem to be hitting a little snag.
I'm currently at this portion of the read.me
#KotshiJsonAdapterFactory
object ApplicationJsonAdapterFactory: KotshiApplicationJsonAdapterFactory()
but KotshiApplicationJsonAdapterFactory seems to give me an unresolved reference error. Now this sounds like an absolutely silly question but is KotshiApplicationJsonAdapterFactory supposed to be a custom class that I set up? If so I don't see anywhere in the documentation regarding it. My gradle has the two dependencies added so I'm absolutely baffled.
#KotshiJsonAdapterFactory makes Kotshi generate a JsonAdapter factory.
Should be placed on an abstract class that implements
JsonAdapter.Factory.
So yes, KotshiApplicationJsonAdapterFactory is a custom class that you've to setup and which fulfills the above condition.
KotshiJsonAdapterFactory is an annotation that you apply to a class that you write. This will make Kotshi generate a class with the same name as your class + the Kotshi prefix. That class will implement the actual factory.
When you write your class it will look like you have a compile error, but it will not prevent compilation.
I recently converted the majority of my project to kotlin. Now I encounter several unusual errors that all seem to relate to annotation libraries. Needless to say, it didn't happen in Java.
I'll describe the cases - one in Dagger and one in Butterknife.
1.
When having 2 #Provides methods in different models with the same name.
For example in file "FooProvider.kt" having a "provideFooOrBar" method
#Module
class FooProvider(private val view: FooActivity) {
...
#Provides #FooScope fun provideView() = view
#Provides #FooScope fun provideFooOrBar() = Foo()
}
And having another file "BarProvider.kt" with the same method name
#Module
class BarProvider(private val view: BarActivity) {
...
#Provides #BarScope fun provideView() = view
#Provides #BarScope fun provideFooOrBar() = Bar()
}
In this case, Dagger fails to generate some factory libraries and I get the following compilation error:
Error:(27, 32) error: cannot find symbol class FooProvider_ProvideFooOrBarFactory
A sample project reproducing the issue can be found at https://github.com/maxandron/DaggerIssue325
2.
This is an issue when using Butterknife. When having two #Bind annotated variables in two different classes - One of them just fails to initialize at runtime without any compilation error!
For example if I have:
class FooActivity {
#Bind(R.id.foo) lateinit var mFoo: View
}
class NotFooActivity {
#Bind(R.id.not_foo) lateinit var mFoo: View
}
Then one of them (or both?) will just fail to initialize without any error. Causing a kotlin.UninitializedPropertyAccessException: lateinit property mFoo has not been initialized exception to be thrown when the field is accessed.
Is it something I'm doing wrong in configuring Kotlin or is it a kotlin bug?
Thank you in advance!
Ron
I was having this issue, so I started to investigate and it's caused because Kapt is only checking the method name when comparing them, and they are added in a set, thus duplicates are not allowed. The same happens for annotated fields, so currently you can have one method/field name per annotation.
I added the class name to the equals method and the annotations were properly handled now, but the tests broke and I don't know how they work, so I hope someone knows how to fix this.
It turned out to be a bug with kapt.
I posted an issue on Kotlin's bug tracker and the issue is now marked as fixed.
This solution was merged
Should be resolved in Kotlin version 1.0.2
So to somewhat answer the kotlin.UninitializedPropertyAccessException: lateinit issue, I was running into the exact same thing in my project. What I did which "solved the issue" for me was to remove Butterknife from the offending class, in this case it was just a viewHolder for my new expandable RecyclerView, and then run the app again.
Running the app, after switching all my #Bind(R.id.my_view_id) to the "old school" findViewById(R.id.my_view_id) as MyViewType worked, but then subsequently afterwards I switched the same class back to Butterknife and the UninitializedPropertyAccessException went away, and it seems like it won't come back unless something in the class changes, then you'll have to repeat this process again.
My suspicion here is that this has something to do with Kotlin not supporting incremental compilation, and somehow by changing the auto-generated code it was forced to recompile. But I could be way off here, just thought I'd share my experience.