google爲了讓廣大Android開發者可以高效地建立優質的app,專門針對開發者提供了Training板塊,這個板塊的學習資料是最一手的,來自google android工程師之手的。這個資料是每個Android開發者都應該學習的手冊,而且它是不斷更新的。連接:
https://developer.android.com/training/index.html
中文版:
http://hukai.me/android-training-course-in-chinese/index.html
html
版本適配android
Tip:爲了能在幾個Android版本中都能提供最好的特性和功能,你應該在你的app中使用Android Support Library,它能使你的app能在舊平臺上使用最近的幾個平臺的APIs。程序員
適配不一樣的系統版本倆種方法:
- 指定最小和目標API級別,具體來講,<uses-sdk> 元素中的 minSdkVersion和 targetSdkVersion屬性,標明在設計和測試app時,最低兼容API的級別和最高適用的API級別(這個最高的級別是須要經過你的測試的)。隨着新版本Android的發佈,一些風格和行爲可能會改變,爲了能使你的app能利用這些變化,並且能適配不一樣風格的用戶的設備,你應該設置 targetSdkVersion 的值去匹配最新的可用Android版本。
- 在運行時檢查系統版本。
@SuppressLint("NewApi")
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
// 若是運行的環境 (部署到什麼版本的手機 )大於3.0
if (android.os.Build.VERSION.SDK_INT > 11) {
SearchView searchView = (SearchView) menu.findItem(
R.id.action_search).getActionView();
searchView.setOnQueryTextListener(this);// 搜索的監聽
}
return true;
}
Note:當解析XML資源時,Android會忽略當前設備不支持的XML屬性。因此你能夠安全地使用較新版本的XML屬性
而不須要擔憂舊版本Android遇到這些代碼時會崩潰。
國際化
爲了支持多國語言,在
res/ 中建立一個額外的 values 目錄以連字符和ISO國家代碼結尾命名,好比 values-es/是爲語言代碼爲"es"的區域設置的簡單的資源文件的目錄。Android會在運行時根據設備的區域設置,加載相應的資源。詳見Providing
app/build.gradle
Android
Studio使用Gradle 編譯運行Android工程. 工程的每一個模塊以及整個工程都有一個build.gradle文件。一般你只須要關
注模塊的build.gradle文件,該文件存放編譯依賴設置,包括defaultConfig設置:
- compiledSdkVersion 是你的應用將要編譯的目標Android版本,此處默認爲你的SDK已安裝的最新Android版本(目前應該是4.1或更高版本,若是你沒有安裝一個可用Android版本,就要先用SDKManager來完成安裝),你仍然可使用較老的版本編譯項目,但把該值設爲最新版本,使你可使用Android的最新特性,同時能夠在最新的設備上優化應用來提升用戶體驗。
- targetSdkVersion 表示你測試過你的應用支持的最高Android版本(一樣用API level)表示).當Android發佈最新版本後,你應該在最新版本的Android測試你的應用同時更新target sdk到Android最新版本,以便充分利用Android新版本的特性。
佈局
- 可選的佈局文件:在XML中定義界面佈局而不是在運行時去動態生成佈局是有多個緣由的,其中最重要的一個緣由是這樣可使得你爲不一樣大小的屏幕建立不一樣的佈局文件。例如,你能夠建立建立2個版本的佈局文件,告訴系統在小的屏幕上使用其中一個佈局文件,在大的屏幕上使用另一個佈局文件。
- 於字符串 @string/edit_message,該字符串資源與id使用了相同的名稱(edit_message)。然而,對於資源的引用是區分類型的(好比id和字符串),所以,使用相同的名稱不會引發衝突
- 當你在用戶界面定義一個文本的時候,你應該把每個文本字符串列入資源文件。對於全部字符串值,字符串資源可以單獨的修改,在資源文件裏你能夠很容易的找到而且作出相應的修改。經過選擇定義每一個字符串,還容許您對不一樣語言本地化應用程序。
- android:layout_weight,「兩份伏特加酒,一份咖啡利口酒」,意思就是這個酒中伏特加酒佔三分之二。(請注意,使用權重的前提通常是給View的寬或者高的大小設置爲0dp,而後系統根據上面的權重規則來計算View應該佔據的空間。可是不少狀況下,若是給View設置了match_parent的屬性,那麼上面計算權重時則不是一般的正比,而是反比,也就是權重值大的反而佔據空間小)
使用字符資源:
- 代碼中:String hello = getResources().getString(R.string.hello_world);
- 佈局中:android:text="@string/hello_world"
屏幕適配
有4種廣泛尺寸:小(small),普通(normal),大(large),超大(xlarge)
4種廣泛分辨率:低精度(ldpi),
中精度(mdpi), 高精度(hdpi), 超高精度(xhdpi)
res/layout-large/
xhdpi:
2.0
hdpi:
1.5
mdpi:
1.0 (基準)
ldpi:
0.75
Note:低密度(ldpi)資源是非必要的,當你提供了hdpi的圖像,系統會把hdpi的圖像按比例縮小一半,去適配ldpi的屏幕。
Activity
爲讓新啓動的activity能查詢,
定義key爲一個public型的常量,一般使用應用程序包名做爲前綴來定義意圖鍵是很好的作法。在應用程序與其餘應用程序進行交互時仍能夠確保意圖鍵惟一。
public final static String EXTRA_MESSAGE ="com.mycompany.myfirstapp.MESSAGE";
Intent intent = new Intent(this, DisplayMessageActivity.class);
EditText editText = (EditText) findViewById(R.id.edit_message);
String message = editText.getText().toString();
intent.putExtra(EXTRA_MESSAGE,message);
TextView textView = new TextView(this);
textView.setTextSize(40);
textView.setText(message);
// Set the text view as the activity layout
setContentView(textView);
使你的activity有一個透明背景:<activity
android:theme="@android:style/Theme.Translucent">
不像其餘編程範式同樣:程序從
main() 方法開始啓動。Android系統根據生命週期的不一樣階段喚起對應的回調函數來執行代碼。系統存在啓動與銷燬一個activity的一套有序的回調函數。
如何實現一個符合用戶期待的app,你須要注意下面幾點:
- 當使用你的app的時候,不會由於有來電通話或者切換到其餘app而致使程序crash。
- 當用戶沒有激活某個組件的時候不要消耗寶貴的系統資源。
- 當離開你的app而且一段時間後返回,不要丟失用戶的使用進度。
- 當設備發送屏幕旋轉的時候,不會crash或者丟失用戶的使用進度。
activity生命週期
- onCreate裏面儘可能少作事情,避免程序啓動過久都看不到界面
- 一般,你不該該使用onPause()來保存用戶改變的數據 (例如填入表格中的我的信息) 到永久存儲(File或者DB你確認用戶期待那些改變可以被自動保存的時候(例如正在撰寫郵件草稿),你能夠把那些數據存到永久存儲 。應該避免在onPause()時執行CPU-intensive 的工做,例如寫數據到DB,由於它會致使切換到下一個activity變應該把那些heavy-load的工做放到onStop()去作)。若是你的activity其實是要被Stop,那麼你應該爲了切換的順暢而減小在OnPause()方法裏面的工做量。
- 請注意,系統每次調用這個方法(onResume)時,activity都處於最前臺,包括第一次建立的時候。因此,你應該實現onResume()來初始化那些你在onPause方法裏面釋放掉的組件,並執行那些activity每次進入Resumed state都須要的初始化動做 (例如開始動畫與初始化那些只有在獲取用戶焦點時才須要的組件)
- 由於系統在activity中止時會在內存中保存了Activity實例。有些時候你不須要實現onStop(),onRestart()甚至是onStart()方法.由於大多數的activity相對比較簡單,activity會本身中止與重啓,你只須要使用onPause()來中止正在運行的動做並斷開系統資源連接。
從新建立Activity
- 一般來講,跳轉到其餘的activity或者是點擊Home都會致使當前的activity執行onSaveInstanceState,由於這種狀況下的activity都是有可能會被destory而且是須要保存狀態以便後續恢復使用的,而從跳轉的activity點擊back回到前一個activity,那麼跳轉前的activity是執行退棧的操做,因此這種狀況下是不會執行onSaveInstanceState的,由於這個activity不可能存在須要重建的操做。
- 當系統開始中止你的Activity時,只有在Activity實例會須要從新建立的狀況下才會調用到onSaveInstanceState()(1) ,在這個方法裏面能夠指定額外的狀態數據到Bunde中。若是這個Activity被destroyed而後這個實例又須要被從新建立時,系統會傳遞在 (1) 中的狀態數據到 onCreate()與 onRestoreInstanceState().
- onRestoreInstanceState()方法會在 onStart() 方法以後執行. 系統僅僅會在存在須要恢復的狀態信息時纔會調用 onRestoreInstanceState() ,所以你不須要檢查 Bundle 是否爲null。
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
super.onSaveInstanceState(savedInstanceState);
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
創建隱式的Intent
儘管Android系統會確保每個肯定的intent會被系統內置的app(such as the Phone, Email, or Calendar app)之一接收,可是你仍是應該在觸發一個intent以前作驗證是否有App接受這個intent的步驟。
Caution: 若是你觸發了一個intent,並且沒有任何一個app會去接收這個intent,那麼你的app會crash。
爲了驗證是否有合適的activity會響應這個intent,須要執行queryIntentActivities() 來獲取到可以接收這個intent的全部activity的list。若是返回的List非空,那麼你才能夠安全的使用這個intent。例如:
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
若是 isIntentSafe 是 true , 那麼至少有一個app能夠響應這個intent。若是是 false 則說明沒有app能夠handle這個intent。Note:
你必須在第一次使用以前作這個檢查,如果不可行,則應該關閉這個功能。若是你知道某個確切的app可以handle這個intent,你也應該提供給用戶去下載這個app的連接。?
一個完整的例子,演示瞭如何建立一個intent來查看地圖,驗證有app能夠handle這個intent,而後啓動它。
// Build the intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
// Verify it resolves
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;
// Start an activity if it's safe
if (isIntentSafe) {
startActivity(mapIntent);
}
若是用戶但願每次都彈出選擇界面,並且每次都不肯定會選擇哪一個app啓動,例如分享功能,用戶選擇分享到哪一個app都是不肯定的,這個時候,須要強制彈出選擇的對話框。(這種狀況下用戶不能選擇默認啓動的app)。爲了顯示chooser, 須要使用createChooser()來建立Intent
Intent intent = new Intent(Intent.ACTION_SEND);
...
// Always use string resources for UI text. This says something like "Share this photo with"
String title = getResources().getText(R.string.chooser_title);
// Create and start the chooser
Intent chooser = Intent.createChooser(intent, title);
startActivity(chooser);
這樣就列出了能夠響應 createChooser() 中Intent的app,而且指定了標題。數據庫
被別的應用啓動
若是你的app的功能對別的app也有用,那麼你的app應該作好響應的準備。例如,若是你建立了一個social app,它能夠分享messages 或者 photos 給好友,那麼最好你的app可以接收 ACTION_SEND 的intent,這樣當用戶在其餘app觸發分享功能的時候,你的app可以出如今待選對話框。
爲了使得其餘的app可以啓動你的activity,你須要在你的manifest文件的 <activity> 標籤下添加 <intent-filter> 的屬性。
例如,這個有intent filter的activity,當數據類型爲文本或圖像時會處理 ACTION_SEND 的intent。
<activity android:name="ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Get the intent that started this activity
Intent intent = getIntent();
Uri data = intent.getData();
// Figure out what to do based on the intent type
if (intent.getType().indexOf("image/") != -1) {
// Handle intents with image data ...
} else if (intent.getType().equals("text/plain")) {
// Handle intents with text ...
}
}
詳情看:Intent過濾(92P)
Fragment
- 你能夠把fragment想象成activity中一個模塊化的部分,它擁有本身的生命週期,接收本身的輸入事件,能夠在acvitity運行過程當中添加或者移除(有點像"子activity",你能夠在不一樣的activities裏面重複使用)可使用包含action bar的 v7 appcompat library。v7 appcompat library 兼容Android2.1(API level 7),也包含了Fragment APIs。
- 和activity其中一個區別是當你建立Fragment的時候,你必須重寫onCreateView()回調方法來定義你的佈局。事實上,這是使Fragment運行起來,惟一一個須要你重寫的回調方法
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="@+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
- 當你用XML佈局文件的方式將Fragment添加進activity時,你的Fragment是不能被動態移除的
- 運用fragment(特別是那些你在運行時添加的)的一個很重要的規則就是在佈局中你必須有一個容器view,fragment的layout將會放在這個view裏面。
- 用addToBackStack(),當你移除或者替換一個fragment並把它放入返回棧中時,被移除的fragment的生命週期是stopped(不是destoryed).當用戶返回從新恢復這個fragment,它的生命週期是restarts。若是你沒把fragment放入返回棧中,那麼當他被移除或者替換時,它的生命週期是destoryed。
- addToBackStack()方法提供了一個可選的String參數爲事務指定了一個惟一的名字。這個名字不是必須的,除非你打算用FragmentManager.BackStackEntry APIs來進行一些高級的fragments操做。
- 常常地,你想fragment之間能相互交互,好比基於用戶事件改變fragment的內容。全部fragment之間的交互須要經過他們關聯的activity,兩個fragment之間不該該直接交互。
Fragments之間的交互
定義一個接口
爲了讓fragment與activity交互,你能夠在Fragment
類中定義一個接口,而且在activity中實現這個接口。Fragment在他們生命週期的onAttach()方法中捕獲接口的實現,而後調用接口的方法來與Activity交互。
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
如今Fragment就能夠經過調用
OnHeadlineSelectedListener 接口實例的 mCallback 中的 onArticleSelected() (也能夠是其它方法)方法與activity傳遞消息。舉個例子,在fragment中的下面的方法在用戶點擊列表條目時被調用,fragment 用回調接口來傳遞事件給父Activity.
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
mCallback.onArticleSelected(position);
}
實現接口
爲了接收回調事件,宿主activity必須實如今Fragment中定義的接口。舉個例子,下面的activity實現了上面例子中的接口。
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener {
...
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Do something here to display that article
}
}
寫Shared Preference:經過相似putInt()與putString()方法來傳遞keys與values。而後執行commit() 來提交改變. (
後來有建議除非是出於線程同
步的須要,不然請使用apply()方法來替代commit(),由於後者有可能會卡到UI Thread.)
存儲在內部仍是外部
全部的Android設備都有兩個文件存儲區域:"internal" 與 "external" 存儲。 那兩個名稱來自於早先的Android系統中,當時的大多設備都內置了不可變的內存(internal storage),而後再加上一個相似SD card(external storage)這樣能夠卸載的存儲部件。後來有一些設備把"internal" 與"external" 的部分都作成不可卸載的內置存儲了,雖然如此,可是這一整塊仍是從邏輯上有被劃分爲"internal"與"external"的。只是如今再也不以是否能夠卸載來區分了。 下面列出了二者的區別:
Internal storage:
- 老是可用的
- 這裏的文件默認是隻能被你的app所訪問的。
- 當用戶卸載你的app的時候,系統會把internal裏面的相關文件都清除乾淨。
- Internal是在你想確保不被用戶與其餘app所訪問的最佳存儲區域。
External storage:
- 並不老是可用的,由於用戶有時會經過USB存儲模式掛載外部存儲器,當取下掛載的這部分後,就沒法對其進行訪問了。
- 是你們均可以訪問的,所以你可能會失去保存在這裏的文件的訪問控制權。
- 當用戶卸載你的app時,系統僅僅會刪除external根目錄(getExternalFilesDir())下的相關文件。
- External是在你不須要嚴格的訪問權限而且你但願這些文件可以被其餘app所共享或者是容許用戶經過電腦訪問時的最佳存儲區域。
Tip: 儘管app是默認被安裝到internal storage的,你仍是能夠經過在程序的manifest文件中聲明android:installLocation 屬性來指定程序也能夠被安裝到external storage。當某個程序的安裝文件很大且用戶的internal storage空間大於external storage時,他們會傾向與將該程序安裝到external storage。
你能夠執行openFileOutput() 來獲取一個 FileOutputStream 用於寫文件到internal目錄
String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;
try{
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
outputStream.write(string.getBytes());
outputStream.close();
} catch(Exception e){
e.printStackTrace();
}
若是,你須要緩存一些文件,你可使用createTempFile()。例如:下面的方法從URL中抽取了一個文件名,而後再在程序的internal緩存目錄下建立了一個以這個文件名命名的文件。編程
public File getTempFile(Context context, String url) {
File file;
try {
String fileName = Uri.parse(url).getLastPathSegment();
file = File.createTempFile(fileName, null, context.getCacheDir());
catch (IOException e) {
// Error while creating file
}
return file;
}
你使用MODE_PRIVATE ,那麼這些文件就不可能被其餘app所訪問
儘管external storage對於用戶與其餘app是可修改的,那麼你可能會保存下面兩種類型的文件。
- Public files :這些文件對與用戶與其餘app來講是public的,當用戶卸載你的app時,這些文件應該保留。例如,那些被你的app拍攝的圖片或者下載的文件。
- Private files: 這些文件應該是被你的app所擁有的,它們應該在你的app被卸載時刪除掉。儘管因爲存儲在external storage,那些文件從技術上而言能夠被用戶與其餘app所訪問,但實際上那些文件對於其餘app是沒有意義的。所以,當用戶卸載你的app時,系統會刪除你的app的private目錄。例如,那些被你的app下載的緩存文件。
若是你想要保存文件爲public形式的,請使用getExternalStoragePublicDirectory()方法來獲取一個 File 對象來表示存儲在external storage的目錄。這個方法會須要你帶有一個特定的參數來指定這些public的文件類型,以便於與其餘public文件進行分類。參數類型包括DIRECTORY_MUSIC 或者 DIRECTORY_PICTURES. 以下:
public File getAlbumStorageDir(String albumName) {
// Get the directory for the user's public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
若是你想要保存文件爲私有的方式,你能夠經過執行getExternalFilesDir() 來獲取相應的目錄,而且傳遞一個指示文件類型的參數。每個以這種方式建立的目錄都會被添加到external storage封裝你的app目錄下的參數文件夾下(以下則是albumName)。這下面的文件會在用戶卸載你的app時被系統刪除。以下示例:
public File getAlbumStorageDir(Context context, String albumName) {
// Get the directory for the app's private pictures directory.
File file = new File(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
請記住,getExternalFilesDir() 方法會建立的目錄會在app被卸載時被系統刪除。若是你的文件想在app被刪除時仍然保留,請使用getExternalStoragePublicDirectory().
無論你是使用 getExternalStoragePublicDirectory() 來存儲能夠共享的文件,仍是使用 getExternalFilesDir() 來儲存那些對與你的app來講是私有的文件,有一點很重要,那就是你要使用那些相似 DIRECTORY_PICTURES 的API的常量。那些目錄類型參數能夠確保那些文件被系統正確的對待。例如,那些以 DIRECTORY_RINGTONES 類型保存的文件就會被系統的media scanner認爲是ringtone而不是音樂。
查詢剩餘空間
若是你事先知道你想要保存的文件大小,你能夠經過執行getFreeSpace() or getTotalSpace() 來判斷是否有足夠的空間來保存文件,從而避免發生IOException。
那些方法提供了當前可用的空間還有存儲系統的總容量。然而,系統並不能保證你能夠寫入經過 getFreeSpace() 查詢到的容量文件, 若是查詢的剩餘容量比你的文件大小多幾MB,或者說文件系統使用率還不足90%,這樣則能夠繼續進行寫的操做,不然你最好不要寫進去。
Note:
你並無被強制要求在寫文件以前必定有要去檢查剩餘容量。你能夠嘗試先作寫的動做,而後經過捕獲
IOException 。這種作法僅適合於你事先並不知道你想要寫的文件的確切大小。例如,若是在把PNG圖片轉換成JPEG以前,你並不知道最終生成的圖片大小是多少。
刪除文件
你應該在不須要使用某些文件的時候,刪除它。刪除文件最直接的方法是直接執行文件的 delete() 方法。
myFile.delete();
若是文件是保存在internal storage,你能夠經過 Context 來訪問並經過執行 deleteFile() 進行刪除
myContext.deleteFile(fileName);
Note: 當用戶卸載你的app時,android系統會刪除如下文件:
- 全部保存到internal storage的文件。
- 全部使用getExternalFilesDir()方式保存在external storage的文件。
然而,一般來講,你應該手動刪除全部經過 getCacheDir() 方式建立的緩存文件,以及那些不會再用到的文件。
數據庫
就像保存文件到設備的internal storage 同樣,Android會保存db到你的程序的private的空間上。你的數據是受保護的,由於那些區域默認是私有的,不可被其餘程序所訪問。
Note:由於那些操做多是很耗時的,請確保你在background thread(AsyncTask or IntentService)裏面去執行 getWritableDatabase() 或者 getReadableDatabase() 。
和查詢信息同樣,刪除數據一樣須要提供一些刪除標準。DB的API提供了一個防止SQL注入的機制來建立查詢與刪除標準。
SQL Injection:(隨着B/S模式應用開發的發展,使用這種模式編寫應用程序的程序員也愈來愈多。可是因爲程序員的水平及經驗也良莠不齊,至關大一部分程序員在編寫代碼的時候,沒有對用戶輸入數據的合法性進行判斷,使應用程序存在安全隱患。用戶能夠提交一段數據庫查詢代碼,根據程序返回的結果,得到某些他想得知的數據,這就是所謂的SQL Injection,即
SQL注入)
這個機制把查詢語句劃分爲選項條款與選項參數兩部分。條款部分定義了查詢的列的特徵,參數部分用來測試是否符合前面的條款。由於處理的結果與一般的SQL語句不一樣,這樣能夠避免SQL注入問題。
Android圖像和動畫
高效顯示Bitmap
- 移動設備的系統資源有限。Android設備對於單個程序至少須要16MB的內存。程序應該在這個最低內存限制下最優化程序的效率。固然,大多數設備的都有更高的限制需求。
- 在過去, 一個比較流行的內存緩存的實現方法是使用軟引用(SoftReference)或弱引用(WeakReference)bitmap緩存, 然而這是不推薦的。從Android 2.3 (API Level 9) 開始,GC變得更加頻繁的去釋放soft/weak references,這使得他們就顯得效率低下。並且在Android 3.0 (API Level 11)以前,備份的bitmap是存放在native memory 中,它不是以可預知的方式被釋放,這樣可能致使程序超出它的內存限制而崩潰。
- 在上面的例子中, 有1/8的程序內存被做爲Cache. 在一個常見的設備上(hdpi),最小大概有4MB (32/8). 若是一個填滿圖片的GridView組件放置在800x480像素的手機屏幕上,大概會花費1.5MB (800x480x4 bytes), 所以緩存的容量大概能夠緩存2.5頁的圖片內容.、
磁盤緩存能夠用來保存那些已經處理好的位圖,而且在那些圖片在內存緩存中不可用時減小加載的次數。固然從磁盤讀取圖片會比從內存要慢,並且讀取操做須要在後臺線程中處理,由於磁盤讀取操做是不可預期的。
Note:若是圖片被更頻繁的訪問到,也許使用 ContentProvider 會更加的合適,好比在Gallery程序中。
內存緩存的檢查是能夠在UI線程中進行的,磁盤緩存的檢查須要在後臺線程中處理。磁盤操做永遠都不該該在UI線程中發生。當圖片處理完成後,最後的位圖須要添加到內存緩存與磁盤緩存中,方便以後的使用。
處理配置改變(Handle Configuration Changes):詳情請看P162
爲了測試上面的效果,嘗試在保留Fragment與沒有這樣作的狀況下旋轉屏幕。你會發現當你保留緩存時,從內存緩存中從新繪製幾乎沒有延遲的現象. 內存緩存中沒有的圖片可能在存在磁盤緩存中.若是兩個緩存中都沒有,則圖像會像平時同樣被處理。
Android管理bitmap memory的演變進程
- 在Android 2.2 (API level 8)以及以前, 當GC發生時, 你的應用的線程是會stopped的. 這致使了一個滯後,它會下降效率. 在Android 2.3上,添加了併發GC的機制, 這意味着在一個bitmap再也不被引用到以後,內存會被當即回收.
- 在Android 2.3.3 (API level 10)以及以前, 一個bitmap的像素級數據是存放在native內存中的. 這些數據與bitmap自己是隔離的, bitmap自己是被存放在Dalvik heap中。在native內存中的pixel數據的釋放是不可預測的,這意味着有可能致使一個程序容易超過它的內存限制並Crash。 自Android 3.0 (API Level 11)起, pixel數據則是與bitmap自己一塊兒存放在dalvik heap中。
在Android 3.0及以上版本管理內存:詳情請看P165
在Android 3.0 (API Level 11) 引進了BitmapFactory.Options.inBitmap. 若是這個值被設置了,decode方法會在加載內容的時候去重用已經存在的bitmap. 這意味着bitmap的內存是被從新利用的,這樣能夠提高性能, 而且減小了內存的分配與回收。然而,使用inBitmap有一些限制。特別是在Android 4.4 (API level 19)以前,只支持同等大小的位圖。詳情請查看inBitmap文檔.
後面的不是很懂,往後再看
添加動畫
濫用動畫或者在錯誤時機使用動畫也是有害的,例如:他們形成了延遲。這節課程告訴你如何應用經常使用動畫類型來提高易用性,在不給用戶用戶增長煩惱的前提下提高性能。
倆個view間漸變
漸變更畫(也叫消失)一般指漸漸的淡出某個 UI 組件,同時同步地淡入另外一個。在你 App 想切換內容或 view的狀況下,這種動畫頗有用。漸變簡短不易察覺,它也能提供從一個界面到下一個之間流暢的轉換。當你不使用它們,無論怎麼樣轉換常常感到生硬而倉促。
將 config_shortAnimTime 系統屬性暫存到一個成員變量裏。這個屬性爲動畫定義了一個標準的「短」持續時間。對於微妙或者快速發生的動畫,這是個很理想的時間段。config_longAnimTime 和 config_mediumAnimTime 也行,若是你想用的話。
// 獲取並緩存系統默認的「短」時長
mShortAnimationDuration = getResources().getInteger(
android.R.integer.config_shortAnimTime);
- 對於淡入的 view,設置 alpha 值爲 0 而且設置 visibility 爲 VISIBLE(要記得他起初被設置成了 GONE)。這讓iew 可見了可是它是透明的。
- 對於淡入的 view,把 alpha 值從 0 動態改變到 1。同時,對於淡出的 view,把 alpha 值從 1 動態變到 0。
- 使用 Animator.AnimatorListener 中的 onAnimationEnd(),設置淡出 view 的 visibility 爲 GONE。即便 alpha 值爲0,也要把 view 的 visibility 設置成 GONE 來防止 view 佔據佈局空間,還能把它從佈局計算中忽略,加速處理過程。
// 設置內容View爲0%的不透明度,可是狀態爲「可見」,
// 所以在動畫過程當中是一直可見的(可是爲全透明)。
mContentView.setAlpha(0f);
mContentView.setVisibility(View.VISIBLE);
// 開始動畫內容View到100%的不透明度,而後清除全部設置在View上的動畫監聽器。
mContentView.animate()
.alpha(1f)
.setDuration(mShortAnimationDuration)
.setListener(null);
// 加載View開始動畫逐漸變爲0%的不透明度,
// 動畫結束後,設置可見性爲GONE(消失)做爲一個優化步驟
//(它將再也不參與佈局的傳遞等過程)
mLoadingView.animate()
.alpha(0f)
.setDuration(mShortAnimationDuration)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLoadingView.setVisibility(View.GONE);
}
});
@Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// 若是用戶當前正在看第一步(也就是第一頁),那就要讓系統來處理返回按鈕。
//這個是結束(finish())當前活動並彈出回退棧。
super.onBackPressed();
} else {
// 不然,返回前一頁
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
viewpager滑動動畫用PageTransformer自定義動畫
Android網絡鏈接和雲服務
管理網絡
在執行網絡操做以前檢查設備當前鏈接的網絡鏈接信息是個好習慣。這樣能夠防止你的程序在無心間鏈接使用了非意向的網絡頻道。若是網絡鏈接不可用,你的應用應該優雅的作出響應。
爲了檢測網絡鏈接,咱們須要使用到下面兩個類:
- ConnectivityManager: 它會回答關於網絡鏈接狀態的查詢,並在網絡鏈接改變時通知應用程序。
- NetworkInfo: 描述一個給定網絡類型(就本節而言是移動網絡或Wi-Fi)的網絡接口的狀態。
public boolean isOnline() {
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
private static final String DEBUG_TAG = "NetworkStatusExample";
...
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
boolean isWifiConn = networkInfo.isConnected();
networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileConn = networkInfo.isConnected();
Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);