github地址:https://github.com/JakeWharton/u2020
该项目是Jack Wharton牵头完成的关于dagger的使用例子,gradle里引入包如下:
dependencies {
compile 'com.android.support:support-v4:19.0.+'
compile 'com.squareup.dagger:dagger:1.2.1' //dagger包
provided 'com.squareup.dagger:dagger-compiler:1.2.1' //dagger编译器
compile 'com.squareup.okhttp:okhttp:1.3.0' //用于网络请求
compile 'com.squareup.picasso:picasso:2.2.0' //用于图片加载
compile 'com.squareup.retrofit:retrofit:1.4.1' //网络交互开源框架
debugCompile 'com.squareup.retrofit:retrofit-mock:1.4.1' //用于测试
compile 'com.jakewharton:butterknife:4.0.1' //注解工具,类似于roboguice
compile 'com.jakewharton.timber:timber:2.2.2' //日志工具,可以直接将日志提交到服务端
debugCompile 'com.jakewharton.madge:madge:1.1.1' //用于测试
debugCompile 'com.jakewharton.scalpel:scalpel:1.1.1' //用于测试
compile 'com.netflix.rxjava:rxjava-core:0.16.1' //rxjava,反应性编程
compile 'com.netflix.rxjava:rxjava-android:0.16.1'
compile 'com.etsy.android.grid:library:1.0.3' //瀑布流自定义控件
}
需要注意的地方是,src->debug和src->main和src->release三个包分别对应调试版本,main版本,释放版本,在DebugU2020Module类下面有一段代码:
@Module(
addsTo = U2020Module.class,
includes = {
DebugUiModule.class,
DebugDataModule.class
},
overrides = true
)
这里的overrides = true就代表着调试版本的所有代码都会将main里面的对应模块覆盖掉进而进行测试,当在IDE切换为release版本后就可以直接上线了,因为测试代码与正式代码之间完全没有联系,切换时不需要更改一处正式代码,究其原因就在这一句话上。
1 接下来要理解这个项目需要对dagger有一定的了解,如果用过roboguice对于理解该框架会有一定帮助,而该项目用到了黄油刀框架,因此就从黄油刀说起。
对于黄油刀首先需要了解这样一句代码:@InjectView(R.id.gallery_grid) ListView listView;也就是说,在实际的使用过程中,activity里的findViewById来初始化listView的过程已经不需要了,因为@InjectView(R.id.gallery_grid)这一句就已经帮你实例化好了,接下来你就可以直接使用了。这个小例子就充分的体现了反转控制的思想,用我个人的理解,黄油刀或者dagger都是在我使用它之前就帮我把它实例化好放到一个全局的池子里,当我需要时,就直接拿来用,这样,创建者和使用者之间就被分离开来,而黄油刀主要是针对于Android内置的一些控件,属性等等的初始化,而dagger就主要针对于自定义类。
2 在这个项目中,还发现了一个比较新奇的作法,我个人称之为行为内置化。顾名思义,就是将一些行为逻辑直接拿到View内部去完成,而不在activity里面做。我觉得这样的作法还是慎用的好,一方面比较考验开发者的基本功,另一方面还不知道重用性如何。从项目包组织结构也可以看出来,它分为data和ui,module也分为datamodule和uimodule,体现出作者希望把数据和ui分离的想法,相关代码如下:
public class GalleryView extends BetterViewAnimator {
@InjectView(R.id.gallery_grid) AbsListView galleryView;
@Inject Picasso picasso;
@Inject GalleryDatabase galleryDatabase;
private Section section = Section.HOT;
private Subscription request;
private final GalleryAdapter adapter;
public GalleryView(Context context, AttributeSet attrs) {
super(context, attrs);
U2020App.get(context).inject(this);
adapter = new GalleryAdapter(context, picasso);
}
@Override protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.inject(this);
galleryView.setAdapter(adapter);
}
@Override protected void onAttachedToWindow() {
super.onAttachedToWindow();
request = galleryDatabase.loadGallery(section, new EndlessObserver<List<Image>>() {
@Override public void onNext(List<Image> images) {
adapter.replaceWith(images);
setDisplayedChildId(R.id.gallery_grid);
}
});
}
@Override protected void onDetachedFromWindow() {
request.unsubscribe();
super.onDetachedFromWindow();
}
}
可以看到在 onFinishInflate方法内,即View中所有的子控件均被映射成xml后,进行控件注入和适配器的初始化,原因在于很多时候我们打开一个页面然后秒退出来,这时页面都还没有初始化完成,如果在onFinishInflate方法内才做这些操作,会避免不必要的顿卡。onAttachedToWindow也是一样的道理,当执行完这个方法马上就开始绘制View了,在此之前最重要的是加载数据。onDetachedFromWindow里取消了观察者的订阅。在传统的使用过程中,适配器以及数据等等的操作都是在activity中完成的。
3 retrofit和rxjava的使用,这两个开源框架都有现成的使用方法和相关知识,如果不习惯英文版可以找中文版来看:
http://square.github.io/retrofit/,rxjava百度下有很多相关的介绍。
一般的retrofit的使用方法如下:
/**
* 登录
*
* @param user 用户
* @param password 密码
*/
public void login(String user, String password, Callback<User> callback) {
UserService service = sRestAdapter.create(UserService.class);
service.login(user, password, callback);
}
interface UserService{
@FormUrlEncoded @POST("/user/login") void login(@Field("user") String user,
@Field("password") String password, Callback<User> callback);
}
然后在需要的地方直接调用login方法,然后在其回调里面对网络请求结果作相关处理。而在这个项目中,作者用observer代替了Callback,代码如下:
@Provides @singleton GalleryService provideGalleryService(RestAdapter restAdapter) {
return restAdapter.create(GalleryService.class);
}
public interface GalleryService {
@GET("/gallery/{section}/{sort}/{page}") //
Observable<Gallery> listGallery( //
@Path("section") Section section, //
@Path("sort") Sort sort, //
@Path("page") int page);
}
调用方法如下:
request = galleryDatabase.loadGallery(section, new EndlessObserver<List<Image>>() {
@Override public void onNext(List<Image> images) {
adapter.replaceWith(images);
setDisplayedChildId(R.id.gallery_grid);
}
});
galleryService.listGallery(section, Sort.VIRAL, 1)
.map(new GalleryToImageList())
.flatMap(new Func1<List<Image>, Observable<Image>>() {
@Override public Observable<Image> call(List<Image> images) {
return Observable.from(images);
}
})
.filter(new Func1<Image, Boolean>() {
@Override public Boolean call(Image image) {
return !image.is_album; // No albums.
}
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(galleryRequest);
相比起来,rxjava显得很臃肿,但它的好处在于它可以对请求结果作相关处理,或者将结果作为参数继续请求,上面红色的字体就代表对结果进行过滤。但以个人角度来说,实际项目当中较少有这种反复请求的情况。
目前理解就到这里了,希望更加深入研究下去。