搭建一个完整的Android工程(一)Dagger2
写在前面
现在越来越多的使用到了开源项目,但是仅限于使用,却不了解,更谈不上深入。也是因为越来越多的开源项目,平时工作中遇到问题也是第一时间寻找对应的开源项目,少了许多独立的思考。现在虽然能很轻松的完整一些有很多功能的App,但自己只是完成了一下界面,核心的通信、图片加载等功能都是使用的开源项目。从今天开始,我希望搭建一个Android项目的起步工程,一点点了解这些开源项目,不求多么的精通,至少不再是只会调用API。代码会在github上开源,至于能不能一直写下去,要看看我的拖延症还有没有救。
新建工程
使用Android Studio,新建一个空白工程,最小支持Android 4.0
添加Dagger2 依赖
修改app目录下的build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
...
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {
...
compile 'com.google.dagger:dagger:2.4'
compile 'org.glassfish:javax.annotation:10.0-b28'
}
在空白工程上测试一下Dagger2
依赖注入的可以通过https://github.com/android-cn/blog/tree/master/java/dependency-injection来了解一下。
通过之前的了解,实现一个简单的例子
HttpEngine.java
@Singleton
public class HttpEngine {
@Inject
public HttpEngine() {
}
}
DataComponent.java
@Singleton
@Component
public interface DataComponent {
void inject(MainActivity mainActivity);
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Inject HttpEngine mHttpEngine;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerDataComponent.builder().build().inject(this);
}
}
Dagger2自动生成的代码
上面的代码可以为MainActivity注入一个HttpEngine对象,现在有两个问题,怎么注入的,注入的是单例的对象吗。
查看Dagger2生成的代码
首先查看MainActivity_MembersInjector.java
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<HttpEngine> mHttpEngineProvider;
public MainActivity_MembersInjector(Provider<HttpEngine> mHttpEngineProvider) {
assert mHttpEngineProvider != null;
this.mHttpEngineProvider = mHttpEngineProvider;
}
public static MembersInjector<MainActivity> create(Provider<HttpEngine> mHttpEngineProvider) {
return new MainActivity_MembersInjector(mHttpEngineProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mHttpEngine = mHttpEngineProvider.get();
}
public static void injectMHttpEngine(
MainActivity instance, Provider<HttpEngine> mHttpEngineProvider) {
instance.mHttpEngine = mHttpEngineProvider.get();
}
}
MainActivity_MembersInjector从名字就能看出,为MainActivity注入Members。在构造的时候传入一个Provider对象,在injectMembers函数中通过Provider的get获取一个HttpEngine对象,注入到MainActivity中,在MainActivity中,被赋值的成员函数用Inject标识。到这里,明白了怎么向MainActivity注入Members的,但是Provider怎么提供要注入的对象的,还是不知道。
DaggerDataComponent.java
public final class DaggerDataComponent implements DataComponent {
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerDataComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static DataComponent create() {
return builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(HttpEngine_Factory.create());
}
@Override
public void inject(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
public static final class Builder {
private Builder() {}
public DataComponent build() {
return new DaggerDataComponent(this);
}
}
}
在DaggerDataComponent的initialize()函数中,创建了一个 MainActivity_MembersInjector 的实例,传入的是一个 HttpEngine_Factory 的实例。在inject()函数中会调用MainActivity_MembersInjector的injectMembers()函数进行注入。
HttpEngine_Factory.java
public enum HttpEngine_Factory implements Factory<HttpEngine> {
INSTANCE;
@Override
public HttpEngine get() {
return new HttpEngine();
}
public static Factory<HttpEngine> create() {
return INSTANCE;
}
HttpEngine_Factory提供了HttpEngine的构造方法,使用enum的方式保证单例模式。
Provider,Injector,Component
至此,一个最简单的注入就完成了,通过上面的例子,可以将Dagger2的注入分为三个部分,Provider,Injector,Component。Provider提供对象或者说构造方法,Injector负责将Provider提供的对象进行注入,至于Injector要使用哪个Provider,由Component来进行指定。
上面的例子,有两个疑问:
- 我们没有在任何地方手动的指定HttpEngine和DataComponent的关联,DaggerDataComponent是怎么找到HttpEngine的构造方法的呢?
- 怎么使用Dagger2创建一个不是单例的Provider
第一个问题,和Android apt有关,以后慢慢研究。
第二个问题,可以使用 @Provides
@Module
public class DataModule {
@Singleton
@Provides
HttpEngine provideHttpEngine() {
return new HttpEngine();
}
}
修改component,指定Module
@Singleton
@Component(modules = DataModule.class)
public interface DataComponent {
void inject(MainActivity mainActivity);
}
会生成一个新的class
public final class DataModule_ProvideHttpEngineFactory implements Factory<HttpEngine> {
private final DataModule module;
public DataModule_ProvideHttpEngineFactory(DataModule module) {
assert module != null;
this.module = module;
}
@Override
public HttpEngine get() {
return Preconditions.checkNotNull(
module.provideHttpEngine(), "Cannot return null from a non-@Nullable @Provides method");
}
public static Factory<HttpEngine> create(DataModule module) {
return new DataModule_ProvideHttpEngineFactory(module);
}
}
get方法获取的不再是单例的对象。
Module就像是一个Provider的仓库,我们可以定义多个仓库,可以在compont中指定使用那个仓库。
Module
关于Module和@Provides,还有一些疑问:
- 一个Compnot指定多个Module会是什么情况。
- Module可以继承吗
- 如何让@Provides作为一个单例
我们修改一下
HttpEngine.java
public interface HttpEngine {
String getTag();
}
添加
public class NormalHttpEngine implements HttpEngine {
@Override
public String getTag() {
return "NormalHttpEngine";
}
}
public class OkHttpEngine implements HttpEngine {
@Override
public String getTag() {
return "OkHttpEngine";
}
}
@Module
public class DataModule2 {
@Singleton
@Provides
HttpEngine provideHttpEngine() {
return new NormalHttpEngine();
}
}
DataComponent修改为
@Singleton
@Component(modules = {DataModule.class, DataModule2.class})
public interface DataComponent {
void inject(MainActivity mainActivity);
}
build,会出现error
Error:(15, 10) error: com.sw926.xyz.com.sw926.xyz.data.HttpEngine is bound multiple times:
@Provides com.sw926.xyz.com.sw926.xyz.data.HttpEngine com.sw926.xyz.com.sw926.xyz.data.DataModule.provideHttpEngine()
@Provides com.sw926.xyz.com.sw926.xyz.data.HttpEngine com.sw926.xyz.com.sw926.xyz.data.DataModule2.provideHttpEngine()
得出结论,可以指定多个Module,但Provider不能冲突。
修改DataModule2和DataComponent
@Module
public class DataModule2 extends DataModule {
@Provides
HttpEngine provideHttpEngine() {
return new NormalHttpEngine();
}
}
@Singleton
@Component(modules = DataModule2.class)
public interface DataComponent {
void inject(MainActivity mainActivity);
}
build,仍然出现error
Error:(13, 16) error: @Provides methods may not override another method. Overrides: @Provides com.sw926.xyz.com.sw926.xyz.data.HttpEngine com.sw926.xyz.com.sw926.xyz.data.DataModule.provideHttpEngine()
**@Provides 的函数不能被override,但是可以被继承**
第三个问题,@Provides 前面加上 @Singleton并不会变成单例的模式,如果想使用单例,可以在Application中创建component,在Activity中进行注入,代码如下:
public class App extends Application {
private DataComponent mDataComponent;
@Override
public void onCreate() {
super.onCreate();
mDataComponent = DaggerDataComponent.builder().build();
}
public DataComponent getDataComponent() {
return mDataComponent;
}
}
public class MainActivity extends AppCompatActivity {
@Inject HttpEngine mHttpEngine;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((App)getApplication()).getDataComponent().inject(this);
}
}
项目地址: https://github.com/sw926/xyz/tree/dagger2
搭建一个完整的Android工程(一)Dagger2的更多相关文章
- 搭建一个完整的Java开发环境
搭建一个完整的Java开发环境 作为一个Java程序员,配置一个java开发环境是必备的技能,今天给广大菜鸟初学者补上一课.环境的配置,大概就分三个1,JDK 2,Tomcat(或者其他的)3,ecl ...
- asp.netmvc 三层搭建一个完整的项目
接下来用 asp.net mvc 三层搭建一个完整的项目: 架构图: 使用的数据库: 一张公司的员工信息表,测试数据 解决方案项目设计: 1.新建一个空白解决方案名称为Company 2.在该解决方案 ...
- 手把手搭建一个完整的javaweb项目
手把手搭建一个完整的javaweb项目 本案例使用Servlet+jsp制作,用MyEclipse和Mysql数据库进行搭建,详细介绍了搭建过程及知识点. 下载地址:http://download.c ...
- 利用vue-cli配合vue-router搭建一个完整的spa流程
好文章备忘录: 转自:https://segmentfault.com/a/1190000009160934?_ea=1849098 demo源码:https://github.com/1590123 ...
- 11. 搭建一个完整的K8S集群
11. 搭建一个完整的Kubernetes集群 1. kubectl的命令遵循分类的原则(重点) 语法1: kubectl 动作 类 具体的对象 例如: """ kube ...
- react全家桶从0搭建一个完整的react项目(react-router4、redux、redux-saga)
react全家桶从0到1(最新) 本文从零开始,逐步讲解如何用react全家桶搭建一个完整的react项目.文中针对react.webpack.babel.react-route.redux.redu ...
- Django1.8教程——从零开始搭建一个完整django博客(一)
第一个Django项目将是一个完整的博客网站.它和我们博客园使用的博客别无二致,一样有分类.标签.归档.查询等功能.如果你对Django感兴趣的话,这是一个绝好的机会.该教程将和你一起,从零开始,搭建 ...
- 转载自 BotVS 「 珍藏版 」如何搭建一个完整的交易框架
[img]http://dn-filebox.qbox.me/8c218c119046b2a25df2d9c7b00c1e0fa6899bdd.png[/img]NO:01 交易策略 ≠ 交易系统. ...
- Kubernetes — 从0到1:搭建一个完整的Kubernetes集群
准备工作 首先,准备机器.最直接的办法,自然是到公有云上申请几个虚拟机.当然,如果条件允许的话,拿几台本地的物理服务器来组集群是最好不过了.这些机器只要满足如下几个条件即可: 满足安装 Docker ...
随机推荐
- Hyperledger fabric Client Node.js Hello World示例程序
简介 Hyperledger fabric Client (HFC)提供了基于Node.js的应用接口来访问Hyperledger区块. 本文介绍了一个使用HFC访问IBM Bluemixr区块服务的 ...
- Laravel4中的Validator
不管写接口还是写web页面,实质都是传入参数,然后进行业务逻辑,然后再输出具体内容.所以,对参数的验证是不可避免的一个环节,比如传过来的email是不是为空,是不是合法的email格式?laravel ...
- MVC5中,加载分部视图,常见的方式
首先,新建一个MVC类型的Web项目: 然后在Model文件夹下定义一个Student实体: public class Student { public int ID { get; set; } pu ...
- .net获取本机公网IP代码
类的代码如下: using System; using System.Net; using System.Text.RegularExpressions; namespace Keleyi.Com { ...
- WPF学习之绘图和动画
如今的软件市场,竞争已经进入白热化阶段,功能强.运算快.界面友好.Bug少.价格低都已经成为了必备条件.这还不算完,随着计算机的多媒体功能越来越强,软件的界面是否色彩亮丽.是否能通过动画.3D等效果是 ...
- 【UWP】使用Action代替Command
在Xaml中,说到绑定,我们用的最多的应该就是ICommand了,通过Command实现ViewModel到View之间的命令处理,例如Button默认就提供了Command支持,如下 Xaml: & ...
- Objective-C语法简记
开始学习iPhone开发了,虽然现在已经有了Swift,但我还是老老实实地学习Objective-C,鄙人入门的程序语言是C,后来学习了C#和Java,现在来学Objective-C,这篇只是一些很简 ...
- WCF双工通讯以及客户端间的间接通讯
由于学习计划安排不当,对WCF的认知一直停滞不前,最近工作上又用回了WCF,重拾一下,看到蒋老师介绍双工通讯的博文,实践一下,积累一下.原想着WCF的双工通讯就是原本的客户端能调用服务端的方法之余,服 ...
- ASP.NET MVC 网站开发总结(四)——校友平台开发总结
又历经一个多月的努力,学校的一个校友平台项目也接近内测的尾声了,简单的总结一下这次的项目开发. 与上次做Wing工作室的门户网站相比,同样是团队开发,参与的人员多了一个,用的时间也差不多一个月,但从总 ...
- 利用Abot爬虫和visjs 呈现漫威宇宙
1. 引言 最近接触Abot爬虫也有几天时间了,闲来无事打算从IMDB网站上爬取一些电影数据玩玩.正好美国队长3正在热映,打算爬取漫威近几年的电影并用vis这个JS库呈现下漫威宇宙的相关电影. Abo ...