Android開發 - Intent和Broadcast Receiver


關鍵字:Intent,Linkify,Broadcast Intent,Broadcast Receiver,Sticky Intent,Pending Intent,Intent Filter,Linkify

 

Intent是一種消息傳遞機制,可以在應用程序內使用,也可以在應用程序間使用。可以用於:

  • 使用類名顯示啟動一個特定的Service或者Activity。
  • 啟動Activity或者Service來執行一個動作的Intent,通常需要使用特定的數據,或者對特定的數據執行動作。
  • 廣播某個時間已經發生。

 

使用Intent來啟動Activity

顯式啟動新的Activity

    Intent intent = new Intent(MyActivity.this, SelectHorseActivity.class); startActivity(intent); 

調用startActivity之后,新的Activity會被創建、啟動和恢復運行,它會移動到Activity棧的頂部。

調用新的Activity的finish或按下設備的返回按鈕將關閉該Activity,並把它從棧中移除。

隱式的Intent和運行時遲綁定

隱式的Intent提供了一種機制,可以讓匿名的應用程序組件響應動作請求。這意味着可以要求系統啟動一個可執行給定動作的Activity,而不必知道需要啟動哪個應用程序或者Activity。

例如希望讓用戶從應用程序中撥打電話,那么可以實現一個新的撥號程序,也可以使用一個隱式的Intent來請求一個在電話號碼(表示為一個URI)上執行動作。

// Create the implicit Intent to use to start a new Activity.
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-2368")); startActivity(intent);

當構建一個新的隱式的Intent時,需要指定一個要執行的動作,另外,也可以提供執行那個動作需要的數據的URI。還可以通過向Intent添加extra來向目標Activity發送額外的數據。

Extra是一種向Intent附加基本類型值的機制。可以在任何Intent上使用重載后的putExtra方法來附加一個新的鍵值對,以后在啟動的Activity中使用對於的getExtra方法來檢索它。

確定Intent能否解析

在調用startActivity之前,確定調用是否可以解析為一個Activity是一種很好的做法。通過調用Intent的resolveActivity方法,並向該方法傳入包管理器,可以對包管理器進行查詢,確定是否有Activity能夠啟動以響應該Intent。

    if (somethingWeird && itDontLookGood) { // Create the implicit Intent to use to start a new Activity.
      Intent intent = 
        new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-2368")); // Check if an Activity exists to perform this action. PackageManager pm = getPackageManager(); ComponentName cn = intent.resolveActivity(pm);  if (cn == null) { // If there is no Activity available to perform the action // Check to see if the Market is available.
        Uri marketUri = Uri.parse("market://search?q=pname:com.myapp.packagename"); Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(marketUri); // If the Market is available, use it to download an application // capable of performing the required action. Otherwise log an // error.
        if (marketIntent.resolveActivity(pm) != null) startActivity(marketIntent); else Log.d(TAG, "Market client not available."); } else startActivity(intent); } 

從Activity返回結果

通過startActivity啟動的Activity獨立於其父Activity,並且在關閉時不會提供任何反饋。

當需要反饋時,可以啟動一個Activity作為另一個Activity的子Activity,用它向父Activity傳遞結果。子Activity只是以一種不同的方式啟動的Activity。因此,必須在應用程序的manifest文件中注冊它們,就像其他任何Activity一樣。在manifest文件中注冊的任何Activity都可以作為子Activity打開,包括系統Activity或者第三方應用程序的Activity。

當子Activity結束時,它會觸發調用Activity內的事件處理程序onActivityResult。

啟動子Activity

顯示啟動一個子Activity以返回結果

  private static final int SHOW_SUBACTIVITY = 1; private void startSubActivity() { Intent intent = new Intent(this, MyOtherActivity.class); startActivityForResult(intent, SHOW_SUBACTIVITY); }

隱式啟動一個子Activity以返回結果

  private static final int PICK_CONTACT_SUBACTIVITY = 2; private void startSubActivityImplicitly() { Uri uri = Uri.parse("content://contacts/people"); Intent intent = new Intent(Intent.ACTION_PICK, uri); startActivityForResult(intent, PICK_CONTACT_SUBACTIVITY); }

返回結果

當准備好返回子Activity時,可以在調用finish以前調用setResult,以便向調用Activity返回一個結果。

setResult方法有兩個參數:結果碼和表示為Intent的結果數據本身。

結果碼是運行子Activity的結果-通常是Activity.RESULT_OK或者Activity.RESULT_CANCELED。在某些情況下,當OK和CANCELED不足以精確描述可用的返回結果時,可以使用自己的響應碼來處理應用程序特定的選擇;setResult支持任意的整數值。

