12/08/2018, 16:03

Dependency Injection với Dagger 2 trong Android

Giới thiệu Khi bạn có một đối tượng cần hoặc phụ thuộc vào đối tượng khác để thực hiện công việc của nó, bạn có một sự phụ thuộc. Sự phụ thuộc có thể được giải quyết bằng cách cho phép đối tượng phụ thuộc tạo ra sự phụ thuộc hoặc yêu cầu một object khác tạo ra sự phụ thuộc đó. Tuy nhiên, trong ...

Giới thiệu

Khi bạn có một đối tượng cần hoặc phụ thuộc vào đối tượng khác để thực hiện công việc của nó, bạn có một sự phụ thuộc. Sự phụ thuộc có thể được giải quyết bằng cách cho phép đối tượng phụ thuộc tạo ra sự phụ thuộc hoặc yêu cầu một object khác tạo ra sự phụ thuộc đó.

Tuy nhiên, trong bối cảnh của Dependency Injection, các sự phụ thuộc được cung cấp cho các lớp mà cần sự phụ thuộc để tránh sự cần thiết tạo ra sự phụ thuộc của chính lớp đó.

Chuẩn bị

Bạn sẽ cần phiên bản Android Studio mới nhất, bạn có thể tải xuống từ trang web Android Developer.

1. Dagger 2 API

Dagger 2 đưa ra một số annotation đặc biệt :

  • @Module : cho các lớp mà có phương thức cung cấp sự phụ thuộc.
  • @Providers : cho các phương thức bên trong các lớp @Module.
  • @Inject : để yêu cầu một sự phụ thuộc ( một hàm khởi tạo, một trường, hoặc một phương thức).
  • @Component : là cầu nối interface giữa các modules và injection.

Trên là những annotation quan trọng nhất bạn cần phải biết để bắt đầu với Dependency Injection sử dụng Dagger 2.

2. Dagger 2 Workflow

Để thực hiện đúng Dagger 2, bạn phải làm theo các bước sau :

  1. Xác định các đối tượng phụ thuộc và các phụ thuộc của nó.

  2. Tạo một class với annotation @Module, sử dụng annotation @Provider cho mọi phương thức mà trả về một sự phụ thuộc.

  3. Yêu cầu phụ thuộc vào các đối tượng phụ thuộc bằng cách sử dụng annotation @Inject.

  4. Tạo một interface bằng cách sử dụng annotation @Component và thêm các class với annotation @Module được tạo ra trong bước 2.

  5. Tạo một object của interface @Component để khởi tạo đối tượng phụ thuộc với các phụ thuộc của nó.

Phân tích phụ thuộc được chuyển từ thời gian chạy sang thời gian biên dịch. Điều này có nghĩa là bạn được thông báo về các sự cố có thể xảy ra trong giai đoạn phát triển, không giống với các thư viện khác, chẳng hạn như Guice. Trước khi sử dụng thư viện Dagger 2, bạn cần chuẩn bị cài đặt Studio Android để truy cập các lớp đã tạo.

3. Thiết lập môi trường Android Studio

Bước 1

Tạo Project :

Bước 2

Để Minimum SDK cho Project là API 10

4. Thiết lập Gradle

Bước 1

Thiết lập build.gradle của Project như sau :

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.0.0'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}
 
allprojects {
    repositories {
        mavenCentral()
        maven{
            url 'https://oss.sonatype.org/content/repositories/snapshots/'
        }
    }
}

Bước 2

Thiết lập build.gradle của app

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
 
android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"
 
    defaultConfig {
        applicationId "com.androidheroes.tutsplusdagger"
        minSdkVersion 10
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
 
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.3'
 
    compile 'com.google.dagger:dagger:2.0-SNAPSHOT'
    apt 'com.google.dagger:dagger-compiler:2.0-SNAPSHOT'
    provided 'org.glassfish:javax.annotation:10.0-b28'
}

5. Sử dụng Dagger 2

Bước 1. Xác định các đối tượng phụ thuộc

Trong ví dụ này, chúng ta sẽ làm việc với 2 class, Vehicle và Motor.

Motor là lớp độc lập, còn Vehicle là lớp phụ thuộc.

Lớp Motor được định nghĩa như sau :

public class Motor {
 
    private int rpm;
 
    public Motor(){
        this.rpm = 0;
    }
 
