Reputation: 36343
I have a suite of libraries distributed as 3 separate aars. Is there any way to be able to obfuscate each library independently, but keep the ability to call methods between modules internally?
E.g. if libA
needs to call myMethod
from libB
, but I don't want myMethod
exposed to clients integrating my libraries (yet all libraries must be obfuscated).
Right now I'm forced to make myMethod
public and exclude it from obfuscation so it can be called from libA
. Is there a better solution to this problem that won't expose myMethod
to clients while still be obfuscated?
Upvotes: 1
Views: 1755
Reputation: 11
I am unsure if you have already found a solution to your problem but I have played a little and find out a working solution. I made a small HelloWorld app to show how it is done: https://github.com/jmineraud/multi-module-android-app.
It contains the repo for the lib as a git submodule (https://github.com/jmineraud/multi-module-android-lib)
The trick is to obfuscate the libraries independently, but not to optimize them (we do not want to remove any function) and that all dependent submodule must reuse the same mapping.txt
file.
For instant in my submodule proguard file, I added:
-dontshrink
-dontoptimize
#-useuniqueclassmembernames # Option does not work with R8
-repackageclasses "com.your.package" # I do not want to obfuscate the whole hierarchy to keep my obfuscated library class separate and avoid obfuscation problems when the lib is used in the app project (which will be in turn obfuscated)
-applymapping "../lib-core/build/outputs/mapping/release/mapping.txt"
The classes I want to keep are annotated with @Keep
from androidx.annotation.Keep
Upvotes: 1
Reputation: 2555
It's a best practice that library expose only interface and not Objects. Also, interface must respect interface segregation principle. If your client do not need to use the method, your library too. Your libA is the first customer of your LibB. Your libraries must be compliant with single responsibility principle. I think you have to re-work library and extract interface for your classes and expose only interfaces.
I think that this will be a tiny task, a quick solution is to add a proguard rules for each protected method if your are using java.
-keep public class mypackage.MyPublicClass {
protected void myProtectedMethod();
}
Sample case for refactoring:
//libA
class LibA {
lateinit var libB:LibB
fun serviceA(){
libB.checkLicence()
}
}
//libB
class LibB{
fun serviceB(){}
//this method is needed by libA, due to obfuscation visibility is public instead of internal
//internal fun checkLicence(){}
fun checkLicence(){}
}
the refactored code maybe:
//new library distributed only internally not to customer
// LibInternal
interface InternalLib {
fun checkLicence()
}
internal class InternalLibImpl : InternalLib {
override fun checkLicence() {
}
}
object InternalLibFactory {
fun create(): InternalLib = InternalLibImpl()
}
//LibB
interface LibB {
fun serviceB()
}
class LibBImpl(private val internalLib: InternalLib) : LibB {
override fun serviceB() {
internalLib.checkLicence()
}
}
object LibBFactory {
fun create(): LibB = LibBImpl(InternalLibFactory.create())
}
//LibA
interface LibA {
fun serviceA()
}
internal class LibAImpl(private val libB: LibB, private val internalLib: InternalLib) {
fun serviceA() {
internalLib.checkLicence()
libB.serviceB()
}
}
object LibAFactory {
fun create(): LibB = LibBImpl(InternalLibFactory.create())
}
Upvotes: 0