作為結果返回的Intent通常包含某段內容的URI和用於返回附加信息的一組extra。

 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.selector_layout); final ListView listView = (ListView)findViewById(R.id.listView1); Button okButton = (Button) findViewById(R.id.ok_button); okButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { long selected_horse_id = listView.getSelectedItemId();  Uri selectedHorse = Uri.parse("content://horses/" + selected_horse_id); Intent result = new Intent(Intent.ACTION_PICK, selectedHorse); setResult(RESULT_OK, result); finish(); } }); Button cancelButton = (Button) findViewById(R.id.cancel_button); cancelButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) {  setResult(RESULT_CANCELED); finish(); } }); }

如果用戶通過按下硬件返回鍵關閉Activity,或者在調用finish之前沒有調用setResult,那么結果碼將被設為RESULT_CANCELED,結果Intent將被設為null。

處理子Activity結果

當一個子Activity關閉的時候,它會觸發其調用Activity的onActivityResult事件處理程序。

onActivityResult接受多個參數:

  • 請求碼 啟動正在返回的子Activity時使用的請求碼
  • 結果碼 子Activity設置的結果碼,用來說明其結果
  • 數據 Intent用來包裝所有返回的數據。
  private static final int SELECT_HORSE = 1; private static final int SELECT_GUN = 2; Uri selectedHorse = null; Uri selectedGun = null; @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch(requestCode) { case (SELECT_HORSE): if (resultCode == Activity.RESULT_OK) selectedHorse = data.getData(); break; case (SELECT_GUN): if (resultCode == Activity.RESULT_OK) selectedGun = data.getData(); break; default: break; } }

原生Android動作  

Standard Activity Actions

 


 

 

Linkify

Linkify是一個輔助類,它會自動地在TextView類(或者TextView的派生類)中通過RegEx模式匹配來創建超鏈接。

那些匹配一個制定的RegEx模式的文本都將會被轉換為一個可以單擊的超鏈接,這些超鏈接可以隱式使用匹配的文本作為目標URI來觸發startActivity(new Intent(Intent.ACTION_VIEW,uri))。

可以制定任何字符串模式來作為可單擊連接的處理;

原生Linkify鏈接類型

final TextView myTextView = (TextView)findViewById(R.id.text_view); Linkify.addLinks(myTextView, Linkify.WEB_URIS|Linkify.EMAIL_ADDRESSES);

或者

  <TextView android:id="@+id/text_view" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" android:autoLink="phone|email"
/>

創建定制的鏈接字符串

要為自己的數據建立鏈接,需要定義自己的linkify字符串,可以通過創建一個新的RegEx模式來匹配希望顯示為超鏈接的文本。

和本地類型一樣,可以通過調用Linkify.addLinks來為目標TextView建立鏈接,只不過這次傳入的是RegEx模式,而不是預設的常量。也可以給它傳遞一個前綴,當單擊鏈接時,該前綴將會被添加到目標URI的前面。

  public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final TextView myTextView = (TextView)findViewById(R.id.text_view); // Define the base URI. String baseUri = "content://com.paad.earthquake/earthquakes/"; // Contruct an Intent to test if there is an Activity capable of // viewing the content you are Linkifying. Use the Package Manager // to perform the test. PackageManager pm = getPackageManager(); Intent testIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(baseUri)); boolean activityExists = testIntent.resolveActivity(pm) != null; // If there is an Activity capable of viewing the content // Linkify the text. if (activityExists) { int flags = Pattern.CASE_INSENSITIVE; Pattern p = Pattern.compile("\\bquake[\\s]?[0-9]+\\b", flags); Linkify.addLinks(myTextView, p, baseUri); } }

 


 

 

使用Intent廣播事件

作為一個系統級的消息傳遞機制,Intent可以在進程間發送結構化的消息。因此,可以通過實現Boradcast Receiver來監聽和響應應用程序內的這些Broadcast Intent。

Broadcast Intent用於向監聽器通知系統的應用程序或應用程序事件,從而可以擴展應用程序間的事件驅動的編程模型。

Broadcast Intent可以使應用程序更加開放;通過使用Intent來廣播一個事件,可以在不用修改原始應用程序的情況下,讓你和第三方開發人員對事件做出反應。在應用程序中,可以通過監聽Broadcast Intent來對設備狀態變化和第三方應用程序事件做出反應。

使用Intent來廣播事件

