Android代碼優(yōu)化.doc_第1頁(yè)
Android代碼優(yōu)化.doc_第2頁(yè)
Android代碼優(yōu)化.doc_第3頁(yè)
Android代碼優(yōu)化.doc_第4頁(yè)
Android代碼優(yōu)化.doc_第5頁(yè)
免費(fèi)預(yù)覽已結(jié)束,剩余12頁(yè)可下載查看

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Android代碼優(yōu)化【轉(zhuǎn)】2010-08-20 23:40為性能設(shè)計(jì):1)避免創(chuàng)建對(duì)象對(duì)象的創(chuàng)建從來(lái)不是免費(fèi)的。雖然GC使得內(nèi)存申請(qǐng)代價(jià)不再高昂,但是申請(qǐng)總是比不申請(qǐng)來(lái)得昂貴。如果你在一個(gè)用戶接口循環(huán)中申請(qǐng)對(duì)象,你將會(huì)強(qiáng)行執(zhí)行周期性的GC,在用戶體驗(yàn)上出現(xiàn)一些小的“打嗝”,因此除非不得已,你應(yīng)該避免創(chuàng)建對(duì)象實(shí)例,下面是一些例子可以幫助理解:當(dāng)你在一組輸入數(shù)據(jù)中抽取字符串時(shí),嘗試返回源數(shù)據(jù)的子串,而非創(chuàng)建一個(gè)副本。你將會(huì)創(chuàng)建一個(gè)新的String對(duì)象,但是它會(huì)和數(shù)據(jù)共享字符數(shù)組char。如果你有一個(gè)返回String的方法,而且你知道它的結(jié)果將會(huì)一直被追加到StringBuffer,改變你的簽名和實(shí)現(xiàn),使這個(gè)函數(shù)里面直接追加,避免創(chuàng)建臨時(shí)對(duì)象。 一個(gè)更激進(jìn)的主意是將多維數(shù)組切成與之平行的一維數(shù)組:一個(gè)int數(shù)組比Integer數(shù)組要好,但也有一個(gè)公認(rèn)的事實(shí)就是兩個(gè)平行的int數(shù)組要比一個(gè)(int,int)對(duì)象數(shù)組要高效很多。對(duì)于其它原始數(shù)據(jù)類型亦如是。如果你需要實(shí)現(xiàn)一個(gè)存儲(chǔ)一組對(duì)象(Foo,Bar)的容器,請(qǐng)記住兩個(gè)平等的Foo和Bar數(shù)組通常元比一個(gè)定制對(duì)象數(shù)組要好(當(dāng)然,對(duì)于此有個(gè)例外,就是當(dāng)你設(shè)計(jì)一個(gè)API供其它代碼訪問(wèn)時(shí);在那樣的情況下,通常最好是為保證API的正確性而犧牲一點(diǎn)速度。但是在你的內(nèi)部代碼,你應(yīng)該盡可能保持高效)。通常來(lái)說(shuō),避免創(chuàng)建臨時(shí)對(duì)象,如果你可以的話。更少的對(duì)象創(chuàng)建意味著更小頻率的GC,這對(duì)用戶體驗(yàn)有直接的影響。2)用Native方法當(dāng)處理字符串時(shí),要毫不猶豫地使用諸如String.indexOf()、String.lastIndexOf()之類的專門方法,這些是典型的用C/C+代碼實(shí)現(xiàn)的方法,它們可以輕易地比實(shí)現(xiàn)同樣功能的Java循環(huán)快10-100倍。對(duì)此建議的一反面是調(diào)用一個(gè)native方法要比調(diào)用一個(gè)解析的方法,不要將native方法用于瑣碎的計(jì)算,如果可以避免的話。優(yōu)先使用Virtual而非Interface假如你有一個(gè)HashMap對(duì)象,你可以聲明它為一個(gè)HashMap或一個(gè)通用的Map:Map myMap1 = new HashMap();HashMap myMap2 = new HashMap();哪一個(gè)更好?一般的會(huì)說(shuō)你該選擇Map,因?yàn)樗试S你改變其實(shí)現(xiàn),對(duì)于通常的編程來(lái)說(shuō)這是對(duì)的,但是對(duì)于嵌入式系統(tǒng)來(lái)說(shuō)這并不是太妙。通過(guò)接口的引用來(lái)調(diào)用一個(gè)方法要比通過(guò)一個(gè)具體類型的引用調(diào)用virtual方法花多2倍的時(shí)間。如果你已經(jīng)選擇了一個(gè)HashMap,因?yàn)樗眠m用你正在做的事情,那通過(guò)Map來(lái)調(diào)用就沒(méi)有什么價(jià)值了??紤]到IDE可以為你重構(gòu)代碼,用Map來(lái)調(diào)用就沒(méi)有太大價(jià)值了,即使你不知道你代碼將去向何方(但是,再一次的,公共的API是又是一個(gè)例外:好的API較少考慮性能)。3)優(yōu)先選擇static而非virtual如果你不必訪問(wèn)一個(gè)對(duì)象的字段,使你的方法成為static方法。它可以被更快地調(diào)用,因?yàn)樗恍枰粋€(gè)虛擬方法表間接調(diào)用。同時(shí)它也是一個(gè)好的做法,因?yàn)閺姆椒ǖ暮灻梢钥闯稣{(diào)用這個(gè)方法不會(huì)改變對(duì)象的狀態(tài)。4)避免內(nèi)部的Getter/Setter在一些像C+的語(yǔ)言中,通常的做法是用getter(如:i=getCount())代替直接地訪問(wèn)字段(i=mCount),在C+這是一個(gè)很好的習(xí)慣,因?yàn)榫幾g器通常能夠內(nèi)聯(lián)這個(gè)訪問(wèn),并且你需要限制或debug字段訪問(wèn),你可以在任何時(shí)候增加代碼。在Android,這是一個(gè)壞主意。虛擬方法調(diào)用代價(jià)是昂貴的,實(shí)例字段查找代價(jià)更高。沿用面一般向?qū)ο缶幊虒?shí)踐在公開接口中提供gettter和setter是合理的,但在一個(gè)類中你應(yīng)該直接訪問(wèn)字段。Cache字段查找訪問(wèn)對(duì)象字段要比訪問(wèn)本地變量慢得多,如下面這段:for (int i = 0; i this.mCount; i+)dumpItem(this.mItemsi);應(yīng)該寫成這樣:int count = this.mCount;Item items = this.mItems;for (int i = 0; i count; i+)dumpItems(itemsi);(我們用一個(gè)顯式的”this”來(lái)表明這是一個(gè)成員變量。)有一個(gè)相似的指引就是,不要在for語(yǔ)句中的第二個(gè)從句中調(diào)用方法。例如下面這段代碼將會(huì)在每次迭代中都會(huì)執(zhí)行一次getCount(),這是一個(gè)巨大的浪費(fèi),你可以將它的值cache為一個(gè)int。for (int i = 0; i this.getCount(); i+) dumpItems(this.getItem(i);通常,如果你將要訪問(wèn)一個(gè)實(shí)例字段多次,一個(gè)好的習(xí)慣就是創(chuàng)建一個(gè)臨時(shí)變量。例如:protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) if (isHorizontalScrollBarEnabled() int size = mScrollBar.getSize(false); if (size = 0) size = mScrollBarSize; mScrollBar.setBounds(0, height size, width, height); mScrollBar.setParams( computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(), false); mScrollBar.draw(canvas); 這是對(duì)成員字段mScrollBar的四次分開查找,通過(guò)將mScrollBar緩存到本地變量,四次成員字段查找變成四次本地變量引用,這樣更為高效。同樣地,方法參數(shù)作為本地變量擁有相同的性能特征。聲明常量為final考慮在類開頭的如下聲明:static int intVal = 42;static String strVal = “Hello, world!”;編譯器產(chǎn)生一個(gè)叫的類初始化器方法,它在類首次使用時(shí)被執(zhí)行。這個(gè)方法將42存到intVal,并為intVal從類文件字符串常量表中抽出一個(gè)引用,當(dāng)這些值在后面被引用到時(shí),它們以字段查找的方式被訪問(wèn)。我們可以用final關(guān)鍵字改進(jìn)之:static final int intVal = 42;static final String strVal = “Hello, world!”;這個(gè)類不再需要一個(gè)方法,因?yàn)槌A看娴街苯佑蒝M處理的類文件靜態(tài)字段初始化器,代碼訪問(wèn)intVal將會(huì)直接使用integer值42,而對(duì)intVal的訪問(wèn)會(huì)用一個(gè)相對(duì)廉價(jià)的“字符串常量”指令來(lái)代替一個(gè)字段查找。聲明一個(gè)方法或類為final并不能直接獲得性能上的好處,但它確實(shí)能起到某些優(yōu)化作用。例如,假如編譯器知道一個(gè)”getter”不能被一個(gè)子類重寫,它能夠內(nèi)聯(lián)這個(gè)方法調(diào)用。你也可以將本地變量聲明為final,然而這并無(wú)真正意義上的性能提升。對(duì)于要地變量,只有在使代碼更清晰(或你不得不,如為了在匿名內(nèi)部類中使用)時(shí)使用final。小心使用增強(qiáng)的For循環(huán)語(yǔ)句增強(qiáng)的For語(yǔ)句可以用于實(shí)現(xiàn)了Iterable接口的Collection,對(duì)于這些對(duì)象,一個(gè)iterator被申請(qǐng)用來(lái)進(jìn)行接口調(diào)用hasNext()和next()。對(duì)于ArrayList,你最好直接遍歷它,但對(duì)于其它c(diǎn)ollections,增強(qiáng)的For循環(huán)語(yǔ)句將會(huì)等同于顯式的迭代用法。盡管如此,下面的代碼展示了增強(qiáng)的For語(yǔ)句的可為人接受的用法:public class Foo int mSplat; static Foo mArray = new Foo27; public static void zero() int sum = 0; for (int i = 0; i mArray.length; i+) sum += mArrayi.mSplat; public static void one() int sum = 0; Foo localArray = mArray; int len = localArray.length; for (int i = 0; i len; i+) sum += localArrayi.mSplat; public static void two() int sum = 0; for (Foo a: mArray) sum += a.mSplat; zero()在循環(huán)中每次迭代獲取靜態(tài)字段兩次計(jì)算數(shù)組長(zhǎng)度一次。one()將所有東西存到本地變量,避免查找。two()用到了增強(qiáng)的For循環(huán)語(yǔ)句,由編譯器產(chǎn)生的代碼拷貝數(shù)組引用和數(shù)組長(zhǎng)度到本地變量,使之成為一個(gè)遍歷數(shù)組元素的一個(gè)很好的選擇。它確實(shí)在主循環(huán)中產(chǎn)生了一個(gè)額外的本地載入/存儲(chǔ),使得它比起one()有點(diǎn)慢并且長(zhǎng)了bytes。總之,增強(qiáng)的For語(yǔ)句對(duì)于數(shù)組表現(xiàn)良好,但對(duì)iterable對(duì)象要小心使用,因?yàn)橛蓄~外的對(duì)象創(chuàng)建。避免Enum類型Enum非常方便,但不幸的是當(dāng)考慮到時(shí)間和速度時(shí)就讓人痛苦。例如這個(gè):public class Foo public enum Shrubbery GROUND, CRAWLING, HANGING 將編譯成一個(gè)900byte的.class文件,在首次使用是時(shí),類初始化器在代表每個(gè)被枚舉的值對(duì)象上激活方法。每個(gè)對(duì)象都有其靜態(tài)字段,并且整個(gè)集合就存儲(chǔ)在一個(gè)數(shù)組(一個(gè)稱為“$values”的靜態(tài)字段)上,對(duì)于僅僅的三個(gè)integer來(lái)說(shuō),那是太多的代碼和數(shù)據(jù)了。這個(gè):Shrubbery shrub = Shrubbery.GROUND;導(dǎo)致了一次靜態(tài)字段查找。如果“GROUND”是一個(gè)static final int編譯器將會(huì)將它看作是一個(gè)常量并內(nèi)聯(lián)它。相反地,當(dāng)然,是運(yùn)用enum你可以得到一個(gè)更優(yōu)雅的API和一些編譯時(shí)的值檢查。因此,通常折衷的辦法是:為API,你應(yīng)該千方百計(jì)地使用enum,但是當(dāng)考慮到性能時(shí)嘗試避免使用它。利用內(nèi)部類使用包作用方域考慮下面的類定義:public class Foo private int mValue; public void run() Inner in = new Inner(); mValue = 27; in.stuff(); private void doStuff(int value) System.out.println(“Value is ” + value); private class Inner void stuff() Foo.this.doStuff(Foo.this.mValue); 在這里我們要特別指出的是這里定義了一個(gè)內(nèi)部類(Foo$Inner),它可以直接訪問(wèn)外部類的私有方法和私有實(shí)例字段,這是合法的,代碼的執(zhí)行的結(jié)果是如預(yù)期般的“Value is 27”。問(wèn)題在于,F(xiàn)oo$Inner是一個(gè)完全獨(dú)立的類,這使得直接訪問(wèn)其私有方法是非法的,為了架起橋梁,編譯器會(huì)產(chǎn)生如下兩個(gè)虛擬方法/*package*/ static int Foo.access$100(Foo foo) return foo.mValue;/*package*/ static void Foo.access$200(Foo foo, int value) foo.doStuff(value);當(dāng)內(nèi)部類代碼需要訪問(wèn)外部類的mValue變量或激活doStuff方法時(shí)就會(huì)調(diào)用這些方法。這就意味著上面的代碼清楚表明了你是通過(guò)訪問(wèn)器來(lái)訪問(wèn)成員字段的,而非直接訪問(wèn)。前面我們討論過(guò)訪問(wèn)器是比直接訪問(wèn)是要慢的,所以這是一個(gè)由于某種特定語(yǔ)言方言所導(dǎo)致的隱性性能打擊。我們可以通過(guò)聲明由內(nèi)部類訪問(wèn)的字段和方法為具有包作用域而非私有作用域來(lái)解決這個(gè)問(wèn)題。這樣運(yùn)行得更快并且移除了額外產(chǎn)生的方法(不幸的是,這也意味著這些字段可以被同包下的其它類所訪問(wèn),這個(gè)是違反了使所有的字段成為私有的標(biāo)準(zhǔn)OO做法的,再一次的,如果你是在設(shè)計(jì)一個(gè)公共的API的話,你可能要慎重地考慮這一優(yōu)化策略)。)避免使用Float類型在Pentium CPU發(fā)布之前,對(duì)于游戲作者來(lái)說(shuō)做很多整型計(jì)算是很正常的事。有了Pentium之后,浮點(diǎn)計(jì)算聯(lián)合處理器成了內(nèi)置功能,你的游戲通過(guò)交錯(cuò)整型和浮點(diǎn)操作比只有整型計(jì)算時(shí)運(yùn)行起來(lái)要快得多。在桌面系統(tǒng)上通常的可以自由的使用浮點(diǎn)數(shù)。不幸的是,嵌入式處理器很少具有硬件浮點(diǎn)支持,所以所有的”float”和”double”操作都是在軟件上進(jìn)行。某些基本的浮點(diǎn)操作可能會(huì)花費(fèi)數(shù)微秒。還有,甚至對(duì)于整型數(shù),一些芯片支持硬件乘法但缺少硬件除法,在這種情況下,整型除法和取模運(yùn)算是在軟件上執(zhí)行的如果你是在設(shè)計(jì)一個(gè)哈希表或做很多數(shù)學(xué)運(yùn)算這就是你需要考慮的事情。為響應(yīng)靈敏性設(shè)計(jì)代碼可能通過(guò)各種性能測(cè)試,但是當(dāng)用戶使用時(shí)還是會(huì)需要漫長(zhǎng)的等待,這些就是那種響應(yīng)不夠靈敏的應(yīng)用它們反應(yīng)遲鈍,掛起或凍住周期很長(zhǎng),或者要花很長(zhǎng)時(shí)間來(lái)處理輸入。在Android上,系統(tǒng)通過(guò)向用戶顯示一個(gè)稱為應(yīng)用無(wú)響應(yīng)(ANR:Application Not Responding)的對(duì)話框來(lái)防止在一段時(shí)間內(nèi)響應(yīng)不夠快。用戶可以選擇讓應(yīng)用繼續(xù),但是用戶并不會(huì)想要每次都來(lái)處理這個(gè)對(duì)話框。因此應(yīng)把你的應(yīng)用設(shè)計(jì)得響應(yīng)靈敏,使得系統(tǒng)不必顯示ANR給用戶。通常地,當(dāng)不能響應(yīng)用戶輸入時(shí)系統(tǒng)顯示一個(gè)ANR。例如,如果一個(gè)應(yīng)用在IO操作(經(jīng)常是網(wǎng)絡(luò)訪問(wèn))上阻塞了,那么主應(yīng)用線程就會(huì)無(wú)法處理正在進(jìn)行的用戶輸入事件。經(jīng)過(guò)一段時(shí)間,系統(tǒng)認(rèn)為應(yīng)用已經(jīng)掛起,向用戶顯示一個(gè)ANR,讓用戶可以選擇關(guān)閉。相同地,如果你的應(yīng)用花太多的時(shí)間在構(gòu)建詳細(xì)的內(nèi)存結(jié)構(gòu)上,又或者在計(jì)算游戲的下一個(gè)動(dòng)作上,系統(tǒng)會(huì)認(rèn)為你的應(yīng)用已經(jīng)掛起。用上面的技術(shù)來(lái)保證這些計(jì)算是高效的一直都是很重要的,但是即使是最高效的代碼運(yùn)行也是需要花費(fèi)時(shí)間的。在這兩種情況下,解決的辦法通常就是創(chuàng)建一個(gè)子線程,在這個(gè)子線程上做你的大部分工作。這樣讓你的主線程(驅(qū)動(dòng)用戶接口事件循環(huán))保持運(yùn)行,并讓你的代碼免于被系統(tǒng)認(rèn)為已經(jīng)凍住。因?yàn)檫@樣的線程化通常都是在類級(jí)別上來(lái)完成的,所以你可以認(rèn)為響應(yīng)性能問(wèn)題是一個(gè)類問(wèn)題(與上面描述的方法級(jí)別的性能問(wèn)題)。這個(gè)文檔討論了Android系統(tǒng)是如何決定一個(gè)應(yīng)用沒(méi)有響應(yīng)的,并提供了指引來(lái)保障你的應(yīng)用是響應(yīng)靈敏的。1)是什么引發(fā)了ANR?在Android系統(tǒng)上,應(yīng)用的響應(yīng)靈敏性由Activity Manager和Window Manager system services所監(jiān)控,當(dāng)它監(jiān)測(cè)到如下的其中一個(gè)條件時(shí),Android就會(huì)為特定的應(yīng)用顯示一個(gè)ANR:5秒內(nèi)對(duì)輸入事件無(wú)響應(yīng)。一個(gè)BroadCastReceiver在10秒內(nèi)沒(méi)有執(zhí)行完畢。怎樣避免ANR?考慮到上面對(duì)ANR的定義,讓我們來(lái)研究一下這是為什么會(huì)發(fā)生以及怎樣最好的組織你的應(yīng)用以避免ANR。Android應(yīng)用正常是運(yùn)行在一個(gè)單獨(dú)的(如main)線程中的,這就意味著在你應(yīng)用主線程中正在做的需要花很長(zhǎng)時(shí)間來(lái)完成的事情都能夠激活A(yù)NR對(duì)話框。因?yàn)槟愕膽?yīng)用并沒(méi)有給自己一個(gè)機(jī)會(huì)來(lái)處理輸入事件或Intent廣播。因此任何運(yùn)行在主線程中的方法應(yīng)該做盡可能少的事情。特別地Activitiy在關(guān)鍵生命周期方法中如onCreate()和onResume()應(yīng)當(dāng)做盡可能少的設(shè)置。潛在地的耗時(shí)長(zhǎng)的操作(如網(wǎng)絡(luò)或數(shù)據(jù)庫(kù)操作,或高耗費(fèi)數(shù)學(xué)計(jì)算如改變位圖大?。?yīng)該在子線程里面完成(或以數(shù)據(jù)庫(kù)操作為例,可以通過(guò)異步請(qǐng)求)。盡管如此,這并不是說(shuō)當(dāng)?shù)却泳€程完成的過(guò)程中你的主線程必須被阻塞你不必調(diào)用Thread.wait()或Thread.sleep(),恰恰相反,你的主線程應(yīng)該為子線程提供一個(gè)Handler,以便子線程完成時(shí)可以提交回給主線程。以這種方式來(lái)設(shè)計(jì)你的應(yīng)用,將會(huì)允許你的主線程一直可以響應(yīng)輸入,以避免由5秒鐘的輸入事件超時(shí)導(dǎo)致的ANR對(duì)話。這些做法同樣應(yīng)該被其它任何顯示UI的線程所效仿,因?yàn)樗鼈儗儆谕瑯宇愋偷某瑫r(shí)。IntentReciever執(zhí)行時(shí)間的特定限制限制了它們應(yīng)該做什么:在后臺(tái)執(zhí)行的一些瑣碎的工作如保存設(shè)置或注冊(cè)通知。至于其它在主線程里被調(diào)用的方法,在BroadcastReceiver中,應(yīng)用應(yīng)該避免潛在的長(zhǎng)耗時(shí)操作或計(jì)算,而是應(yīng)該用子線程來(lái)完成密集任務(wù)(因?yàn)锽roadcastReceiver的生命周期是短暫的)。對(duì)Intent broadcast作出響應(yīng),你的應(yīng)用應(yīng)該啟動(dòng)一個(gè)Service來(lái)執(zhí)行長(zhǎng)耗時(shí)的動(dòng)作。同樣,你也應(yīng)該避免從Intent Receiver中啟動(dòng)Activity,因?yàn)樗鼤?huì)產(chǎn)生一個(gè)新的屏,偷走任何用戶正在運(yùn)行的應(yīng)用的焦點(diǎn)。對(duì)Intent broadcast作出的響應(yīng),假如你的應(yīng)用需要向用戶顯示什么東西,應(yīng)該用Notification Manager來(lái)完成。增強(qiáng)響應(yīng)靈敏性通常,在一個(gè)應(yīng)用中,100到200微秒是一個(gè)讓用戶感覺(jué)到阻滯的閾值,因此這里有些小技巧讓你用來(lái)使你的應(yīng)用看起來(lái)響應(yīng)更靈敏。如果你的應(yīng)用正在后臺(tái)對(duì)用戶輸入作出響應(yīng),顯示正在進(jìn)行的進(jìn)度(ProgressBar和ProgressDialog對(duì)此很有用)。特別是對(duì)于游戲,在子線程中做移動(dòng)的計(jì)算。如果你的應(yīng)用有一個(gè)耗時(shí)的初始化過(guò)程,考慮用閃屏或盡可能快地渲染主界面并異步地填充信息。在這兩種情況下你都應(yīng)該表明進(jìn)度正在進(jìn)行,以免用戶覺(jué)得你的應(yīng)用被凍住了。為無(wú)縫設(shè)計(jì):即使你的應(yīng)用是快速且響應(yīng)靈敏的,一些設(shè)計(jì)仍然能句對(duì)用戶造成問(wèn)題因?yàn)榕c其它應(yīng)用未計(jì)劃的交互或者對(duì)話,意外的數(shù)據(jù)丟失,無(wú)意識(shí)的阻塞等等。為了避免這些問(wèn)題,有助于理解你的應(yīng)用運(yùn)行的環(huán)境和可以影響你的應(yīng)用的系統(tǒng)交互??傊?,你應(yīng)該倔盡全力地開發(fā)一個(gè)與系統(tǒng)和其它應(yīng)用無(wú)縫交互的應(yīng)用。一個(gè)常見(jiàn)的無(wú)縫問(wèn)題就是一個(gè)應(yīng)用的后臺(tái)進(jìn)程(如service或broadcast receiver)對(duì)某事件作出響應(yīng)而彈出對(duì)話框,這看起來(lái)仿佛并無(wú)大礙,特別是當(dāng)你在模擬器上單獨(dú)地構(gòu)建和測(cè)試你的應(yīng)用時(shí)。然而,當(dāng)你的應(yīng)用在真正的設(shè)備上運(yùn)行,后臺(tái)線程顯示對(duì)話框時(shí),你的應(yīng)用當(dāng)時(shí)可能沒(méi)有獲得用戶焦點(diǎn)。這就會(huì)出現(xiàn)你的應(yīng)用會(huì)在活動(dòng)的應(yīng)用后面顯示對(duì)話框,或者從當(dāng)前應(yīng)用中獲得焦點(diǎn)并顯示對(duì)話框的情況,而管論當(dāng)時(shí)用戶正在做什么(如正在打電話等)。那樣的行為可能對(duì)你的應(yīng)用或用戶不起作用。為了避免這些問(wèn)題,你的應(yīng)用應(yīng)該利用適當(dāng)?shù)南到y(tǒng)資源Notification類,來(lái)通知用戶。利用通知,你的應(yīng)用可以通過(guò)在狀態(tài)條上顯示一個(gè)圖標(biāo)來(lái)通知用戶事件已經(jīng)發(fā)生,而非獲得焦點(diǎn)和打斷用戶。另外一個(gè)無(wú)縫問(wèn)題的例子就是,Activity由于未能正確實(shí)現(xiàn)onPause()及其它生命周期方法而無(wú)意中丟失了狀態(tài)或用戶數(shù)據(jù)。又或者,你的應(yīng)用要暴露供其它應(yīng)用使用的數(shù)據(jù),你應(yīng)該通過(guò)ContentProvider來(lái)實(shí)現(xiàn),而非通過(guò)一個(gè)全世界都可讀的原始文件或數(shù)據(jù)庫(kù)。這些例子的共同特點(diǎn)就是,它們都是關(guān)于如何跟系統(tǒng)和其它應(yīng)用協(xié)作得更好,Android系統(tǒng)的設(shè)計(jì)就是將所有的應(yīng)用看作是一個(gè)松散耦合的組件聯(lián)邦,不是一堆墨盒代碼。這就使你作為一個(gè)開發(fā)者可以將整個(gè)系統(tǒng)視為只是一個(gè)更大一點(diǎn)的組件聯(lián)邦。這樣有得于你將應(yīng)用與其它應(yīng)用清晰和無(wú)縫的集成,所以作為回報(bào),你應(yīng)該更好的設(shè)計(jì)你的代碼。這個(gè)文檔討論了常見(jiàn)的無(wú)縫集成問(wèn)題和怎樣去避免它們。它包含了如下主題:別丟棄數(shù)據(jù)一定要記住Android是一個(gè)移動(dòng)平臺(tái)??雌饋?lái)很顯然,但是記住這個(gè)事實(shí)很重要,就是任何Activity(如”正在打進(jìn)來(lái)的電話”應(yīng)用)在任何時(shí)候都有可能彈出來(lái)覆蓋你的Activity,這會(huì)激活onSaveInstanceState()和onPause()方法,并導(dǎo)致你的應(yīng)用被殺死。如果當(dāng)其它Activity出現(xiàn)時(shí),用戶正好在你的應(yīng)用上編輯數(shù)據(jù),你的應(yīng)用被殺死的同時(shí)那些數(shù)據(jù)也很可能會(huì)丟失。當(dāng)然了,除非你先保存了進(jìn)行中的工作?!癆ndroid方式”是這么做的:能接收和編輯用戶輸入的應(yīng)用需要重寫onSaveInstanceState()方法并以恰當(dāng)?shù)姆绞奖4嫠鼈兊臓顟B(tài),當(dāng)用戶重新訪問(wèn)應(yīng)用時(shí),就能重新獲得數(shù)據(jù)。一個(gè)運(yùn)用這個(gè)行為經(jīng)典的例子就是郵件應(yīng)用,當(dāng)用戶正在撰寫郵件時(shí)另一人Activity開始了,應(yīng)用應(yīng)該將正在編輯的郵件保存為草稿。不要暴露原始數(shù)據(jù)如果你不想穿著內(nèi)衣在大街上散步,同樣你的數(shù)據(jù)也不應(yīng)如此。盡管可能暴露某些應(yīng)用可以方便其它應(yīng)用讀取,但這通常不是最好的主意。暴露原始數(shù)據(jù)要求其它的應(yīng)用能夠理解你的數(shù)據(jù)格式;如果你改變了格式,你將會(huì)破壞其它沒(méi)有同時(shí)更新的應(yīng)用?!癆ndroid方式”就是創(chuàng)建一個(gè)ContentProvider通過(guò)一個(gè)清晰的、深思熟慮的、可維護(hù)的API來(lái)暴露你的數(shù)據(jù)給其它應(yīng)用。使用ContentProvider就像一個(gè)Java接口來(lái)分離和組件化兩段緊密耦合的代碼,這就意味著你能夠修改你數(shù)據(jù)的內(nèi)部格式而不用修改由ContentProvider暴露的接口,這樣就不會(huì)影響其它應(yīng)用。別打斷用戶如果能確定一個(gè)用戶是帶有目的性的運(yùn)行一個(gè)應(yīng)用才是安全的。那就是為什么你除非是直接響應(yīng)當(dāng)前活動(dòng)的用戶輸入,不然就要避免產(chǎn)生Activity的原因。那就是說(shuō),不要從后臺(tái)運(yùn)行的BroadcastReceiver和Service中調(diào)用startActivity()。如果這樣做將會(huì)打斷任何正在運(yùn)行的應(yīng)用,并使用戶惱怒。甚至你的Activity可能成為一個(gè)“擊鍵強(qiáng)盜”接收一些用戶正在為上一個(gè)Activity提供的輸入,視乎你的應(yīng)用所做的,這是這可能是個(gè)壞消息。取代直接從后臺(tái)直接產(chǎn)生Activity UIs,你應(yīng)該用NotificationManager來(lái)設(shè)置通知,這將會(huì)出現(xiàn)在狀態(tài)條上,當(dāng)用戶空閑時(shí)可以點(diǎn)擊它們來(lái)看你的應(yīng)用向他們顯示了什么。(注意,當(dāng)你的Activity已經(jīng)在前臺(tái)時(shí)所有這些都沒(méi)適用:這時(shí),對(duì)于輸入的響應(yīng),用戶期望看到你的下一個(gè)Activity。)有太多事要做?在一個(gè)線程里做如果你的應(yīng)用需要做一個(gè)代價(jià)高昂或長(zhǎng)耗時(shí)的計(jì)算,你可能要將它移到一個(gè)線程里。這個(gè)將會(huì)防止顯示“Application Not Responding”對(duì)話框給用戶,最終導(dǎo)致你的應(yīng)用完全終止。默認(rèn)地,在一個(gè)Activity中的代碼和其所有的View運(yùn)行在同一個(gè)線程上。這與處理UI事件的線程是同一個(gè)。例如,當(dāng)一個(gè)鍵被按下時(shí),一個(gè)key-down事件被添加到Activity主線程的隊(duì)列。事件處理系統(tǒng)需要很快地讓這個(gè)事件出列并處理這個(gè)事件。不然,系統(tǒng)數(shù)秒后將會(huì)認(rèn)為應(yīng)用已經(jīng)掛起并替用戶殺死這個(gè)應(yīng)用。如果你有長(zhǎng)耗時(shí)的代碼,讓它在你的Activity上內(nèi)聯(lián)運(yùn)行將會(huì)在使它運(yùn)行在事件處理線程上,這很大程度上阻塞了了事件處理句柄。這會(huì)延緩輸入處理并導(dǎo)致ANR對(duì)話框。為了避免之,將你的計(jì)算移到一個(gè)線程中。在為響應(yīng)靈敏性設(shè)計(jì)中已經(jīng)討論了如何做。5)不要過(guò)載一個(gè)單一的Activity屏任何值得使用的應(yīng)用都可能會(huì)有幾個(gè)不同的屏幕。當(dāng)設(shè)計(jì)你的UI屏幕時(shí),請(qǐng)一定要運(yùn)用多個(gè)Activity對(duì)象實(shí)例。依賴于你的開發(fā)背景,你可能像解釋某些類似Java Applet的東西一樣來(lái)解釋一個(gè)Activity,Activity是你應(yīng)用的入口點(diǎn)。然而,那并不是準(zhǔn)確:一個(gè)pplet的子類是一個(gè)Javapplet的單一入口點(diǎn),而一個(gè)Activity應(yīng)該被看作一個(gè)潛在的進(jìn)入你的應(yīng)用的多個(gè)入口點(diǎn)。在你的”main”Activity和任何其它你可能有的Activity之間的唯一不同就是,那“miain”Activity碰巧是那個(gè)唯一在你的AndroidManifest.xml文件中對(duì)“ent.action.MAIN”動(dòng)作有興趣的一個(gè)而已。所以,當(dāng)設(shè)計(jì)你的應(yīng)用時(shí),把你的應(yīng)用看成一個(gè)Activity對(duì)象的聯(lián)邦。從長(zhǎng)遠(yuǎn)來(lái)看,這會(huì)使得你的代碼更具可維護(hù)性。6)擴(kuò)展系統(tǒng)主題當(dāng)提到用戶接口的觀感時(shí),協(xié)調(diào)是很重要的。

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論