首页 > 代码库 > Android开发- Intent和Broadcast Receiver

Android开发- Intent和Broadcast Receiver

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

  • ACTION_MAIN
  • ACTION_VIEW
  • ACTION_ATTACH_DATA
  • ACTION_EDIT
  • ACTION_PICK
  • ACTION_CHOOSER
  • ACTION_GET_CONTENT
  • ACTION_DIAL
  • ACTION_CALL
  • ACTION_SEND
  • ACTION_SENDTO
  • ACTION_ANSWER
  • ACTION_INSERT
  • ACTION_DELETE
  • ACTION_RUN
  • ACTION_SYNC
  • ACTION_PICK_ACTIVITY
  • ACTION_SEARCH
  • ACTION_WEB_SEARCH
  • ACTION_FACTORY_TEST

 


 

 

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 =http://www.mamicode.com/ 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);

 

Android开发- Intent和Broadcast Receiver