Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access main project from module in Android Studio?

I am using this library which I have installed locally as a module. I'm able to access it via my main project, but I'm not able to do the opposite. For example, access a variable in my main project from this library...

I tried adding this line in the library's build.gradle:

    implementation project(':app')

But I get this weird error:

Circular dependency between the following tasks:
:placepicker:generateDebugRFile
\--- :placepicker:generateDebugRFile (*)
    
(*) - details omitted (listed previously)

How can I fix this? My project is in Java and my library is in Kotlin

like image 693
Bandy Avatar asked Aug 01 '20 11:08

Bandy


1 Answers

"Circular dependency" can be fixed only by removing dependency that causes this issue on one of two sides.

If you need to access some data from the library code you can implement an interface in a library that will be extended by some class in your project. Then you will be able to use extended class in your library and access methods defined in the interface.

Example

Let's imagine you need to get a reference to the application context within your library. You should create an interface:

interface ContextAccessor {
    // Marking it as optional just in case
    // you will not be able to get a context
    // from an object that implemented ContextAccessor
    fun getApplicationContext(): Application?
}

Because you added the library as a dependency in your project you have access to ContextAccessor. Extend some class with this interface and implement the getApplicationContext method. Let's say you want to extend some Activity.

class MyActivity: Activity, ContextAccessor {
    ... other code here

    override fun getApplicationContext(): Application? = application
}

Now from within your MyActivity class, you can set ContextAccessor into your library as if it was dependency injection.

class MyActivity: Activity, ContextAccessor {
    ... other code here 
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val someLibraryClassInstance = SomeLibraryClass()
        someLibraryClassInstance.setContextAccessor(this)
        // OR -> `someLibraryClassInstance.contextAccessor = this`
    }
}

WARNING: when you save a reference to any Android component, especially Activity, Fragment, Dialog etc., make sure you later remove this reference when the object is going to be destroyed to avoid memory leaks.

An example of how to remove a reference on a little bit modified code from the previous code snippet:

class MyActivity: Activity, ContextAccessor {
    ... other code here 

    private val someLibraryClassInstance = SomeLibraryClass()   
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
         
        // ContextAccessor reference is set to some library class
        someLibraryClassInstance.setContextAccessor(this)
    }

    override fun onDestroy() {
        super.onDestroy()

        // Super important!
        someLibraryClassInstance.setContextAccessor(null)
        // OR create some method like `someLibraryClassInstance.removeContextAccessor(this)`
    }
}

Same classes in Java

interface ContextAccessor {
    // Marking it as optional just in case
    // you will not be able to get a context
    // from an object that implemented ContextAccessor
    Application getApplicationContext();
}
public class MyActivity extends Activity implements  MyActivity.ContextAccessor {
    
    private SomeLibraryClass someLibraryClassInstance = SomeLibraryClass();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ContextAccessor reference is set to some library class
        someLibraryClassInstance.setContextAccessor(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Super important!
        someLibraryClassInstance.setContextAccessor(null);
        // OR create some method like `someLibraryClassInstance.removeContextAccessor(this)`
    }

    @Override
    public Application getApplicationContext() {
        return super.getApplication();
    }
}

Update (10 aug 2020): how to use ContextAccessor?

Here is how you can use ContextAccessor in your library:

class SomeLibraryClass {
    private var mContextAccessor: ContextAccessor?

    fun setContextAccessor(contextAccessor: ContextAccessor?) {
        mContextAccessor = contextAccessor
    }
    
    fun someOtherMethod() {
        mContextAccessor?.getAppContext()?.let { nonNullContext ->
            // use nonNullContext here
        }
    }
}
like image 129
Jenea Vranceanu Avatar answered Sep 29 '22 19:09

Jenea Vranceanu