所有Android 開(kāi)發(fā)者都要知道的開(kāi)發(fā)常識(shí)
軟件開(kāi)發(fā)流程
一個(gè)完整的軟件開(kāi)發(fā)流程離不開(kāi)策劃、交互、視覺(jué)、軟件、測(cè)試、維護(hù)和運(yùn)營(yíng)這七個(gè)環(huán)節(jié),這七個(gè)環(huán)節(jié)并不是孤立的,它們是開(kāi)發(fā)一款成功產(chǎn)品的前提,但每一項(xiàng)也都可以形成一個(gè)學(xué)科,是一個(gè)獨(dú)立的崗位,隨著敏捷開(kāi)發(fā)的流行,以及來(lái)到了體驗(yàn)為王的時(shí)代,現(xiàn)代軟件開(kāi)發(fā)更多的是注重效率和敏捷,而不是循規(guī)蹈矩的遵循這些開(kāi)發(fā)流程,比如軟件開(kāi)發(fā)的崗位不再僅僅是個(gè)技術(shù)崗位,它需要去參與前期的設(shè)計(jì)和評(píng)審、可以在視覺(jué)和交互方面提出自己的見(jiàn)解,在開(kāi)發(fā)的過(guò)程中需要自測(cè)程序盡快解決現(xiàn)存問(wèn)題,運(yùn)營(yíng)和維護(hù)的過(guò)程中也需要軟件的幫助。可見(jiàn)現(xiàn)代軟件開(kāi)發(fā)對(duì)開(kāi)發(fā)者的綜合素質(zhì)(這并不是facebook所講的全棧工程師)越來(lái)越高,自稱為碼農(nóng)或者程序猿顯然是不合理的,因?yàn)檫@個(gè)過(guò)程是腦力勞動(dòng)和體力腦動(dòng)并存,稱呼自己為工程師顯得更為合理。
策劃:需求收集(通過(guò)用戶調(diào)研、灰度發(fā)布、大數(shù)據(jù)分析、競(jìng)品分析、領(lǐng)導(dǎo)拍腦袋等方式獲取需求)、需求整理(將需求歸類、劃分優(yōu)先級(jí)等)、將需求轉(zhuǎn)換成解決方案(輸出設(shè)計(jì)文檔);
交互:從心理學(xué)(利用人性的弱點(diǎn))、人性化(心智)、個(gè)性化的角度將解決方案轉(zhuǎn)換成可交互的功能和界面(需要輸出交互文檔),比如加載等待、消息提示、頁(yè)面布局、頁(yè)面內(nèi)和頁(yè)面間的交互邏輯、頁(yè)面切換動(dòng)畫等等,這個(gè)過(guò)程中一般會(huì)使用Axure或者PowerPoint來(lái)制作交互文檔;
視覺(jué):根據(jù)交互圖,使用PhotoShop來(lái)做視覺(jué)效果,在Android上的圖片格式大多是png和jpg,對(duì)于需要屏幕適配,程序又適合做屏幕適配的地方可以使用九圖,格式為*.9.png。
軟件:根據(jù)視覺(jué)和交互效果將需求轉(zhuǎn)化為具體的實(shí)現(xiàn),在實(shí)現(xiàn)的過(guò)程中可能會(huì)因?yàn)樾枨蟆⒔换セ蛘咭曈X(jué)的變動(dòng)導(dǎo)致軟件實(shí)現(xiàn)的變動(dòng),因?yàn)椴邉潯⒔换ァ⒁曈X(jué)這每一個(gè)環(huán)節(jié)都可能會(huì)有信息失真的現(xiàn)象,或者是由于市場(chǎng)環(huán)境的變化、獲取信息不夠準(zhǔn)確、領(lǐng)導(dǎo)拍腦袋等等情況導(dǎo)致軟件始終處于被動(dòng)狀態(tài),所以現(xiàn)在會(huì)提倡敏捷開(kāi)發(fā)、結(jié)對(duì)編程、程序設(shè)計(jì)、同行評(píng)審、單元測(cè)試來(lái)提高程序的靈活性和穩(wěn)定性;
測(cè)試:軟件達(dá)到可交互的標(biāo)準(zhǔn)后,需要將可交互的程序提供測(cè)試,其中灰度發(fā)布(用戶測(cè)試)、自測(cè)(開(kāi)發(fā)自測(cè))、SQA(品質(zhì)保證)都算是測(cè)試;
維護(hù)和運(yùn)營(yíng):通過(guò)測(cè)試程序達(dá)到穩(wěn)定標(biāo)準(zhǔn)后,軟件就可以上線了,軟件上線后,需要去維護(hù),用戶反饋的問(wèn)題要及時(shí)解決、用戶有疑問(wèn)要及時(shí)解答;根據(jù)后臺(tái)統(tǒng)計(jì)信息、抓住可運(yùn)營(yíng)的節(jié)日、民族文化需要做運(yùn)營(yíng)來(lái)提高用戶使用產(chǎn)品的粘度,讓更多的用戶知道、使用產(chǎn)品都是運(yùn)營(yíng)應(yīng)該做的。
注:
可以查看這個(gè)答案了解一個(gè)APP從創(chuàng)意到上線的具體流程,開(kāi)發(fā)一個(gè)APP有多難?
可以查看筆戈科技的這篇文章了解一個(gè)手機(jī)(平板或其它電子產(chǎn)品也差不多)的誕生需要哪些環(huán)節(jié),一個(gè)手機(jī)的誕生過(guò)程
提問(wèn)的智慧
大多數(shù)工作都是以結(jié)果為導(dǎo)向的,特別是軟件開(kāi)發(fā)這個(gè)職業(yè),績(jī)效考核、KPI這些都是在考核你工作的成果,所以工作更多地是需要你解決問(wèn)題的能力,至于學(xué)習(xí)這個(gè)事情,還是在工作之外的時(shí)間去做吧。對(duì)于提高解決問(wèn)題能力我有兩個(gè)建議:
學(xué)會(huì)學(xué)習(xí)和思考:學(xué)習(xí)的過(guò)程中要廣度和深度并存,Android應(yīng)用開(kāi)發(fā)本身對(duì)技術(shù)功底的要求不高(因?yàn)楹芏嗟讓拥臇|西都被google、框架、開(kāi)源代碼給封裝起來(lái)了,多數(shù)時(shí)候你只需要看ReadMe或者API知道怎么用就可以了),更多地是在你遇到問(wèn)題的時(shí)候知道這個(gè)問(wèn)題能夠通過(guò)什么方法和方式來(lái)解決。書要看,但多逛逛論壇、QQ群、Github、StackOverflow、CSDN博客專欄對(duì)自己都是有益的。
學(xué)會(huì)提問(wèn):你身邊有很多資源,比如同事、StackOverflow、QQ技術(shù)交流群、搜索引擎,當(dāng)你遇到問(wèn)題的時(shí)候完全可以利用身邊的資源來(lái)解決遇到的問(wèn)題,如果一個(gè)問(wèn)題在一個(gè)小時(shí)之內(nèi)自己都不能夠解決它,我就會(huì)通過(guò)搜索引擎、Github、QQ技術(shù)交流群、同事、StackOverflow(以上排序是按優(yōu)先級(jí)排列的)來(lái)解決它。如果你需要好的答案你就需要有好的提問(wèn),特別是在QQ群或者論壇,在提問(wèn)的過(guò)程中需要體現(xiàn)出你的思考,能夠通過(guò)搜索引擎解決的問(wèn)題堅(jiān)決不問(wèn)他人,這是對(duì)別人的尊重,在這里推薦幾個(gè)鏈接,認(rèn)真看會(huì)對(duì)你有莫大的幫助:
如何用好 Google 等搜索引擎?
程序員應(yīng)該如何提問(wèn)?
提問(wèn)的智慧
Smart Questions
解決bug的方法
為了寫這一項(xiàng)我專門在知乎上提過(guò)一個(gè)問(wèn)題:
你有哪些解決bug的技巧?
在知道如何快速解決bug之前,你需要知道什么是bug。沒(méi)有完成策劃、交互、視覺(jué)要求的功能,這不叫bug,這叫功能缺陷;一個(gè)功能完成后不能正常使用也不叫bug,因?yàn)樗具€沒(méi)達(dá)到可測(cè)試的標(biāo)準(zhǔn)。我認(rèn)為當(dāng)你的程序達(dá)到可測(cè)試標(biāo)準(zhǔn)之后發(fā)現(xiàn)的問(wèn)題才叫bug。綜合我自己解決bug的經(jīng)驗(yàn)和知乎上的回答,總結(jié)常見(jiàn)的解決bug的方法有(你想要高效解決bug的前提是你能夠快速定位到缺陷所在的位置,所以以下方法多數(shù)講的是如何快速定位問(wèn)題,至于真正解決bug,需要你自己修改程序才行):
斷點(diǎn)調(diào)試:
以Eclipse為例:
1、打斷點(diǎn):
(1)打斷點(diǎn):
(2)清除斷點(diǎn):
2、啟動(dòng)調(diào)試模式的兩種方式:
(1)通過(guò)debug as啟動(dòng)調(diào)試程序:右鍵工程名--]Debug AS --]Android Application --]模擬器或者真機(jī)會(huì)彈出......watching for the debugger......的提示框,不要點(diǎn)擊等待其自動(dòng)消失 --] 此時(shí)已經(jīng)進(jìn)入調(diào)試模式,操作程序到達(dá)打斷點(diǎn)的地方。
(2)在程序運(yùn)行過(guò)程中,在DDMS視圖下選中要調(diào)試的程序,啟動(dòng)調(diào)試模式:
3、調(diào)試:請(qǐng)自行嘗試F5、F6、F7、F8這幾個(gè)調(diào)試的快捷鍵;
4、watch成員變量:在調(diào)試的過(guò)程中,比如在執(zhí)行for、while、do while循環(huán)、遞歸、系統(tǒng)回調(diào)等程序時(shí)可以通過(guò)watch來(lái)觀察成員變量或者方法返回值的變化情況,watch的方法:
注:更多關(guān)于在Eclipse IDE中調(diào)試Android程序的知識(shí)請(qǐng)參見(jiàn):Android eclipse中程序調(diào)試
打印:
打印調(diào)試的方法對(duì)于循環(huán)、異步加載、遞歸、JNI等代碼段非常有用,特別是在循環(huán)中,在循環(huán)次數(shù)非常大時(shí),通過(guò)打斷點(diǎn)調(diào)試顯然是一件費(fèi)力的事情,這時(shí)候打印就顯得更“智能”了,我通常會(huì)通過(guò)下面封裝的打印調(diào)試類來(lái)輸出打印信息,這個(gè)類可以打印print、log、行號(hào)、文件名、StrictMode等信息,當(dāng)不需要打印信息時(shí),只需要將DEBUG_MODE改為false就可以了:
- import android.content.Context;
- import android.os.StrictMode;
- import android.util.Log;
- import android.widget.Toast;
- /**
- * 調(diào)試打印類
- *
- * */
- public class DebugUtils{
- private DebugUtils( ){
- }
- public static void println( String printInfo ){
- if( Debug.DEBUG_MODE && null != printInfo ){
- System.out.println( printInfo );
- }
- }
- public static void print( String printInfo ){
- if( Debug.DEBUG_MODE && null != printInfo ){
- System.out.print( printInfo );
- }
- }
- public static void printLogI( String logInfo ){
- printLogI( TAG, logInfo );
- }
- public static void printLogI( String tag, String logInfo ){
- if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
- Log.i( tag, logInfo );
- }
- }
- public static void printLogE( String logInfo ){
- printLogE( TAG, logInfo );
- }
- public static void printLogE( String tag, String logInfo ){
- if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
- Log.e( tag, logInfo );
- }
- }
- public static void printLogW( String logInfo ){
- printLogW( TAG, logInfo );
- }
- public static void printLogW( String tag, String logInfo ){
- if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
- Log.w( tag, logInfo );
- }
- }
- public static void printLogD( String logInfo ){
- printLogD( TAG, logInfo );
- }
- public static void printLogD( String tag, String logInfo ){
- if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
- Log.d( tag, logInfo );
- }
- }
- public static void printLogV( String logInfo ){
- printLogV( TAG, logInfo );
- }
- public static void printLogV( String tag, String logInfo ){
- if( Debug.DEBUG_MODE && null != tag || null != logInfo ){
- Log.v( tag, logInfo );
- }
- }
- public static void printLogWtf( String logInfo ){
- printLogWtf( TAG, logInfo );
- }
- public static void printLogWtf( String tag, String logInfo ){
- if( Debug.DEBUG_MODE && null != tag && null != logInfo ){
- Log.wtf( tag, logInfo );
- }
- }
- public static void showToast( Context context, String toastInfo ){
- if( null != context && null != toastInfo ){
- Toast.makeText( context, toastInfo, Toast.LENGTH_LONG ).show( );
- }
- }
- public static void showToast( Context context, String toastInfo, int timeLen ){
- if( null != context && null != toastInfo && ( timeLen ] 0 ) ){
- Toast.makeText( context, toastInfo, timeLen ).show( );
- }
- }
- public static void printBaseInfo( ){
- if( Debug.DEBUG_MODE ){
- StringBuffer strBuffer = new StringBuffer( );
- StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
- strBuffer.append( "; class:" ).append( stackTrace[ 1 ].getClassName( ) )
- .append( "; method:" ).append( stackTrace[ 1 ].getMethodName( ) )
- .append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) )
- .append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) );
- println( strBuffer.toString( ) );
- }
- }
- public static void printFileNameAndLinerNumber( ){
- if( Debug.DEBUG_MODE ){
- StringBuffer strBuffer = new StringBuffer( );
- StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
- strBuffer.append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) )
- .append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) );
- println( strBuffer.toString( ) );
- }
- }
- public static int printLineNumber( ){
- if( Debug.DEBUG_MODE ){
- StringBuffer strBuffer = new StringBuffer( );
- StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
- strBuffer.append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) );
- println( strBuffer.toString( ) );
- return stackTrace[ 1 ].getLineNumber( );
- }else{
- return 0;
- }
- }
- public static void printMethod( ){
- if( Debug.DEBUG_MODE ){
- StringBuffer strBuffer = new StringBuffer( );
- StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
- strBuffer.append( "; number:" ).append( stackTrace[ 1 ].getMethodName( ) );
- println( strBuffer.toString( ) );
- }
- }
- public static void printFileNameAndLinerNumber( String printInfo ){
- if( null == printInfo || !Debug.DEBUG_MODE ){
- return;
- }
- StringBuffer strBuffer = new StringBuffer( );
- StackTraceElement[ ] stackTrace = new Throwable( ).getStackTrace( );
- strBuffer.append( "; fileName:" ).append( stackTrace[ 1 ].getFileName( ) )
- .append( "; number:" ).append( stackTrace[ 1 ].getLineNumber( ) ).append( "\n" )
- .append( ( null != printInfo ) ? printInfo : "" );
- println( strBuffer.toString( ) );
- }
- public static void showStrictMode( ) {
- if (DebugUtils.Debug.DEBUG_MODE) {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());
- }
- }
- public static void d(String tag, String msg){
- if(DebugUtils.Debug.DEBUG_MODE){
- Log.d(tag, msg);
- }
- }
- public class Debug{
- public static final boolean DEBUG_MODE = true;
- }
- public static final String TAG = "Debug";
- }
目視法:
這適合于code review,但是不太靠譜,因?yàn)槿说木Ξ吘褂邢蓿袝r(shí)候你多敲一個(gè)分號(hào),縮進(jìn)不對(duì)都有可能導(dǎo)致程序出現(xiàn)問(wèn)題,但在代碼量較少時(shí)是一個(gè)高效率的方法。
自動(dòng)化測(cè)試:
Android的自動(dòng)化測(cè)試(分白盒測(cè)試和黑盒測(cè)試)工具有:monkey、Robotium、Appium、云端測(cè)試(比如testin),具體用法可參見(jiàn):
android實(shí)用測(cè)試方法之Monkey與MonkeyRunner
Robotium
Testin
Appium中文教程
排除法:
調(diào)試、打印、目視這三種方法適合于可以復(fù)現(xiàn)的問(wèn)題,對(duì)于隨機(jī)問(wèn)題(實(shí)際上不存在隨機(jī)問(wèn)題,只是問(wèn)題不那么容易復(fù)現(xiàn)而已),比如在線程、音頻播放、AnsynTask、Timer切換或者結(jié)束時(shí)剛好做了相應(yīng)地人為操作導(dǎo)致出現(xiàn)靈異現(xiàn)象。這時(shí)候可以通過(guò)排除法來(lái)排查問(wèn)題,具體的方法是首先大概定位到出現(xiàn)問(wèn)題的位置,然后將代碼一段一段地注釋,觀察程序現(xiàn)象,逐步縮小出現(xiàn)問(wèn)題的范圍。
版本管理介紹
在較大的軟件開(kāi)發(fā)過(guò)程中,可能有多個(gè)軟件工程師同時(shí)開(kāi)發(fā)一個(gè)項(xiàng)目的情況,比如有負(fù)責(zé)讀取數(shù)據(jù)、獲取網(wǎng)絡(luò)數(shù)據(jù)等API封裝的,有負(fù)責(zé)程序架構(gòu)的,有負(fù)責(zé)上層界面實(shí)現(xiàn)的,為了能夠最終編譯一個(gè)完成的程序出來(lái),需要將代碼整合,這個(gè)時(shí)候最方便的方法就是使用版本管理工具,固定時(shí)間上傳(比如每天、沒(méi)改動(dòng)一個(gè)功能等等),這樣能夠?qū)崟r(shí)保證服務(wù)器上的代碼是最完整、最新的,也可以避免由于自然災(zāi)害、電腦異常導(dǎo)致本地電腦掛掉損失掉代碼的問(wèn)題。
常見(jiàn)的版本管理工具有SVN和Git,我也使用過(guò)CVS,關(guān)于版本管理工具的介紹參見(jiàn):
版本控制
版本控制系統(tǒng)的選擇之路
git教程
git簡(jiǎn)易指南
注:對(duì)于windows用戶來(lái)說(shuō),建議使用烏龜殼系列的版本控制客戶端,使用github的朋友可以使用github for windows客戶端:
tortoisegit tortoisecvs tortoisesvn github for windows
編譯
通常我們用Eclipse或者Android Studio開(kāi)發(fā)android程序時(shí),只需要運(yùn)行程序就可以在模擬器或者機(jī)器上運(yùn)行程序了,但為了保證代碼的完整性、能夠在服務(wù)器上編譯,需要通過(guò)編譯工具將代碼編譯成apk,常見(jiàn)的編譯工具有:ant、gradle,但這兩種編譯工具都是需要通過(guò)手動(dòng)敲命令來(lái)完成編譯功能(當(dāng)然你也可以自己寫腳本來(lái)實(shí)現(xiàn)編譯自動(dòng)化),jenkins是一個(gè)持續(xù)集成的工具,通過(guò)它可以代碼克隆、編譯以及程序加密自動(dòng)化,其實(shí)它也是通過(guò)批處理來(lái)實(shí)現(xiàn)的,ant、gradle和jenkins的具體用法自行谷歌,使用起來(lái)很簡(jiǎn)單,目前android studio和github上很多功能都是通過(guò)gradle來(lái)編譯的。
專業(yè)術(shù)語(yǔ)介紹
以下解釋完全是本人的理解,詳細(xì)解釋可自行谷歌。
版本迭代:按照需求優(yōu)先級(jí),在保證基本功能OK后持續(xù)開(kāi)發(fā)和升級(jí),這樣能夠降低軟件開(kāi)發(fā)的風(fēng)險(xiǎn),并且能夠及時(shí)解決用戶反饋的問(wèn)題,船小好掉頭嘛;
敏捷開(kāi)發(fā):小步快跑,大概意思就是不要過(guò)于注重文檔,要注重當(dāng)面交流,能夠在實(shí)現(xiàn)時(shí)高保真的還原用戶的需求場(chǎng)景,并且能夠快速地解決用戶的需求。
單元測(cè)試:白盒測(cè)試的一種,對(duì)核心方法通過(guò)寫程序來(lái)測(cè)試自己的程序,單元測(cè)試的目的是讓你有意識(shí)地降低程序間的耦合,保證每一個(gè)方法都是最小單元,但這對(duì)于測(cè)試程序邏輯是沒(méi)有幫助,這是我自己的理解。。。
灰度發(fā)布:先找一部分用戶來(lái)使用即將發(fā)布的程序(這部分用戶可以是隨機(jī)抽取、制定年齡段、指定地區(qū)或者通過(guò)某種方式知道他是活躍用戶),在測(cè)試的過(guò)程中給與用戶一點(diǎn)好處讓用戶寫用戶體驗(yàn)報(bào)告、反饋問(wèn)題等方式來(lái)發(fā)現(xiàn)程序存在的問(wèn)題和缺陷;
DA統(tǒng)計(jì):也叫后臺(tái)統(tǒng)計(jì),通過(guò)在程序中埋點(diǎn)的方式,在有網(wǎng)絡(luò)的情況下將用戶的操作行為和數(shù)據(jù)上傳到后臺(tái),將每個(gè)用戶的信息都上傳回來(lái)就叫大數(shù)據(jù),通過(guò)建模對(duì)這些數(shù)據(jù)分析就叫大數(shù)據(jù)分析。
開(kāi)放平臺(tái):比如分享到QQ空間、分享到微信、訊飛語(yǔ)音、友盟的后臺(tái)統(tǒng)計(jì)、天氣、地圖等等都叫做開(kāi)放平臺(tái),它提供了一些開(kāi)放的接口給開(kāi)發(fā)者,方便開(kāi)發(fā)者使用它的服務(wù),開(kāi)放平臺(tái)多數(shù)服務(wù)都是免費(fèi)的,但有時(shí)候也可能不穩(wěn)定,比如用的人少它自然就活不下去了,然后就沒(méi)有然后了。
同行評(píng)審:你的同行和你一起看看你的代碼,發(fā)現(xiàn)是否有問(wèn)題;
結(jié)對(duì)編程:在寫代碼的過(guò)程中,有個(gè)人坐在你旁邊或者你坐在別人旁邊,編寫邊討論,降低程序出現(xiàn)邏輯和低級(jí)錯(cuò)誤的概率。
Android開(kāi)發(fā)資源
參見(jiàn)我的另一篇文章:Android開(kāi)發(fā)者網(wǎng)址導(dǎo)航
建議
盡量閱讀官方文檔,這才是原汁原味、不失真的開(kāi)發(fā)指導(dǎo);
即使你認(rèn)為設(shè)計(jì)程序是浪費(fèi)時(shí)間,你只是喜歡寫程序,至少你也得用思維導(dǎo)圖理清思路,思維導(dǎo)圖對(duì)于幫助你理解設(shè)計(jì)文檔、理清思路有很大的幫助;
不要用Intent傳遞大量的數(shù)據(jù),這有可能導(dǎo)致ANR或者報(bào)異常;
在退出頁(yè)面后,系統(tǒng)不一定會(huì)及時(shí)執(zhí)行onDestory方法,如果你在onDestory方法里做關(guān)閉文件、釋放內(nèi)存的操作可能出現(xiàn)退出程序又立即進(jìn)入時(shí),由于需要重新初始化這些信息導(dǎo)致代碼重入的異常;
在改動(dòng)JNI后,運(yùn)行程序之前記得卸載掉已經(jīng)安裝在模擬器或者真機(jī)上的該程序,如果直接運(yùn)行,android不會(huì)load最新編譯的so,也就不能立即看到修改后的效果;
代碼至少每天備份一次,或者是完善一個(gè)功能就備份一次,不要堆積之后一次性備份,因?yàn)樵谀愕拇a出問(wèn)題需要回溯代碼時(shí)你需要從服務(wù)器上重新取代碼,同時(shí)也可以避免代碼不是最新導(dǎo)致最后和其他人合并時(shí)不知道改了哪些地方;
將打印信息封裝成一個(gè)方法,用一個(gè)標(biāo)志位控制這個(gè)這個(gè)方法的方法體是否需要執(zhí)行,這樣在由debug版釋放到release版本時(shí),不需要傻傻地一行一行地去掉代碼,你只需要改變標(biāo)志位的值就可以了;
對(duì)于有返回值的JNI函數(shù),即使你不返回任何值,用NDK編譯JNI的時(shí)候也不會(huì)報(bào)錯(cuò),所以在寫JNI代碼的時(shí)候,一定要仔細(xì)檢查代碼;
JNI頻繁讀寫文件操作會(huì)影響程序的運(yùn)行性能,可以考慮一次性在內(nèi)存中申請(qǐng)一塊大內(nèi)存作為緩存空間,用這種空間換時(shí)間的方式可以大大提高程序的運(yùn)行效率;
不要指望類的finalize方法去處理需要回收和銷毀的工作,因?yàn)閒inalize是系統(tǒng)回調(diào)的方法,調(diào)用時(shí)機(jī)不可預(yù)見(jiàn),切記;
使用文件流、Cursor時(shí),使用結(jié)束后記得一定要關(guān)閉,否則可能導(dǎo)致內(nèi)存泄漏,嚴(yán)重的情況可能引發(fā)程序崩潰;
優(yōu)先使用Google搜索引擎(少用百度),如果不能正常使用Google搜索引擎建議通過(guò)代理、VPN、修改hosts文件等方式搭建梯子。這里提供一個(gè)免費(fèi)的谷歌搜索引擎
對(duì)于不需要使用硬件加速的activity(沒(méi)有動(dòng)畫效果、視頻播放以及各種多媒體文件的操作都可以關(guān)掉硬件加速),在AndroidManifest.xml文件中通過(guò)“android:hardwareAccelerated="false"”關(guān)掉硬件加速可節(jié)省應(yīng)用內(nèi)存;
對(duì)于需要橫豎屏轉(zhuǎn)換的應(yīng)用,又不想在橫豎屏切換的時(shí)候重新跑onCreate方法,可以在AndroidManifest.xml文件中對(duì)應(yīng)的Activity標(biāo)簽下調(diào)用“android:configChanges="screenSize|orientation"”;
為了減輕應(yīng)用程序主進(jìn)程的內(nèi)存壓力,對(duì)于耗內(nèi)存比較多的界面(比如視頻播放界面、flash播放界面等),可以在AndroidManifest.xml文件中對(duì)應(yīng)的Activity標(biāo)簽下調(diào)用“android:process=".processname"”單開(kāi)一個(gè)進(jìn)程,但在退出這個(gè)界面的時(shí)候一定要在該界面的onDestory方法中調(diào)用System的kill方法來(lái)殺掉該進(jìn)程;
在res/values/arrays.xml文件中定義的單個(gè)數(shù)組的元素個(gè)數(shù)不宜過(guò)大,過(guò)大會(huì)導(dǎo)致加載數(shù)據(jù)時(shí)非常慢,有時(shí)候你需要使用數(shù)組資源時(shí)數(shù)據(jù)有可能還沒(méi)加載完成;
一個(gè)Activity中最耗費(fèi)內(nèi)存的是activity的背景(多數(shù)情況如此,特別是對(duì)于分辨率很大的機(jī)器,一個(gè)界面的背景算下來(lái)都需要好幾兆內(nèi)存),所以在程序界面較多時(shí),可以考慮將圖片轉(zhuǎn)換成靜態(tài)的drawable,然后多個(gè)activity共用這一張背景圖;
可以通過(guò)為application、activity自定義主題的方式來(lái)關(guān)掉多點(diǎn)觸摸功能,只需要在自定義的主題下添加這兩個(gè)標(biāo)簽:
- [item name="android:windowEnableSplitTouch"]false[/item]
- [item name="android:splitMotionEvents"]false[/item]
很多游戲進(jìn)入時(shí),播放的片頭動(dòng)畫多數(shù)是一個(gè)視頻文件;
Android單個(gè)dex文件的方法數(shù)不能超過(guò)65536個(gè),android使用多個(gè)dex能否避開(kāi)65536方法數(shù)限制?
使用模擬器genymotion代替android自帶模擬器(它需要虛擬機(jī)vituralbox的支持,不過(guò)官網(wǎng)已經(jīng)提供了一個(gè)集成虛擬機(jī)的安裝包了,直接下載下來(lái)安裝即可),可以大大提高使用模擬器的體驗(yàn)(流暢、快),它也可以以插件的形式集成在Eclipse中,這是視頻教程
給Application或者activity設(shè)置自定義主題時(shí),最好不要設(shè)置為全透明,否則在activity按Home鍵回退到桌面的時(shí)候效果很渣;
如果你需要取消toast顯示的功能,在一個(gè)類中你只需要實(shí)例化該類一次(也就是說(shuō)將Toast定義成一個(gè)全局的成員變量),這樣你就可以調(diào)用mToast.cancel()了,我把它寫成了一個(gè)靜態(tài)類:
- public class ToastUtils {
- private ToastUtils( ){
- }
- public static void showToast( Context context, String toast ){
- if( null == mToast ){
- mToast = Toast.makeText( context, toast, Toast.LENGTH_LONG );
- }else{
- mToast.setText( toast );
- }
- mToast.show( );
- }
- public static void cancel( ){
- if( null != mToast ){
- mToast.cancel( );
- }
- }
- public static Toast mToast = null;
- }
你可以定義一個(gè)靜態(tài)類來(lái)實(shí)現(xiàn)防止按鈕被重復(fù)點(diǎn)擊導(dǎo)致重復(fù)執(zhí)行一段代碼的問(wèn)題:
- /**
- * 按鈕重復(fù)點(diǎn)擊
- *
- * */
- public class BtnClickUtils {
- private BtnClickUtils( ){
- }
- public static boolean isFastDoubleClick() {
- long time = System.currentTimeMillis();
- long timeD = time - mLastClickTime;
- if ( 0 [ timeD && timeD [ 1000) {
- return true;
- }
- mLastClickTime = time;
- return false;
- }
- private static long mLastClickTime = 0;
- }
放在apk的assets或者raw目錄下的數(shù)據(jù)文件最好做加密處理,在需要使用的時(shí)候才解密,這樣可以避免在apk被他人破解時(shí)數(shù)據(jù)也被破解的問(wèn)題;
最好不要再activity的onCreate方法里面調(diào)用popupwindow的show方法,有可能由于activity沒(méi)有完全初始化導(dǎo)致程序異常(android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid),如果非要在一進(jìn)activity就顯示popupwindow,建議用handler.post、View.postDelay來(lái)處理;
對(duì)于自定義View,在構(gòu)造方法里面是獲取不到視圖的寬高的(此時(shí)獲取長(zhǎng)寬都為0),需要在onMeasure方法中或者跑了onMeasure方法后才能夠獲取到視圖的寬高,不過(guò)你可以通過(guò)在構(gòu)造方法里面強(qiáng)制測(cè)量視圖的寬高來(lái)實(shí)現(xiàn)在構(gòu)造方法里獲取視圖的寬高信息,具體見(jiàn)MeasureSpec介紹及使用詳解
如果你覺(jué)得在安裝Eclipse后還需要配置android開(kāi)發(fā)環(huán)境很麻煩,你可以直接使用ADT Bundle,它是一個(gè)懶人套餐,下載下來(lái)就可以用了,可以在這里下載。
有時(shí)間看看阿里技術(shù)嘉年華、InfoQ演講與訪談、Google IO視頻,可以學(xué)習(xí)到一些解決問(wèn)題、做大項(xiàng)目的經(jīng)驗(yàn)。
當(dāng)應(yīng)用中動(dòng)畫比較多,并且動(dòng)畫都是通過(guò)圖片來(lái)切換的時(shí)候,可以考慮借用Cocos的精靈表單思想,這樣就可以避免圖片命名的煩惱。
工具推薦
代碼對(duì)比:Beyond compare
屏幕取色:ColorPix
梯子:紅杏
思維導(dǎo)圖: mindmanager
在線工具:在線工具