******************************************************************
**支持不同的語言:
*要使app兼容多種語言的設備,可以在res文件夾下建立不同的values子文件夾,在裏面放置你的string,color,dimensions等xml文件
不同語言的values命名格式詳見:
http://blog.csdn.net/lllkey/article/details/9350977
工程目錄結構如下:
MyProject/
res/
values/
strings.xml
values-es/
strings.xml
values-fr/
strings.xml
******************************************************************
**支持不同的屏幕:
*要使app兼容不同的屏幕,可以在res文件夾下建立不同的layout子文件夾,如layout(默認豎屏-portrait),layout-large,layout-land(橫屏佈局),layout-large-land
結構如下:
MyProject/
res/
layout/ # default (portrait)
main.xml
layout-land/ # landscape
main.xml
layout-large/ # large (portrait)
main.xml
layout-large-land/ # large landscape
main.xml
******************************************************************
**支持不同的設備:
*在使用Android Support Library時,必須檢查系統版本,代碼如下:
private void setUpActionBar() {
// Make sure we're running on Honeycomb or higher to use ActionBar APIs
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
這樣做了,不會因爲系統版本不對,而導致程序崩潰
*在AndroidManifest.xml中可以修改themes和styles
******************************************************************
**管理Activity生命週期
*在AnroidManifest.xml中申明的啓動器Activity中必須申明intent.action.MAIN和intent.category.LAUNCHER過濾,不然的話,將不會顯示桌面圖標且在程序列表中不會顯示,意味着沒有入口
*Activity的生命週期中,只有onResumed,onStopped,onPaused中有長時間的過程,稱之爲靜態,而created,started,destroyed這三個階段都是瞬間過程
繼續
在這種狀態下,Activity處於前臺,且用戶可以與其交互。(有時也稱爲“運行”狀態。)
暫停
在這種狀態下,Activity被在前臺中處於半透明狀態或者未覆蓋整個屏幕的另一個Activity—部分阻擋。 暫停的Activity不會接收用戶輸入並且無法執行任何代碼。
停止
在這種狀態下,Activity被完全隱藏並且對用戶不可見;它被視爲處於後臺。 停止時,Activity實例及其諸如成員變量等所有狀態信息將保留,但它無法執行任何代碼。
*Activity解決的問題:
如果用戶在使用您的應用時接聽來電或切換到另一個應用,它不會崩潰。
在用戶未主動使用它時不會消耗寶貴的系統資源。
如果用戶離開您的應用並稍後返回,不會丟失用戶的進度。
當屏幕在橫向和縱向之間旋轉時,不會崩潰或丟失用戶的進度。
*Activity中,如果把密度度高的操作放在pause階段,可能影響系統對下一個Acitity的過度速度。且resume,start,pause(大多數情況下)是可視化的。
*Activity中,created階段處理的問題:
創建一個實例,實現生命週期中出現一次的基本應用啓動邏輯(用戶界面,實例化某些變量,配置UI以及一些需要響應UI的監聽業務邏輯)
*Activity中,started階段處理的問題:
在這個方法中,實例化那些需要在Stopped方法中銷燬的可能導致內存泄漏的資源。還須注意的是,在內存緊張的情況下,系統可能也不會執行onStop()方法,這個跟使用finish()直接結束Activity的情況有些類似。
*Activity中,resumed階段處理的問題:
在這個方法中,由於只有從started開始纔可能是可視化的,而started這個階段又是瞬間的,所以可以在這個階段有關以View界面爲基礎的那些關於UI的效果,比如動畫。打開獨立設備(照相機),最後在paused階段釋放;
*Activity中,paused階段處理的問題:
使用的場景:前臺Activity有時會被其他導致Activity暫停的可視組件阻擋。 例如,當半透明Activity打開時(比如對話框樣式中的Activity),上一個Activity會暫停。 只要Activity仍然部分可見但目前又未處於焦點之中,它會一直暫停。
處理的業務:
*a.停止不應在暫停時繼續的進行之中的操作(比如視頻)或保留任何應該永久保存的信息,以防用戶堅持離開應用;
*b.停止動畫或其他可能消耗 CPU 的進行之中的操作。
*c.提交未保存的更改,但僅當用戶離開時希望永久性保存此類更改(比如電子郵件草稿)。
*d.釋放系統資源,比如廣播接收器、傳感器手柄(比如 GPS) 或當您的Activity暫停且用戶不需要它們時仍然可能影響電池壽命的任何其他資源。
*Activity中,stopped階段處理的問題:
調用onStopped方法後,它就不再可見,並且釋放用戶不使用時不需要的資源,解放可能導致內存泄漏的資源。比如:將草稿筆記本的內容永久保存在內存中
*Activity中,destroyed階段處理的問題:
Activity包含您在 onCreate() 期間創建的後臺線程或其他如若未正確關閉可能導致內存泄露的長期運行資源,您應在 onDestroy() 期間終止它們。
*思維拓展:
看到這幾個生命週期的階段,會發現,有好幾個地方的業務似乎是有重疊的。深究了下,其實,重疊是必然的,舉個例子,如果你僅僅是把當前的Acitivity壓棧了,實例化的對象其實還是存在於內存中,這時,這個Activity並沒有調用onDestroyed()方法,如果你只是在onDestroyed()方法釋放那些可能導致內存泄漏的資源,就有可能執行不了代碼而引發內存泄漏的風險。同理,created對應destroyed,started對應stopped。之前開發時,一直不是很理解,什麼樣的業務要放在onStart()方法中進行,現在其實顯而易見,因爲你的那些可能導致內存泄漏的資源,在不銷燬Activity實例的前提下,會在onStopped()方法中被銷燬,如果你onRestart這個Activity,必然會使這些資源找不到,因此,需要在onStart()中重新實例化,重新引用。而且,假設在onCreate()方法中,你直接是這個Activity finished了,那麼系統是不會調用下面三個階段,而是直接進入onDestroy()方法中,所以,在銷燬階段,也需要對可能導致內存泄漏的資源進行銷燬。這樣想想, 這五個階段是非常科學的。之前,是因爲沒有接觸到這種必要資源的把控,所以,對這個概念不是很清楚,特此記錄。
*Activity重新創建:
如果要使每個View可以在系統恢復中恢復狀態,每個視圖就必須要有唯一id。其次是,除了View需要保存的狀態信息,還有一些變量也需要保存,這就在銷燬是用onSaveInstanceState()——默認保存View的狀態,將需要保存的信息以鍵值對的方式存入Bundle;在後面需要重新創建Activity對象時,將相同的Bundle對象同時傳遞給onRestoreInstanceState()和onCreate()方法:
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
如果要在onCreate()方法中恢復數據時,必須先判斷Bundle是否爲null,然後在手動加載:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance
}
...
}
而onRestoreInstanceState()是在onStart()方法後進行,並且只有在需要恢復數據狀態的情況下才會被調用,所以不需要檢驗Bundle對象是否爲null:
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);
}
******************************************************************
**使用Fragment創建動態UI
*在高於API11的系統版本中,可以直接使用框架中的Fragment類,反之可以使用位置支持庫中的FragmentActivity這個類
*對於Fragment切換後的重新實例化,必須從新加載數據問題:
*a.通過使用add,hide,show等方法,進行切換,這種是利用容器的內容可以重疊的原理實現的,不過我個人認爲,這種方法,只能用於切換的Fragment比較少的情況下,並且碎片內容不復雜的情況,因爲覆蓋意味着資源並沒有釋放。
public void switchContent(Fragment from, Fragment to) {
if (mContent != to) {
mContent = to;
FragmentTransaction transaction = mFragmentMan.beginTransaction().setCustomAnimations(
android.R.anim.fade_in, R.anim.slide_out);
if (!to.isAdded()) { // 先判斷是否被add過
transaction.hide(from).add(R.id.content_frame, to).commit(); // 隱藏當前的fragment,add下一個到Activity中
} else {
transaction.hide(from).show(to).commit(); // 隱藏當前的fragment,顯示下一個
}
}
}
*b.通過重寫onSavedInstanceState()方法,保存數據,但是不需要繼承超類
*c.把Fragment的UI和數據分離,在切換Fragment時,只是後端服務的不同
public class UIFragment extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment,
container, false);
return view;
}
...
}
public class HeadlessFragment extends Fragment{
@Override
public void onCreate(Bundle savedInstanceState) {
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return null;
}
...
}
前者是指一般的定義了UI的Fragment,後者則是無UI的Fragment,即在onCreateView()中返回的是null。將與UI處理無關的異步任務都可以放到後者中,而且一般地都會在onCreate()中加上setRetainInstance(true),故而可以在橫豎屏切換時不被重新創建和重複執行異步任務。
這樣做了之後,便可以不用管UI Fragment的重新創建與否了,因爲數據和異步任務都在無UI的Fragment中,再通過Activity 的 FragmentManager 交互即可。
只需記得在Headless Fragment銷燬時將持有的數據清空、停止異步任務。
*當你執行替換或移除 Fragment 等 Fragment 事務時,最好能讓用戶向後導航和“撤消”所做更改。要通過 Fragment 事務允許用戶向後導航,你必須調用 addToBackStack(),然後再執行 FragmentTransaction。因爲當你移除或替換 Fragment 並向返回堆棧添加事務時,已移除的 Fragment 會停止(而不是銷燬)。如果用戶向後導航,還原該 Fragment,它會重新啓動。如果你沒有向返回堆棧添加事務,那麼該 Fragment 在移除或替換時就會被銷燬。
*Activity與Fragment進行通信:
通過使用回調接口的方式,在Fragment中聲明一個需要重寫的接口,然後在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);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
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
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// If article frag is available, we're in two-pane layout...
// Call a method in the ArticleFragment to update its content
articleFrag.updateArticleView(position);
} else {
// Otherwise, we're in the one-pane layout and must swap frags...
// Create fragment and give it an argument for the selected article
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
//ListView的Item監聽事件
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
}
******************************************************************
**數據保存
*使用Sharedpreference保存數據
//reference
Context context = getActivity();
SharedPreferences sharedPref = context.getSharedPreferences(
getString(R.string.preference_file_key), Context.MODE_PRIVATE);
//write
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(getString(R.string.saved_high_score), newHighScore);
editor.commit();
//read
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
int defaultValue = getResources().getInteger(R.string.saved_high_score_default);
long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);
*存儲器保存數據時,內部存儲的數據在應用卸載時會刪除,而外部存儲不會。當用戶卸載您的應用時,只有在您通過 getExternalFilesDir() 將您的應用的文件保存在目錄中時,系統纔會從此處刪除您的應用的文件。
*標準I/O流的文件操作:
//get file
getFilesDir()
返回表示您的應用的內部目錄的 File 。
getCacheDir()
返回表示您的應用臨時緩存文件的內部目錄的 File 。 務必刪除所有不再需要的文件並對在指定時間您使用的內存量實現合理大小限制,比如,1MB。 如果在系統即將耗盡存儲,它會在不進行警告的情況下刪除您的緩存文件。
File file = new File(context.getFilesDir(), filename);
//write
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();
}
//create tempCacheFile
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;
}
*SQL數據庫:
http://developer.android.com/intl/zh-cn/training/basics/data-storage/databases.html
*判斷Intent所指向的Activity是否存在:
要確認是否存在可響應意向的可用Activity,請調用 queryIntentActivities() 來獲取能夠處理您的Intent 的Activity列表。 如果返回的 List 不爲空,您可以安全地使用該意向。例如:
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
// 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);
}
*顯示應用選擇器:
使用場景:在一個URL鏈接點擊時,如果你的系統有多個瀏覽器,此時,選擇器就會提供列表
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().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);
// Verify the intent will resolve to at least one activity
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}