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);