首页 > 代码库 > 我的Android 4 学习系列之Intent 和 Broadcast Reciever

我的Android 4 学习系列之Intent 和 Broadcast Reciever

<style></style>
目录
  • Intent 简介
  • 使用隐式和显式Intent启动Activity、子Acitivity和Service
  • 使用Linkify
  • 使用Broadcast Intent 广播事件
  • 使用 Pending Intent
  • Intent Filter 和 Broadcast Reciever 简介
  • 使用 Intent Filter 扩展应用程序的功能
  • 监听 Broadcast Intent
  • 监听设备状态改变
  • 在运行时管理manifest Reciever
Intent简介

Intent是一种消息传递机制,可以在应用程序内使用,也可以在程序间使用。Intent 可以用于:

  • 使用类名可以显式启动一个特定的Service或Activity
  • 启动Activity或Service来执行一个动作的Intent,通常需要使用特定的数据,或者对特定的数据执行动作
  • 广播某个事件已经发生

Intent 最常用的一个用法是显式的(通过要装载的类)或者隐式的(通过请求对一条数据执行某个动作)启动新的Acitivity。后一种情况,动作不一定由调用应用程序中的Acitivity执行。

使用Intent传播动作(甚至在应用程序内传播动作),而不是显式的加载类,这是一条基本的Android设计原则。它鼓励组件之间分离,允许无缝的替换应用程序元素。它还提供了一个简单的用于扩展应用程序功能的模型基础。

使用隐式和显式Intent启动Activity、子Acitivity和Service

1. 使用 Intent 启动 Activity

Intent 最常用的用途是绑定应用程序组件,并在应用程序之间进行通信。

要创建并显示一个Activity,可以调用 startActivity(myIntent);

startActivity 方法会查找并启动一个与Intent最匹配的Activity。

显式启动新的Activity

可以创建一个新的Intent来指定当前Activity上下文以及要启动的Acitivity的类。然后把Intent传递给startAcitiviy

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

startActivity(intent);

隐式的Intent和运行时延迟

隐式的Intent提供了一种机制,可以让匿名的应用程序响应动作请求。也就是说可以要求系统启动一个可执行给定动作的Activity,而不必知道需要启动那个应用程序或者Acitivity。

if(somethingWeired && itDontLookGood){

     Intent intent = new Intent(Intent.ACTION_DAIL, Uri.parse(“tel:555-2368”));

     startActivity(intent);

}

当构建一个新的隐式的Intent时,需要指定一个要执行的动作,另外,也可以提供执行那个动作的数据的URI。还可以通过向Intent添加extra来向目标Activity发送额外的数据。extra 作为一个 Buddle 对象存储在Intent中,可以通过getExtra方法检索。

确定Intent是否解析

虽然Intent机制可以让匿名的应用程序响应动作请求,但是,你无法保证用户设备上安装了特定的应用程序。所以,在调用startActivity之前,确定调用目标是否可以解析为一个Activity是一种很好的方法。

通过调用Intent的resolveActivity方法,并向该方法传入包管理器,可以对包管理器进行查询,确定是否有Activity能够启动以响应该Intent。

