一、概述
上一篇主要介绍了dagger的基本使用方法,这篇则分享一下我之前基于mvp架构的项目中的简单应用,也作为一个记录;最近才了解到mvpclean,把dagger的依赖注入部分放在presentation层比较合适。
二、dagger架构思路
由于还不算很熟悉dagger,所以只是将网络请求的httpclient以及数据库采用依赖注入的方式,并没有涉及到一些对象的作用域甚至包含、继承等更复杂的组织形式,适合一些简单的小型项目。
- 提供一个AppComponent,暴露出全局Context、数据中心、http请求帮助对象、数据库帮助对象以及SharedPreferences帮助对象
- 提供一个ActivityComponent,依赖AppComponent,获取暴露的依赖实例,同时暴露出activity对象实例
- 提供一个FragmentComponent,依赖AppComponent,获取暴露的依赖实例,也暴露activity对象实例
注意:此处fragment直接依赖于AppComponent而并没有采取上篇所述的依赖于ActivityComponent,并提供AppModule的形式,复杂的大型应用应考虑差异性,此处目的只是拿到一些全局的依赖实例
三、环境与版本
当时开发时没有采取最新的版本,所以可能和现在有所差别,使用时应灵活调整。
Project的build.gradle如下:
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath "io.realm:realm-gradle-plugin:3.1.1"
}
module的gradle文件:
apply plugin: 'com.android.application'
apply plugin: 'android-apt'
apply plugin: 'realm-android'
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
……
// realm所需,应该是跟.so文件有关
ndk {
abiFilters = ["armeabi"]
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
google {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "google"]
}
}
// realm所需
packagingOptions {
exclude "lib/mips/librealm-jni.so"
exclude "lib/x86/librealm-jni.so"
exclude "lib/x86_64/librealm-jni.so"
}
// realm所需,请根据需要选择
splits {
abi {
enable true
reset()
include 'armeabi', 'armeabi-v7a', 'arm64-v8a'
universalApk true
}
}
}
dependencies {
……
//network
compile "com.google.code.gson:gson:2.7"
compile "com.squareup.retrofit2:retrofit:2.2.0"
compile "com.squareup.retrofit2:converter-gson:2.2.0"
compile "com.squareup.retrofit2:adapter-rxjava2:2.2.0"
compile "com.squareup.okhttp3:okhttp:3.6.0"
compile "com.squareup.okhttp3:logging-interceptor:3.6.0"
compile "com.github.bumptech.glide:glide:3.7.0"
compile "com.github.bumptech.glide:okhttp3-integration:1.4.0@aar"
compile "org.jsoup:jsoup:1.10.1"
//di
compile "com.google.dagger:dagger:2.0.2"
apt "com.google.dagger:dagger-compiler:2.0.2"
provided "org.glassfish:javax.annotation:10.0-b28"
}
realm的配置也并没有弄清楚,个人建议可以使用room或者greendao在配置方面更简单。编译有问题的话请百度下realm相关。
四、dagger编码—AppComponent相关
AppComponent暴露全局的依赖实例,把依赖分为两块,AppModule负责暴露的依赖实例,HttpModule负责网络请求帮助类的依赖实例。
HttpModule代码如下:
@Module
public class HttpModule {
@Singleton
@Provides
GsonBuilder provideGsonBuilder() {
return new GsonBuilder();
}
@Provides
@Singleton
public Gson getGson(GsonBuilder builder) {
return builder
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.serializeNulls()
.setLenient()
.setFieldNamingStrategy(new AnnotateNaming())
.create();
}
@Singleton
@Provides
OkHttpClient.Builder provideOkHttpBuilder() {
return new OkHttpClient.Builder();
}
@Singleton
@Provides
OkHttpClient provideOkHttpClient(OkHttpClient.Builder builder) {
builder.connectTimeout(10, TimeUnit.SECONDS);
builder.readTimeout(20, TimeUnit.SECONDS);
builder.writeTimeout(20, TimeUnit.SECONDS);
builder.retryOnConnectionFailure(true);
builder.addNetworkInterceptor(new HttpHeadInterceptor());
builder.addInterceptor(getInterceptor());
builder.cache(getCache());
return builder.build();
}
@Singleton
@Provides
Retrofit.Builder provideRetrofitBuilder() {
return new Retrofit.Builder();
}
@Singleton
@Provides
@DataUrl // 非图像数据Retrofit
Retrofit provideDataRetrofit(Retrofit.Builder builder, OkHttpClient client, Gson gson) {
return createRetrofit(builder, client, gson, DataApis.HOST);
}
@Provides
@Singleton
DataApis provideDataService(@DataUrl Retrofit retrofit) {
return retrofit.create(DataApis.class);
}
@Singleton
@Provides
@ImageUrl // 图像数据Retrofit
Retrofit provideImageRetrofit(Retrofit.Builder builder, OkHttpClient client, Gson gson) {
return createRetrofit(builder, client, gson, ImageApis.HOST);
}
@Provides
@Singleton
ImageApis provideImageService(@ImageUrl Retrofit retrofit) {
return retrofit.create(ImageApis.class);
}
// 硬盘缓存
private Cache getCache() {
File cacheFile = new File(Constants.PATH_CACHE); // 自定义路径
Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
return cache;
}
// http请求报文拦截器
private HttpLoggingInterceptor getInterceptor() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
if (BuildConfig.DEBUG) {
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); // 测试
} else {
interceptor.setLevel(HttpLoggingInterceptor.Level.NONE); // 打包
}
return interceptor;
}
// http缓存拦截器
class HttpHeadInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!SystemUtil.isNetworkConnected()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (SystemUtil.isNetworkConnected()) {
int maxAge = 0;
// 有网络时, 不缓存, 最大保存时长为0
response.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.removeHeader("Pragma")
.build();
} else {
// 无网络时,设置超时为4周
int maxStale = 60 * 60 * 24 * 28;
response.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.removeHeader("Pragma")
.build();
}
return response;
}
}
// 生成Retrofit对象
private Retrofit createRetrofit(Retrofit.Builder builder, OkHttpClient client, Gson gson, String url) {
return builder
.baseUrl(url)
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
}
@DataUrl限定符如下:
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface DataUrl {
}
@ImageUrl限定符如下
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ImageUrl {
}
DataApis数据接口文件:
public interface DataApis {
// String HOST = "false"; // 测试地址
String HOST = "true"; // 生产地址
@Headers({
"Content-Type: application/json"
})
@POST("/api")
Flowable<Response<HttpResponse<User>>> login(@Body HttpRequest<UserRequestBody> user);
}
ImageApis图片接口文件:
public interface ImageApis {
// String HOST = "false"; // 测试地址
String HOST = "true"; // 生产地址
@POST("/upload.go")
Flowable<Response<UploadImageResponse>> uploadCollectImage(@Body RequestBody body);
}
通过数据中心DataManager来管理网络请求、数据库、键值对数据(接口的方式)
网络请求帮助类:
public interface HttpHelper {
}
public class RetrofitHelper implements HttpHelper {
private DataApis mDataApis;
private ImageApis mImageApis;
// 参数注意,必须是在某个module中标注过可以提供的
@Inject
public RetrofitHelper(DataApis dataApis, ImageApis imageApis) {
this.mDataApis = dataApis;
this.mImageApis = imageApis;
}
}
数据库帮助类:
public interface DBHelper {
}
public class RealmHelper implements DBHelper {
private static final String DB_NAME = "myRealm.realm";
private Realm mRealm;
@Inject
public RealmHelper() {
mRealm = Realm.getInstance(new RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.name(DB_NAME)
.build());
}
}
键值对帮助类:
public interface PreferencesHelper {
}
public class ImplPreferencesHelper implements PreferencesHelper {
private static final String SHAREDPREFERENCES_NAME = "my_sp";
private final SharedPreferences mSPrefs;
@Inject
public ImplPreferencesHelper() {
mSPrefs = App.getInstance().getSharedPreferences(SHAREDPREFERENCES_NAME, Context.MODE_PRIVATE);
}
}
数据中心DataManager:
public class DataManager implements HttpHelper, DBHelper, PreferencesHelper {
HttpHelper mHttpHelper;
DBHelper mDbHelper;
PreferencesHelper mPreferencesHelper;
public DataManager(HttpHelper httpHelper, DBHelper dbHelper, PreferencesHelper preferencesHelper) {
mHttpHelper = httpHelper;
mDbHelper = dbHelper;
mPreferencesHelper = preferencesHelper;
}
}
接下来就可以编写AppModule了:
@Module
public class AppModule {
private final App application;
public AppModule(App application) {
this.application = application;
}
@Provides
@Singleton
App provideApplicationContext() {
return application;
}
@Provides
@Singleton
HttpHelper provideHttpHelper(RetrofitHelper retrofitHelper) {
return retrofitHelper;
}
@Provides
@Singleton
DBHelper provideDBHelper(RealmHelper realmHelper) {
return realmHelper;
}
@Provides
@Singleton
PreferencesHelper providePreferencesHelper(ImplPreferencesHelper implPreferencesHelper) {
return implPreferencesHelper;
}
@Provides
@Singleton
DataManager provideDataManager(HttpHelper httpHelper, DBHelper DBHelper, PreferencesHelper preferencesHelper) {
return new DataManager(httpHelper, DBHelper, preferencesHelper);
}
}
最后在AppComponent暴露出依赖实例:
@Singleton
@Component(modules = {AppModule.class, HttpModule.class})
public interface AppComponent {
App getContext(); // 提供App的Context
DataManager getDataManager(); // 数据中心
RetrofitHelper retrofitHelper(); // 提供http帮助类
RealmHelper realmHelper(); // 提供数据库帮助类
ImplPreferencesHelper preferencesHelper(); // 提供sp帮助类
}
最后是在自定义application中依赖注入AppComponent:
public class App extends Application {
private static App instance;
public static AppComponent appComponent;
public static synchronized App getInstance() {
return instance;
}
@Override
public void onCreate() {
super.onCreate();
instance = this;
if (appComponent == null) {
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(instance))
.httpModule(new HttpModule())
.build();
}
}
public static AppComponent getAppComponent() {
return appComponent;
}
}
注意:
1.在AppComponent中暴露实例后,才可供后续依赖的component使用
2.HttpModule上采用@DataUrl和@ImageUrl是为了处理接口地址不同时Retrofit依赖问题
3.AppModule中的provideHttpHelper的参数实例化时构造函数需要的参数是从HttpModule中获取的,这也就是component层级组合modules的作用
五、dagger编码—ActivityComponent相关
ActivityComponent依赖于AppComponent,获取暴露的实例
ActivityModule如下:
@Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
this.mActivity = activity;
}
@Provides
@ActivityScope
public Activity provideActivity() {
return mActivity;
}
}
@ActivityScope作用域:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
ActivityComponent如下:
@ActivityScope
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
Activity getActivity();
void inject(MainActivity activity);
}
此后在ActivityComponent中注册的activity在component依赖注入后就可以使用AppComponent暴露的实例:
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements BaseView {
@Inject
protected T mPresenter;
protected ActivityComponent getActivityComponent() {
return DaggerActivityComponent.builder()
.appComponent(App.getAppComponent())
.activityModule(getActivityModule())
.build();
}
protected ActivityModule getActivityModule(){
return new ActivityModule(this);
}
getActivityComponent().inject(this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
……
initInject();
}
protected abstract void initInject();
}
public class MainActivity extends BaseActivity<MainActivityPresenter> implements MainActivityContract.View{
@Override
protected void initInject() {
getActivityComponent().inject(this);
}
}
可以在基类的onCreate中的抽象方法中依赖注入,继承基类的子类实现改抽象方法即可,Fragment也类似就不多做展示。
另外Presenter提供的依赖如下:
public class MainActivityPresenter extends RxPresenter<MainActivityContract.View> implements MainActivityContract.Presenter {
private DataManager mDataManager;
@Inject
public MainActivityPresenter(DataManager mDataManager) {
this.mDataManager = mDataManager;
}
}
该处构造函数提供了BaseActivity中Presenter的依赖实例,而参数则是从ActivityComponent依赖的AppComponent暴露的实例获取
六、总结
以上就是我之前mvp架构中dagger的使用,还有很多不合理之处需改善,打算学习完MvpClean和架构组件后重构代码,有错误和建议欢迎提出。