开源项目U2020 ------ Jack Wharton

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显得很臃肿,但它的好处在于它可以对请求结果作相关处理,或者将结果作为参数继续请求,上面红色的字体就代表对结果进行过滤。但以个人角度来说,实际项目当中较少有这种反复请求的情况。

目前理解就到这里了,希望更加深入研究下去。

你可能感兴趣的:(开源项目U2020 ------ Jack Wharton)