解锁Dagger2使用姿势(一)
毫无疑问,Dagger2的 上手是有门槛的,有门槛是因为它里边的概念多,用起来复杂,可是一旦你学会了Dagger2的使用,你一定会爱不释手的。与ButterKnife和AndroidAnnotations不同,Dagger2是由Google开发和维护(之前是Squreup),在 性能上可以说是做到的极致(Dagger2在编译期间进行了依赖注入,完全去除了反射机制),Dagger2要解决的问题也和ButterKnife以及AndroidAnnotations不同,后者主要是解决控件的初始化,线程的切换等等,而Dagger2则类似于Java中的Spring框架,主要是为了解决应用程序在运行时的耦合问题,使用Dagger2可以帮助我们实现低耦合高聚合。OK,那么今天我主要是想通过几个小Demo带大家来学习一下Dagger2的使用。
本文主要包括以下三方面内容:
1.Dagger2的引入
2.ViewPager加载网络图片,使用Dagger2实现解耦
3.ViewPager+TabLayout+Fragment制作导航页,使用Dagger2实现解耦
OK,那就开始吧!
1.Dagger2的引入
在项目中引入Dagger2需要修改两个地方的gradle文件,首先是project的gradle文件,修改成如下样子:
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
然后修改module的gradle文件:
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
compile 'com.google.dagger:dagger:2.6'
apt 'com.google.dagger:dagger-compiler:2.6'
compile 'com.squareup.picasso:picasso:2.5.2'
}
这里有几个地方需要说一下,首先Dagger2我们可以直接在jCenter中搜索到的,可以搜到之后直接添加,那么和Dagger2有关的是两个文件:
compile 'com.google.dagger:dagger:2.6'
apt 'com.google.dagger:dagger-compiler:2.6'
但是这两个一个用了compile一个用了apt,使用apt表示该引用类库只在编译的时候起作用,在打包的时候并不会打包到apk中去。
2.ViewPager加载网络图片,使用Dagger2实现解耦
正常情况下,我们使用ViewPager加载网络图片可能是这样写(这里我就贴出关键的ViewPagerAdapter,完整代码大家在文末自行下载):
public class VpAdapter extends PagerAdapter {
private Context context;
private List<String> list; public VpAdapter(Context context, List<String> list) {
this.context = context;
this.list = list;
} @Override
public int getCount() {
return list.size();
} @Override
public boolean isViewFromObject(View view, Object object) {
return view==object;
} @Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView iv = new ImageView(context);
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
Picasso.with(context).load(list.get(position)).into(iv);
container.addView(iv);
return iv;
} @Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
然后在Activity中来初始化Adapter,并设置给ViewPager:
VpAdapter adapter = new VpAdapter(this,list);
viewPager.setAdapter(adapter);
OK,这样我们的ViewPager就能加载出网络图片了,可是这种写法耦合性太强,我们需要进行适当的解耦,解耦,当然是使用Dagger2进行解耦了,OK,使用了Dagger2之后,我们先来看看怎么改造ViewPagerAdapter,如下:
public class VpAdapter extends PagerAdapter { ....
.... @Inject
public VpAdapter(Context context, List<String> list) {
this.context = context;
this.list = list;
} ....
....
}
首先我在VpAdapter的构造方法上添加了一个@Inject注解,这是我们接触到Dagger2中的第一个注解,那它有什么含义呢?@Inject注解有两个不同含义和用法,第一种就是标记在构造方法上,一会在Activity中当系统需要对VpAdater的实例进行注入的时候,会自动调用具有@Inject注解的构造方法;第二种用法就是标记在需要依赖的变量上,以让Dagger2为其提供依赖。OK,改造完VpAdapter之后,我们再来看看如何改造MainActivity,首先,当我再需要获取一个VpAdapter实例的时候我已经不需要new了:
@Inject
VpAdapter adapter;
声明一个变量,该变量具有@Inject注解(注意,这是@Inject注解的第二种用法),这个表示该变量需要依赖,需要由Dagger2为其提供依赖。可是仅仅这样就能实现VpAdapter变量的初始化吗?肯定是不可以。一个现实的问题是VpAdapter在实例化的时候需要传递的参数怎么传?这时,我们就得引出Dagger2中的另外一个注解了,@Module,使用了@Module注解的类专门提供依赖,谁需要依赖,都可以从这里获取。OK,那我们来看看我们这里提供依赖的类:
@Module
public class AppModule {
private Context context; public AppModule(Context context) {
this.context = context;
} @Provides
Context providesContext() {
return context;
} @Provides
List<String> providesImageUrlList() {
List<String> list = new ArrayList<>();
list.add("http://img4.cache.netease.com/photo/0001/2016-08-13/900x600_BUBDM7GI00AO0001.jpg");
list.add("http://img4.cache.netease.com/photo/0001/2016-08-13/900x600_BUBDM7JI00AO0001.jpg");
list.add("http://img3.cache.netease.com/photo/0001/2016-08-13/900x600_BUBDM85900AO0001.jpg");
list.add("http://img3.cache.netease.com/photo/0001/2016-08-13/900x600_BUBDM8F500AO0001.jpg");
list.add("http://i0.sinaimg.cn/dy/slidenews/76_img/2016_32/76522_1882718_992616.jpg");
return list;
}
}
我在VpAdapter构造方法初始化的时候需要传递两个参数,一个是上下文,还有一个是图片地址的集合,这两个参数我都在这个Module中来提供,@Module注解上文已经说过了,这里还有一个@Provides注解,该注解是专门用来注解方法的,并且该注解只可以在@Module中使用,使用了@Provides注解的方法,在需要提供依赖的时候被调用(这里就是当系统初始化VpAdapter的时候调用)。OK,仅仅这样还不够,我们还需要有一个东西能够将@Module和@Inject连接起来,这个东西叫做@Compoent,也可以称作为一个注入器。所有的注入器都要以接口的形式来定义,接口中添加注入的方法,一般情况下我们使用inject作为方法名,方法的参数就是我们要注入的容器。Dagger2会帮我们生成一个名为DaggerXXXX的@Component的实现类。OK,我们来看看本案例的注入器:
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MainActivity activity);
}
modules参数表示我们要注入的@Module类,也可以是多个@Module类,注意inject方法的参数为MainActivity,这里不可以写作MainActivity的父类Activity,因为这里写啥,Dagger就回去对应的类中寻找@Inject注解进行注入,很明显我们需要注入的变量都在MainActivity中,而不是在Activity中。
注入器也有了,那我们最后再来看看怎样在MainActivity中进行注入吧:
@Inject
VpAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerAppComponent.builder().appModule(new AppModule(this)).build().inject(this);
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
viewPager.setAdapter(adapter);
}
有没有发现代码一下变得非常简洁。注意DaggerAppComponent类是根据我们的AppComponent类自动生成的,appModule方法中传入提供依赖的Module,而inject方法则表示调用@Component的实现类将Module提供的实例注入的VpAdapter的构造方法中。这样写了之后我们的工程就算完成了。
运行效果如下:
OK,我们再来总结一下我们这里所接触到的几个注解:
1. @Inject(两个作用,一个是标记在构造方法上让Dagger来使用,另一个是标记在需要依赖的变量上让Dagger2为其提供依赖)
2. @Provides(注解方法,该注解只可以在@Module中使用,使用了该注解的方法在需要提供依赖时被调用)
3. @Module(用@Module注解的类是专门用来提供依赖)
4. @Component(一个接口,@Inject和@Module之间的桥梁,也称作注入器)
3.ViewPager+TabLayout+Fragment制作导航页,使用Dagger2实现解耦
上面那个例子是不是觉得还不过瘾?那我们再来看一个稍微复杂一点的案例:
先来看看运行效果:
上面滚动的东西是一个TabLayout(不懂TabLayout的小伙伴可以参考使用TabLayout快速实现一个导航栏),下面是ViewPager,ViewPager中放的是Fragment。
先来看看布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context="org.lenve.dagger2vpfragment.MainActivity"> <android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
app:tabMode="scrollable"
android:layout_width="match_parent"
android:layout_height="48dp"></android.support.design.widget.TabLayout> <android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v4.view.ViewPager>
</LinearLayout>
再来看看Fragment的适配器:
public class MyAdapter extends FragmentPagerAdapter {
private List<String> titles;
private List<Fragment> fragments; @Inject
public MyAdapter(FragmentManager fm, List<String> titles, List<Fragment> fragments) {
super(fm);
this.titles = titles;
this.fragments = fragments;
} @Override
public Fragment getItem(int position) {
return fragments.get(position);
} @Override
public int getCount() {
return fragments.size();
} @Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
}
注意该适配器的构造方法有三个参数,所以我一会需要在Module中提供至少三个对应的方法,来为这个构造方法进行注入,那么Module是什么样子呢?
@Module
public class AppModule {
private AppCompatActivity appCompatActivity; public AppModule(AppCompatActivity appCompatActivity) {
this.appCompatActivity = appCompatActivity;
}
@Provides
FragmentManager providesFragmentManager() {
return appCompatActivity.getSupportFragmentManager();
}
@Provides
List<String> providesTitles() {
List<String> list = new ArrayList<>();
for (int i = 0; i < 9; i++) {
list.add("张三:" + i);
}
return list;
}
@Provides
List<Fragment> providesFragmentList(List<String> titles) {
List<Fragment> fragments = new ArrayList<>();
for (String title : titles) {
fragments.add(BaseFragment.getInstance(title));
}
return fragments;
}
}
三个方法分别返回Adapter需要的三个参数。再来看看AppComponent:
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MainActivity activity);
}
OK,万事俱备,只差最后在MainActivity进行注入了:
public class MainActivity extends AppCompatActivity { private ViewPager viewPager;
private TabLayout tabLayout;
@Inject
MyAdapter adapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerAppComponent.builder().appModule(new AppModule(this)).build().inject(this);
tabLayout = (TabLayout) findViewById(R.id.tab_layout);
viewPager = (ViewPager) findViewById(R.id.viewpager);
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
}
}
OK,这个Demo和我们第一Demo有点类似,所以我没有做过多解释,完整的Project在文末下载。
Dagger2其他常用注解我会在下一篇文章中介绍。
以上。
Demo 下载http://download.csdn.net/detail/u012702547/9603154
解锁Dagger2使用姿势(一)的更多相关文章
- 解锁Dagger2使用姿势(二) 之带你理解@Scope
关于Dagger2使用的基础如果你还不了解,可以参考我的上一篇文章解锁Dagger2使用姿势(一),这有助于你理解本篇文章. OK,我们在上篇文章介绍另外Dagger2使用过程中四个基本的注解,分别是 ...
- 解锁Spring框架姿势1
Spring 介绍:Spring 框架是一个Java平台,它为开发Java应用程序提供全面的基础架构支持.Spring负责基础架构,因此您可以专注于应用程序的开发. Spring可以让您从" ...
- Android九宫格解锁有多少种姿势
参考知乎:知乎.
- Shell分析服务器日志,解锁各种新姿势
1.查看有多少个IP访问: awk '{print $1}' log_file|sort|uniq|wc -l 2.查看某一个页面被访问的次数: grep "/index.php" ...
- Dagger2 单例
解锁Dagger2使用姿势(二) 之带你理解@Scope Dagger2从0基础使用,到单例注入的正确姿势 Android之dagger2的简单运用和详细解读(入门)
- Redis实现分布式锁的正确姿势
分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Re ...
- 【分布式缓存系列】Redis实现分布式锁的正确姿势
一.前言 在我们日常工作中,除了Spring和Mybatis外,用到最多无外乎分布式缓存框架——Redis.但是很多工作很多年的朋友对Redis还处于一个最基础的使用和认识.所以我就像把自己对分布式缓 ...
- [BZOJ1016] [JSOI2008] 最小生成树计数 (Kruskal)
Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...
- Redis分布式锁的正确实现方式
前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...
随机推荐
- hbase安装(zookeeper等)
文库:http://wenku.baidu.com/link?url=5mnYL7ZuxUBWZnrnmak4JRVF5fJquJmjgmZy788i7UW8lUk4QXD8Nc_haPz33vjt9 ...
- ActionBar官方教程(6)把图标变成一个返回到上级的按钮,同一个app间,不同app间,不同fragment间
Navigating Up with the App Icon Enabling the app icon as an Up button allows the user to navigate yo ...
- C# SerializableDictionary序列化/反序列化
说明:Dictionary对象本身不支持序列化和反序列化,需要定义一个继承自Dictionary, IXmlSerializable类的自定义类来实现该功能.感觉完全可以把这样的类封装到C#库中,很具 ...
- MVC——母版与分部
背景: 母版是因为有一些网站里的很多网页都是采用相同的布局,所以只需要写一个母版,然后在母版该写不同模板的地方加上@RenderBody(),然后创建不同模块的时候只需要创建视图,然后选择母版就可以了 ...
- codeigniter实现ajax分页
<?php /** *417 add 主要是实现ajax分页 **/ class MY_Pagination extends CI_Pagination{ public function __c ...
- 浏览器加载和渲染html的顺序-css渲染效率的探究
1.浏览器加载和渲染html的顺序1.IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的.2.在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都 ...
- Java问题汇集(1)
1.javax.el.PropertyNotFoundException: Property 'Price' not found on type pet.petshop.dto.WareDto 简答: ...
- NullableKey:解决Dictionary中键不能为null的问题 zt
2012-12-29 02:26 by 老赵, 1745 visits 众所周知,.NET中Dictionary的键不能为null,否则会抛出NullReferenceException,这在某些时候 ...
- HDU 5667 Sequence 矩阵快速幂
官方题解: 观察递推式我们可以发现,所有的fi都是a的幂次,所以我们可以对fi取一个以a为底的log,gi=loga fi 那么递推式变gi=b+c∗gi−1+ ...
- 在Python中的格式化
str= '@SES/%i/'%-1print strstr1='@SES/%i/'%1print str1str2='@SES/%i/'%2print str2 打印出的结果: @SES/-1/@S ...