from http://oyanglul.us
最近再一次偶然的机会在github上见到了这样一个repo http://www.github.com/donnfelker/android-bootstrap 能让你迅速搭建起基本ui和框架.但是基本上没有什么文档,非常可惜.环境搭好 了,却不知道在哪里加代码. 于是我玩几天准备把我的理解写一下,以供找不到文 档的同学可以快速上个手.
101 什么是 android bootstrap
Android Bootstrap 其实是一堆框架的集合, 让你迅速搭好android 开发的基本 框架. 里面包括
- Fragments
- Account Manager
- android-maven-plugin
- Dagger
- ActionBarSherlock
- Menu Drawer
- Robotium
- Parse API
很多是UI的框架我就不解释了, 如 Fragments, ActionBarSherlock. 但是我想 讲的是
- 依赖注入框架 Dagger
- UI testing 框架 Robotium
- backend服务Parse.
- android maven
本章要介绍两个注入框架 Dagger 和 butterknife
Dagger
这又是一个依赖注入的框架,个人觉得依赖注入的模式貌似是为java专门准备的.使 得木纳的 java 代码结构变得灵活清爽, 松耦合, 易测试. 而 注入方式个人也比较喜欢 annotation 的方式而不是讨厌的 xml,把所有的依赖 配置都放到一个文件里并不无不妥, 但是都放到 xml 里, OMG, 放到可读性最屎 的 xml 里, 找所有依赖配置都要去翻这个难读得 xml…想着就头疼. 当项目变 大时, 一大波 xml 来袭………Orz
先来解释一下依赖注入
简单来说就是好莱坞原则
不要call我, 我会call你的.
对于好莱坞agent来说,他知道什么时候用什么演员,因 此,演员只需要留下联系方式, 也就是注入, 等待agent call他.
因此, 也叫控制反转.
其实, 也就是更优雅的实现组合模式, 传统的组合模式会需要 new 这些依赖, 也就是要各式各样的factory, 而依赖注入也就是说给你传进去.
代码上来说, dagger 的这个例子非常好:
比如我开咖啡店, 我要卖不同的咖啡种类, 雀巢的银桥的丝袜的 什么 espresso,amerino之类的. 我是 个非常抠塞的奸商, 我不想为每一种咖啡专门买一个昂贵的专用咖啡机. 经过研究发现这些 咖啡机只存在一些不同, 比如不同的加热方式, 滴漏方式,filter或者 水泵流量或温度不同.
所以,我决定实现一个 configurable 的 coffeemaker.
package coffee; import dagger.Lazy; import javax.inject.Inject; class CoffeeMaker { @Inject Lazy<Heater> heater; // Don't want to create a possibly costly heater until we need it. @Inject Pump pump; public void brew() { heater.get().on(); pump.pump(); System.out.println(" [_]P coffee! [_]P "); heater.get().off(); } }
这是我的咖啡机.提供一个煮的按钮,可以看到, 组装咖啡机 的水泵和加热器都是注入进来的. 那他们是在哪构造的呢.
而作为老板的我,要怎样用这个咖啡机呢, 按一下”煮”按钮, 当然. 但是在那之 前,我们先要决定如何组装一个想要的咖啡机.
class CoffeeApp implements Runnable { @Inject CoffeeMaker coffeeMaker; @Override public void run() { coffeeMaker.brew(); } public static void main(String[] args) { ObjectGraph objectGraph = ObjectGraph.create(new DripCoffeeModule());(ref:graph) CoffeeApp coffeeApp = objectGraph.get(CoffeeApp.class); coffeeApp.run(); } }
客户说要americano,所以老板我给咖啡机装成滴漏式, 如代码第 (graph) 行. 构造Graph意思相当于要构造滴漏式咖啡机的图, 图会根据Module里provider组 件以及以及被Inject的地方建立联系. 也就是说, 根据用户的需求用不同的组件 蓝图来构造咖啡机.
下面来看组件式在哪被初始化的.
interface Heater { void on(); void off(); boolean isHot(); } class ElectricHeater implements Heater { boolean heating; @Override public void on() { System.out.println("~ ~ ~ heating ~ ~ ~"); this.heating = true; } @Override public void off() { this.heating = false; } @Override public boolean isHot() { return heating; } }
这是电加热器的接口实现, 他的初始化方法会放到一个module里的 @provide
标记的方法里. 这个被标记的方法会再 Heater 被注入的地方被调用.
import dagger.Module; import dagger.Provides; import javax.inject.Singleton; @Module( injects = CoffeeApp.class, includes = PumpModule.class ) class DripCoffeeModule { @Provides @Singleton Heater provideHeater() { return new ElectricHeater(); } }
看到这样的好处了吧, 很清爽的把Module中得Heater和Pump注入到CoffeeApp中, 不需要setter注入,也不需要构造函数注入, 只需要将组件的构造函数声明为 @Inject
, 或者放 到一个Module里的provider中, 就可以在咖啡机中 @Inject
该组件.
在 androidbootstrap 里的 Dagger
说了这些应该大概知道 dagger 要怎么玩乐吧,那么我们 首先来看一下 androidbootstrap 的 src 目录结构好了.
├── main │?? └── java │?? └── com │?? └── donnfelker │?? └── android │?? └── bootstrap │?? ├── AndroidModule.java │?? ├── BootstrapApplication.java │?? ├── BootstrapModule.java (ref:module) │?? ├── BootstrapServiceProvider.java │?? ├── RootModule.java │?? ├── authenticator │?? │?? ├── AccountAuthenticatorService.java │?? │?? ├── ApiKeyProvider.java │?? │?? ├── BootstrapAccountAuthenticator.java │?? │?? ├── BootstrapAuthenticatorActivity.java │?? │?? ├── LogoutService.java │?? │?? └── SherlockAccountAuthenticatorActivity.java │?? ├── core │?? │?? ├── AvatarLoader.java │?? │?? ├── BootstrapService.java │?? │?? ├── CheckIn.java │?? │?? ├── Constants.java │?? │?? ├── GravatarUtils.java │?? │?? ├── ImageUtils.java │?? │?? ├── Location.java │?? │?? ├── News.java │?? │?? ├── PauseTimerEvent.java │?? │?? ├── ResumeTimerEvent.java │?? │?? ├── StopTimerEvent.java │?? │?? ├── TimerPausedEvent.java │?? │?? ├── TimerService.java │?? │?? ├── TimerTickEvent.java │?? │?? ├── UserAgentProvider.java │?? │?? └── ViewSummary.java │?? ├── evernote │?? ├── ui │?? │?? ├── AlternatingColorListAdapter.java │?? │?? ├── AsyncLoader.java │?? │?? ├── BarGraphDrawable.java │?? │?? ├── BootstrapActivity.java │?? │?? ├── BootstrapFragmentActivity.java │?? │?? ├── BootstrapPagerAdapter.java │?? │?? ├── BootstrapTimerActivity.java │?? │?? ├── CarouselActivity.java │?? │?? ├── CheckInsListAdapter.java │?? │?? ├── CheckInsListFragment.java │?? │?? ├── HeaderFooterListAdapter.java │?? │?? ├── ItemListFragment.java │?? │?? ├── NewsActivity.java │?? │?? ├── NewsListAdapter.java │?? │?? ├── NewsListFragment.java │?? │?? ├── TextWatcherAdapter.java │?? │?? ├── ThrowableLoader.java │?? │?? ├── UserActivity.java (ref:activity) │?? │?? ├── UserListAdapter.java (ref:adapter) │?? │?? ├── UserListFragment.java (ref:fragment) │?? │?? └── view │?? │?? └── CapitalizedTextView.java │?? └── util │?? ├── Ln.java │?? ├── SafeAsyncTask.java │?? └── Strings.java └── test └── java └── com └── donnfelker └── android └── bootstrap └── core └── core ├── BootstrapApiClientUtilTest.java └── BootstrapServiceTest.java
好吧, 这样一眼就应该能看到 BootstrapModule 肯定是 依赖注入用的组件对不 对. 比如说我现在做的应用是关于 Evernote的, 在 Evernote 提供的 android SDK 中有一个最重要的类EvernoteSession, 因为当初始化后并登陆, 你就可以 用这个 Session 来调用所有 evernote API.
因此, 我把它看成一个插件, 也就 是说, 我什么时候要用到 evernote 的时候, 我只需要 @Inject 这个 session 即可. 那么, 这时候, 我只需要吧 EvernoteSession 的构造方法放到 这个 Module 里了.
public class BootstrapModule { ... @Singleton @Provides EvernoteSession provideEvernoteSession(final Context context) { return EvernoteSession.getInstance(context, Constants.Evernote.CONSUMER_KEY, Constants.Evernote.CONSUMER_SECRET, Constants.Evernote.EVERNOTE_SERVICE); } }
Butterknife
再来看 src 目录, 很有意思, 在 ui
下有三组 xxxActivity ,xxxListAdapter, xxxFragment. 这三个类是这样的
- xxxActivity: 负责单个view的显示.
- xxxListAdapter: 负责List内容的更新.
- xxxListFragment: 这是继承 actionbarsherlock 的 SherlockFragment.负责
组装数据以及处理事件.
点开UserActivity, 会看见开头有这么个 annotation @InjectView
@InjectView(R.id.iv_avatar) protected ImageView avatar;
按最老套的获取 view 会这样写:
ImageView avatar; ... @Override public void onCreate(){ avatar = (ImageView)findViewById(R.id.title); ...
是不是觉得以前的写法弱爆了. 当然这是最基本的 view inject, 还有 Click Listener Injection 等更高阶的用法 可以继续参考文档