其它人的學習文章

http://blog.joycode.com/ghj/archives/author/ghj1976/page/2

Android啓動畫面Splash

方法一,兩個Activity

核心代碼:

package ghj1976.HelloWorld;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;

public class SplashActivity extends Activity {

	private final int SPLASH_DISPLAY_LENGHT = 8000; // 延遲八秒

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.splash);

		new Handler().postDelayed(new Runnable() {
			public void run() {
				Intent mainIntent = new Intent(SplashActivity.this,
						HelloWorldActivity.class);
				SplashActivity.this.startActivity(mainIntent);
				SplashActivity.this.finish();
			}

		}, SPLASH_DISPLAY_LENGHT);

	}
}

說明:

Handler().postDelayed  是延遲指定的時間再執行

Handler類主要可以使用如下3個方法來設置執行Runnable對象的時間:

//  立即執行Runnable對象  
public final boolean post(Runnable r);
//  在指定的時間(uptimeMillis)執行Runnable對象  
public final boolean postAtTime(Runnable r, long uptimeMillis);
//  在指定的時間間隔(delayMillis)執行Runnable對象  
public final boolean postDelayed(Runnable r, long delayMillis); 

有關 Handler 類的更詳細可以看這篇文章:http://book.51cto.com/art/201006/207064.htm

下面兩行代碼啓動一個新的Activity,同時關閉當前Activity。

SplashActivity.this.startActivity(mainIntent);

SplashActivity.this.finish();

對 finish 方法的解釋如下: http://android.toolib.net/reference/android/app/Activity.html

Call this when your activity is done and should be closed. The ActivityResult is propagated back to whoever launched you via onActivityResult().

image

圖來自: http://www.ibm.com/developerworks/cn/opensource/os-cn-android-actvt/

如上所示,Android 程序員可以決定一個 Activity 的“生”,但不能決定它的“死”,也就時說程序員可以啓動一個 Activity,但是卻不能手動的“結束”一個 Activity。

當你調用 Activity.finish()方法時,結果和用戶按下 BACK 鍵一樣:告訴 Activity Manager 該 Activity 實例完成了相應的工作,可以被“回收”。

隨後 Activity Manager 激活處於棧第二層的 Activity 並重新入棧,同時原 Activity 被壓入到棧的第二層,從 Active 狀態轉到 Paused 狀態。

例如上面例子中:從 SplashActivity 中啓動了 HelloWorldActivity,則當前處於棧頂端的是 HelloWorldActivity,第二層是 SplashActivity 。

當我們調用 SplashActivity.finish()方法時(我們是在SplashActivity中通過SplashActivity.this.finish()調用的),SplashActivity 從 Active 狀態轉換 Stoped 狀態,並被系統從棧中移除,標誌可以被“回收”。

Activity 的狀態與它在棧中的位置關係如下圖:

image

 

上圖的例子是

從 Activity1 中啓動了 Activity2,則當前處於棧頂端的是 Activity2,第二層是 Activity1,當我們在 Activity2中調用 Activity2.finish()方法時,Activity Manager 重新激活 Activity1 併入棧,Activity2 從 Active 狀態轉換 Stoped 狀態,同時標註Activity2可以被“回收” 。Activity1. onActivityResult(int requestCode, int resultCode, Intent data)方法被執行,Activity2 返回的數據通過 data參數返回給 Activity1。

 

方法二:一個 Activity

 

佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<LinearLayout android:id="@+id/splashscreen"
		android:orientation="vertical" android:layout_width="fill_parent"
		android:layout_height="fill_parent">

		<TextView android:id="@+id/info" android:layout_width="fill_parent"
			android:layout_height="wrap_content" android:gravity="center"
			android:paddingTop="10px" android:text="This is a splash !" />
	</LinearLayout>

	<TextView android:layout_width="fill_parent"
		android:paddingTop="10px" android:layout_height="wrap_content"
		android:text="This is a Context" />
</LinearLayout>

說明:

這裏有一個id爲splashscreen的LinearLayout,是程序啓動時顯現的部分。當啓動完成後,它會被隱藏。

核心代碼:

package ghj1976.AndroidTest;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.View;
import android.view.Window;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends Activity {

	private LinearLayout splash;
	private TextView tv;

	private static final int STOPSPLASH = 0;
	// time in milliseconds
	private static final long SPLASHTIME = 1000;

	private Handler splashHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case STOPSPLASH:
				SystemClock.sleep(4000);
				splash.setVisibility(View.GONE);
					break;
			}
			super.handleMessage(msg);
		}
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getWindow().requestFeature(Window.FEATURE_PROGRESS);
		setContentView(R.layout.main);

		splash = (LinearLayout) findViewById(R.id.splashscreen);
		tv = (TextView) findViewById(R.id.info);
		tv.setText("正在建立數據連接");

		Message msg = new Message();
		msg.what = STOPSPLASH;
		splashHandler.sendMessageDelayed(msg, SPLASHTIME);
	}
}

說明

我們在應用啓動後發送一個消息,把 指定區域設置爲隱藏, splash.setVisibility(View.GONE);  就實現了 啓動界面。

Android到底那個Acitivity啓動

啓動那個Acitivity有兩種方式:implicit(隱藏) intent 和 explicit(明確) intent

Explicit Intent

明確的指定了要啓動的Acitivity
比如以下Java代碼,明確指定了要啓動B:
Intent intent= new Intent(this, B.class) 

 

Implicit Intent


沒有明確的指定要啓動哪個Activity ,而是通過設置一些Intent Filter來讓系統去篩選合適的Acitivity去啓動。

當使用startActivity時,隱式Intent解析到一個單一的Activity。如果存在多個Activity都有能力在特定的數據上執行給定的動作的話,Android會從這些中選擇最好的進行啓動。

 

Implicit Intent 到底發給哪個activity?

這需要進行三個匹配,一個是action,一個是category,一個是data。根據三個的匹配結果,找到應該啓動的Activity。

 

Action Implicit Intent

動作匹配指Android Intent Filter包含特定的動作或沒有指定的動作。

一個Intent Filter有一個或多個定義的動作,如果沒有任何一個能與Intent指定的動作匹配的話,這個Intent Filter在算作是動作匹配檢查失敗。

<intent-filter>元素中可以包括子元素<action>,比如:
<intent-filter>
<action android:name=”com.example.project.SHOW_CURRENT” />
<action android:name=”com.example.project.SHOW_RECENT” />
<action android:name=”com.example.project.SHOW_PENDING” />
</intent-filter>
一條<intent-filter>元素至少應該包含一個<action>,否則任何Intent請求都不能和該<intent-filter>匹配。如果Intent請求的Action和<intent-filter>中個某一條<action>匹配,那麼該Intent就通過了這條<intent-filter>的動作測試。

Category Implicit Intent

種類匹配更爲嚴格。Intent Filter必須包含所有在解析的Intent中定義的種類。一個沒有特定種類的Intent Filter只能與沒有種類的Intent匹配。

<intent-filter>元素可以包含<category>子元素,比如:
<intent-filter . . . >
<category android:name=”android.Intent.Category.DEFAULT” />
<category android:name=”android.Intent.Category.BROWSABLE” />
</intent-filter>
只有當Intent請求中所有的Category與組件中某一個IntentFilter的<category>完全匹配時,纔會讓該Intent請求通過測試,IntentFilter中多餘的<category>聲明並不會導致匹配失敗。

Data Implicit Intent