if(somethingWeild && itDontLookGood){
        // Create the impliciy Intent Intent to use to start a new Activity
        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-23698"));


        // 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 Google Play Store is available.
            Uri marketUri = Uri.parse("market://search?q=pname:com.myapp.packagename");
            Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(marketUri);

            // If Google Play Store 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提供数据输入(比如用户从一个列表中选择某项)的情况,子Activity特别适合。

 

(1)启动子Activity

startActivityForResult 的第二个参数是请求码。这个值将在后面用于唯一标识返回了结果的子Activity。

 

显示启动子Activity:

  private static final int SHOW_SUBACTIVITY = 1;


  private void startSubActivity(){
      Intent intent = new Intent(this, MyOtherActivty.class);
      startActivityForResult(intent, SHOW_SUBACTIVITY);
  }

 

隐式启动子Activity:

  private static final int PICK_CONTACT_SUBACTIVITY = 2;


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

(2)返回结果

当准备返回子Activity时,可以在调用finish以前调用setResult,以便向调用Activity返回一个结果。

setResult方法有两个参数:结果码和表示为Intent的结果数据本身。

结果码是运行子Activity的结果 -- 通常是Activity.RESULT_OK 或者 Activity.RESULT_CANCELED或者自定义的Response code,setResult支持任意的整数值。

作为结果返回的Intent -- 通常是URI和一组extra。

(3)处理子Activity结果

当一个Activity关闭的时候,它会触发其调用Activity的onActivityResult事件处理程序。可以通过重写这个方法来处理从子Activity返回的结果。

onActivityResult接收多个参数:

  • 请求码:启动正在返回的子Activity时候使用的请求码。
  • 结果码:子Activity设置的结果码,用来说明其结果。可以是任意整数。
  • 数据:Intent用来包装返回的数据。URI,extra。

原生的Activity动作

  1. ACTION_ALL_APPS:列出所有已安装应用程序
  2. ACTION_ANSWER:处理来电
  3. ACTION_BUG_REPORT:报告bug
  4. ACTION_CALL:打开拨号程序,并且根据URI拨打电话
  5. ACTION_CALL_BUTTON:调用拨号Activity
  6. ACTION_DELETE:启动一个Activity,允许删除Intent的数据URI中指定的数据
  7. ACTION_DIAL:打开一个拨号程序
  8. ACTION_EDIT:请求一个Activity,要求该Activity可以编辑Intent的数据URI中的数据
  9. ACTION_PICK:启动一个Activity,从Content Provider中选择一项URI
  10. ACTION_SEARCH:启动特定的搜索Activity
  11. ACTION_SEARCH_LONG_PRESS:允许截获对硬件搜索键的长按操作,通常由系统处理,以提供对搜索的语音支持
  12. ACTION_SENDTO:启动一个Activity来向Intent中URI所指定联系人发一条消息
  13. ACTION_SEND:发送Intent中指定的数据
  14. ACTION_VIEW:这是最常用的动作。视图要求以最合理的方式查看Intent的数据URI中提供的数据。不同的应用程序会根据所提供Intent数据的URI模式来处理视图要求。一般情况下,http会打开浏览器;tel会打开拨号程序;geo地址会在google地图应用程序中显示出来,而联系人信息将会在联系人呢程序中显示出来。
  15. ACTION_WEB_SEARCH:打开一个浏览器,根据SearchManager.QUERY键提供的查询执行web搜索

 

使用Linkify

Linkify是一个辅助类,他会自动在TextView类(或TextView派生类)中通过RegEx模式匹配来创建超链接。

1. 原生的Linkify链接类型

TextView textView = (TextView)findViewById(R.id.myEditText);
    Linkify.addLinks(textView, Linkify.WEB_URLS|Linkify.EMAIL_ADDRESSES);

<TextView
      android:layout_height="wrap_content"
      android:layout_width="match_parent"
      android:autoLink="phone|email" />

 

2. 创建定制的链接字符串

String baseUri = "content://com.paad.earthquake/earthquaks";
    PackageManager pm = getPackageManager();
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(baseUri));
    boolean activityExists = intent.resolveActivity(pm) == null;
    if(activityExists){
        int flags = Pattern.CASE_INSENSITIVE;
        Pattern p = Pattern.compile("\\bquake[\\s]?[0-9]+\\b", flags);
        Linkify.addLinks(myTextView, p, baseUri);
    }

 

3. 使用MatchFilter

class MyMatchFilter implements MatchFilter{

    @Override
    public boolean acceptMatch(CharSequence arg0, int arg1, int arg2) {
        //
        return (arg1 == 0 || arg0.charAt(arg2) != ‘!‘);
    }
  }

4. 使用Transform Filter

  class MyTransformFilter implements TransformFilter{

    @Override
    public String transformUrl(Matcher match, String url) {
        // TODO Auto-generated method stub
        return url.toLowerCase().replace(" ", "");
    }
  }

 

使用Broadcast Intent 广播事件

Broadcast Intent用于向监听器通知系统的应用程序或者应用程序事件,从而可以扩展应用程序间的事件驱动的编程模型。

1. 使用Intent来广播事件

public static final String NEW_LIFEFROM_DELETED = "com.paad.action.NEW_LIFEFROM";

 

Intent intent = new Intent(LifeformDetectedReciever.NEW_LIFEFORM);
intent.putExtra(LifeformDetectedReciever.EXTRA_LIFEFORM_NAME, detectedLifeform);
intent.putExtra(LifeformDetectedReciever.EXTRA_LONGITUDE, currentLongitude);
intent.putExtra(LifeformDetectedReciever.EXTRA_LATITUDE, currentLattitude);