    public int getRpm(){
        return rpm;
    }
 
    public void accelerate(int value){
        rpm = rpm + value;
    }
 
    public void brake(){
        rpm = 0;
    }
}

Lớp Motor này chỉ có một thuộc tính là rpm, được chỉnh sửa qua 2 phương thức là accelerate() và brake(). Lấy giá trị của rpm thông qua phương thức getRpm().

Lớp Vehicle được định nghĩa như sau :

public class Vehicle {
 
    private Motor motor;
 
    public Vehicle(Motor motor){
        this.motor = motor;
    }
 
    public void increaseSpeed(int value){
        motor.accelerate(value);
    }
 
    public void stop(){
        motor.brake();
    }
 
    public int getSpeed(){
        return motor.getRpm();
    }
}

Trong lớp này, bạn có thể thấy chúng ta ko tạo mới một đối tượng Motor.

Bước 2. Tạo lớp Module

Bây giờ, bạn phải tạo một class với annotation @Module. Lớp này sẽ cung cấp các đối tượng mà bạn sẽ cần tới sự phụ thuộc của nó.

import javax.inject.Singleton;
 
import dagger.Module;
import dagger.Provides;
 
@Module
public class VehicleModule {
 
    @Provides @Singleton
    Motor provideMotor(){
        return new Motor();
    }
 
    @Provides @Singleton
    Vehicle provideVehicle(){
        return new Vehicle(new Motor());
    }
}

Như đã nêu ở Bước 1, lớp Vehicle cần model Motor để hoạt động bình thường. Đó là lí do tại sao bạn cần phải tạo ra 2 providers, một cho Motor ( model độc lập) và một cho Vehicle ( cho biết sự phụ thuộc của nó).

Đừng quên rằng mỗi provider ( hoặc phương thức ) phải có annotation @Provider và lớp phải có annotation @Module. Annotation @Singleton chỉ ra rằng sẽ chỉ có một instance của đối tượng.

Bước 3. Yêu cầu sự phụ thuộc trong các đối tượng phụ thuộc

Bây giờ bạn đã có các provider cho các model khác nhau của bạn, bạn cần phải yêu cầu chúng. Chẳng hạn như Vehicle cần Motor, bạn phải thêm annotation @Inject trong hàm khởi tạo của Vehicle :

@Inject
public Vehicle(Motor motor){
    this.motor = motor;
}

Bạn có thể dùng annotation @Inject để yêu cầu các sự phụ thuộc trong hàm khởi tạo, các thuộc tính, hoặc các phương thức. Trong trường hợp này, chúng ta sử dụng @Inject trong hàm khởi tạo.

Bước 4. Kết nối Module với Inject

Sự kết nối giữa provider của các sự phụ thuộc, @Module, và các class yêu cầu chúng thông qua @Inject được tạo ra bằng cách sử dụng @Component, là 1 interface :

import javax.inject.Singleton;
 
import dagger.Component;
 
@Singleton
@Component(modules = {VehicleModule.class})
public interface VehicleComponent {
 
    Vehicle provideVehicle();
 
}

Bên trong annotation @Component, bạn phải chỉ định module nào được sử dụng, trong trường hợp này chúng ta sử dụng VehicleModule mà chúng ta đã tạo ra trước đó. Nếu bạn cần nhiều Module, thêm chúng vào tương tự và ngăn cách nhau bằng dấu phẩy.

Bên trong Interface, thêm các phương thức cho mọi object mà bạn cần và nó sẽ tự trả cho bạn các sự phụ thuộc của nó. Trong ví dụ hiện tại chúng ta chỉ cần một object Vehicle, nên chỉ có một phương thức.

Bước 5. Sử dụng interface Component để lấy các đối tượng

Bây giờ, bạn đã sẵn sàng mọi kết nối, bạn phải lấy một instance của interface này và gọi các phương thức của nó để có được đối tượng bạn cần.

Chúng ta hay thực hiện nó trong onCreate() của MainActivity như sau :

public class MainActivity extends ActionBarActivity {
 
    Vehicle vehicle;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        VehicleComponent component = Dagger_VehicleComponent.builder().vehicleModule(new VehicleModule()).build();
 
        vehicle = component.provideVehicle();
 
        Toast.makeText(this, String.valueOf(vehicle.getSpeed()), Toast.LENGTH_SHORT).show();
    }
}
0