Android开发- Intent和Broadcast Receiver

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 


 

 

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);

 

以上是关于Android开发- Intent和Broadcast Receiver的主要内容,如果未能解决你的问题,请参考以下文章

Android开发之Intent.Action Android中Intent的各种常见作用【转】

Android开发实践 Intent

转载Android开发学习笔记:Intent的简介以及属性的详解

Android开发Intent应用概述

Android开发学习笔记:Intent的简介以及属性的详解转

Android开发学习之路--Broadcast Receiver初体验