sendBroadcast(intent);

2. 使用Broadcast Reciever来监听广播

Broadcast Reciever(通常简称接收器),可以用来接受 Broadcast Intent。要使 Broadcast Reciever能够接受广播,需要对其进行注册,既可以代码注册,也可以在mainifest文件中注册(此时称为manifest接收器)。无论如何注册,都需要使用一个Intent Filter来指定它要监听那些Intent和数据。

要想创建一个Broadcast Reciever 需要扩展BroadcastReciever类:

public class MyBroadcastReciever extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            //  响应接收到的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 final static String ACTION_BURN = "com.paad.alien.action.BURN_IT_WITH_FIRE";
        public final static String NEW_LIFEFROM = "com.paad.alien.action.NEW_LIFEFROM";
        @Override
        public void onReceive(Context context, Intent intent) {
            // 从Intent获得lifeform的细节
            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

影响特定Activity的UI的Broadcast Receiver通常在代码中注册。在代码中注册的Broadcast Receiver只会在包含它的应用程序组件运行时响应Broadcast Intent。在这种情况下,在onResume处理程序中注册接收器,并在onPause()中注销他是一种好的做法。

private IntentFilter filter = new IntentFilter(LifeformDetectedReceiver.NEW_LIFEFROM);
    private LifeformDetectedReceiver receiver = new LifeformDetectedReceiver();
    @Override
    public void onResume(){
        super.onResume();
        //注册Broadcast Receiver
        registerReceiver(receiver, filter);
    }
    @Override
    public void onPause(){
        // 注销Broadcast Receiver
        unregisterReceiver(receiver);
        super.onPause();
    }

 

在manifest中注册Broadcast Receiver

<receiver android:name=".LifeformDetectedReceiver">
            <intent-filter>
                <action android:name="com.paad.alien.action.NEW_LIFEFROM" />
            </intent-filter>
        </receiver>

 

3. 广播有序的Intent

可以使用sendOrderedBroadcast(intent, requiredPermission);

也可以在manifest中指定优先级priority。

<receiver android:name=".LifeformDetectedReceiver">
            <intent-filter android:priority="100">
                <action android:name="com.paad.alien.action.NEW_LIFEFROM" />
            </intent-filter>
        </receiver>

4. 广播Sticky Intent

Sticky Intent 是 Broadcast Intent 的变体,可以保存它们最后一次广播的值,并且当有一个新的接收器注册为接受该广播时,它们会把这些值作为Intent返回。

sendStickyBroadcast(intent);

removeStickyBroadcast(intent);

Local Broadcast Manager 局部广播管理器

Local Broadcast Manager 包含在 Android Support Library中,用于简化注册Broadcast Intent,以及在应用程序内的组件之间发送Broadcast Intent 的工作。

使用 Pending Intent

PendingIntent 提供了一种创建可由其他应用程序在稍晚的时间触发的Intent的机制。 Pending Intent 通常用于包装在响应将来的事件时触发的Intent,例如单击Widget或者Notificaction。

PendingIntent 类提供了构建Pending Intent的静态方法,以便启动Activity、启动Service或者广播Intent。

Intent Filter 和 Broadcast Reciever 简介

在Activity和Service中,Intent代表了对在某个数据集上执行的动作的请求,Intent Filter则声明了一个应用程序组件能够对一个类型的数据执行操作。Inetent Filter 还用来指定Broadcast Receiver感兴趣接受的动作。

使用Intent Filter为隐式Intent服务

在manifest中添加intent-filter,并使用下面的标签以及相关属性:

  • action : 指定为之服务的动作的名称。每个intent Filter必须要有至少一个action(动作)标签。Action应该是一个描述性的唯一的字符串,所以最好的做法使用基于Java的包命名约定的命名系统。
  • category: 使用android:name 属性来指定应该在那种情况下为action提供服务,每个inter Filter 可以包含多个category标签。既可以指定自定义的category也可以使用Android提供的标准值:

(1)ALTERNATIVE 可以把这个动作指定为在特定数据类型上执行的默认动作的可选项。

(2)SELECTED_ALTERNATIVE 与ALTERNATIVE相似。使用在有很多种可能性的时候。

(3)BROWSABLE 指定一个在浏览器内部可用的动作。

(4)DEFAULT 通过设置这个类型可以使一个组件成为Intent Filter内指定的数据类型的默认动作。

(5)HOME 通过将一个Intent Filter的类别设置为HOME,而不指定一个action,就可以把它作为本地屏幕的可选项。

(6)LAUNCHER 使用这个类别,会让一个Activity出现在应用程序的启动器中。

  • data data标签允许指定组件可以执行的数据类型;根据情况,可以包含多个数据标签。可以使用一下属性的任意组合来指定你的组件所支持的数据:
  • android:host 制定一个有效的主机名
  • android:mimetype 指定组件可以执行的数据类型
  • android:path 指定URI的有效路径
  • android:port 指定主机的有效端口
  • android:scheme 要求一种特定的模式(如content or http)

1. Android 如何解析 Intent Filter

当在startActivity中传入一个隐式Intent时,决定启用那个Activity的过程教Intent的解析。Intent解析的目的是使用一下步骤来找出最匹配的Intent Filter:

(1)Android将已安装包的可用的IntentFilter放到一个List中

(2)如果Intent Filter包含了指定的动作,那么就认为动作匹配了。对于category来说,IntentFilter必须包含待解析的Intent中所有category,但可以包含Intent中所不包含的的其他category。一个没有指定category的Intent Filter只能和没有任何category的Intent匹配。

(3)最后,Intent的数据URI的每一个部分都和Intent Filter的data标签进行比较。

  • MIME 匹配是指匹配的数据的数据类型。
  • scheme URI协议的一部分
  • hostname 或 data authority 是 URI 位于 sheme 和 host 之间的部分
  • 数据path是authority之后的内容

(4)当隐式启动一个Activity时,如果这个进程解析出多个组件,那么所有可能匹配的组件都会呈现供用户选择。对于Broadcast Receiver,每个匹配的接收器将接受Broadcast Intent

2. 在Activity中找到和使用接收到Intent

当通过隐式Intent启动应用程序组件时,它需要找出需要执行的动作和对那些数据执行动作。

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取Fragment的引用
        FragmentManager fm = getFragmentManager();
        ToDoListFragment todoListFragment = (ToDoListFragment)fm.findFragmentById(R.id.toDoListFragment);
        todoItems = new ArrayList<String>();

        aa = new ArrayAdapter<String>(this, R.layout.todolist_item, todoItems);
        todoListFragment.setListAdapter(aa);
        Intent intent = getIntent();
        String action = intent.getAction();
        Uri data = http://www.mamicode.com/intent.getData();

    }

