在我们的日常编码的过程中,常常会遇到这种需求。例如:这个版本我们使用okhttp作为网络通信库,如果下个版本我们想要用volley作为网络通信库,那该怎么办呢?我们总不能对使用okhttp的地方一个个改成volley吧!这样得要改到猴年马月啊!因此,通常情况下,我们接入第三方库的时候总会有一个隔离层,方便我们日后切换不同的第三方库。接下来,笔者将手把手演示如何搭建一个隔离层。
技术方案
一般,我们搭建隔离层的技术方案有两种:
- 使用代理方式实现
- 使用Hilt反射方式实现
我们首先使用代理的方式来实现隔离层,以切换log为例。
- 首先,我们需要针对不同的库,定义一个统一的接口
package com.example.test;
public interface ILogCallback {
void d(String tag,String msg);
void e(String tag,String msg);
}
- 实现该接口,例如我们定义了BLog和XLog这两个类,在接口的方法中做不同的实现以示功能的区分。
package com.example.test;
import android.util.Log;
public class BLog implements ILogCallback{
@Override
public void d(String tag, String msg) {
Log.d("BLog"+tag,msg);
}
@Override
public void e(String tag, String msg) {
Log.e("BLog"+tag,msg);
}
}
package com.example.test;
import android.util.Log;
public class XLog implements ILogCallback{
@Override
public void d(String tag, String msg) {
Log.d("XLog"+tag,msg);
}
@Override
public void e(String tag, String msg) {
Log.e("XLog"+tag,msg);
}
}
- 定义一个统一的类,来决定我们要使用的是BLog还是XLog类。
package com.example.test;
public class LogHelper {
private ILogCallback callback;
private static LogHelper helper;
private LogHelper() {
}
public static LogHelper getInstance() {
if (helper == null) {
helper = new LogHelper();
}
return helper;
}
public void init(ILogCallback callback) {
this.callback = callback;
}
public void e(String tag, String msg) {
callback.e(tag, msg);
}
public void d(String tag, String msg) {
callback.d(tag, msg);
}
}
5.在application中初始化
package com.example.test;
import android.app.Application;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LogHelper.getInstance().init(new XLog());
//LogHelper.getInstance().init(new BLog());
}
}
package com.example.test
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
@Inject
lateinit var student:Student
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
LogHelper.getInstance().d("MainActivity","Hello World")
}
}
这样,我们就只需要更改application中的一句代码就能够灵活地切换BLog和XLog了。
接下来,我们来间接如何使用Hilt来搭建隔离层架构。首先,我们得要搞清楚Hilt究竟是一个什么东西,它与dagger2有什么区别。简单来说,Hilt与Dagger2的关系就是okhttp与retrofit的关系,hilt仅仅只是对dagger2的二次封装而已,为了更加好用,核心的功能还是由dagger2来实现的。
hilt相较于dagger2的最明显特征就是:
- 简单
- 提供了Android专属的API
学习Hilt,我们需要知道下面几个概念。
- Hilt组件
对于您可以从中执行字段注入的每个 Android 类,都有一个关联的 Hilt 组件,您可以在 @InstallIn 注解中引用该组件。每个 Hilt 组件负责将其绑定注入相应的 Android 类。
- Hilt组件的生命周期
- 组件作用域
默认情况下,Hilt 中的所有绑定都未限定作用域。这意味着,每当应用请求绑定时,Hilt 都会创建所需类型的一个新实例。
不过,Hilt 也允许将绑定的作用域限定为特定组件。Hilt 只为绑定作用域限定到的组件的每个实例创建一次限定作用域的绑定,对该绑定的所有请求共享同一实例。
了解完上面这几个概念之后,我们用一个案例来说明如何使用Hilt。
- Hilt的引入
首先,将 hilt-android-gradle-plugin 插件添加到项目的根级 build.gradle 文件中:
plugins {
...
id 'com.google.dagger.hilt.android' version '2.44' apply false
}
然后,应用Gradle插件并在app/build。gradle文件中添加下面依赖项:
...
plugins {
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
}
android {
...
}
dependencies {
implementation "com.google.dagger:hilt-android:2.44"
kapt "com.google.dagger:hilt-compiler:2.44"
annotationProcessor "com.google.dagger:hilt-compiler:2.44"
}
- 新建一个module
package com.example.test;
public class Student {
}
package com.example.test;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.ActivityComponent;
import dagger.hilt.components.SingletonComponent;
// ActivityComponent.class 能注入到Activity,不能注入到Application
// SingletonComponent.class 能注入到Activity, 能注入到Application
@Module
@InstallIn(SingletonComponent.class) // 注入到Activity里面去(按规则来,该是哪个注入,就写哪个,不要乱搞)
public class StudentMoudle {
// @Singleton // 上面的InstallIn 必须是 (SingletonComponent.class) 才能全局单例
@ActivityScoped // 上面的InstallIn 必须是 (ActivityComponent.class) 才能局部单例
@Provides
public Student getStudnet(){
return new Student();
}
}
package com.example.test
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var stu:Student
@Inject
lateinit var stu2:Student
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Toast.makeText(this,"${stu2.hashCode()}",Toast.LENGTH_LONG).show()
Toast.makeText(this,"${stu.hashCode()}",Toast.LENGTH_LONG).show()
// LogHelper.getInstance().d("MainActivity","Hello World")
}
}
与dagger2相比,Hilt不需要在创建component注解了。
接下来,我们就利用Hilt来搭建隔离层框架
首先,我们需要为BLog和XLog类做一些修改
package com.example.test;
import android.util.Log;
import javax.inject.Inject;
public class XLog implements ILogCallback{
@Inject
public XLog(){
}
@Override
public void d(String tag, String msg) {
Log.d("XLog"+tag,msg);
}
@Override
public void e(String tag, String msg) {
Log.e("XLog"+tag,msg);
}
}
package com.example.test;
import android.util.Log;
import javax.inject.Inject;
public class BLog implements ILogCallback{
@Inject
public BLog(){}
@Override
public void d(String tag, String msg) {
Log.d("BLog"+tag,msg);
}
@Override
public void e(String tag, String msg) {
Log.e("BLog"+tag,msg);
}
}
接着,创建两个注解,我们通过切换不同的注解来更换不同的log
package com.example.test.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
@Qualifier // 此注解就是为了让 hilt 区分这个注解(做自己的逻辑处理) 自己定义标识,限定符号
@Retention(RetentionPolicy.RUNTIME)
public @interface BLog {
}
package com.example.test.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
@Qualifier // 此注解就是为了让 hilt 区分这个注解(做自己的逻辑处理) 自己定义标识,限定符号
@Retention(RetentionPolicy.RUNTIME)
public @interface XLog {
}
最后,新建一个Module,在里面进行绑定
package com.example.test;
import javax.inject.Singleton;
import dagger.Binds;
import dagger.Module;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
@Module
@InstallIn(SingletonComponent.class)
public abstract class LogInterfaceModule {
@com.example.test.annotation.XLog
@Binds
@Singleton
public abstract ILogCallback bind(XLog log);
@com.example.test.annotation.BLog
@Binds
@Singleton
public abstract ILogCallback bindB(com.example.test.BLog log);
}
package com.example.test;
import android.app.Application;
import com.example.test.annotation.BLog;
import com.example.test.annotation.XLog;
import javax.inject.Inject;
import dagger.hilt.android.HiltAndroidApp;
@HiltAndroidApp
public class MyApplication extends Application {
//只需要切换不同的注解便可以切换log
@Inject
// @XLog
@BLog
public ILogCallback logCallback;
@Override
public void onCreate() {
super.onCreate();
// LogHelper.getInstance().init(new XLog());
// LogHelper.getInstance().init(new BLog());
}
public ILogCallback getLog(){
return logCallback;
}
}
package com.example.test
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var stu:Student
@Inject
lateinit var stu2:Student
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
(application as MyApplication).log.d("xxxx","MainActivity")
}
}
参考资料
https://developer.android.com/training/dependency-injection/hilt-android#component-hierarchy