Android開發(fā)是有趣的——這毫無疑問。然而,還是有很多平臺(tái)迫使我們編寫重復(fù)的樣板代碼。很多時(shí)候這都與需要你處理的UI組件相關(guān)。有一些確實(shí)是你需要的,當(dāng)你希望你的應(yīng)用程序架構(gòu)能夠干干凈凈的時(shí)候。有很多操作在后臺(tái)異步執(zhí)行;事實(shí)上,最后很容易成為一堆意大利面條似的代碼,不可讀或者就是讓人感覺不正確。
今天,我們將看看6個(gè)可以幫助保持代碼清晰和可讀性的Android庫(kù),并且使用示例項(xiàng)目以方便你可以看到庫(kù)的作用。
項(xiàng)目
我們將使用之前我們?cè)赗etrofit指南中使用過的Retrofit 2 Sample應(yīng)用程序。這是一個(gè)簡(jiǎn)單的開源項(xiàng)目,可以在GitHub上找到。它需要一個(gè)公司名稱和一個(gè)Git存儲(chǔ)倉(cāng)庫(kù),并列出所有的貢獻(xiàn)者,貢獻(xiàn)者顯示為帶有頭像的一個(gè)列表。雖然它不是一個(gè)革命性的app,但是它展示了如何執(zhí)行網(wǎng)絡(luò),使用圖像,創(chuàng)建列表組件,以及處理用戶輸入。你可以隨意擺弄這個(gè)功能齊全的玩具項(xiàng)目。
讓我們將注釋庫(kù)應(yīng)用到代碼,來看看它們?nèi)绾螏椭S護(hù)Android app代碼的整潔。
-
Butter Knife
每當(dāng)你需要訪問代碼中的視圖時(shí),你需要獲取該視圖的對(duì)象實(shí)例。你可以通過編寫rootView.findViewById()方法來實(shí)現(xiàn),然后將返回的對(duì)象轉(zhuǎn)換為正確的視圖類型。但是,你的代碼很快就會(huì)建立起來,但是尤其是在onCreate和onCreateView方法中會(huì)有惱人的類似語(yǔ)句。想想看;在那些onCreate方法中,你初始化一切,綁定偵聽器,把整個(gè)UI綁在一起。你擁有的UI元素越多,那么單個(gè)方法就會(huì)越長(zhǎng)。
讓我們舉個(gè)簡(jiǎn)單的例子:
Butter-Knife
此視圖將需要三個(gè)視圖:兩個(gè)EditTexts和一個(gè)Button,我們需要在片段中引用。一般我們會(huì)這樣做:
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { // Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_search, container, false);
companyEditText = (EditText) rootView.findViewById(R.id.company_edittext);
repositoryEditText = (EditText) rootView.findViewById(R.id.repository_edittext);
searchButton = (Button) rootView.findViewById(R.id.search_button);
searchButton.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
searchContributors();
}
}); return rootView;
}
在代碼中除了從布局中查找視圖,將它們存儲(chǔ)在活動(dòng)的字段中,以及添加一個(gè)匿名內(nèi)部類作為監(jiān)聽器來處理搜索命令之外,沒有太多事情發(fā)生。通過Butter Knife,我們可以使我們的工作和編碼更容易。視圖對(duì)象存儲(chǔ)在字段中,因此我們可以簡(jiǎn)單地向每個(gè)字段添加Butter Knife @BindView注解,如下所示:
@BindView(R.id.company_edittext) EditText companyEditText;
@BindView(R.id.repository_edittext) EditText repositoryEditText;
@BindView(R.id.search_button) Button searchButton;
我們還需要使onCreateView方法知道Butter Knife的存在?,F(xiàn)在,初始化代碼將只包含以下簡(jiǎn)短語(yǔ)句:
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { // Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_search, container, false);
ButterKnife.bind(this, rootView); return rootView;
}
我們還可以進(jìn)一步,跳過綁定監(jiān)聽器到searchButton方法,并改為注解onSearchButtonClicked方法,通過神奇地將之綁定到按鈕點(diǎn)擊的@OnClick注解:
@OnClick(R.id.search_button)
public void onSearchButtonClicked(View searchButton) { searchContributors();
}
在官方的Butter Knife主頁(yè)還有其他的例子。不妨一一查看一番!一般說來,如果你需要以編程方式訪問視圖元素,那么Butter Knife會(huì)讓你的代碼更簡(jiǎn)潔和可讀。
- Ice Pick
許多Android應(yīng)用程序面臨的一個(gè)常見問題是活動(dòng)和片段生命周期的不正確處理。是啊,我們知道,它不是Android框架最優(yōu)雅的部分。但是,在AndroidManifest文件中禁用橫向模式,這樣當(dāng)用戶將設(shè)備側(cè)向移動(dòng)時(shí),應(yīng)用程序不會(huì)崩潰并非是一個(gè)正確的解決方案——首先,因?yàn)轱@得有點(diǎn)傻,其次,代碼不能正確處理的配置更改仍然會(huì)發(fā)生并破壞一切!因此,你必須正確處理應(yīng)用程序組件的狀態(tài)和生命周期。
實(shí)現(xiàn)的目的是將活動(dòng)中所有字段的內(nèi)容存儲(chǔ)到bundle中,然后由Android框架通過生命周期正確管理。這可能是相當(dāng)無聊。
幸運(yùn)的是,Ice Pick使我們的生活變得容易多了,因?yàn)槟阍俨槐匾粋€(gè)個(gè)添加所有的變量到bundle去保存。同樣從bundle中再次讀取數(shù)據(jù),如果存在,那么會(huì)很有挑戰(zhàn)性,但I(xiàn)ce Pick簡(jiǎn)化了很多很多。因此,作為示例,假設(shè)我們需要記住最后一家公司和存儲(chǔ)庫(kù)搜索的組合。
首先,我們對(duì)要保存到bundle的字段進(jìn)行注解。
@State String lastSearchCombination;
現(xiàn)在我們需要在onSaveInstanceState()方法中調(diào)用Ice Pick:
@Overridepublic void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}
也在onCreateView()方法中調(diào)用Ice Pick來恢復(fù)狀態(tài):
@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { // Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_search, container, false);
ButterKnife.bind(this, rootView);
Icepick.restoreInstanceState(this, savedInstanceState);
return rootView;
}
記?。耗憧梢员4娴絙undle的內(nèi)容的限制仍然存在。但是,沒有必要因?yàn)闉閎undle鍵添加常量或?yàn)閟avedInstanceState添加空檢查搞得一團(tuán)亂。
- Dart和Henson
與Ice Pick類似,Dart幫助我們避免為從一個(gè)活動(dòng)傳遞到另一個(gè)活動(dòng)的Intent Extras寫入所有鍵和檢查。它也適用于Fragments。這里有一個(gè)小例子,展示了我是如何使用@InjectExtra注釋將搜索關(guān)鍵字從搜索屏幕傳遞到貢獻(xiàn)者列表,實(shí)際上將執(zhí)行搜索的地方。
所以我使用@InjectExtra注解定義了兩個(gè)類變量:
@InjectExtra String repositoryQuery;
@InjectExtra String companyQuery;
@InjectExtra String repositoryQuery;@InjectExtra String companyQuery;
一旦Dart.inject(this, getActivity());被調(diào)用,那么這些都將會(huì)被自動(dòng)初始化?,F(xiàn)在在Bundle中被添加到Intent的extras最終將如何。你可以手動(dòng)進(jìn)行,但這里使用Henson是非常有道理的。為了使它工作,我添加以下代碼到我的
Intent intentContributorsFragment =
Henson.with(getActivity())
.gotoContributorsFragment()
.companyQuery(companySearchKeyword)
.repositoryQuery(repositorySearchKeyword).build();
Intent intentContributorsActivity =
Henson.with(getActivity())
.gotoContributorsActivity().build();
intentContributorsActivity.putExtras(intentContributorsFragment);
startActivity(intentContributorsActivity);
這簡(jiǎn)化了代碼中Activity之間的通信,而無需每次都手動(dòng)指定每個(gè)extra。
Parceler
Parceler幫助你進(jìn)行對(duì)象序列化。它可以幫助你傳遞任何對(duì)象作為Intent extra,而不會(huì)讓你面對(duì)對(duì)象序列化的煩惱。
最好的事情是,Icepick,Henson和Dart也能很好地和Parceler一起玩。在我們的應(yīng)用程序示例中,我使用@Parcel注釋了我的Contributor類。這允許我使用Dart傳遞Contributor作為Intent Extra,使我的代碼簡(jiǎn)潔和可讀。Timber
當(dāng)我寫代碼的時(shí)候,過不了一會(huì),我總有犯錯(cuò)誤的傾向。通常情況下,這會(huì)導(dǎo)致應(yīng)用程序的意外行為。我需要重現(xiàn)它,這樣我才能解決這個(gè)問題。當(dāng)你知道重現(xiàn)的步驟時(shí),調(diào)試器會(huì)很方便,但是通常情況下,日志也包含了真相!
在Android中開箱即用的Log類足夠好,因?yàn)榭梢蕴峁┎煌娜罩居涗浖?jí)別,等等。然而,每個(gè)Log.d()語(yǔ)句有兩個(gè)參數(shù);首先是tag,第二是message。99%的時(shí)間里,tag將是this.class.getName(),并且一次又一次地寫會(huì)很煩人。幸運(yùn)的是,使用Timber庫(kù),你只要這樣做:
Timber.d("Informative output that needs to be logged.");
…并且它將為你提供正確的默認(rèn)tag!此外,請(qǐng)記住,你需要在使用之前初始化Timber。查看我已添加調(diào)用的ContributorsApplication.onCreate()代碼:
Timber.plant(new Timber.DebugTree());
這就是正確初始化Timber所有需要做的事情,所有沒有理由你的app不使用Timber。
- Dagger和Dagger2
最后,但并非最不重要的,Dagger和Dagger2庫(kù)在app中管理依賴注入的表現(xiàn)真令人驚嘆。為你處理依賴注入是編寫代碼的超棒做法。你指定應(yīng)用程序的組件以及它們應(yīng)如何相互交互。你可以定義代碼的哪些部分需要其他部件的工作,瞧,這個(gè)庫(kù)將為你初始化子部件,并根據(jù)需要注入它們。你可以檢查示例項(xiàng)目代碼以查看使用示例。
然而,Dagger和Dagger 2涉及面太廣泛了,所以在這篇文章中我們就不做詳細(xì)解釋了。如果你想從使用Dagger開始,那么有一個(gè)很好的代碼例子,coffee maker,它也得到了優(yōu)秀的注釋支持。
結(jié)論
有很多有趣的Android庫(kù),我在這里只列出了一些。安裝起來相當(dāng)容易,因?yàn)槟阒恍枰付ㄒ蕾囮P(guān)系就可以了。這些都是被積極維護(hù)的項(xiàng)目,所以它們有偉大的文檔。
你要做的就是小心構(gòu)建過程。當(dāng)你開始結(jié)合多個(gè)庫(kù)與注解處理器時(shí),確保使用provided()或annotationprocessor(),而不是在build.gradle中將它們結(jié)合起來。