3. 传递责任

可以使用 startNextMatchActivity 方法将处理动作的责任传递给下一个最佳匹配的Activity。

Intent intent = getIntent();
        if(isDuringBreak){
            startNextMatchingActivity(intent);
        }

4. Demo一下,选择联系人信息

(1)创建一个新的ContactPicker项目,其中包含一个ContactPicker Activity

package com.example.contactpicker;

import android.app.Activity;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

public class ContactPickerActivity extends Activity {

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.contact_picker, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

(2)通过修改main.xml,包含一个ListView控件,后面将使用这个控件显示联系人。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.contactpicker.ContactPickerActivity" >

    <ListView
        android:id="@+id/contactListView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    />

</LinearLayout>

(3)创建一个新的包含一个单独的TextView控件 listitemlayout.xml 布局资源。它将用来在ListView中显示具体的每个联系人。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/itemTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:textSize="16dp"
        android:textColor="#FFF"   
    />
</LinearLayout>

(4)修改 ContactPicker Activity。重写onCreate方法。

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

a。创建一个新的Cursor来遍历存储在联系人列表中联系人,并使用SimpleCursorArrayAdapter绑定到ListView上。


        final Cursor c = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
        String[] from = new String[]{ Contacts.DISPLAY_NAME_PRIMARY };
        int[] to = new int[]{ R.id.itemTextView };
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.listitemlayout, c, from, to);
        ListView lv = (ListView)findViewById(R.id.contactListView);
        lv.setAdapter(adapter);

 

b。为 ListView 添加一个 onItemClickListener。从该列表中选择一个联系人时,应该将该项的路径返回给调用Activity。


        lv.setOnItemClickListener(new ListView.OnItemClickListener(){

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                //将cursor移至选中项
                c.moveToPosition(position);
                //获得行Id
                int rowId = c.getInt(c.getColumnIndexOrThrow("_id"));
                //构建result URI
                Uri outURI = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, rowId);
                Intent outData = http://www.mamicode.com/new Intent();
                outData.setData(outURI);
                setResult(Activity.RESULT_OK, outData);
                finish();
            }
        });
    }

