I want to write an Android library, which in turn uses another Androd library.
Let's say I want to write libHigh which uses another libLow
There is an interface in libLow:
interface LowLevelInterface{
fun methodA()
}
and I implement this in my higher level library libHigh:
open class OpenClassImpl : LowLevelInterface {
override fun methodA(){//..}
}
It is an 'open' class, because later in app layer I expect to extend from OpenClassImpl.
But I dont want to make the interface 'LowLevelInterface' visible for later upper app level usage.
How can I hide the interface from libLow for the upper app level?
It depends on your existing code whether this will work, or how much effort it will be, but you could have a property implementing the interface instead of doing it directly:
open class OpenClassImpl {
// or protected/private
internal val lowLevelImpl: LowLevelInterface = {
// can access privates of OpenClassImpl here
}
}
and change the relevant calls to pass it.
Related
I have a application which has a dynamic feature module. In dynamic feature module, there is a form with images, input fields and also it has a buttton which access another third-party library.
Third-party library has a activity and fragment. While opening fragment inside activity, I am receiving below error, although there is container in activity's layout:
No view found for id 0x7f080053 (com.app.sample:id/container) for fragment SampleFragment{eed53f7 (5e4c0693-09a2-4725-a6de-1df49dd818f0) id=0x7f080053}
When accessing drawables in this third-party library, getting below error:
java.lang.NoSuchFieldError: No static field ic_back of type I in class Lcom.third.library/R$drawable; or its superclasses (declaration of 'com.third.library.R$drawable' appears in /data/app/com.app.sample-QtC8XuamC1fHEVU4FUpWaA==/split_thirdparty.apk)
It is fine when I use this library in a application without dynamic feature module.
Generally, when SplitCompat.installActivity(this) isn't called in Activity2, this won't work. While not having the source code, you'd have to extract the package and re-package it properly, because the Activity2 (or even the whole library package) likely isn't compatible with DFM.
After you enable SplitCompat for your base app, you need to enable SplitCompat for each activity that your app downloads in a dynamic feature module.
Here's another answer of mine, which demonstrates access through reflection.
Dynamic Delivery is relatively new feature so it has a lot of limitations. One of those limitations it that you cannot access code and resources of a Dynamic Module in a conventional way, thus it cannot be a dependency for other modules. Currently you can access Dynamic Module via reflection and having dynamic features defined through public interfaces in a common library module and loading their actual implementations (located in the dynamic feature modules) at runtime with a ServiceLoader. It has its performance downsides. They can be minimized with R8 using ServiceLoaderRewriter but not completely removed.
While using reflection is very bug prone we can minimize it either with #AutoService — AutoService is an annotation processor that will scan the project for classes annotated with #AutoService, for any class it finds it will automatically generate a service definition file for it.
Here is small example of how it is done
// All feature definitions extend this interface, T is the dependencies that the feature requires
interface Feature<T> {
fun getMainScreen(): Fragment
fun getLaunchIntent(context: Context): Intent
fun inject(dependencies: T)
}
interface VideoFeature : Feature<VideoFeature.Dependencies> {
interface Dependencies {
val okHttpClient: OkHttpClient
val context: Context
val handler: Handler
val backgroundDispatcher: CoroutineDispatcher
}
}
internal var videoComponent: VideoComponent? = null
private set
#AutoService(VideoFeature::class)
class VideoFeatureImpl : VideoFeature {
override fun getLaunchIntent(context: Context): Intent = Intent(context, VideoActivity::class.java)
override fun getMainScreen(): Fragment = createVideoFragment()
override fun inject(dependencies: VideoFeature.Dependencies) {
if (videoComponent != null) {
return
}
videoComponent = DaggerVideoComponent.factory()
.create(dependencies, this)
}
}
And to actually access code of Dynamic Feature use
inline fun <reified T : Feature<D>, D> FeatureManager.getFeature(
dependencies: D
): T? {
return if (isFeatureInstalled<T>()) {
val serviceIterator = ServiceLoader.load(
T::class.java,
T::class.java.classLoader
).iterator()
if (serviceIterator.hasNext()) {
val feature = serviceIterator.next()
feature.apply { inject(dependencies) }
} else {
null
}
} else {
null
}
}
Taken from here. Also there a lot more info there so I would recommend you to check it.
Generally I just would not recommend to use Dynamic Feature as dependency and plan your app architecture accordingly.
Hope it helps.
For the resources, this code part can be usage
R.id.settings would be:
getResources().getIdentifier("settings", "id", "com.library.package");
I have an abstract class and all classes in some package derive from it. Is there a way to create a list that dynamically instantiates all these classes that reside in some package when using Kotlin on Android?
Here is an example:
com.example.service
BaseService
com.example.service.emailservice
GmailService
OutlookService
All classes in com.example.service.emailservice derive from BaseService abstract class that resides in com.example.service. I want to create a list that contains GmailService and OutlookService objects. I could instantiate them manually and add them to a list, but in future I may add new service, lets say YandexService, which should appear in list too. This requires manual instantiation again. Is there a way to automatically instantiate classes that reside in some package?
There are two mayor ways to do it.
The first one - easier and dirtier one called Reflection.
You may find a lot of examples in java. Not so much in Kotlin though, but from what I have read here it is more than possible.
Usage of Reflection though, is not recommended in production, and generally considered as a bad approach to fulfill something.
The second way is Annotation Processors - this way is way harder. It is considered a clean way to do such tasks though and it is a general standard for code generating techniques. This way you can do all kinds of magic if you put your mind to it. Here is nice article about how to do it.
Generally I would recommend to use semiautomatic approach.
For example:
In your base service with init method
abstract class BaseService {
abstract fun init(): BaseService
}
Make all your services implement this BaseService
class Service : BaseService() {
override fun init(): BaseService {
return Service()
}
}
...
And then just create a list of classes
val services = listOf(Service(), Service1(), Service2())
and to init them do
services.forEach { it.init() }
This is not very different from what you have and may require some logical and architectural changes in your Services and App overall, but it won't be dirty and it won't require tremendous learning curve and time expenses.
Hope it helps.
use this library
add to gradle.build
implementation 'org.reflections:reflections:0.9.11'
then in your package com.example.service create a class from where you will be creating your Services, let's call it ServiceFactory here is implementation:
package com.example.service
import org.reflections.Reflections
class ServiceFactory {
init {
val reflections = Reflections(javaClass.`package`.name)
val subTypes = reflections.getSubTypesOf(BaseService::class.java)
val yourServices = subTypes.map { it.getConstructor().newInstance() }
yourServices.forEach { println(it.javaClass.name) }
}
}
fun main() {
ServiceFactory()
}
out:
com.example.service.emailservice.OutlookService
com.example.service.emailservice.GmailService
Something you were looking for ?
EDIT: of course you can instantiate this services from different place, but in that case you need to hardcode Reflections("com.example.service")
I'm trying to build an library that helps my application communicate with other devices using bluetooth
In order to achieve this I created an interface like so:
interface IBluetoothAdapter {
....
}
On normal runs I use it like so:
internal class BTAdapter : IBluetoothAdapter {
private val mAdapter = BluetoothAdapter.getDefaultAdapter()
}
and I use it to send messages to the actual bluetooth adapter
when I run my tests I mock IBluetoothAdapter and this works decently
in order to facilitate the above my actual class has this implementation:
class Communicator(val address:String) {
private var mAdapter: IBluetoothAdapter = BTAdapter()
#VisibleForTesting
#TestOnly
fun setAdapter(adapter: IBluetoothAdapter) {
mAdapter = adapter
}
.....
}
Is there a better way to do this?
Basically what I want to do is when the application runs in tests to always use one specific class (the mock) and when it runs normally to always use another (the one I created)
What your code is doing is called Dependency_injection
Is there a better way to do this?
There is not one "better" way to do it but there are different ways to do it that depend on the tools you are using (see why-do-i-need-an-ioc-container-as-opposed-to-straightforward-di-code for details.)
I would have implemented it the same way you have done it because your code can be used without any di containers so it is has less external dependencies.
I have learned that Open Close Principle is allowing extension to classes and restricting from modification. So in Kotlin, when we use extension function
Are we extending a class
Or are we modifying a class
Can extension functions in kotlin be an example for Open/Close Principle?
I assume extension means to apply inheritance and modification means to add or change code of existing class.
Thanks
The extenstion function is designed for situations where you want to add a function to a built-in or third-party class. You cannot do this by default because built-in functions are not modifiable.
An example implementation to add a toUnsigned method to the built-in Byte class:
fun Byte.toUnsigned(): Int {
return if (this < 0) this + 256 else this.toInt()
}
As Byte is a built-in class you cannot modify it directly. However, you can define an extension function as per above code. You can then call the extension function in the following way:
val x: Byte = -1
println(x.toUnsigned()) // Prints 255
Keep in mind that this is just syntactic sugar - you're not actually modifying the class or its instances. Therefore, you have to import an extension function/property wherever you want to use it (since it isn't carried along with the instances of the class).
Source : https://kotlinlang.org/docs/tutorials/kotlin-for-py/extension-functionsproperties.html
'Extension' in the context of the Open Closed Principle usually does not mean inheritance, it means somehow extending the class with new functionality. 'Modification' does refer to changing the code of the class, as you say.
The extension facility of Kotlin allows you to add a method to a class without editing the code of the class. This is perfectly in keeping with the Open Closed Principle -- the class is extended with new functionality without the class itself being changed.
ScreenDef is a class, I add a function setDevice for the class, which one is correct between Code A and Code B? why?
I think that Code B is correct, right?
Code C
data class ScreenDef(
val brightness: Int
): DeviceDef
class ScreenHelper(val mContext: Context) {
fun setScreen(aScreenDef: ScreenDef){
}
}
Code A
fun ScreenDef.setDevice(mContext: Context) {
ScreenHelper(mContext).setScreen(this)
}
Code B
fun ScreenDef.setDevice(mContext: Context) {
ScreenHelper(mContext).setScreen(it)
}
You should use this. it is referred as shorthand if there is only one parameter in lambdas.
context?.let {
it.resources.getInt(R.int.anyint) // just for example
}
In above snippet, it is the shorthand for lamda parameter(in case of only one parameter).
context?.let { cxt -> // here we have manually defined a parameter
cxt.resources.getInt(R.int.anyint) // just for an example
}
In this snippet, instead of it we have created cxt that is exactly same as it.
Actually you are taking the concept of Extension function wrong.
You are creating a data class ScreenDef and want to create an extension function to it, why? If you really want to have a member function just create a normal class and have a function in it.
Extension function should be created when target class is not maintained by you. For example: Activity, Fragments are not maintained by you and if you want to add a custom function, you have to extend them and do it. So to prevent it extension function comes into picture and they are really handy that's why we love it.
You can rather argue, whats wrong with creating extension function for a class created by us. It may or might not be true. It actually depends.
Let's take an example, suppose we have developed a library to draw simple symbols on canvas and there are several function we have created. It turned out to be so good that people are using it, we decided to created advanced version, that can draw more complex symbols that requires using our already developed simple lib. So when we extend the classes of simple lib we might need some functionality to improve some thing etc. in that case if we have imported our simple lib as dependency then its good to create extension function otherwise we would have to create one more child of that class and create desired function. If we have import our lib as source code, we can just go to the source fine and create a function inside it.
I hope it helps.