TextView mTextView皆看;
mTextView=(TextView) findViewById(R.id.mTextView);
mTextView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
作為一名Android程序員,對(duì)于上面這種機(jī)械化的代碼你一定寫到想吐了背零,或許多數(shù)時(shí)候你只是copy ,paste,然后再改一改腰吟,完了你可能又會(huì)覺得這種代碼毫無(wú)營(yíng)養(yǎng),寫得實(shí)在沒勁徙瓶。俗話說:“不會(huì)偷懶的程序員不是好程序員”毛雇,今天我們就來(lái)探討下如何偷懶嫉称。
到這里可能你已經(jīng)知道我要說的是什么了,是的灵疮,我要說的就是Android中的IOC框架织阅,這類框架中比較早的有:Afinal,Xutils震捣,目前開發(fā)者中呼聲比較高的有Android Annotations蒲稳,ButterKnife,Dagger伍派,RoboGuice等江耀。我在這里就簡(jiǎn)單介紹下比較有代表性的Android Annotations和ButterKnife。
什么是IOC诉植?
Inversion of Control祥国,英文縮寫為IOC,字面翻譯:控制反轉(zhuǎn)晾腔。什么意思呢舌稀?就是一個(gè)類里面需要用到很多個(gè)成員變量,傳統(tǒng)的寫法灼擂,你要用這些成員變量壁查,那么你就new 出來(lái)用唄!IOC的原則是:NO剔应,我們不要new睡腿,這樣耦合度太高,你配置個(gè)xml文件峻贮,里面標(biāo)明哪個(gè)類席怪,里面用了哪些成員變量,等待加載這個(gè)類的時(shí)候纤控,我?guī)湍阕⑷耄╪ew)進(jìn)去挂捻;當(dāng)然了,你又會(huì)覺得船万,寫個(gè)配置文件刻撒,臥槽,這多麻煩耿导。于是乎声怔,又出現(xiàn)了另一種方案,得你嫌配置文件麻煩碎节,那用注解唄在你需要注入的成員變量上面加個(gè)注解捧搞,例如:@Inject,這樣就行了,你總不能說這么個(gè)單詞麻煩吧胎撇。當(dāng)然了介粘,有了配置文件和注解,那么怎么注入呢晚树?其實(shí)就是把字符串類路徑變成類么姻采,這個(gè)時(shí)候需要用到反射。
什么是反射爵憎?
首先JAVA語(yǔ)言并不是一種動(dòng)態(tài)編程語(yǔ)言慨亲,而為了使語(yǔ)言更靈活,JAVA引入了反射機(jī)制宝鼓。 JAVA反射機(jī)制是在運(yùn)行狀態(tài)中刑棵,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法愚铡;對(duì)于任意一個(gè)對(duì)象蛉签,都能夠調(diào)用它的任意一個(gè)方法;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語(yǔ)言的反射機(jī)制沥寥。Java反射機(jī)制主要提供了以下功能: 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類碍舍;在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象;在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法邑雅;在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法片橡;生成動(dòng)態(tài)代理。
什么是注解淮野?
JAVA1.5之后引入的注解和反射捧书,注解的實(shí)現(xiàn)依賴于反射。JAVA中的注解是一種繼承自接口java.lang.annotation.Annotation的特殊接口录煤。那么接口怎么能夠設(shè)置屬性呢鳄厌?簡(jiǎn)單來(lái)說就是JAVA通過動(dòng)態(tài)代理的方式為你生成了一個(gè)實(shí)現(xiàn)了接口Annotation的實(shí)例,然后對(duì)該代理實(shí)例的屬性賦值妈踊,這樣就可以在程序運(yùn)行時(shí)(如果將注解設(shè)置為運(yùn)行時(shí)可見的話)通過反射獲取到注解的配置信息。說的通俗一點(diǎn)泪漂,注解相當(dāng)于一種標(biāo)記廊营,在程序中加了注解就等于為程序打上了某種標(biāo)記。程序可以利用JAVA的反射機(jī)制來(lái)了解你的類及各種元素上有無(wú)何種標(biāo)記萝勤,針對(duì)不同的標(biāo)記露筒,就去做相應(yīng)的事件。標(biāo)記可以加在包敌卓,類慎式,方法,方法的參數(shù)以及成員變量上。
以上算是背景介紹吧瘪吏,也正是基于以上需求和實(shí)現(xiàn)原理癣防,一大波Android IOC框架應(yīng)運(yùn)而生。我們先來(lái)看第一個(gè):Android Annotations掌眠。我們先比較下常規(guī)寫法和Annotations寫法的代碼蕾盯。
常規(guī)寫法
先隨便來(lái)一段吧~~
public class MainActivity extends Activity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
textView.setText("test");
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(this, ChildActivity.class);
startActivity(intent);
}
});
}
}
Android Annotations寫法
@EActivity(R.layout.activity_main)
public class MainActivity extends Activity {
@ViewById(R.id.text)
TextView textView;
@AfterViews
public void init() {
textView.setText("annotations test");
}
@Click(R.id.text)
void buttonClick() {
Intent intent = new Intent(this,ChildActivity_.class);
startActivity(intent);
}
}
就是這么簡(jiǎn)單
Android Annotations就這么寫?可能你會(huì)問蓝丙,oncreat()去哪了级遭,這可是執(zhí)行入口,你又會(huì)擔(dān)心setContentView()呢渺尘,布局怎么加載挫鸽?看上面的代碼第一行@EActivity(R.layout.activity_main),就這一句全搞定鸥跟,同時(shí)也不需要搞一大堆findViewById掠兄,不需要搞一大推setOnClickListener。Android Annotations能干的事可遠(yuǎn)不止這些锌雀,Http請(qǐng)求蚂夕,開啟線程,事件綁定等等都可以通過一個(gè)注解標(biāo)記搞定腋逆。
其他語(yǔ)法舉例
色值 @ColorRes
@ColorRes(R.color.backgroundColor)
int someColor;
@ColorRes
int backgroundColor;
動(dòng)畫 @AnimationRes
@AnimationRes(R.anim.fadein)
XmlResourceParser xmlResAnim;
@AnimationRes
Animation fadein;
自定義View @EViewGroup
@EViewGroup(R.layout.title_with_subtitle)
public class TitleWithSubtitle extends RelativeLayout {
@ViewById
protected TextView title, subtitle;
public TitleWithSubtitle(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setTexts(String titleText, String subTitleText) {
title.setText(titleText);
subtitle.setText(subTitleText);
}
}
HttpClient @HttpsClient
@HttpsClient
HttpClient httpsClient;
UiThread @UiThread
void myMethod() {
doInUiThread("hello", 100);
}
@UiThread
void doInUiThread(String aParam, long anotherParam) {
[...]
}
怎么實(shí)現(xiàn)的婿牍?
Android Annotations的實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單。它會(huì)使用標(biāo)準(zhǔn)的Java注解處理工具自動(dòng)添加一個(gè)額外的編譯步驟生成的源代碼惩歉。其實(shí)就是生成一個(gè)原有類的子類等脂,這個(gè)子類才是真正運(yùn)行用的類。例如上面代碼使用@EActivity注解的MainActivity撑蚌,將生成這個(gè)MainActivity的一個(gè)子類上遥,它的名字是“MainActivity_”。該子類重載一些方法(例如onCreate())争涌,通過委托方式調(diào)用了activity的相關(guān)方法粉楚。所以這里有個(gè)大坑,所有Activity的相關(guān)操作都要操作其子類亮垫,例如 AndroidManifest.xml中類名要寫成android:name=".MainActivity_"模软,開啟MainActivity要寫成 startActivity(new Intent(this, MainActivity_.class));下面是上文中AndroidAnnotations方式寫法的MainActivity的子類MainActivity_,我貼出來(lái)大家隨便感受下饮潦。
public final class MainActivity_
extends MainActivity
implements HasViews, OnViewChangedListener
{
private final OnViewChangedNotifier onViewChangedNotifier_ = new OnViewChangedNotifier();
@Override
public void onCreate(Bundle savedInstanceState) {
OnViewChangedNotifier previousNotifier = OnViewChangedNotifier.replaceNotifier(onViewChangedNotifier_);
init_(savedInstanceState);
super.onCreate(savedInstanceState);
OnViewChangedNotifier.replaceNotifier(previousNotifier);
setContentView(layout.activity_main);
}
private void init_(Bundle savedInstanceState) {
OnViewChangedNotifier.registerOnViewChangedListener(this);
}
@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
onViewChangedNotifier_.notifyViewChanged(this);
}
@Override
public void setContentView(View view, LayoutParams params) {
super.setContentView(view, params);
onViewChangedNotifier_.notifyViewChanged(this);
}
@Override
public void setContentView(View view) {
super.setContentView(view);
onViewChangedNotifier_.notifyViewChanged(this);
}
public static MainActivity_.IntentBuilder_ intent(Context context) {
return new MainActivity_.IntentBuilder_(context);
}
public static MainActivity_.IntentBuilder_ intent(Fragment supportFragment) {
return new MainActivity_.IntentBuilder_(supportFragment);
}
@Override
public void onViewChanged(HasViews hasViews) {
textView = ((TextView) hasViews.findViewById(id.text));
if (textView!= null) {
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
MainActivity_.this.buttonClick();
}
}
);
}
init();
}
public static class IntentBuilder_
extends ActivityIntentBuilder<MainActivity_.IntentBuilder_>
{
private Fragment fragmentSupport_;
public IntentBuilder_(Context context) {
super(context, MainActivity_.class);
}
public IntentBuilder_(Fragment fragment) {
super(fragment.getActivity(), MainActivity_.class);
fragmentSupport_ = fragment;
}
@Override
public void startForResult(int requestCode) {
if (fragmentSupport_!= null) {
fragmentSupport_.startActivityForResult(intent, requestCode);
} else {
if (context instanceof Activity) {
Activity activity = ((Activity) context);
ActivityCompat.startActivityForResult(activity, intent, requestCode, lastOptions);
} else {
context.startActivity(intent);
}
}
}
}
}
ButterKnife
再說說ButterKnife吧燃异,其實(shí)原理和Android Annotations差不多,只是一些寫法和細(xì)節(jié)上有略微差別继蜡,最主要一點(diǎn)是沒有Android Annotations的坑回俐,Android Studio上有個(gè)ButterKnife的插件逛腿,這個(gè)插件提供的炫酷技能幾乎是一鍵搞定findViewById和setOnClickListener,基于此ButterKnife被很多開發(fā)者所推崇仅颇,我們還是簡(jiǎn)單對(duì)比下代碼吧单默。
常規(guī)寫法
public class MainActivity extends Activity {
TextView textView;
ListView listView;
ListViewAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
listView = (ListView) findViewById(R.id.ListView);
textView.setText("test");
adapter = new ListViewAdapter(MainActivity.this);
listView.setAdapter(adapter);
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
text.setText("你點(diǎn)擊了按鈕");
}
});
}
class ListViewAdapter extends BaseAdapter {
private Context mContext;
public ListViewAdapter(Context context) {
mContext = context;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return 1000;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
LayoutInflater layoutInflater = ((Activity) mContext)
.getLayoutInflater();
convertView = layoutInflater.inflate(R.layout.list_item,
parent, false);
holder.imageview = (ImageView) convertView
.findViewById(R.id.headshow);
holder.textview0 = (TextView) convertView
.findViewById(R.id.name);
holder.textview1 = (TextView) convertView
.findViewById(R.id.text);
convertView.setTag(convertView);
} else {
convertView = (View) convertView.getTag();
}
holder.textview0.setText("star");
holder.textview1.setText("test");
return convertView;
}
}
static class ViewHolder {
ImageView imageview;
TextView textview0;
TextView textview1;
}
}
ButterKnife寫法
public class MainActivity extends Activity {
@InjectView(R.id.text)
TextView text;
@InjectView(R.id.ListView)
ListView listView;
ListViewAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
adapter = new ListViewAdapter(MainActivity.this);
listView.setAdapter(adapter);
text.setText("ButterKnife test");
}
@OnClick(R.id.text)
void onClick() {
text.setText("你點(diǎn)擊了按鈕");
}
class ListViewAdapter extends BaseAdapter {
private Context mContext;
public ListViewAdapter(Context context) {
mContext = context;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return 1000;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater layoutInflater = ((Activity) mContext)
.getLayoutInflater();
convertView = layoutInflater.inflate(
R.layout.list_item, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(convertView);
} else {
convertView = (View) convertView.getTag();
}
holder.textview0.setText("butterknife");
holder.textview1.setText("test");
return convertView;
}
}
static class ViewHolder {
@InjectView(R.id.headshow)
ImageView imageview;
@InjectView(R.id.name)
TextView textview0;
@InjectView(R.id.text)
TextView textview1;
public ViewHolder(View view) {
ButterKnife.inject(this, view);
}
}
}
整體寫法與Android Annotations類似所灸,實(shí)現(xiàn)原理上ButterKnife的實(shí)現(xiàn)也是在編譯的過程中生成了另外一個(gè)類來(lái)幫我們完成一些基本操作宇姚,以上面的代碼為例擂红,生成了一個(gè)名為MainActivity$$ViewInjector的類朽缎,這里我就不再貼代碼了敛摘。但與Android Annotations所不同的是牢硅,我們?cè)诖a中操作的還是MainActivity瓢宦,而并不是MainActivity$$ViewInjector造烁。
ButterKnife其他語(yǔ)法列舉
資源注入
class ExampleActivity extends Activity {
@BindString(R.string.title) String title;
@BindDrawable(R.drawable.graphic) Drawable graphic;
@BindColor(R.color.red) int red;
@BindDimen(R.dimen.spacer) Float spacer;
// ...
}
Fragment注入
public class FancyFragment extends Fragment {
@InjectView(R.id.button1) Button button1;
@InjectView(R.id.button2) Button button2;
@Override View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
ButterKnife.inject(this, view);
// TODO Use "injected" views...
return view;
}
}
回調(diào)函數(shù)注入
// 帶有 Button 參數(shù)
@OnClick(R.id.submit)
public void sayHi(Button button) {
button.setText("Hello!");
}
// 不帶參數(shù)
@OnClick(R.id.submit)
public void submit() {
// TODO submit data to server...
}
// 同時(shí)注入多個(gè) View 事件
@OnClick({ R.id.door1, R.id.door2, R.id.door3 })
public void pickDoor(DoorView door) {
if (door.hasPrizeBehind()) {
Toast.makeText(this, "You win!", LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Try again", LENGTH_SHORT).show();
}
}
性能如何
關(guān)于IOC框架的基本寫法和實(shí)現(xiàn)原理明场,通過上面兩個(gè)例子汽摹,相信大家都已經(jīng)有所了解。但是前面已經(jīng)說了IOC的注解機(jī)制是依賴JAVA的反射苦锨,可能很多開發(fā)者都會(huì)嗤之以鼻:反射會(huì)影響性能逼泣。在早期的JAVA語(yǔ)言中反射是會(huì)帶來(lái)不小的性能消耗,而隨著語(yǔ)言自身的進(jìn)步和完善舟舒,到了現(xiàn)在情況有所好轉(zhuǎn)拉庶。但我們移動(dòng)設(shè)備的性能,不比后臺(tái)服務(wù)器擁有充足的內(nèi)存和運(yùn)算能力秃励。當(dāng)大量的使用注解的時(shí)候氏仗,會(huì)不會(huì)對(duì)APP造成什么不良的影響,會(huì)不會(huì)影響到APP的執(zhí)行性能夺鲜?在這里先明確的聲明皆尔,上述框架不會(huì)給APP帶來(lái)任何副作用,相反它強(qiáng)大易用的api能為你帶來(lái)前所未有的編程體驗(yàn)币励。
上述框架的實(shí)現(xiàn)慷蠕,都是通過使用jdk 1.6引入的Java Annotation Processing Tool,在編譯器中加了一層額外的自動(dòng)編譯步驟食呻,用來(lái)生成基于你源碼的代碼流炕。運(yùn)行期運(yùn)行的其實(shí)就是這個(gè)二次編譯的代碼,實(shí)際上反射注解這一過程是在編譯期完成的搁进,而并不會(huì)影響Runtime浪感。所以上述IOC框架并不會(huì)影響到APP得性能。簡(jiǎn)單的做了幾組測(cè)試饼问,分別用Android Annotations,ButterKnife揭斧,以及常規(guī)寫法寫了同樣實(shí)現(xiàn)的代碼莱革,然后打印代碼執(zhí)行時(shí)間峻堰。每組大概跑200次,然后計(jì)算代碼執(zhí)行的平均耗時(shí)盅视,發(fā)現(xiàn)三組數(shù)據(jù)基本是在同一個(gè)水平上捐名,并不能判斷孰優(yōu)孰劣。
IDE集成方法
不管是Eclipse還是Android Studio都可以很方便的集成上述框架闹击,而且資源包很小镶蹋,具體方法大家網(wǎng)上找一找,我就不再列出具體步驟了赏半。從網(wǎng)上偷了張ButterKnife的插件技能圖贺归,大家隨便感受下。
既然此類框架如此炫酷高效断箫,那么我們是否應(yīng)該大肆推崇拂酣,廣泛采用呢。這個(gè)具體還是根據(jù)自己的項(xiàng)目實(shí)際情況來(lái)定吧仲义,據(jù)我小范圍了解婶熬,目前部分大廠出品的部分應(yīng)用并未采用此類框架,最后我們還是來(lái)看看此類框架的優(yōu)缺點(diǎn)吧埃撵。
優(yōu)點(diǎn)
1:提高開發(fā)效率
2:減少代碼量
缺點(diǎn)
1:代碼可讀性差
2:增加新人學(xué)習(xí)成本
3:加速觸及65535方法數(shù)問
你用或者不用赵颅,框架就在那里,不悲不喜暂刘!
(如有刊物饺谬,歡迎指正)