(5)修改manifest文件,并更新Activity的intent-filter标签以添加在联系人数据上对ACTION_PICK动作支持。

<activity
            android:name=".ContactPickerActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.PICK" />

                <category android:name="android.intent.category.DEFAULT" />

                <data
                    android:path="contacts"
                    android:scheme="content" />
            </intent-filter>
        </activity>

(6)至此完成了子Activity。要对其测试,需创建一个ContactPickerTester Activity。创建一个新的布局资源—contact_picker_tester.xml,它包含了一个用来显示选中的联系人的TextView和一个用来启动子Activity的Button。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.contactpicker.ContactPickerTesterActivity" >

    <TextView
        android:id="@+id/selected_contact_textview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    />
    <Button
        android:id="@+id/pick_contact_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Pick Contact"
    />

</LinearLayout>

(7)重写ContactPickerTester的onCreate方法来添加一个Button的Click listener,这样它就可以通过指定ACTION_PICK和联系人数据库URI(content://contacts/)来隐式启动一个新的 子Activity。

package com.example.contactpicker;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class ContactPickerTesterActivity extends Activity {

    public static final int PICK_CONTACT = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.contact_picker_tester);
        Button button = (Button)findViewById(R.id.pick_contact_button);
        button.setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts/"));
                startActivityForResult(intent, PICK_CONTACT);
            }
        });
    }

}

 

(8)当子Activity返回时,使用结果来填充带有已选中的联系人姓名的TextView:

@Override
    public void onActivityResult(int reqCode, int resCode, Intent data){
        super.onActivityResult(reqCode, resCode, data);
        switch(reqCode){
            case (PICK_CONTACT) : {
                if(resCode == Activity.RESULT_OK){
                    Uri contactData = http://www.mamicode.com/data.getData();
                    Cursor c = getContentResolver().query(contactData, null, null, null, null);
                    c.moveToFirst();
                    String name = c.getString(c.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
                    c.close();
                    TextView tv = (TextView)findViewById(R.id.selected_contact_textview);
                    tv.setText(name);
                }
                break;
            }
            default : break;
        }
    }

(9)当测试工具完成后,还需要修改manifest。在uses-permission标签中添加一个READ_CONTACTS权限,以允许应用程序能够访问联系人数据库。

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

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".ContactPickerActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.PICK" />

                <category android:name="android.intent.category.DEFAULT" />

                <data
                    android:path="contacts"
                    android:scheme="content" />
            </intent-filter>
        </activity>
        <activity
            android:name=".ContactPickerTesterActivity"
            android:label="@string/title_activity_contact_picker_tester" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

Screenshot_2014-09-06-16-40-19

Screenshot_2014-09-06-16-41-17

使用 Intent Filter 扩展应用程序的功能

已经使用Intent Filter声明Activity在不同类型的数据上能够执行的动作,最好应用程序同样可以查找到在一个特定的数据上那个动作可以执行的。Android提供一个插件模型,可以让你的应用程序利用由你自己或者第三方应用程序组件所匿名提供的功能,这些应用程序组件是你现在还没有想到的,不用修改项目或者重新编译你的项目。

1。向应用程序提供匿名的动作

要想使用这种机制使现有的应用程序可以匿名使用Activity的动作,需要在它们的manifest文件中使用Intent-filter标签来发布它们。

<activity
            android:name=".NostromoContoller">
            <intent-filter
                android:label="@string/Nuke_From_Orbit"><!-- 描述动作的label属性应该是可读的 -->
                <action android:name="com.pad.nostomo.NUKE_FROM_ORBIT" />
                <data android:mimeType="vnd.moonbase.cursor.item/*" />
                <!-- Category 应该是ALTERNATIVE和SELECTED_ALTERNATIVE中的一个或者两个 -->
                <category android:name="android.intent.category.ALTERNATIVE" />
                <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
            </intent-filter>
        </activity>

2。从第三方Intent Receiver中发现新的动作

通过Package Manager,可以创建一个指定了数据类型和action类别的Intent,让系统返回一个能够在数据上执行该动作的Activity列表。

3。把匿名的动作作为菜单项集成

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        super.onCreateOptionsMenu(menu);


        //创建一个Intent用来解析那个动作应该出现在菜单中
        Intent intent = new Intent();
        intent.setData(MoonBaseProvider.CONTENT_URI);
        intent.addCategory(Intent.CATEGORY_SELECTED_ALTERNATIVE);


        //正常的菜单选项用来为要添加的菜单项设置group和ID值
        int menuGroup = 0;
        int menuItemId = 0;
        int menuItemOrder = Menu.NONE;


        //提供调用动作的组件名称--通常为当前的Activity
        ComponentName caller = getComponentName();


        //首先定义应该添加的一些Intent
        Intent[] specificIntents = null;


        //通过前面Intent创建的菜单项将填充这个数组
        MenuItem[] outSpecificItems = null;


        //设置任意选项的标识
        int flags = Menu.FLAG_APPEND_TO_GROUP;


        //填充菜单
        menu.addIntentOptions(menuGroup, menuItemId, menuItemOrder, caller, specificIntents, intent, flags, outSpecificItems);


        return true;
    }

