Complete Guide to Dagger and Hilt dependency injectionLast updated: 2019-06-01

Dependency Injection

Lets split Dependency Injection into two parts dependency and injection.Dependency is a situation in which two classes in your code base are interdependent. Consider there is a class FirstClass and another class SecondClass . when the FirstClass make use of SecondClass , then FirstClass depends on SecondClass and SecondClass is the dependency. To describe the situation in another term, FirstClass is a client and SecondClass is a Service.

How Client obtain reference to services
  • Instantiate
  • Static method call
  • Static Global Variable
  • Receive refrences from outside
Ways to inject Service from outside
  • Constructor Injection
  • Method Injection
  • Field Injection or Property Injection

Constructor injection is something like the services will be getting injected from outside through the Constructor .This method is actually very simple one and the signature of the constructor always reflects the depedencies and also easy to mock the services while in tetsing. The Constructor Injection makes that the servcies injected through the Constructor can be finalized and helps to maintain thread safety.

Method injection is something like the services will be getting injected from outside through the method parameters. The Method injection reflects the depedencies. Can lead to implicit ordering requirment AKA Temporal Coupling

Field Injection is something like the services will be getting injected from outside through the variables will be same as method injection. Can lead to implicit ordering requirment AKA Temporal Coupling

Architectural Pattern

While you can create application entirely in java, there must be some situation in which something cant be achieved using java. So programmers will use the JNI to write java native method to handle those siutation when an application cannot be written entirely in java

JavaVM and JNIEnv

JNI defines two Key Data Structures, JavaVM and JNIEnv. The JavaVM will provide the invocation interface function which will allow us to create and destroy a javaVM. Usually we can have multiple javaVMs per process but in android it is allowed as one. JNIEnv provides most of the JNI functions. The native function will receive the JNIEnv as the first arguments. The JNIEnv is used for thread-local storage. This is the reason a JNIEnv cannot be shared between threads. If a piece of code has no otherway to pass get its JNIEnv, we have to share the javaVM and use the GetEnv to discover the thread's JNIEnv

Accessing Java code from Native

Native code access the javaVM featured by calling JNI functions. JNI functions are exposed by inteface pointer.An interface pointer is a pointer to pointer pointer. The pointer points to an array of pointers, each of a pointer will point to an interface function

Jni interface pointer will be accessed by JNIEnv *

Loading native methods in java code

Native methods are loaded inside java program using System.loadLibrary method

The Jni interface pointer is valid only in current thread. The native method, therefore, must not pass the jni interface pointer to another thread. The native method receives the JNI interface pointer through the arguments. The VM is guranteed to pass the same interface pointer while calling the native methods multiple time from a same thread but a native method can be called from different java thread, and this may lead to get different jni interface pointers

Native Method Arguments
  • Native inteface pointer is the first arguments to pass as an argument to the native method JNIEnv
  • The second argument differs based on whether the native method is static or non static
  • The second argument is the reference the object incase of nonstatic and is a reference to its java class incaseof static method
  • The remaining arguments are same as java arguments
Resolving Native Method Name

Dynamic linkers resolve entries based on their names

  • The prefix should starts with Java_
  • mangled fully-qualified class name
  • Underscore ("_") seperator
  • Mangled method name
  • For overloaded native method, two underscore ("__") followed by mangled signature
Below is the sample implementation java code with native implementation

package mypackage;

public class JniTest {

  public native void add(int a, int b);

}

In the above code, we used native keyword to access the native method add in C code



void Java_mypackage_JniTest_add(JNIEnv *env,jobject obj,jint a,jint b){
//JNIEnv *env is the interface pointer
//jobject obj is the this object
//jint a is the first integer argument of java code
//jint b is the second integer argument of java code
}

Primitive Types
Java Type Native Type
boolean jboolean
byte jbyte
char jchar
short jshort
int jint
long jlong
float jfloat
double jdouble
void void
Referencing Java Objects

Primitive data types are copied from java to native methods ands java objects are passed by references. The Virtual Machine should keep track on the objects passed java and native, so that it will not get destroyed by Garbage Collector. Also the native code have to inform the VM that the object is no longer needed and can be destroyed by the garbage collector.

Types of References

Two types of references jni is holding. one is Local Reference and other one is Global Reference Local Reference is the reference that will be alive untill the native method returns but the Global reference will be in memory untill they explicitly removed forcefully All the object passed from VM to native methods as local references and all the objects that are returned by the jni methods are all local references. The jni will provide the chance to create global reference from the local reference and now the native method will return either global reference or local reference.

How VM free References?

VM used to track all the object references passed from java to native methods and registry has been maintained. whenever an object needs to be freed, the registry will be deleted, thus the objects will be garbage collected.

Accessing Fields and Methods

JNI can access the methods and fields by their symbolic name and type Structures. for example , to call a method name add in the class calculation, the native code obtain a method id as shown below

jmethodID methodid = env->GetMethodID(calculation,"add","(ILjava/lang/String;)D");

Now the native code can use the method id wherever it required

jdouble value = env->CallDoubleMethod(obj,methodid,10,str);