編輯:Android資訊
對於大型軟件開發經驗較少的程序員來說,這可能是一個不太容易理解的抽象概念,因為它與我們平常使用的簡單函數調用,或者通過庫調用接口的方式不太一樣。 在 Intent 的使用中你看不到直接的函數調用,相對函數調用來說,Intent 是更為抽象的概念,利用 Intent 所實現的軟件復用的粒度是Activity/Service ,比函數復用更高一些,另外耦合也更為松散。
Android 中與Intent 相關的還有 Action/Category 及 Intent Filter 等,另外還有用於廣播的 Intent ,這些元素摻雜在一起,導致初學者不太容易迅速掌握 Intent 的用法。在講解這些名詞之前,我們先來從下面的例子中感受一下 Intent 的一些基本用法,看看它能做些什麼,之後再來思考這種機制背後的意義。
理解 Intent 的關鍵之一是理解清楚Intent 的兩種基本用法:一種是顯式的 Intent ,即在構造 Intent 對象時就指定接收者,這種方式與普通的函數調用類似, 只是復用的粒度有所差別;另一種是隱式的 Intent ,即Intent 的發送者在構造 Intent 對象時,並不知道也不關心接收者是誰,這種方式與函數調用差別比較大,有利於降低發送者和接收 者之間的耦合。另外 Intent 除了發送外,還可用於廣播。
下面的一小節我們來看看顯式 Intent 的用法。
顯式的Intent(Explicit Intent)
同一個應用程序中的Activity切換
通常一個應用程序中需要多個UI 屏幕,也就需要多個Activity 類,並且在這些 Activity 之間進行切換,這種切換就是通過 Intent 機制來實現的。
在同一個應用程序中切換 Activity時,我們通常都知道要啟動的 Activity 具體是哪一個,因此常用顯式的 Intent 來實現。下面的例子用來實現一個非常簡單的應用程序 SimpleIntentTest ,它包括兩個UI 屏幕也就是兩個 Activity——SimpleIntentTest類和 TestActivity 類, SimpleIntentTest類有一個按鈕用來啟動 TestActivity。
程序的代碼非常簡單, SimpleIntentTest類的源代碼如下:
package com.tope.samples.intent.simple; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; public class SimpleIntentTest extends Activity implements View.OnClickListener{ /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout. main ); Button startBtn = (Button)findViewById(R.id. start_activity ); startBtn.setOnClickListener( this ); } public void onClick(View v) { switch (v.getId()) { case R.id. start_activity : Intent intent = new Intent( this , TestActivity. class ); startActivity(intent); break ; default : break ; } } }
上面的代碼中,主要是為“Start activity” 按鈕添加了 OnClickListener, 使得按鈕被點擊時執行 onClick() 方法, onClick() 方法中則利用了 Intent 機制,來啟動 TestActivity,關鍵的代碼是下面這兩行:
Intent intent = new Intent( this , TestActivity. class );
startActivity(intent);
這裡定義 Intent 對象時所用到的是 Intent 的構造函數之一:
Intent ( Context packageContext, Class <?> cls)
兩個參數分別指定 Context 和 Class ,由於將Class 設置為 TestActivity.class,這樣便顯式的指定了TestActivity 類作為該Intent 的接收者,通過後面的startActivity() 方法便可啟動 TestActivity 。
TestActivity 的代碼更為簡單(定義 TestActivity類需要新建 TestActivity.java 文件,如果你是一個初學者,你需要學會如何在 Eclipse 或其他開發環境下添加一個新的類,這裡不作詳述,請參考其他文檔),如下所示
package com.tope.samples.intent.simple; import android.app.Activity; import android.os.Bundle; public class TestActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout. test_activity ); } }
可見 TestActivity僅僅是調用 setContentView 來顯示 test_activity.xml 中的內容而已。對於 test_activity.xml及本例中所用到其他 xml 文件這裡不作多余說明。
如果我們僅僅是做上面的一些 工作,還不能達到利用 SimpleIntentTest 啟動 TestActivity的目的。事實上,這樣做會出現下面的 Exception ,導致程序退出。以下是利用 logcat 工具記錄的log 信息(省略了後半部分):
I/ActivityManager( 569): Displayed activity com.tope.samples/.SimpleIntentTest: 3018 ms I/ActivityManager( 569): Starting activity: Intent { comp={com.tope.samples/com.tope.samples.TestActivity} } D/AndroidRuntime( 932): Shutting down VM W/dalvikvm( 932): threadid=3: thread exiting with uncaught exception (group=0x4000fe70) E/AndroidRuntime( 932): Uncaught handler: thread main exiting due to uncaught exception E/AndroidRuntime( 932): android.content.ActivityNotFoundException: Unable to find explicit activity class {com.tope.samples/com.tope.samples.TestActivity}; have you declared this activity in your AndroidManifest.xml? E/AndroidRuntime( 932): at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1480) E/AndroidRuntime( 932): at android.app.Instrumentation.execStartActivity(Instrumentation.java:1454) E/AndroidRuntime( 932): at android.app.Activity.startActivityForResult(Activity.java:2656) E/AndroidRuntime( 932): at android.app.Activity.startActivity(Activity.java:2700) E/AndroidRuntime( 932): at com.tope.samples.SimpleIntentTest.onClick(SimpleIntentTest.java:24) …
從這些log 中我們可以看到點擊按鈕後 startActivity 的調用過程,主要的原因是:“android.content.ActivityNotFoundException: Unable to find explicit activity class {com.tope.samples/com.tope.samples.TestActivity}; have you declared this activity in your AndroidManifest.xml?”
從這些log 我們可以看到原因是找不到 TestActivity這個 Activity ,並且 log 中還給出了提示:你是否在AndroidManifest.xml 中聲明了這個 Activity?解決問題的方法也就是按照提示在 AndroidManifest.xml 中增加TestActivity 的聲明,如下所示:
<? xml version = "1.0" encoding = "utf-8" ?> < manifest xmlns:android = "http://schemas.android.com/apk/res/android" package = "com.tope.samples" android:versionCode = "1" android:versionName = "1.0" > < application android:icon = "@drawable/icon" android:label ="@string/app_name" > < activity android:name = ".SimpleIntentTest" android:label = "@string/app_name" > < intent-filter > < action android:name = "android.intent.action.MAIN" /> < category android:name ="android.intent.category.LAUNCHER" /> </ intent-filter > </ activity > < activity android:name = ".TestActivity" /> </ application > < uses-sdk android:minSdkVersion = "3" /> </ manifest >
完成這個修改後再重新運行該程序,就一切都正常了。
從 AndroidManifest.xml修改的過程我們可以體會到, Intent 機制即使在程序內部且顯式指定接收者,也還是需要在 AndroidManifest.xml 中聲明 TestActivity。這個過程並不像一個簡單的函數調用,顯式的 Intent 也同樣經過了Android 應用程序框架所提供的支持,從滿足條件的 Activity 中進行選擇,如果不在 AndroidManifest.xml中進行聲明,則 Android 應用程序框架找不到所需要的 Activity。
請讀者通過我們的示例來逐步理解 AndroidManifest.xml在這個過程中所扮演的角色,這樣有利於理解 Intent的作用 ,及後面的 Intent Filter。當然,這個例子僅僅是開始,且看下文分解 。
不同應用程序之間的Activity切換
上面的例子我們所做的是在同 一應用程序中進行 Activity 的切換,那麼在不同的應用程序中,是否也能這麼做呢,答案是肯定的,不過對應的代碼要稍作修改。本例中我們需要兩個應用程序,可利用上例中 的SimpleIntentTest作為其中之一,另外還需要寫一個新的程序,來調用 SimpleIntentTest 應用程序中的 TestActivity。
我們新建程序 CrossIntentTest(注意不是新建一個類,如果是 Eclipse 環境,選擇 File->New->Project新建工程),其中只有一個 Activity ,其源代碼與 SimpleIntentTest.java 類似 :
package com.tope.samples.intent.cross; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; public class CrossIntentTest extends Activity implements View.OnClickListener{ /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout. main ); Button startBtn = (Button)findViewById(R.id. start_activity ); startBtn.setOnClickListener( this ); } public void onClick(View v) { switch (v.getId()) { case R.id. start_activity : Intent intent = new Intent(); intent.setClassName( "com.tope.samples.intent.simple" , "com.tope.samples.intent.simple.TestActivity" ); startActivity(intent); break ; default : break ; } } }
注意比較它與 SimpleIntentTest的不同之處主要在於初始化 Intent 對象的過程:
Intent intent = new Intent(); intent.setClassName( "com.tope.samples.intent.simple" , "com.tope.samples.intent.simple.TestActivity" ); startActivity(intent);
這裡采用了 Intent 最簡單的不帶參數的構造函數 , 然後通過 setClassName() 函數來指定要啟動哪個包中的哪個 Activity, 而不是像上例中的通過 Intent ( Context packageContext, Class <?> cls) 這個構造函數來初始化Intent 對象,這是因為,要啟動的 TestActivity 與 CrossIntentTest 不在同一個包中 , 要指定 Class 參數比較麻煩 , 所以通常啟動不同程序的 Activity 時便采用上面的 setClassName() 的方式。除此之外,你也可以利用Android 提供的類似的 setComponent() 方法,具體使用方法請參考 Android SDK的文檔。
另外我們還需要修改SimpleIntentTest 程序中的 AndroidManifest.xml 文件,為 TestActivity 的聲明添加Intent Filter ,即將原來的
<activity android:name = ".TestActivity" />
修改為:
<activity android:name = ".TestActivity" > <intent-filter> <action android:name = "android.intent.action.DEFAULT" /> </intent-filter> </activity >
對於不同應用之間的 Activity 的切換,這裡需要在 Intent Filter中 設置至少一個 Action,否則其他的應用將沒有權限調用這個 Activity 。這裡我們開始接觸 Intent Filter和 Action 這些概念了,讀者應該可以感覺到,設置Intent Filter 和 Action 主要的目的,是為了讓其他需要調用這個 Activity 的程序能夠順利的調用它。除了Action之外, Intent Filter 還可以設置 Category 、 Data等,用來更加精確的匹配 Intent 與 Activity。
隱式Intent(Implicit Intent)
如果 Intent 機制僅僅提供上面的顯式 Intent 用法的話,這種相對復雜的機制似乎意義並不是很大。確實,Intent 機制更重要的作用在於下面這種隱式的 Intent ,即 Intent 的發送者不指定接收者,很可能不知道也不關心接收者是誰,而由 Android 框架去尋找最匹配的接收者。
最簡單的隱式 Intent
我們先從最簡單的例子開始。 下面的 ImplicitIntentTest 程序用來啟動 Android 自帶的打電話功能的 Dialer 程序。
ImplicitIntentTest 程序只包含一個java 源文件 ImplicitIntentTest.java,代碼如下所示:
package com.tope.samples.intent.implicit; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; public class ImplicitIntentTest extends Activity implements View.OnClickListener{ /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout. main ); Button startBtn = (Button)findViewById(R.id. dial ); startBtn.setOnClickListener( this ); } public void onClick(View v) { switch (v.getId()) { case R.id. dial : Intent intent = new Intent(Intent. ACTION_DIAL ); startActivity(intent); break ; default : break ; } } }
該程序在Intent 的使用上,與上節中的使用方式有很大的不同,即根本不指定接收者,初始化 Intent 對象時,只是傳入參數,設定 Action為 Intent.ACTION_DIAL :
Intent intent = new Intent(Intent. ACTION_DIAL ); startActivity(intent);
這裡使用的構造函數的原型如下:
Intent ( String action);
這裡讀者可暫時將action理解為描述這個 Intent 的一種方式,這種使用方式看上去比較奇怪, Intent 的發送者只是指定了 Action為 Intent.ACTION_DIAL ,那麼怎麼找到接收者呢?來看下面的例子。
增加一個接收者
事實上接收者如果希望能夠接收某些 Intent ,需要像上節例子中一樣,通過在 AndroidManifest.xml中增加Activity 的聲明,並設置對應的 Intent Filter 和 Action ,才能被 Android 的應用程序框架所匹配。為了證明這一點,我們修改上一 節 SimpleIntentTest 程序中的 AndroidManifest.xml 文件,將 TestActivity 的聲明部分改為:
<activity android:name = ".TestActivity" > <intent-filter > <action android:name = "android.intent.action.DEFAULT" /> <action android:name = "android.intent.action.DIAL" /> <category android:name = "android.intent.category.DEFAULT" /> </intent-filter > </activity >
修改完之後注意要重新安裝 SimpleIntentTest 程序的apk 包,然後再嘗試運行 ImplicitIntentTest 程序(不是SimpleIntentTest 程序)
這個截圖中的第二幅表示可以選擇 Dialer 或者 SimpleIntentTest 程序來完成 Intent.ACTION_DIAL ,也就是說,針對 Intent.ACTION_DIAL, Android 框架找到了兩個符合條件的 Activity,因此它將這兩個 Activity 分別列出,供用戶選擇。
回過頭來看我們是怎麼做到這一點的。我們僅僅在 SimpleIntentTest 程序的 AndroidManifest.xml 文件中增加了下面的兩行:
<action android:name = "android.intent.action.DIAL" /> category android:name = "android.intent.category.DEFAULT"/>
這兩行修改了原來的 Intent Filter,這樣這個 Activity 才能夠接收到我們發送的 Intent 。我們通過這個改動及其作用,可以進一步理解隱式 Intent, Intent Filter 及 Action, Category 等概念—— Intent 發送者設定 Action 來說明將要進行的動作,而 Intent 的接收者在 AndroidManifest.xml 文件中通過設定 Intent Filter來聲明自己能接收哪些Intent 。
過年前發了一篇介紹 Translucent System Bar 特性的文章 Translucent System Bar 的最佳實踐 ,收到很多開發者的關注和反
之前一直頭痛於沒有辦法在多個程序中共享資源,用作公共類庫的方法也是使用的導出jar再導入的辦法,現在終於初步搞明白了,可算解脫了~,分享出來。 建立公共庫 首先建
在日常的開發中,尤其是app開發,因為不像web端那樣 出錯以後可以熱更新,所以app開發 一般對軟件質量有更高的要求(你可以想一下 一個發出去的版本如果有重大缺
不廢話,先看效果,嗯…看起來有點卡,截圖軟件的問題: 圖中普通列表是ListView樣式,沒有設置Header和Footer時使用默認的下拉刷新和上