環境架設:
- 安裝eclipse.
- 選擇help-->install new software 安裝android sdk.
- 在work-with欄位填上 http://dl-ssl.google.com/android/eclipse/site.xml
- 必須安裝的套件是Developer Tools-->Android DDMS , Developer Tools-->Android Developer Tools。
- Window-->android adk manager, 安裝Google Api。版本除了新增api之外,基本api的使用感覺差異不大,建議裝最新的。 有相容性問題的話就使用之前習慣用的版本。
使用習慣:
- 在左端的project欄位右擊,選擇import-->android-->Existing Android Code Into Workspace來匯入project
- 建議關掉auto build (在project的menutab選擇),全部手動。因為有時auto build不會更新到code...
- Android lint 在minSdkVersion設定為比較低的版本,code裡面有用到較高版本才提供的api的時候,會出現「call requires api level X」的問題。 直接點X的地方使用quick fix加上suppress NewAPI的方式解決。
- Android lint(語法檢查功能)的相關問題,有時更動過之後會發現其他的相同問題不會消失。(最常發生在更改resource的string.xml的時候。)請記得手動build一次,才會被更新。
- 程式碼遇到中文字出現亂碼問題: eclipse preferences-->general-->workspace-->text file encoding, 改為utf-8。
- 你的app有用到google map / gcm 等等Google API的話: project's 右擊-->properties-->android-->build target, 把 "Google APIs"打勾
- 設定 eclipse-->preferences-->run/debug-->launching-->launch operation--> always launch previously application,這樣即使藍色游標還停在xml上面的時候按下debug,也不會導致系統叫出非android的debugger,導致.orig檔的產生。
- 若是你有匯入其他project,如何避免打開.java的時候成為無法編輯的.class file:
左擊你的project, 選擇Properties
選擇Java Build Path頁面
選擇Java projects tab
加上你要導入的project
選擇 Order and Export tab
把你導入的 library project 使用右邊的move按鈕上移到列表的最上方. - 用不到的Resource圖檔要拿掉。因為建apk的時候會全部進去...
- 出現大量import錯誤的時候,可以在Project explorer裡面該project的src目錄上面點擊滑鼠右鍵-->source-->organize imports,就會自動幫你修正該project裡面所有的java檔案的imports!
- 若是你發現可啓動的AVD裡面沒有你之前加過的版本的機器,應該是你的的PC的usb線有連著實機。
app簽名:
- keytool -genkey -alias mysign.keystore -keyalg RSA -validity 36500 -keystore mysign.keystore (需注意,google要求該簽名有效期至少20年才能傳上google play。)
- 要得到google play可上傳的apk,請使用project-->export的方法加上前一步驟提到的sign。
- 不小心把debug時的apk傳上去,該package name只能放棄。(除非你一直使用該debug sign下去... 我不知道debug sign什麼時候會消失...)
上傳新版到google play:
- package name要一致(通常不會有人改,因為你的code的擺放目錄也會跟著大改)
- androidmanifest.xml 的version number要+1,version name也要變化。
- 所使用在apk上的簽名要跟之前用的一樣
一些規則:
- debug在上傳apk時遇到 INSTALL_FAILED_CONFLICTING_PROVIDER --- 請修改 androidmanifest.xml裡面的 android:authorities. 有用到content provider的時候要改的。必須每個package不同,所以建議使用package name。這樣要replace字串也方便。
- Android Developer Tools版本14以上的resource id已經不是constant, 若是你的library有用到android的resource id, 請把之前用switch case的寫法全部換成if-else。 可以在游標放在switch上面,按ctrl-1讓eclipse幫你改。但是太複雜的switch-case (像是用return的 case,或是沒break的case...) eclipse會無法處理。
AndroidManifest.xml相關:
-
<uses-sdk android:minSdkVersion="10" />此參數必需設定。之前有遇到用較高版本的api,會比較容易出現out of memory error的狀況。 可以看需要決定要用跟build設定的target api版本相同或是較舊的。
<uses-permission> 跟 <uses-feature> 請放在<application>前面。 - <action android:name="android.intent.action.MAIN" /> 有幾組,app列表就會看到幾個App Icon
- 沒寫在Android manifest.xml裡面的Acticity,手動create之後系統會給你exception... 這意味著無法在執行期間新建Activity。
- 支援所有螢幕大小(放在<application>前面):
<supports-screens android:xlargeScreens="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>
- Sony系列手機的os, app顯示在"應用程式"區的名字,強制抓string resource裡面的「app_name」標籤。所以定義為其他字的話,桌面上的app的名稱將會顯示空白...
<string name="app_name">Appname</string>
Layout file相關:
- RelativeLayout,若是該view的定位參數有參考到其他view,此view需要移動順序到參考view的下方(把xml定義碼移到參考view的下方)。要不然會complie error。
- view的id可以重複,但是記住一個規則:同一層layout以下的id儘量不要重複。否則findviewbyid會找不到第一個重名之後的.當然你也可以一層一層往下挖啦。
- FrameLayout下面的child view 使用layout_gravity參數會有效果。
- <android.support.v13.app.FragmentTabHost>
裡面要包兩個tag:<TabWidget>,content用的Layout。 - include其他layout檔的時候,被include的root layout有指定id的話,以該id為準。沒指定的時候才會使用<include> tag裡面有記載的id。不過在同一個layout檔裡面若是使用relativelayout等等指定物件的相對位置的排版方式,在指定相對位置的時候,卻不會吃到被include的layout檔的layout id。因此還是得指定一個id。但是...就是這個但是。在code裡面只吃<include> tag裡面有記載的id...
widget相關:
- listview的cell建議加上 adjust view bounds這個屬性,會幫你自動調整view。
- 有重要訊息會在畫面下方的話,儘量使用Relative view 定義該widget 的Align parent bottom。 要不然面對各種不同的解析度的機子,你會不知道該怎麼測...
- 不同語系的訊息處理:Resource裡面的strings.xml要定義strings_zh.xml等等
針對不同語言的訊息。然後程式碼內需要顯示文字的地方全部用context.getResources().getString(R.string.xxxxx)的方式取。 - 發現按back鍵不會回到上一層的時候(我在使用fragment的時候遇到),在setcontentview()之後加上下列動作:view.setFocusableInTouchMode(true);view.requestFocus();
- 令人頭痛的TextView換行碼的問題:建議使用下面的方法替換你的字串:
str = str.replace("\r\n", "\n");str = str.replace("\r", "\n");
str = str.replace("\n", System.getProperty("line.separator")); - scrollview裡面掛的view請勿設定margin, 會出現切邊的問題。
- scrollview想強制指定scroll到某個元件的區域,可使用scrollView.smoothScrollTo(xPos, yPos)。但是,取元件的xPos跟yPos的時候,要取scrollView第一層的layout的下一層才會有值。再往下一層的話就沒了...(因為android的xPos, yPos是相對座標)
- 使用程式碼撰寫layout,各個widget的layoutparams的宣告,請宣告為上一層的widget所使用的格式。例:上一層是LinearLayout, 就宣告為LinearLayout.LayoutParams,上一層是ListView, 就宣告為AbsListView.LayoutParams.
- 文字加上刪除線的畫法:TextView.setPaintFlags(TextView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
- ListView若是有加Header或是Footer, setAdapter()之後再getAdapter(),回來的會是「HeaderViewListAdapter」。若是有取回判斷的設計的話請注意。原本的adapter要再用.getWrappedAdapter()才能取得。切記,position參數會加上header跟footer的數量。
ExpandableListView的話,直接用getExpandableListAdapter()即可。
lib/build path相關:
- 如何讓想含入的project變成lib: 修改project.properties, 加上
android.library=true (直接在project properties->android-> Is Library上面打勾也可) - java build path的order and export裡面的所有可以打勾的項目都要打勾。不打勾的話可能會在某些static initial的時候出現NoclassDefFoundError.
- NoclassDefFoundError還有另一個可能,就是主project的build path裡面沒有新增該class有用到的jar並export。只加在lib project裡面,有時候會怪怪的。
- library jar的增減都需要clean build才會生效。
- order and export的順序是:jar檔最前面,libproject第二,<project>/src跟<project>/gen為最後。
Resource相關:
- 檔案名一定要小寫。
- 多語言的目錄名稱:values / values-zh-rCN / values-zh-rTW
- 多Layout的目錄名稱:layout / layout-land / layout-ldpi
- 多解析度的目錄名稱:drawable / drawable-hdpi / drawable-ldpi
- strings可以有不同檔案。 檔案格式為strings_xxx.xml。
- layout component 的id: 要使用系統內建的activity,像是ListActivity, 需要綁定的固定id格式為: @+android:id/list ("id/"之後的字可以看exception的提示), tabhost的固定id為: "@+android:id/tabhost"
程式碼取用(findViewById之類的動作)的時候的寫法:android.R.id.list / android.R.id.tabhost - 一般id的格式為 +id/xxxxx
context相關:
- dialog是一個常常引發badtokenexception的兇手。但是又不允許使用getApplicationContext()。比較簡單的解決方法:在打開dialog之前使用if (!isFinishing())判斷Activity是不是關了。
- 若是你用了雙層Tab, 取context是為了要顯示dialog的時候,要取當下context的parent. 也就是要用this.getParent()來取。就可以避開bad token exception.
- 其實在Activity的框架下,Context是它的subclass. 建議在Activity裡面若是你設計的inner class會需要傳context進去的話,改傳Activity進去.
- broadcastreceiver的onReceiveBroadcast()裡面不可以再call registerBroadcastReceiver(),會造成無窮迴圈。
sql相關:
- like指令要使用select args(問號對應string陣列的方法)處理,需要把萬用字元%跟_加在select args裡面,硬寫在selection裡面的話會失效。
- DISTINCT指令只能用db的rawquery處理.
- 數字的判斷若是不用問號對應string陣列的方法列舉硬寫的話,必須加上雙引號。例:"_id = \"100\" "
proguard相關:
- 直接強制remove Log.x() --- -assumenosideeffects class android.util.Log { *; }
使用此法的前提是一定要使用-optimizations參數。建議照一般的建議,在project.properties裡面使用#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
此法無法remove System.println()。 - 有使用reflect法取得資料結構的話,必須keep該class。proguard報error的時候,也要keep出問題的class。
使用下面的兩行組來keep你必須keep的東西:-dontwarn com.google.android.gms.**-keep class com.google.android.gms.** { *;}
像是apache這個proguard打不散,必須keep的東西...-dontwarn org.apache.**-keep class org.apache.** { *;}
- 比較常用的一些指令:
-libraryjars libs/android-support-v13.jar
memory leak問題:
- activity裡面別用static variable keep住context。非用static context不可的話請直接拿application的。
- listview 的cell裡面有用到ImageView的話,要註冊RecyclerListener去處理bitmap的recycle問題或是把imageview跟bitmap之間的連結移除。
- ViewPager只要記得使用的pageradapter的destroyItem()的Override要處理bitmap的recycle問題即可。
- 切記,不要用view的background顯示大圖檔。
- Samsung的Note跟S3在allocate bitmap的時候吃記憶體很重,還會把resource的allocate算在app身上,導致一個app可用的64M記憶體輕鬆被用光。須針對這兩款機型特別做記憶體的相關測試。
Drawable相關:
- bitmap建議全部改new成bitmapdrawable處理。
- LayerDrawable在2.3.5版os上面常常會畫錯。我的建議是自己疊。
Bitmap bitmap = Bitmap.createBitmap( width,height,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
overlay.draw(canvas);
bd.draw(canvas);
畫完之後就可以把舊的bitmap recycle了。 selector的按壓變色若是失效,在normal state的item後面加上「 android:state_focused="false" android:state_pressed="false" android:state_selected="false" android:state_checked="false"」這幾個條件。
Javascript Interface相關:
- android 4.2之後,系統因為安全性的問題禁止了javascriptinterface裡面沒有annotation宣告的method的使用。webview.
addJavascriptInterface()這個動作有用到的javascript interface class,裡面的method需要加上@JavascriptInterface 這個annotation。否則執行時會給出找不到method的exception.
ActivityResult運作流程:
- 起始: startActivityForResult(intent, requestCode);
- 下一層的回傳:
Intent returnIntent = new Intent();returnIntent.putExtra("result",result); //想傳啥都可以setResult(RESULT_OK, returnIntent);finish(); - 回傳之後的處理:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
} - 注意點:Activity本身無法判別被指定的requestCode。有點可惜無法好好利用這個參數讓activity對於不同的requestCode有不同的動作。
Acticity lifecycle:
- 這張圖應該都有看過。
- 不過兩個acticity之間的互動的流程就比較少人提了。
定義原本的activity為A,新喚起的Acticity為B。B被心喚起之後,整個Acticity的遷移動作流程:
A.onPause()->B.onCreate()->B.onStart()->B.onResume()->A.onStop()。
Virtual device emulator相關:
- CPU使用intel,用eclipse在avd上面跑debug,卻發現Eclipse的console出現錯誤:
- Installation error: INSTALL_FAILED_MISSING_SHARED_LIBRARY原因通常是intel的android system image裡面沒有google map的package。
下面是解決方法:
- 取得GApps https://goo.im/gapps/
- 從GPapps的package裡面取得以下的兩個檔案:
/system/framework/com.google.android.maps.jar
/system/framework/com.google.android.maps.xml - 進入 [android-sdk目錄]/sdk/platform-tools
- 啟動在avd manager裡面已經建好的cpu使用intel的avd。沒有的話請自己建一個。
emulator -avd [avd manager裡面建好的avd的名字] -partition-size 512
(使用partition size參數的原因是不加參數的話,新增的lib會push進不去。) - adb remount
- 把步驟2的檔案push進intel的system image裡面。
adb push [存放檔案ㄌㄨ]/com.google.android.maps.xml /system/etc/permissions
adb push [存放檔案路徑]/com.google.android.maps.jar /system/framework - adb reboot
gradle相關:
遇到難解的編譯錯誤的時候,請記得:
- make跟rebuild編譯成功,並不代表debug / release build會成功。請切記要確認debug build確定會過。
- 砍掉build目錄,clean build有時不一定能夠完全清乾淨。
- 執行Android Studio的 MenuBar->File->Invalidate Carches/ Restart,會把整個project的關聯砍掉重新整理過。
- buildtools版本建議更新。
- 遇到import找不到的時候,先把其他確定有錯的import先處理乾淨,也許就會恢復正常。
- 多試幾次... 有時候就會好了。Orz
- 若是有getMethod()等等reflect的使用需求的話,可能會需要跳過型態檢查。
請在project根目錄的build.gradle裡面加上:
allprojects {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
}
} - 執行時遇到com.android.dex.DexIndexOverflowException:在主project的build.gradle的defaultConfig裡面加上「multiDexEnabled true」。
- 程式碼裡面有用到if (true)或是if (false)做switch的話,請改成if (Boolean.TRUE) / if(Boolean.FALSE)。剛遇到一個非常難解的gradle bug是這種寫法造成的...
Java語法相關:
- String.contains(string),若是參數給的是空字串,contains測試永遠為真。
SimpleDateFormat的parse功能,對於時區的處理:
- 以英文顯示的(CST / HST等等),要先設定locale為us才能讀取。
- android7.0未滿的版本,只支援java 6。ISO 8601的UTC時區「Z」,以SimpleDateFormat格式的「yyyy-MM-dd'T'HH:mm:ss.SSSZ」去parse的話會被忽略,認定為local timezone。必須手動取代為「+0000」。
- TimeZone.getTimeZone(String),遇到「+0000」的時區無法處理。需要在前面加上「GMT」或是「UTC」
0 件のコメント:
コメントを投稿