Intent的數據URI中的部分會與Intent Filter中的data標籤比較。如果Intent Filter定義scheme,host/authority,path或mimetype,這些值都會與Intent的URI比較。任何不匹配都會導致Intent Filter從列表中刪除。沒有指定data值的Android Intent Filter會和所有的Intent數據匹配。

  • mimetype是正在匹配的數據的數據類型。當匹配數據類型時,你可以使用通配符來匹配子類型(例如,earthquakes/*)。如果Intent Filter指定一個數據類型,它必須與Intent匹配;沒有指定數據的話全部匹配。
  • scheme是URI部分的協議——例如,http:,mailto:,tel:。
  • host-name或“data authority”是介於URI中scheme和path之間的部分(例如,www.google.com)。匹配主機名時,Intent Filter的scheme也必須通過匹配。
  • 數據path是緊接在“data authority”的後面(例如,/ig)。path只在scheme和host-name部分都匹配的情況下才匹配。

數據在<intent-filter>中的描述如下:
<intent-filter . . . >
<data android:type=”video/mpeg” android:scheme=”http” . . . />
<data android:type=”audio/mpeg” android:scheme=”http” . . . />
</intent-filter>
<data>元素指定了希望接受的Intent請求的數據URI和數據類型,URI被分成三部分來進行匹配:scheme、authority和path。其中,用setData()設定的Inteat請求的URI數據類型和scheme必須與IntentFilter中所指定的一致。若IntentFilter中還指定了authority或path,它們也需要相匹配纔會通過測試。

解析出來後的處理邏輯

如果這個過程中多於一個組件解析出來的話,它們會以優先度來排序,可以在Android Intent Filter的節點裏添加一個可選的標籤。最高等級的組件會返回。

Android本地的應用程序組件和第三方應用程序一樣,都是Intent解析過程中的一部分。它們沒有更高的優先度,可以被新的Activity完全的代替,這些新的Activity宣告自己的Intent Filter能響應相同的動作請求。

 

 

應用程序的啓動

AndroidManifest.xml 文件中,把那個 activity 配置了 <action android:name="android.intent.action.MAIN" /> ,那就就是最先被啓動的 Activity ,如果多個設置了,則第一個設置的是最新被啓動的 Activity。

如下圖設置,是 .SplashActivity 先啓動的

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="ghj1976.HelloWorld" android:versionCode="1"
	android:versionName="1.0">
	<uses-sdk android:minSdkVersion="3" />
	<application android:icon="@drawable/icon" android:label="@string/app_name">
		<activity android:name=".HelloWorldActivity" android:label="@string/app_name">
		</activity>
		<activity android:name=".SplashActivity" android:label="@string/app_name">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
	</application>
</manifest>

上面代碼中的

activity android:name:  activity 的類名,必須有。

android.intent.action.MAIN   決定應用程序最先啓動的Activity

android.intent.category.LAUNCHER     決定應用程序是否顯示在程序列表裏

 

參考資料

Intent Filter匹配

http://www.moandroid.com/?p=1651

什麼時候加上android.intent.category.DEFAULT和LAUNCHER

http://www.crazydevelop.com/content.aspx?ID=6416

Android 開發之:Intent.createChooser() 妙用

http://www.oschina.net/bbs/thread/9299

Android 任務共用性Affinity

建議首先閱讀下面這篇文章,這樣才能對本文有所瞭解:

Android Application Task Activities的關係
http://www.cnblogs.com/ghj1976/archive/2011/04/29/2032412.html 尤其要明白 Task 是啥。

 

什麼是Affinity

在某些情況下,Android需要知道一個Activity屬於哪個Task,即使它沒有被啓動到一個具體的Task裏。這是通過任務共用性(Affinities)完成的。任務共用性(Affinities)爲這個運行一個或多個Activity的Task提供了一個獨特的靜態名稱,默認的一個活動的任務共用性(Affinity)是實現了該Activity的.apk包的名字。

 

當開始一個沒有Intent.FLAG_ACTIVITY_NEW_TASK標誌的Activity時,任務共用性affinities不會影響將會運行該新活動的Task:它總是運行在啓動它的Task裏。但是,如果使用了NEW_TASK標誌,那麼共用性(affinity)將被用來判斷是否已經存在一個有相同共用性(affinity)的Task。如果是這樣,這項Task將被切換到前面而新的Activity會啓動於這個Task的頂層。

 

這種特性在您必須使用NEW_TASK標誌的情況下最有用,尤其是從狀態欄通知或桌面快捷方式啓動活動時。結果是,當用戶用這種方式啓動您的應用程序時,它的當前Task將被切換到前臺,而且想要查看的Activity被放在最上面。

 

你可以在程序清單(Manifest)文件的應用程序application標籤中爲.apk包中所有的活動分配你自己的任務共用性Affinites,或者在活動標記中爲各個活動進行分配。

一些說明其如何使用的例子如下:

  • 如果您的.apk包含多個用戶可以啓動的高層應用程序,那麼您可能需要對用戶看到的每個Activity(活動)指定不同的affinities。一個不錯的命名慣例是以附加一個以冒號分隔的字符串來擴展您的.apk包名。例如,“ com.android.contacts ”.apk可以有affinities:“com.android.contacts:Dialer”和“ com.android.contacts:ContactsList”。
  • 如果您正在替換一個通知,快捷方式,或其他可以從外部發起的應用程序的“內部”活動,你可能需要明確設定您替代活動的taskAffinity和您準備替代的應用程序一樣。例如,如果您想替換contacts詳細信息視圖(用戶可以創建並調用快捷方式),你得把taskAffinity設置成“com.android.contacts”。

 

在前面的文章“Android四種Activity的加載模式”我們提到:Activity的加載模式受啓動Activity的Intent對象中設置的Flag和manifest文件中Activity的<activity>元素的特性值交互控制。

 

跟 Task 有關的 manifest文件中Activity的特性值介紹

android:allowTaskReparenting
    用來標記Activity能否從啓動的Task移動到有着affinity的Task(當這個Task進入到前臺時)

   “true”,表示能移動,“false”,表示它必須呆在啓動時呆在的那個Task裏。

    如果這個特性沒有被設定,設定到<application>元素上的allowTaskReparenting特性的值會應用到Activity上。默認值爲“false”。

    一般來說,當Activity啓動後,它就與啓動它的Task關聯,並且在那裏耗盡它的整個生命週期。噹噹前的Task不再顯示時,你可以使用這個特性來強制Activity移動到有着affinity的Task中。典型用法是:把一個應用程序的Activity移到另一個應用程序的主Task中。
    例如,如果 email中包含一個web頁的鏈接,點擊它就會啓動一個Activity來顯示這個頁面。這個Activity是由Browser應用程序定義的,但是,現在它作爲email Task的一部分。如果它重新宿主到Browser Task裏,當Browser下一次進入到前臺時,它就能被看見,並且,當email Task再次進入前臺時,就看不到它了。

    Actvity的affinity是由taskAffinity特性定義的。Task的affinity是通過讀取根Activity的affinity決定。因此,根Activity總是位於相同affinity的Task裏。由於啓動模式爲“singleTask”和“singleInstance”的Activity只能位於Task的底部,因此,重新宿主只能限於“standard”和“singleTop”模式。

android:alwaysRetainTaskState
    用來標記Activity所在的Task的狀態是否總是由系統來保持。

    “true”,表示總是;“false”,表示在某種情形下允許系統恢復Task到它的初始化狀態。默認值是“false”。

    這個特性只針對Task的根Activity有意義;對其它Activity來說,忽略之。
    一般來說,特定的情形如當用戶從主畫面重新選擇這個Task時,系統會對這個Task進行清理(從stack中刪除位於根Activity之上的所有Activivity)。典型的情況,當用戶有一段時間沒有訪問這個Task時也會這麼做,例如30分鐘。
    然而,當這個特性設爲“true”時,用戶總是能回到這個Task的最新狀態,無論他們是如何啓動的。這非常有用,例如,像Browser應用程序,這裏有很多的狀態(例如多個打開的Tab),用戶不想丟失這些狀態。

android:clearTaskOnLaunch
    用來標記是否從Task中清除所有的Activity,除了根Activity外(每當從主畫面重新啓動時)

   “true”,表示總是清除至它的根Activity,“false”表示不。默認值是“false”。

    這個特性只對啓動一個新的Task的Activity(根Activity)有意義;對Task中其它的Activity忽略。
    當這個值爲“true”,每次用戶重新啓動這個Task時,都會進入到它的根Activity中,不管這個Task最後在做些什麼,也不管用戶是使用BACK還是HOME離開的。當這個值爲“false”時,可能會在一些情形下(參考alwaysRetainTaskState特性)清除Task的Activity,但不總是。
    假設,某人從主畫面啓動了Activity P,並從那裏遷移至Activity Q。接下來用戶按下HOME,然後返回Activity P。一般,用戶可能見到的是Activity Q,因爲它是P的Task中最後工作的內容。然而,如果P設定這個特性爲“true”,當用戶按下HOME並使這個Task再次進入前臺時,其上的所有的Activity(在這裏是Q)都將被清除。因此,當返回到這個Task時,用戶只能看到P。
    如果這個特性和allowTaskReparenting都設定爲“true”,那些能重新宿主的Activity會移動到共享affinity的Task中;剩下的Activity都將被拋棄,如上所述。

android:finishOnTaskLaunch
    用來標記當用戶再次啓動它的Task(在主畫面選擇這個Task)時已經存在的Activity實例是否要關閉(結束)

   “true”,表示應該關閉,“false”表示不關閉。默認值是“false”。
    如果這個特性和allowTaskReparenting都設定爲“true”,這個特性勝出。Activity的affinity忽略。這個Activity不會重新宿主,但是會銷燬。

android:launchMode
    用於指示Activity如何啓動。這裏有四種模式,與Intent對象中的Activity Flags(FLAG_ACTIVITY_*變量)共同作用,來決定Activity如何啓動來處理Intent。它們是:

    "standard"
    "singleTop"
    "singleTask"
    "singleInstance"

    默認模式是“standard”。
    前面文章:“Android四種Activity的加載模式”已經詳細描述,這裏就不做描述了.

android:noHistory
    用於標記當用戶從Activity上離開並且它在屏幕上不再可見時Activity是否從Activity stack中清除並結束(調用finish()方法)——“true”,表示它應該關閉,“false”,表示不需要。默認值是“false”。
    “true”值意味着Activity不會留下歷史痕跡。因爲它不會在Activity stack的Task中保留,因此,用戶不能返回它。

    比如啓用界面的就可以借用這個。

android:taskAffinity
   這就是本文所描述的任務共用性。

   Activity爲Task擁有的一個affinity。擁有相同的affinity的Activity理論上屬於相同的Task(在用戶的角度是相同的“應用程序”)。Task的affinity是由它的根Activity決定的。
   affinity決定兩件事情——Activity重新宿主的Task(參考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK標誌啓動的Activity宿主的Task。
    默認情況,一個應用程序中的所有Activity都擁有相同的affinity。捏可以設定這個特性來重組它們,甚至可以把不同應用程序中定義的Activity放置到相同的Task中。爲了明確Activity不宿主特定的Task,設定該特性爲空的字符串。
    如果這個特性沒有設置,Activity將從應用程序的設定那裏繼承下來(參考<application>元素的taskAffinity特性)。應用程序默認的affinity的名字是<manifest>元素中設定的package名。

 

跟 Task 有關的 Intent對象中設置的Flag

FLAG_ACTIVITY_BROUGHT_TO_FRONT
    這個標誌一般不是由程序代碼設置的,如在launchMode中設置singleTask模式時系統幫你設定。

FLAG_ACTIVITY_CLEAR_TOP
    如果設置,並且這個Activity已經在當前的Task中運行,因此,不再是重新啓動一個這個Activity的實例,而是在這個Activity上方的所有Activity都將關閉,然後這個Intent會作爲一個新的Intent投遞到老的Activity(現在位於頂端)中。
    例如,假設一個Task中包含這些Activity:A,B,C,D。如果D調用了startActivity(),並且包含一個指向Activity B的Intent,那麼,C和D都將結束,然後B接收到這個Intent,因此,目前stack的狀況是:A,B。
    上例中正在運行的Activity B既可以在onNewIntent()中接收到這個新的Intent,也可以把自己關閉然後重新啓動來接收這個Intent。如果它的啓動模式聲明爲“multiple”(默認值),並且你沒有在這個Intent中設置FLAG_ACTIVITY_SINGLE_TOP標誌,那麼它將關閉然後重新創建;對於其它的啓動模式,或者在這個Intent中設置FLAG_ACTIVITY_SINGLE_TOP標誌,都將把這個Intent投遞到當前這個實例的onNewIntent()中。
    這個啓動模式還可以與FLAG_ACTIVITY_NEW_TASK結合起來使用:用於啓動一個Task中的根Activity,它會把那個Task中任何運行的實例帶入前臺,然後清除它直到根Activity。這非常有用,例如,當從Notification Manager處啓動一個Activity。

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
    如果設置,這將在Task的Activity stack中設置一個還原點,當Task恢復時,需要清理Activity。也就是說,下一次Task帶着FLAG_ACTIVITY_RESET_TASK_IF_NEEDED標記進入前臺時(典型的操作是用戶在主畫面重啓它),這個Activity和它之上的都將關閉,以至於用戶不能再返回到它們,但是可以回到之前的Activity。
    這在你的程序有分割點的時候很有用。例如,一個e-mail應用程序可能有一個操作是查看一個附件,需要啓動圖片瀏覽Activity來顯示。這個Activity應該作爲e-mail應用程序Task的一部分,因爲這是用戶在這個Task中觸發的操作。然而,當用戶離開這個Task,然後從主畫面選擇e-mail app,我們可能希望回到查看的會話中,但不是查看圖片附件,因爲這讓人困惑。通過在啓動圖片瀏覽時設定這個標誌,瀏覽及其它啓動的Activity在下次用戶返回到mail程序時都將全部清除。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    如果設置,新的Activity不會在最近啓動的Activity的列表中保存。

FLAG_ACTIVITY_FORWARD_RESULT
    如果設置,並且這個Intent用於從一個存在的Activity啓動一個新的Activity,那麼,這個作爲答覆目標的Activity將會傳到這個新的Activity中。這種方式下,新的Activity可以調用setResult(int),並且這個結果值將發送給那個作爲答覆目標的Activity。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
    這個標誌一般不由應用程序代碼設置,如果這個Activity是從歷史記錄裏啓動的(常按HOME鍵),那麼,系統會幫你設定。

FLAG_ACTIVITY_MULTIPLE_TASK
    不要使用這個標誌,除非你自己實現了應用程序啓動器。與FLAG_ACTIVITY_NEW_TASK結合起來使用,可以禁用把已存的Task送入前臺的行爲。當設置時,新的Task總是會啓動來處理Intent,而不管這是是否已經有一個Task可以處理相同的事情。
    由於默認的系統不包含圖形Task管理功能,因此,你不應該使用這個標誌,除非你提供給用戶一種方式可以返回到已經啓動的Task。
    如果FLAG_ACTIVITY_NEW_TASK標誌沒有設置,這個標誌被忽略。

FLAG_ACTIVITY_NEW_TASK
    如果設置,這個Activity會成爲歷史stack中一個新Task的開始。一個Task(從啓動它的Activity到下一個Task中的Activity)定義了用戶可以遷移的Activity原子組。Task可以移動到前臺和後臺;在某個特定Task中的所有Activity總是保持相同的次序。
    這個標誌一般用於呈現“啓動”類型的行爲:它們提供用戶一系列可以單獨完成的事情,與啓動它們的Activity完全無關。
    使用這個標誌,如果正在啓動的Activity的Task已經在運行的話,那麼,新的Activity將不會啓動;代替的,當前Task會簡單的移入前臺。參考FLAG_ACTIVITY_MULTIPLE_TASK標誌,可以禁用這一行爲。
    這個標誌不能用於調用方對已經啓動的Activity請求結果。

FLAG_ACTIVITY_NO_ANIMATION
    如果在Intent中設置,並傳遞給Context.startActivity()的話,這個標誌將阻止系統進入下一個Activity時應用Acitivity遷移動畫。這並不意味着動畫將永不運行——如果另一個Activity在啓動顯示之前,沒有指定這個標誌,那麼,動畫將被應用。這個標誌可以很好的用於執行一連串的操作,而動畫被看作是更高一級的事件的驅動。

FLAG_ACTIVITY_NO_HISTORY
    如果設置,新的Activity將不再歷史stack中保留。用戶一離開它,這個Activity就關閉了。這也可以通過設置noHistory特性。

FLAG_ACTIVITY_NO_USER_ACTION
    如果設置,作爲新啓動的Activity進入前臺時,這個標誌將在Activity暫停之前阻止從最前方的Activity回調的onUserLeaveHint()。
    典型的,一個Activity可以依賴這個回調指明顯式的用戶動作引起的Activity移出後臺。這個回調在Activity的生命週期中標記一個合適的點,並關閉一些Notification。
    如果一個Activity通過非用戶驅動的事件,如來電或鬧鐘,啓動的,這個標誌也應該傳遞給Context.startActivity,保證暫停的Activity不認爲用戶已經知曉其Notification。

FLAG_ACTIVITY_PREVIOUS_IS_TOP
    If set and this intent is being used to launch a new activity from an existing one, the current activity will not be counted as the top activity for deciding whether the new intent should be delivered to the top instead of starting a new one. The previous activity will be used as the top, with the assumption being that the current activity will finish itself immediately.

FLAG_ACTIVITY_REORDER_TO_FRONT
    如果在Intent中設置,並傳遞給Context.startActivity(),這個標誌將引發已經運行的Activity移動到歷史stack的頂端。
    例如,假設一個Task由四個Activity組成:A,B,C,D。如果D調用startActivity()來啓動Activity B,那麼,B會移動到歷史stack的頂端,現在的次序變成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP標誌也設置的話,那麼這個標誌將被忽略。

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed.

FLAG_ACTIVITY_SINGLE_TOP
    如果設置,當這個Activity位於歷史stack的頂端運行時,不再啓動一個新的。

 

參考資料:

Android應用程序模型:應用程序,任務,進程和線程
http://blog.csdn.net/iefreer/archive/2009/08/18/4460196.aspx

Task和Activity相關
http://chengbs.iteye.com/blog/798810

Android四種Activity的加載模式

建議首先閱讀下面兩篇文章,這樣纔可以更好的理解Activity的加載模式:

Android的進程,線程模型
http://www.cnblogs.com/ghj1976/archive/2011/04/28/2031586.html 其中對“Android的單線程模型”的描述,明白Activity的一些注意事項。

Android Application Task Activities的關係
http://www.cnblogs.com/ghj1976/archive/2011/04/29/2032412.html  尤其要明白 Task 是啥。

 

一個Activty的生命週期

Activty的生命週期的也就是它所在進程的生命週期。

image 

每一個活動( Activity )都處於某一個狀態,對於開發者來說,是無法控制其應用程序處於某一個狀態的,這些均由系統來完成。
但是當一個活動的狀態發生改變的時候,開發者可以通過調用 onXX() 的方法獲取到相關的通知信息。

 

在實現 Activity 類的時候,通過覆蓋( override )這些方法即可在你需要處理的時候來調用。

  • onCreate :當活動第一次啓動的時候,觸發該方法,可以在此時完成活動的初始化工作。
    onCreate 方法有一個參數,該參數可以爲空( null ),也可以是之前調用 onSaveInstanceState ()方法保存的狀態信息。
  • onStart :該方法的觸發表示所屬活動將被展現給用戶。
  • onResume :當一個活動和用戶發生交互的時候,觸發該方法。
  • onPause :當一個正在前臺運行的活動因爲其他的活動需要前臺運行而轉入後臺運行的時候,觸發該方法。這時候需要將活動的狀態持久化,比如正在編輯的數據庫記錄等。
  • onStop :當一個活動不再需要展示給用戶的時候,觸發該方法。如果內存緊張,系統會直接結束這個活動,而不會觸發 onStop 方法。 所以保存狀態信息是應該在onPause時做,而不是onStop時做。活動如果沒有在前臺運行,都將被停止或者Linux管理進程爲了給新的活動預留足夠的存儲空間而隨時結束這些活動。因此對於開發者來說,在設計應用程序的時候,必須時刻牢記這一原則。在一些情況下,onPause方法或許是活動觸發的最後的方法,因此開發者需要在這個時候保存需要保存的信息。
  • onRestart :當處於停止狀態的活動需要再次展現給用戶的時候,觸發該方法。
  • onDestroy :當活動銷燬的時候,觸發該方法。和 onStop 方法一樣,如果內存緊張,系統會直接結束這個活動而不會觸發該方法。
  • onSaveInstanceState :系統調用該方法,允許活動保存之前的狀態,比如說在一串字符串中的光標所處的位置等。
    通常情況下,開發者不需要重寫覆蓋該方法,在默認的實現中,已經提供了自動保存活動所涉及到的用戶界面組件的所有狀態信息。 

Activity棧

上面提到開發者是無法控制Activity的狀態的,那Activity的狀態又是按照何種邏輯來運作的呢?這就要知道 Activity 棧。

 

每個Activity的狀態是由它在Activity棧(是一個後進先出LIFO,包含所有正在運行Activity的隊列)中的位置決定的。

當一個新的Activity啓動時,當前的活動的Activity將會移到Activity棧的頂部。

如果用戶使用後退按鈕返回的話,或者前臺的Activity結束,在棧上的Activity將會移上來並變爲活動狀態。如下圖所示:

image

一個應用程序的優先級是受最高優先級的Activity影響的。當決定某個應用程序是否要終結去釋放資源,Android內存管理使用棧來決定基於Activity的應用程序的優先級。

Activity狀態
一般認爲Activity有以下四種狀態:

活動的:當一個Activity在棧頂,它是可視的、有焦點、可接受用戶輸入的。Android試圖盡最大可能保持它活動狀態,殺死其它Activity來確保當前活動Activity有足夠的資源可使用。當另外一個Activity被激活,這個將會被暫停。
暫停:在很多情況下,你的Activity可視但是它沒有焦點,換句話說它被暫停了。有可能原因是一個透明或者非全屏的Activity被激活。
當被暫停,一個Activity仍會當成活動狀態,只不過是不可以接受用戶輸入。在極特殊的情況下,Android將會殺死一個暫停的Activity來爲活動的Activity提供充足的資源。當一個Activity變爲完全隱藏,它將會變成停止。
停止:當一個Activity不是可視的,它“停止”了。這個Activity將仍然在內存中保存它所有的狀態和會員信息。儘管如此,當其它地方需要內存時,它將是最有可能被釋放資源的。當一個Activity停止後,一個很重要的步驟是要保存數據和當前UI狀態。一旦一個Activity退出或關閉了,它將變爲待用狀態。
待用: 在一個Activity被殺死後和被裝在前,它是待用狀態的。待用Acitivity被移除Activity棧,並且需要在顯示和可用之前重新啓動它。

 

activity的四種加載模式

在android的多activity開發中,activity之間的跳轉可能需要有多種方式,有時是普通的生成一個新實例,有時希望跳轉到原來某個activity實例,而不是生成大量的重複的activity。加載模式便是決定以哪種方式啓動一個跳轉到原來某個Activity實例。

在android裏,有4種activity的啓動模式,分別爲:

  • standard: 標準模式,一調用startActivity()方法就會產生一個新的實例。
  • singleTop: 如果已經有一個實例位於Activity棧的頂部時,就不產生新的實例,而只是調用Activity中的newInstance()方法。如果不位於棧頂,會產生一個新的實例。
  • singleTask: 會在一個新的task中產生這個實例,以後每次調用都會使用這個,不會去產生新的實例了。
  • singleInstance: 這個跟singleTask基本上是一樣,只有一個區別:在這個模式下的Activity實例所處的task中,只能有這個activity實例,不能有其他的實例。

這些啓動模式可以在功能清單文件AndroidManifest.xml中進行設置,<activity>中的launchMode屬性。

相關的代碼中也有一些標誌可以使用,比如我們想只啓用一個實例,則可以使用 Intent.FLAG_ACTIVITY_REORDER_TO_FRONT 標誌,這個標誌表示:如果這個activity已經啓動了,就不產生新的activity,而只是把這個activity實例加到棧頂來就可以了。

Intent intent = new Intent(ReorderFour.this, ReorderTwo.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);  

Activity的加載模式受啓動Activity的Intent對象中設置的Flag和manifest文件中Activity的<activity>元素的特性值交互控制。

下面是影響加載模式的一些特性

核心的Intent Flag有:

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

FLAG_ACTIVITY_SINGLE_TOP

核心的<activity>特性有:

taskAffinity

launchMode

allowTaskReparenting

clearTaskOnLaunch

alwaysRetainTaskState

finishOnTaskLaunch

四種加載模式的區別

所屬task的區別

一般情況下,“standard”和”singleTop”的activity的目標task,和收到的Intent的發送者在同一個task內,就相當於誰調用它,它就跟誰在同一個Task中。

除非Intent包括參數FLAG_ACTIVITY_NEW_TASK。如果提供了FLAG_ACTIVITY_NEW_TASK參數,會啓動到別的task裏。

“singleTask”和”singleInstance” 總是把要啓動的activity作爲一個task的根元素,他們不會被啓動到一個其他task裏。

 

是否允許多個實例

“standard”和”singleTop”可以被實例化多次,並且是可以存在於不同的task中;這種實例化時一個task可以包括一個activity的多個實例;

“singleTask”和”singleInstance”則限制只生成一個實例,並且是task的根元素。

singleTop 要求如果創建intent的時候棧頂已經有要創建的Activity的實例,則將intent發送給該實例,而不創建新的實例。

 

是否允許其它activity存在於本task內

“singleInstance”獨佔一個task,其它activity不能存在那個task裏;

如果它啓動了一個新的activity,不管新的activity的launch mode 如何,新的activity都將會到別的task裏運行(如同加了FLAG_ACTIVITY_NEW_TASK參數)。

而另外三種模式,則可以和其它activity共存。

 

是否每次都生成新實例

“standard”對於每一個啓動Intent都會生成一個activity的新實例;

“singleTop”的activity如果在task的棧頂的話,則不生成新的該activity的實例,直接使用棧頂的實例,否則,生成該activity的實例。

比如:

現在task棧元素爲A-B-C-D(D在棧頂),這時候給D發一個啓動intent,如果D是 “standard”的,則生成D的一個新實例,棧變爲A-B-C-D-D。

如果D是singleTop的話,則不會生產D的新實例,棧狀態仍爲A-B-C-D

如果這時候給B發Intent的話,不管B的launchmode是”standard” 還是 “singleTop” ,都會生成B的新實例,棧狀態變爲A-B-C-D-B。

“singleInstance”是其所在棧的唯一activity,它會每次都被重用。

“singleTask”  如果在棧頂,則接受intent,否則,該intent會被丟棄,但是該task仍會回到前臺。 當已經存在的activity實例處理新的intent時候,會調用onNewIntent()方法,如果收到intent生成一個activity實例,那麼用戶可以通過back鍵回到上一個狀態;如果是已經存在的一個activity來處理這個intent的話,用戶不能通過按back鍵返回到這之前的狀態。

 

參考資料

Android的七巧板Activity之一 Activity的生命週期

http://winuxxan.blog.51cto.com/2779763/502523

Android的七巧板Activity之二 Activity的加載模式

http://winuxxan.blog.51cto.com/2779763/504047

Android Activity生命週期

http://www.haoni.org/2011/02/25/androidactivityshengmingzhouqi/

android中activity的四種加載模式

http://blog.csdn.net/pcwings/archive/2010/09/19/5895197.aspx

區分Activity的四種加載模式

http://marshal.easymorse.com/archives/2950

Hello Android 第三版 (二)

http://blog.csdn.net/cqwty/archive/2010/09/08/5870219.aspx

只生成一個Activity的實例

http://www.eoeandroid.com/home-space-uid-43043-do-blog-id-6.html

activity的啓動方式(launch mode)

http://liubin.nanshapo.com/2010/12/23/activity-launch-mode/

Android應用程序模型:應用程序,任務,進程和線程

http://blog.csdn.net/iefreer/archive/2009/08/18/4460196.aspx

Android Application Task Activities的關係

什麼是Android  Application?

簡單來說,一個apk文件就是一個Application。

任何一個Android Application基本上是由一些Activities組成,當用戶與應用程序交互時其所包含的部分Activities具有緊密的邏輯關係,或者各自獨立處理不同的響應。

這些Activities捆綁在一起成爲了一個處理特定需求的Application, 並且以“.apk”作爲後綴名存在於文件系統中。

Android平臺默認下的應用程序 例如:Email、Calendar、Browser、Maps、Text Message、Contacts、Camera和Dialer等都是一個個獨立的Apps。

 

安裝 Application的過程也可以簡單理解爲將其所包裹的Activities導入到當前的系統中,如果系統中已經存在了相同的Activities, 那麼將會自動將其關聯,而不會重複安裝相同的Activities,避免資源的浪費。

Application卸載的過程也會檢查當前所關聯的 Activities是否有被其它Application標籤所關聯,如果僅僅是提供當前的Application使用,那麼將會徹底被移除,相反則不做 任何操作。

 

就像我們已經知道的,Application基本上是由四個模塊組成:Activity、Service、Content Provider 和 Broadcast Receiver,其中Activity是實現應用的主體。

 

什麼是 Activity Stack?

操作應用程序時,有時需要調用多個Activities來完成需求,例如:發送郵件程序,首先是進入郵件主界面,然後啓動一個新的Activity用於填寫新郵件內容,同時可以調出聯繫人列表用於插入收件人信息等等。在這個操作過程中 Android平臺有一個專門用於管理Activities堆棧的機制,其可以方便的線性記錄Activities實例,當完成某個操作時,可以通過導航功能返回之前的Activity(通過按操作檯的“Back”按鈕)。

每次啓動新的Activity都將被添加到Activity Stack。用戶可以方便的返回上一個Activity直到Home Screen,到達Home Screen後,將無法再繼續查看堆棧記錄(俗話說:到頭了)。如果當前Task被中止(Interrupting the task),返回到系統主界面後啓動了其它操作,當希望返回到前一個Task繼續執行時,只需要再次通過主界面的Application launcher或者快捷方式啓動這個Task的Root Activity便可返回其中止時的狀態繼續執行。

相對於Views、Windows、Menus和Dialogs而言,Activity是唯一可被記錄在History stack中的數據,所以當你所設計的應用程序需要用戶由A界面進入到次一級界面B,當完成操作後需要再次返回A,那麼必須考慮將A看作爲 Activity,否則將無法從歷史堆棧中返回。

 

什麼是Task

當我們需要一個Activity可以啓動另一個Activity,可能另外一個Activity是定義在不同應用程序中的Activity。

例如,假設你想在你的應用中讓用戶顯示一些地方的街景。而這裏已經有一個Activity可以做到這一點,因此,你的Activity所需要做的只是在Intent對象中添加必要的信息,並傳遞給startActivity()。地圖瀏覽將會顯示你的地圖。當用戶按下BACK鍵,你的Activity會再次出現在屏幕上。

對於用戶來說,看起來好像是地圖瀏覽與你的Activity一樣,屬於相同的應用程序,即便是它定義在其它的應用程序裏,並運行在那個應用程序的進程裏。

Android通過將這兩個Activity保存在同一個Task裏來體現這一用戶體驗。簡單來說,一個Task就是用戶體驗上的一個“應用”。
它將相關的Activity組合在一起,以stack的方式管理(就是前面提到的Activity Stack),這就是Task。

 

在Android平臺上可以將task簡單的理解爲幽多個Activity共同協作完成某項應用,而不管Activity具體屬於哪個Application,

通過下圖可以更清晰的理解Application、task、Activity三者之間的關係:

image

 

Task 有啥用?

我們用過Android的手機就會知道有下面的場景:

假設我們首先在用IReader在看書,從選書到具體書的閱讀界面,這是有好幾個Activity。我們每一個點擊的Activity都被放在閱讀這個Task對應的Activity Stack中了,這可以放我們通過回退鍵返回每一個前面的Activity。

我們在閱讀到一半時,想看看Sina微博,按Home鍵離開了IReader。

在Sina微博界面也是有多個Activity,我們一步到閱讀界面。這時候我們每一個點擊的Activity都被放在Sina微博這個Task對應的Activity Stack中了,這可以放我們通過回退鍵返回每一個前面的Activity。

我們這時候再回到IReader讀書界面,原先的狀態還是保留的。

顯然每一個Task有自己的 Activity Stack。

Task就是這樣爲了方便人們使用手機而設置的,就像前面提到的場景Task可以跨Application。

 

下面這個圖從另外一個角度描述了Application Task Activities的關係

image

 

Task通過Application launcher、Home screen的快捷方式或者 由 “Recent Tasks”(長時間按住Home鍵)最近使用過的Task記錄中啓動。

當從一個Activity中啓動另外一個Activity時,Back鍵將作用於返回前一個Activity,與此同時 新開啓的Activity將被添加到Activity Stack中。

有關更詳細的可以參看這篇文章:

[譯]關於Activity和Task的設計思路和方法
http://blogold.chinaunix.net/u2/85193/showart_1966109.html

 

參考資料:

http://skyswim42.egloos.com/3127700

關於Activity和Task的設計思路和方法
http://blogold.chinaunix.net/u2/85193/showart_1966109.html

Android Task
http://www.apkbus.com/forum.php?mod=viewthread&tid=146

Android的進程,線程模型

Android 包括一個應用程序框架、幾個應用程序庫和一個基於 Dalvik 虛擬機的運行時,所有這些都運行在 Linux 內核之上。

通過利用 Linux 內核的優勢,Android 得到了大量操作系統服務,包括進程和內存管理、網絡堆棧、驅動程序、硬件抽象層、安全性等相關的服務。

 

有關Java虛擬機跟進程,線程的關係請參看下面這篇文章:

進程、線程與JVM、CLR
http://blog.csdn.net/ghj1976/archive/2010/04/13/5481038.aspx

 

下面這篇文章對Android的進程和線程描述的很好,我在這篇文章基礎補充了一些圖片和信息。

http://blog.csdn.net/L_serein/archive/2011/03/22/6269270.aspx 

android進程模型:

在安裝Android應用程序的時候,Android會爲每個程序分配一個Linux用戶ID,並設置相應的權限,這樣其它應用程序就不能訪問此應用程序所擁有的數據和資源了。

在 Linux 中,一個用戶ID 識別一個給定用戶;在 Android 上,一個用戶ID 識別一個應用程序。

應用程序在安裝時被分配用戶 ID,應用程序在設備上的存續期間內,用戶ID 保持不變。

 

默認情況下,每個apk運行在它自己的Linux進程中。當需要執行應用程序中的代碼時,Android會啓動一個jvm,即一個新的進程來執行,因此不同的apk運行在相互隔離的環境中。

下圖顯示了:兩個 Android 應用程序,各自在其自己的基本沙箱或進程上。他們是不同的Linux user ID。

image

開發者也可以給兩個應用程序分配相同的linux用戶id,這樣他們就能訪問對方所擁有的資源。

爲了保留系統資源,擁有相同用戶id的應用程序可以運行在同一個進程中,共享同一個jvm。

如下圖,顯示了兩個 Android 應用程序,運行在同一進程上。

不同的應用程序可以運行在相同的進程中。要實現這個功能,首先必須使用相同的私鑰簽署這些應用程序,然後必須使用 manifest 文件給它們分配相同的 Linux 用戶 ID,這通過用相同的值/名定義 manifest 屬性 android:sharedUserId 來做到。

image

Android進程知識的補充:

下圖是標準的Android 架構圖,

其中我們可以看到在“Android本地庫 & Java運行環境層”中,Android 運行時中,

Dalvik是Android中的java虛擬機,可支持同時運行多個虛擬機實例;每個Android應用程序都在自己的進程中運行,都擁有一個獨立的Dalvik虛擬機實例;
所有java類經過java編譯器編譯,然後通過SDK中的dx工具轉成.dex格式交由虛擬機執行。

image

Android系統進程

init進程(1號進程),父進程爲0號進程,執行根目錄底下的init可執行程序,是用戶空間進程
——-> /system/bin/sh
——-> /system/bin/mediaserver
——-> zygote
—————–> system_server
—————–>com.android.phone
—————–>android.process.acore(Home)
… …

kthreadd進程(2號進程),父進程爲0號進程,是內核進程,其他內核進程都是直接或者間接以它爲父進程

  

Android的單線程模型

當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如:用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。

在開發Android 應用時必須遵守單線程模型的原則: Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。

如果在非UI線程中直接操作UI線程,會拋出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,這與普通的java程序不同。

由於UI線程負責事件的監聽和繪圖,因此,必須保證UI線程能夠隨時響應用戶的需求,UI線程裏的操作應該向中斷事件那樣短小,費時的操作(如網絡連接)需要另開線程,否則,如果UI線程超過5s沒有響應用戶請求,會彈出對話框提醒用戶終止應用程序。

如果在新開的線程中需要對UI進行設定,就可能違反單線程模型,因此android採用一種複雜的Message Queue機制保證線程間通信。

 

Message Queue:

Message Queue是一個消息隊列,用來存放通過Handler發佈的消息。Android在第一次啓動程序時會默認會爲UI thread創建一個關聯的消息隊列,可以通過Looper.myQueue()得到當前線程的消息隊列,用來管理程序的一些上層組件,activities,broadcast receivers 等等。你可以在自己的子線程中創建Handler與UI thread通訊。 

通過Handler你可以發佈或者處理一個消息或者是一個Runnable的實例。每個Handler都會與唯一的一個線程以及該線程的消息隊列管理。

Looper扮演着一個Handler和消息隊列之間通訊橋樑的角色。程序組件首先通過Handler把消息傳遞給Looper,Looper把消息放入隊列。Looper也把消息隊列裏的消息廣播給所有的Handler,Handler接受到消息後調用handleMessage進行處理。

實例如下:

public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);
   editText = (EditText) findViewById(R.id.weather_city_edit);
   Button button = (Button) findViewById(R.id.goQuery);
   button.setOnClickListener(this);  

   Looper looper = Looper.myLooper();  //得到當前線程的Looper實例,由於當前線程是UI線程也可以通過Looper.getMainLooper()得到  
    messageHandler = new MessageHandler(looper);  //此處甚至可以不需要設置Looper,因爲 Handler默認就使用當前線程的Looper  
} 

public void onClick(View v) {
   new Thread() {
      public void run() {
          Message message = Message.obtain();
          message.obj = "abc";
          messageHandler.sendMessage(message);  //發送消息 
       }
   }.start();
} 

Handler messageHandler = new Handler {
   public MessageHandler(Looper looper) {
      super(looper);
  }
   public void handleMessage(Message msg) {
      setTitle((String) msg.obj);
   }
}

對於這個實例,當這個activity執行玩oncreate,onstart,onresume後,就監聽UI的各種事件和消息。

當我們點擊一個按鈕後,啓動一個線程,線程執行結束後,通過handler發送一個消息,由於這個handler屬於UI線程,因此這個消息也發送給UI線程,然後UI線程又把這個消息給handler處理,而這個handler是UI線程創造的,他可以訪問UI組件,因此,就更新了頁面。

由於通過handler需要自己管理線程類,如果業務稍微複雜,代碼看起來就比較混亂,因此android提供了AsyncTask類來解決此問題。

 

AsyncTask:

首先繼承一下此類,實現以下若干方法,

onPreExecute(), 該方法將在執行實際的後臺操作前被UI thread調用。可以在該方法中做一些準備工作,如在界面上顯示一個進度條。 

doInBackground(Params…), 將在onPreExecute 方法執行後馬上執行,該方法運行在後臺線程中。這裏將主要負責執行那些很耗時的後臺計算工作。

可以調用publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。 

onProgressUpdate(Progress…),在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。 

onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,後臺的計算結果將通過該方法傳遞到UI thread.

使用時需要遵循以下規則:

1)Task的實例必須在UI thread中創建 

2)execute方法必須在UI thread中調用 

3)不要手動的調用這些方法,只調用execute即可

4)該task只能被執行一次,否則多次調用時將會出現異常

示例如下:

public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
       editText = (EditText) findViewById(R.id.weather_city_edit);
       Button button = (Button) findViewById(R.id.goQuery);
       button.setOnClickListener(this);
}  

public void onClick(View v) {
       new GetWeatherTask().execute(“aaa”);
} 

class GetWeatherTask extends AsyncTask<String, Integer, String> {
    protected String doInBackground(String... params) {
         return getWetherByCity(params[0]);
    }
    protected void onPostExecute(String result) {
         setTitle(result);
    }
}

 

參考資料:

Android進程和線程模型

http://blog.csdn.net/L_serein/archive/2011/03/22/6269270.aspx

Hello Android 第三版 (二)

http://blog.csdn.net/cqwty/archive/2010/09/08/5870219.aspx

理解 Android 上的安全性

http://www.ibm.com/developerworks/cn/xml/x-androidsecurity/

Android 的Margin和Padding屬性以及支持的長度單位

Android的Margin和Padding跟Html的是一樣的。如下圖所示:黃色部分爲Padding,灰色部分爲Margin。

image

通俗的理解 Padding 爲內邊框,Margin 爲外邊框

對應的屬性爲

android:layout_marginBottom="25dip"
android:layout_marginLeft="10dip"
android:layout_marginTop="10dip"
android:layout_marginRight="10dip"
android:paddingLeft="1dip"
android:paddingTop="1dip"
android:paddingRight="1dip"
android:paddingBottom="1dip"

如果左右上下都是相同的設置則可以直接設置

android:layout_margin="10dip"
android:padding="5dip"

 

Android支持的長度單位。

  • px(像素):屏幕上的點。
    pixels(像素). 不同設備顯示效果相同,一般我們HVGA代表320×480像素,這個用的比較多。
  • in(英寸):長度單位。
  • mm(毫米):長度單位。
  • pt(磅):1/72英寸。
    point,是一個標準的長度單位,1pt=1/72英寸,用於印刷業,非常簡單易用;
  • dp(與密度無關的像素):一種基於屏幕密度的抽象單位。在每英寸160點的顯示器上,1dp = 1px。
  • dip:與dp相同,多用於android/ophone示例中。
    device independent pixels(設備獨立像素). 不同設備有不同的顯示效果,這個和設備硬件有關,一般我們爲了支持WVGA、HVGA和QVGA 推薦使用這個,不依賴像素。
  • sp(與刻度無關的像素):與dp類似,但是可以根據用戶的字體大小首選項進行縮放。
    scaled pixels(放大像素). 主要用於字體顯示best for textsize。

爲了使用戶界面能夠在現在和將來的顯示器類型上正常顯示,建議大家始終使用sp作爲文字大小的單位,Android默認的字號也是用的sp。

將dip作爲其他元素的單位,比如長度、高度。當然,也可以考慮使用矢量圖形,而不是用位圖。

 

dp是與密度無關,sp除了與密度無關外,還與scale無關。

如果屏幕密度爲160,這時dp和sp和px是一樣的。1dp=1sp=1px,但如果使用px作單位,如果屏幕大小不變(假設還是3.2寸),而屏幕密度變成了320。

那麼原來TextView的寬度設成160px,在密度爲320的3.2寸屏幕裏看要比在密度爲160的3.2寸屏幕上看短了一半。

但如果設置成160dp或160sp的話。系統會自動將width屬性值設置成320px的。

也就是160 * 320 / 160。其中320 / 160可稱爲密度比例因子。也就是說,如果使用dp和sp,系統會根據屏幕密度的變化自動進行轉換.

 

參考資料:

dip, dp, px, sp區別
http://sifutian.iteye.com/blog/680935

android:layout_gravity 和 android:gravity 的區別

gravity 這個英文單詞是重心的意思,在這裏就表示停靠位置的意思。

android:layout_gravity 和 android:gravity 的區別

從名字上可以看到,android:gravity是對元素本身說的,元素本身的文本顯示在什麼地方靠着換個屬性設置,不過不設置默認是在左側的。

android:layout_gravity是相對與它的父元素說的,說明元素顯示在父元素的什麼位置。

比如說button: android:layout_gravity 表示按鈕在界面上的位置。 android:gravity表示button上的字在button上的位置。

 

可選值

這兩個屬性可選的值有:top、bottom、left、right、center_vertical、fill_vertical、center_horizontal、fill_horizontal、center、fill、clip_vertical。

而且這些屬性是可以多選的,用“|”分開。

默認這個的值是:Gravity.LEFT

對這些屬性的描述:

出自:

http://androidmirror.com/guide/topics/resources/drawable-resource.html

http://android.toolib.net/reference/android/graphics/drawable/ClipDrawable.html

Value Description
top Put the object at the top of its container, not changing its size.
將對象放在其容器的頂部,不改變其大小.
bottom Put the object at the bottom of its container, not changing its size.
將對象放在其容器的底部,不改變其大小.
left Put the object at the left edge of its container, not changing its size.
將對象放在其容器的左側,不改變其大小.
right Put the object at the right edge of its container, not changing its size.
將對象放在其容器的右側,不改變其大小.
center_vertical Place object in the vertical center of its container, not changing its size.
將對象縱向居中,不改變其大小.
垂直對齊方式:垂直方向上居中對齊。
fill_vertical Grow the vertical size of the object if needed so it completely fills its container.
必要的時候增加對象的縱向大小,以完全充滿其容器.
垂直方向填充
center_horizontal Place object in the horizontal center of its container, not changing its size.
將對象橫向居中,不改變其大小.
水平對齊方式:水平方向上居中對齊
fill_horizontal Grow the horizontal size of the object if needed so it completely fills its container.
必要的時候增加對象的橫向大小,以完全充滿其容器.
水平方向填充
center Place the object in the center of its container in both the vertical and horizontal axis, not changing its size.
將對象橫縱居中,不改變其大小.
fill Grow the horizontal and vertical size of the object if needed so it completely fills its container. This is the default.
必要的時候增加對象的橫縱向大小,以完全充滿其容器.
clip_vertical Additional option that can be set to have the top and/or bottom edges of the child clipped to its container’s bounds. The clip is based on the vertical gravity: a top gravity clips the bottom edge, a bottom gravity clips the top edge, and neither clips both edges.

附加選項,用於按照容器的邊來剪切對象的頂部和/或底部的內容. 剪切基於其縱向對齊設置:頂部對齊時,剪切底部;底部對齊時剪切頂部;除此之外剪切頂部和底部.

垂直方向裁剪

clip_horizontal Additional option that can be set to have the left and/or right edges of the child clipped to its container’s bounds. The clip is based on the horizontal gravity: a left gravity clips the right edge, a right gravity clips the left edge, and neither clips both edges.

附加選項,用於按照容器的邊來剪切對象的左側和/或右側的內容. 剪切基於其橫向對齊設置:左側對齊時,剪切右側;右側對齊時剪切左側;除此之外剪切左側和右側.

水平方向裁剪

簡單記憶 : horizontal 都是操作的水平方向,即橫向, vertical 都是炒作的垂直方向,即縱向。

對於LinearLayout何時生效的問題

參看:也談layout_gravity和gravity
http://www.lephone.net/viewthread.php?tid=325

對於 LinearLayout

當 android:orientation="vertical"  時, 只有水平方向的設置才起作用,垂直方向的設置不起作用。即:left,right,center_horizontal 是生效的。

當 android:orientation="horizontal" 時, 只有垂直方向的設置才起作用,水平方向的設置不起作用。即:top,bottom,center_vertical 是生效的。

Android設置窗口的背景圖

drawable- hdpi、drawable- mdpi、drawable-ldpi的區別:

Android2.1(含)以後的版本中有drawable-mdpi、drawable-ldpi、drawable-hdpi三個目錄,這三個目錄主要是爲了支持多分辨率。

dpi是“dot per inch”的縮寫,每英寸像素數。

四種密度分類: ldpi (low), mdpi (medium), hdpi (high), and xhdpi (extra high)
一般情況下的普通屏幕:ldpi是120,mdpi是160,hdpi是240,xhdpi是320。

WVGA,HVGA,QVGA的區別
VGA是”Video Graphics Array”,顯示標準爲 640*480。
WVGA(Wide VGA)分辨率爲 480*800
HVGA(Half VGA)即VGA的一半分辨率爲 320*480
QVGA(Quarter VGA)即VGA非四分之一分辨率爲240*320

 

drawable-(hdpi,mdpi,ldpi)和WVGA,HVGA,QVGA的聯繫
hdpi裏面主要放高分辨率的圖片,如WVGA (480×800),FWVGA (480×854)     長寬比  5:3
mdpi裏面主要放中等分辨率的圖片,如HVGA (320×480)                            長寬比   3:2
ldpi裏面主要放低分辨率的圖片,如QVGA (240×320)                                 長寬比  4:3
系統會根據機器的分辨率來分別到這幾個文件夾裏面去找對應的圖片。

 

在開發程序時爲了兼容不同平臺不同屏幕,我們可以上面制定長寬比裁剪圖片,並確保有足夠分辨率,並把它放入對應目錄即可。

比如我們希望設置我們應用窗口的背景,則可以簡單的用上面提到的規範裁剪對應的圖片,然後做下面步驟即可:

1、將背景圖片COPY到對應的drawable文件夾中,假設這裏用的是bg1.png

2、修改main.xml,添加 android:background="@drawable/bg1" 即可:

3、這時候的佈局文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/bg1">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Hello Android"/>
</LinearLayout>

 

參考資料:

Android2.1中的 drawable(hdpi,ldpi,mdpi) 的區別
http://blog.csdn.net/infsafe/archive/2010/03/29/5426562.aspx

Android開發中的drawable-(hdpi,mdpi,ldpi)和WVGA,HVGA,QVGA的區別以及聯繫
http://disanji.net/2011/04/25/android-development-drawable-hdpimdpildpi-wvgahvgaqvga-diff-connection/

Android調用天氣預報的WebService簡單例子

下面例子改自網上例子:http://express.ruanko.com/ruanko-express_34/technologyexchange5.html

不過網上這個例子有些沒有說明,有些情況不一樣了,所以我重新寫了。

一、獲取並使用KSOAP包

在Android SDK中並沒有提供調用WebService的庫,因此,需要使用第三方的SDK來調用WebService。PC版本的WebService庫非常豐富,但這些對Android來說過於龐大。適合手機的WebService客戶端的SDK有一些,比較常用的是KSOAP2。

KSOAP2 地址:http://code.google.com/p/ksoap2-android/

我下載的最新的是: ksoap2-android-assembly-2.5.4-jar-with-dependencies.jar

注意:

我在使用ksoap2-android時犯了一個低級錯誤:使用時報錯誤:The import org.ksoap2 cannot be resolved。
當時分析這個問題時一直以爲是Eclipse出了問題,找了好多方法都不行,
實際是我下載的ksoap2-android-assembly-2.5.4-jar-with-dependencies.jar文件是錯誤的導致的,走了彎路。

http://code.google.com/p/ksoap2-android/wiki/HowToUse?tm=2 頁面 通過鼠標右鍵鏈接另存爲存的是同名的一個純文本的Html文件。而不是我們想要的。

我是在
http://code.google.com/p/ksoap2-android/source/browse/m2-repo/com/google/code/ksoap2-android/ksoap2-android-assembly/2.5.4/ksoap2-android-assembly-2.5.4-jar-with-dependencies.jar  點 View raw file 才正確下載對應文件的。

image

 

選擇我們的項目,右鍵菜單中 Build Path –> Add External Archives… 增加這個下載的包

image

增加好後,我們在 選擇我們的項目,右鍵菜單中 Build Path –> Configure Build Path 的 Libraries 中可以看到下面圖:

image

二,分以下幾步來調用 WebService

 

1、指定 WebService 的命名空間和調用方法

import org.ksoap2.serialization.SoapObject;
private static final String NAMESPACE = "http://WebXml.com.cn/";
private static final String METHOD_NAME = "getWeatherbyCityName";

SoapObject rpc = new SoapObject(NAMESPACE, METHOD_NAME);

SoapObject類的第一個參數表示WebService的命名空間,可以從WSDL文檔中找到WebService的命名空間。

第二個參數表示要調用的WebService方法名。

2、設置調用方法的參數值,如果沒有參數,可以省略,設置方法的參數值的代碼如下:

rpc.addProperty("theCityName", "北京");

要注意的是,addProperty方法的第1個參數雖然表示調用方法的參數名,但該參數值並不一定與服務端的WebService類中的方法參數名一致,只要設置參數的順序一致即可。

3、生成調用Webservice方法的SOAP請求信息。

SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut = rpc;
envelope.dotNet = true;
envelope.setOutputSoapObject(rpc);

創建SoapSerializationEnvelope對象時需要通過SoapSerializationEnvelope類的構造方法設置SOAP協議的版本號。

該版本號需要根據服務端WebService的版本號設置。

在創建SoapSerializationEnvelope對象後,不要忘了設置SOAPSoapSerializationEnvelope類的bodyOut屬性,

該屬性的值就是在第一步創建的SoapObject對象。

4、創建HttpTransportsSE對象。

這裏不要使用 AndroidHttpTransport ht = new AndroidHttpTransport(URL); 這是一個要過期的類

private static String URL = "http://www.webxml.com.cn/webservices/weatherwebservice.asmx";
HttpTransportSE ht = new HttpTransportSE(URL); 
ht.debug = true;

5、使用call方法調用WebService方法

private static String SOAP_ACTION = "http://WebXml.com.cn/getWeatherbyCityName";
ht.call(SOAP_ACTION, envelope);

網上有人說這裏的call的第一個參數爲null,但是經過我的測試,null是不行的。

第2個參數就是在第3步創建的SoapSerializationEnvelope對象。

6、獲得WebService方法的返回結果

有兩種方法:

1、使用getResponse方法獲得返回數據。

private SoapObject detail;
detail =(SoapObject) envelope.getResponse();

2、使用 bodyIn 及 getProperty。

private SoapObject detail;
SoapObject result = (SoapObject)envelope.bodyIn;
detail = (SoapObject) result.getProperty("getWeatherbyCityNameResult");

7、 這時候執行會出錯,提示沒有權限訪問網絡

需要修改 AndroidManifest.xml 文件,賦予相應權限

簡單來說就是增加下面這行配置:<uses-permission android:name="android.permission.INTERNET"></uses-permission>

完整的 AndroidManifest.xml 文件 如下:

 

注:Android 中在代碼中爲了調試寫了system.out.print()輸出項

在菜單:Window–>show view–>other–>找到Android,選擇Logcat 是可以看到輸出的,

如果你想在一個單獨的窗口看到system.out.print()的輸出的話,可以在logcat界面點那個綠色的“+”好,

在Filter name 和 By log tag裏面均填入System.out,這樣的話你就能在單獨的界面查看system.out.print()的輸出了!!

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="ghj1976.MyWeather" android:versionCode="1"
	android:versionName="1.0">

	<application android:icon="@drawable/icon" android:label="@string/app_name">
		<activity android:name=".MyWeatherActivity" android:label="@string/app_name">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
	</application>
	<uses-permission android:name="android.permission.INTERNET"></uses-permission>

</manifest>

完整的代碼如下:

package ghj1976.MyWeather;

import java.io.UnsupportedEncodingException;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
//import org.ksoap2.transport.AndroidHttpTransport;
import org.ksoap2.transport.HttpTransportSE;

public class MyWeatherActivity extends Activity {

	private Button okButton;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		okButton = (Button) this.findViewById(R.id.btn_Search);
		okButton.setOnClickListener(new Button.OnClickListener() {
			@Override
			public void onClick(View v) {
				  String city = "北京";
				  getWeather(city);
			}

		});
	}

	private static final String NAMESPACE = "http://WebXml.com.cn/";

	// WebService地址
	private static String URL = "http://www.webxml.com.cn/webservices/weatherwebservice.asmx";

	private static final String METHOD_NAME = "getWeatherbyCityName";

	private static String SOAP_ACTION = "http://WebXml.com.cn/getWeatherbyCityName";

	private String weatherToday;

	private SoapObject detail;

	public void getWeather(String cityName) {
		try {
			System.out.println("rpc------");
			SoapObject rpc = new SoapObject(NAMESPACE, METHOD_NAME);
			System.out.println("rpc" + rpc);
			System.out.println("cityName is " + cityName);
			rpc.addProperty("theCityName", cityName);

			SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
			envelope.bodyOut = rpc;
			envelope.dotNet = true;
			envelope.setOutputSoapObject(rpc);

			HttpTransportSE ht = new HttpTransportSE(URL);

			//AndroidHttpTransport ht = new AndroidHttpTransport(URL);
			ht.debug = true;

			ht.call(SOAP_ACTION, envelope);
			//ht.call(null, envelope);

			//SoapObject result = (SoapObject)envelope.bodyIn;
			//detail = (SoapObject) result.getProperty("getWeatherbyCityNameResult");

			detail =(SoapObject) envelope.getResponse();

			//System.out.println("result" + result);
			System.out.println("detail" + detail);
			Toast.makeText(this, detail.toString(), Toast.LENGTH_LONG).show();
			parseWeather(detail);

			return;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void parseWeather(SoapObject detail)
			throws UnsupportedEncodingException {
		String date = detail.getProperty(6).toString();
		weatherToday = "今天:" + date.split(" ")[0];
		weatherToday = weatherToday + "\n天氣:" + date.split(" ")[1];
		weatherToday = weatherToday + "\n氣溫:"
				+ detail.getProperty(5).toString();
		weatherToday = weatherToday + "\n風力:"
				+ detail.getProperty(7).toString() + "\n";
		System.out.println("weatherToday is " + weatherToday);
		Toast.makeText(this, weatherToday, Toast.LENGTH_LONG).show();

	}
}

參考資料

在Android中訪問WebService接口

http://www.cnblogs.com/yy-7years/archive/2011/01/24/1943286.html

Android調用WebService

http://express.ruanko.com/ruanko-express_34/technologyexchange5.html

中國氣象局的WebService地址

http://www.webxml.com.cn/WebServices/WeatherWebService.asmx

Android與服務器端數據交互(基於SOAP協議整合android+webservice)

http://www.cnblogs.com/zhangdongzi/archive/2011/04/19/2020688.html

«page 2 of 76»

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章