在應用程序組件中,可以構建希望廣播的Intent,然后使用sendBroadcast方法來發送它。

  public final static String EXTRA_LIFEFORM_NAME = "EXTRA_LIFEFORM_NAME"; public final static String EXTRA_LATITUDE = "EXTRA_LATITUDE"; public final static String EXTRA_LONGITUDE = "EXTRA_LONGITUDE"; public static final String ACTION_BURN = "com.paad.alien.action.BURN_IT_WITH_FIRE"; public static final String NEW_LIFEFORM = "com.paad.alien.action.NEW_LIFEFORM"; //   private void detectedLifeform(String detectedLifeform, double currentLongitude, double currentLatitude) { Intent intent = new Intent(NEW_LIFEFORM); intent.putExtra(EXTRA_LIFEFORM_NAME, detectedLifeform); intent.putExtra(EXTRA_LONGITUDE, currentLongitude); intent.putExtra(EXTRA_LATITUDE, currentLatitude);  sendBroadcast(intent); }

使用Broadcast Receiver來監聽廣播

要使Broadcast Receiver能夠接收廣播,就需要對其進行注冊,既可以使用代碼,也可以在應用程序的manifest文件中注冊(此時成為manifest接收器)。無論怎么注冊,都需要使用一個Intent Filter來指定它要監聽哪些Intent和數據。

對於包含manifest接收器的應用程序,在Intent被廣播出去的時候,應用程序不一定非要處於運行狀態才能執行接收。當匹配的Intent被廣播出去的時候,它們會被自動的啟動。

public class LifeformDetectedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // Get the lifeform details from the intent.  } }

onReceive處理程序必須在5秒鍾內完成,否則就會顯示Force Close對話框。

一般情況下,Broadcast Receiver將會更新內容、啟動Service、更新Activity UI,或者使用Notification Manager來通知用戶。5秒鍾的執行限制保證了主要的處理工作不能夠、也不應該有Broadcast Receiver直接完成。

public class LifeformDetectedReceiver extends BroadcastReceiver { public final static String EXTRA_LIFEFORM_NAME = "EXTRA_LIFEFORM_NAME"; public final static String EXTRA_LATITUDE = "EXTRA_LATITUDE"; public final static String EXTRA_LONGITUDE = "EXTRA_LONGITUDE"; public static final String ACTION_BURN = "com.paad.alien.action.BURN_IT_WITH_FIRE"; public static final String NEW_LIFEFORM = "com.paad.alien.action.NEW_LIFEFORM"; @Override public void onReceive(Context context, Intent intent) { // Get the lifeform details from the intent.
    Uri data = intent.getData(); String type = intent.getStringExtra(EXTRA_LIFEFORM_NAME); double lat = intent.getDoubleExtra(EXTRA_LATITUDE, 0); double lng = intent.getDoubleExtra(EXTRA_LONGITUDE, 0); Location loc = new Location("gps"); loc.setLatitude(lat); loc.setLongitude(lng); if (type.equals("facehugger")) { Intent startIntent = new Intent(ACTION_BURN, data); startIntent.putExtra(EXTRA_LATITUDE, lat); startIntent.putExtra(EXTRA_LONGITUDE, lng); context.startService(startIntent); } } }

在代碼中注冊Broadcast Receiver

在代碼中注冊的接收器只會在包含它的應用程序組件運行時響應Broadcast Intent。

public class MyActivity extends Activity { private IntentFilter filter = new IntentFilter(LifeformDetectedReceiver.NEW_LIFEFORM); private LifeformDetectedReceiver receiver = new LifeformDetectedReceiver();  @Override public synchronized void onResume() { super.onResume(); // Register the broadcast receiver.  registerReceiver(receiver, filter); } @Override public synchronized void onPause() { // Unregister the receiver  unregisterReceiver(receiver); super.onPause(); } }

在應用程序的manifest中注冊Broadcast Receiver

 

廣播有序的Intent sendOrderedBroadcast

廣播Sticky Intent

Sticky Intent是Broadcast Intent的有用變體,可以保存它們最后一次廣播的值,並且當有一個新的接收器被注冊為接收該廣播時,它們會把這些值作為Intent返回。

要廣播自己的Sticky Intent,應用程序必須具有BROADCAST_STICKY用戶權限,然后需要調用sendStickyBroadcast並傳入相關的Intent。

要刪除一個Sticky Intent,可以調用removeStickyBroadcast,並傳入要刪除的Sticky Intent。

 

Local Broadcast Manager

Local Broadcast Manager(局部廣播管理器)包含在Android Support Library中,用於簡化注冊Broadcast Intent,以及在應用程序內的組件之間發送Broadcast Intent的工作。

局部廣播的作用域要小一些,所以使用Local Broadcast Manager比發送全局廣播更加高效。而且使用Local Broadcast Manager也確保了應用程序外部的任何組件都收不到你廣播的Intent,所以不會有私人數據或敏感數據泄露出去的風險。