监听 Broadcast Intent

很多的系统service都会广播Intent来指示一种变化。可以使用这些信息来向自己的项目中添加基于系统事件的功能,如时区的变化、数据连接状态、新的SMS信息或者电话呼叫。

  1. ACTION_BOOT_COMPLETED 系统完成了他的启动序列。要想接收这个广播,应用程序需要具有RECEIVE_BOOT_COMPLETED的权限。
  2. ACTION_CAMERA_BUTTON 当单机拍照按键时触发
  3. ACTION_DATE_CHANGED 和ACTION_TIME_CHANGED 如果日期和时间被手动修改了触发
  4. ACTION_MEDIA_EJECT 如果用户选择弹出外部存储媒介,则首先触发这个事件。
  5. ACTION_MEDIA_MOUNTED 和 ACTION_MEDIA_UNMOUNTED 任何时候,新的外部存储媒介被成功的添加或者从设备移除时,都会触发这两个事件
  6. ACTION_NEW_OUTGOING_CALL 当向外进行拨打电话时就会广播。监听这个广播就会截获播出的电话呼叫。拨打的电话号码存储在EXTRA_PHONE_NUMBER extra中,而返回的Intent中的resultData则是实际拨打的号码。要为这个动作注册一个Broadcast Receiver,应用程序必须声明PROCESS_OUTGOING_CALLS使用权限
  7. ACTION_SCREEN_OFF 和 ACTION_SCREEN_ON 当屏幕关闭、打开时进行广播
  8. ACTION_TIMEZONE_CHANGED 当手机的当前时区发生改变的时候就会广播这个动作。这个Intent包含一个time_zone extra。它返回新的java.util.TimeZone的ID
监听设备状态改变

1. 监听电量的变化

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;

 

注意,不能在manifest文件的receiver中注册电量变化的动作:然而,可以使用下面的动作字符串监控与电源的连接情况以及电源的连接的连接情况以及低电量情况,每个字符串以android.intent.action作为前缀:

ACTION_BATTERY_LOW

ACTION_BATTERY_OKEY

ACTION_POWER_CONNECTED

ACTION_POWER_DISCONNECTED

 

2. 监听连接的变化

连接的变化,包括带宽、延迟和Intent连接是否可用等信息,对于应用程序来说都是重要的信号。特别地,你可能在失去连接时停止时循环更新或者延迟下载一个重要的文件,知道有wi-fi连接时再去下载。

 

3. 监听Doc的变化

Android 设备可以放在一个汽车上的dock上或者桌子上的dock上。这些dock从术语上讲可以模拟或者数字的dock。通过注册一个Receiver来监听Intent.ACTION_DOCK_EVENT(android.intent.action.ACTION_DOCK_EVENT),可以确定docking的状态的类型。

 

在运行时管理manifest Reciever

使用Package Manager的setComponentEnabledSetting方法,可以在运行时启用和禁用应用程序的manifest Receiver。可以使用这种技术来启用和禁用任何应用程序组件(包括Activity和Service),但对于manifest Receiver尤其有用。

ComponentName myReceiverName = new ComponentName(this, ContactPickerActivity.class);
PackageManager pm = getPackageManager();

// 启用一个manifest Receiver
pm.setComponentEnabledSetting(myReceiverName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

//禁用一个manifest Receiver
pm.setComponentEnabledSetting(myReceiverName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);

我的Android 4 学习系列之Intent 和 Broadcast Reciever