        //注冊一個局部Broadcaset Receive
        LocalBroadcastManager lbm=LocalBroadcastManager.getInstance(this); lbm.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //TODO
 } },new IntentFilter(LOCAL_ACTION)); //發送局部Broadcast Intent
        lbm.sendBroadcast(new Intent(LOCAL_ACTION)); lbm.sendBroadcastSync(new Intent(LOCAL_ACTION));

 

Pending Intent

PendingIntent類提供了一種創建可由其他應用程序在稍晚的時間觸發的Intent的機制。

 

創建Intent Filter和Broadcast Receiver

使用Intent Filter為隱式Intent提供服務

通過使用Intent Filter,應用程序組件可以聲明它們支持的動作和數據。

要把一個Activity或者Service注冊為一個可能的Intent處理程序,可以在它的manifest節點中添加一個intent-filter標簽並使用下面的標簽

  • action 使用android:name屬性指定要為之服務的動作的名稱。每個Intent Filter必須要有至少一個action標簽。
  • category 使用android:name屬性來制定應該在哪種情況下為action提供服務。每個IntentFilter標簽可以包含多個category標簽。既可以制定自己的category也可以使用Android提供的標准值。
  • data data標簽允許制定組件可以執行的數據類型;根據情況,也可以包含多個數據標簽。可以使用以下屬性的任意組合來制定你的組件所支持的數據:
    •   android:host 指定一個有效的主機名
    •   android:mimetype 制定組件可以執行的數據類型。
    •   android:path 指定URI的有效路徑值。
    •   android:port 指定主機的有效端口
    •   android:scheme 要求一種特定的模式

在以下的例子中,以http://blog.radioactiveyak.com形式開頭的鏈接都將由這個Activity來處理。

    <activity android:name=".MyBlogViewerActivity">
      <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="http" android:host="blog.radioactiveyak.com"/>
      </intent-filter>
    </activity>

 

 

監聽電量變化

想要在一個Activity中監聽電池電量或者充電狀態的變化,可以使用Intent Filter注冊一個Receiver來實現,該Intent Filter通過Battery Manager來監聽Intent.ACTION_BATTERY_CHANGED廣播。

包含當前電池電量和充電狀態的Broadcast Intent是一個sticky Intent,因此不需要實現一個Broadcast Receiver就可以在任何時間獲取到當前的電池狀態。

    IntentFilter batIntentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent battery = context.registerReceiver(null, batIntentFilter); int status = battery.getIntExtra(BatteryManager.EXTRA_STATUS, -1); boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL;

監聽鏈接變化

獲取當前鏈接狀態的詳細信息,需要使用Connectivity Manager。

    String svcName = Context.CONNECTIVITY_SERVICE; ConnectivityManager cm = (ConnectivityManager)context.getSystemService(svcName); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); boolean isConnected = activeNetwork.isConnectedOrConnecting(); boolean isMobile = activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE;

監聽Dock變化

Android設備可以放在一個汽車的dock上或者桌子的dock上。通過注冊一個Recevier來監聽Intent.ACTION_DOCK_EVENT,可以確定docking的狀態和類型。

和電池狀態一樣,dock事件的Broadcast Intent也是sticky的。

以下示例顯示了當注冊了一個監聽dock事件的Receiver后,如何從返回的Intent中獲得當前的docking狀態。

    IntentFilter dockIntentFilter = new IntentFilter(Intent.ACTION_DOCK_EVENT); Intent dock = context.registerReceiver(null, dockIntentFilter); int dockState = dock.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED ); boolean isDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;

 

在運行時管理Manifest Receiver

使用Package Manager的setComponentEnabledSetting方法,可以在運行時啟動和禁用應用程序的manifest Receiver。

想要減少應用程序的開銷,當應用程序不需要響應一些系統事件時,最好禁用監聽這些常見系統時間的manifest Receiver。這項技術也能夠讓你定時執行一個基於系統事件的動作-如當設備鏈接到Wi-Fi時去下載一個大文件-而不用考慮每次應用程序啟動后鏈接改變時廣播的開銷。

以下示例顯示了如何在運行時啟用和禁用一個manifest Receiver。

    ComponentName myReceiverName = new ComponentName(this, MyReceiver.class); PackageManager pm = getPackageManager(); // Enable a manifest receiver
 pm.setComponentEnabledSetting(myReceiverName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); // Disable a manifest receiver
 pm.setComponentEnabledSetting(myReceiverName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); // View Output
    Log.d(TAG, "Is Charging? " + isCharging); Log.d(TAG, "Is Connected? " + isConnected); Log.d(TAG, "Is Mobile? " + isMobile); Log.d(TAG, "Is Docked? " + isDocked);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM