Android学习 开发文档(Training)06 app间交互

interacting with other Apps

为了实现activity之间的交互,我们必须使用Intent。当我们使用startActivity()来向系统传递一个intent的时候,系统会用intent来定位和开启一个合适的activity。

intent可以明确指定一个activity的实例,也可以是模糊的比如拍照。

1. sending the user to another app

当你想要是一个独立的app执行一个动作,就好比开启一个地图,你必须使用模糊intent。

1.1building an implicit intent

模糊intent不会声明将要开始的组建类名,而是声明一个将要执行的动作。动作会说明你想要执行的操作。intent也会包含和动作相关的数据,比如uri数据。

如果数据是uri,有一个简单的intent构造函数还定义动作和数据。

Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

当你的app调用startActivity()的时候,手机就会用你给的电话号码拨打。

Here are a couple other intents and their action and Uri data pairs:

  • view a map
// Map point based on address
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
// Or map point based on latitude/longitude
// Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
  • view a web page:
Uri webpage = Uri.parse("http://www.android.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);

其他类型的模糊intent需要额外不同类型的数据,比如一个字符串。可以使用putExtra()来添加一个或者多个。

默认情况下,基于uri系统会决定合适的MIME类型来供intent使用。If you don’t include a Uri in the intent, you should usually use setType() to specify the type of data associated with the intent. Setting the MIME type further specifies which kinds of activities should receive the intent.

  • send an email with an attachment:
Intent emailIntent = new Intent(Intent.ACTION_SEND);
// The intent does not have a URI, so declare the "text/plain" MIME type
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"[email protected]"}); // recipients
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));
// You can also attach multiple items by passing an ArrayList of Uris
  • create a calendar event:
Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);
Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30);
Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30);
calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());
calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());
calendarIntent.putExtra(Events.TITLE, "Ninja class");
calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");

1.2 verify there is an app to receive the intent

为了保证app不奔溃,我们必须在启动前验证是不是存在app来接受我们的intent。调用 queryIntentActivities() 来得到可用app的列表。如果列表没有空说明存在。

PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
        PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

1.3 start a activity with the intent

如果你已经创建了你的intent并且设置了外部的参数,调用startActivity()。如果有多个app可以执行这个intent系统会给出一个对话框来让你进行选择。

startActivity(intent);

下面是一个稍微复杂一点的例子

// Build the intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

// Verify it resolves
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;

// Start an activity if it's safe
if (isIntentSafe) {
    startActivity(mapIntent);
}

1.3 show a app chooser

如果存在超过一个的app可以执行你的intent,你可以选择一个默认的打开程序
。但如果是分享类的,你可能需要多个app来执行这个操作。为了创建这么一个选择的对话框,你可以使用createChooser()来创建一个intent。

Intent intent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);

// Verify the intent will resolve to at least one activity
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

2. getting a result from an activity

你也可以开启一个activity,然后得到一个结果。调用startActivityForResult()
比如,你可以打开照相机app,然后接收到拍摄的照片。当然,返回值的activity必须被设计为return一个值。执行完毕后,它发送另一个app作为结果,你的ativity通过onActivityResult()接收。

2.1 start the activity

这个时候的intent和之前的intent其实没有什么特殊的,但是startActivityForResult()需要一个额外的int参数。这个integer参数是一个request code,定义了你的要求。当你接受结果intent的时候,回调函数会提供相同的要求代码,方便你的app可以很好的识别结果和处理。

static final int PICK_CONTACT_REQUEST = 1;  // The request code
...
private void pickContact() {
    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
    pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
    startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}

2.2 receieve the result

返回结果的时候,系统会调用onActivityResult()方法。这个方法包含三个参数

  • The request code you passed to startActivityForResult().
  • A result code specified by the second activity. This is either
    RESULT_OK if the operation was successful or RESULT_CANCELED if the
    user backed out or the operation failed for some reason.
  • An Intent that carries the result data.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check which request we're responding to
    if (requestCode == PICK_CONTACT_REQUEST) {
        // Make sure the request was successful
        if (resultCode == RESULT_OK) {
            // The user picked a contact.
            // The Intent's data Uri identifies which contact was selected.

            // Do something with the contact here (bigger example below)
        }
    }
}

为了很好的处理结果,必须了解结果intent的格式。如果是自己的app就很容易得知,如果是系统平台的app,系统会给出他们自己的接口。For instance, the People app always returns a result with the content URI that identifies the selected contact, and the Camera app returns a Bitmap in the “data” extra (see the class about Capturing Photos).

Bonus: Read the contact data:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check which request it is that we're responding to
    if (requestCode == PICK_CONTACT_REQUEST) {
        // Make sure the request was successful
        if (resultCode == RESULT_OK) {
            // Get the URI that points to the selected contact
            Uri contactUri = data.getData();
            // We only need the NUMBER column, because there will be only one row in the result
            String[] projection = {Phone.NUMBER};

            // Perform the query on the contact to get the NUMBER column
            // We don't need a selection or sort order (there's only one result for the given URI)
            // CAUTION: The query() method should be called from a separate thread to avoid blocking
            // your app's UI thread. (For simplicity of the sample, this code doesn't do that.)
            // Consider using CursorLoader to perform the query.
            Cursor cursor = getContentResolver()
                    .query(contactUri, projection, null, null, null);
            cursor.moveToFirst();

            // Retrieve the phone number from the NUMBER column
            int column = cursor.getColumnIndex(Phone.NUMBER);
            String number = cursor.getString(column);

            // Do something with the phone number...
        }
    }
}

3. allowing other apps to start your activity

之前我们都是通过我们的app来启动其他的app,这里我们开始学习如何从其他的app通过action来启动我们自己的app。我们需要在manifest文件中为<activity>添加<intent-filter>元素。

在你的app安装之后,系统会识别你的intent filter并且将信息添加到内部所有intent的分类中。当调用startActivity() or startActivityForResult()的时候,如果是一个模糊的intent,那么寻找这个可以执行intent的相关app。

3.1 add an intent filter

intent filter应该根据活动的类型和activity接受的数据尽可能准确。

  • action
    定义要执行的操作的一个字符串,通常使用平台定义好的值类似于ACTION_SEND
  • data
    对于数据的一个描述,在intent filter中使用元素。you can specify just the MIME type, just a URI prefix, just a URI scheme, or a combination of these and others that indicate the data type accepted.
  • category
    提供一个附加的方式来标识处理intent的activity,通常和用户的手势或者定位有关系
    。系统中有几种不同的类别,但是很多是几乎不怎么用到的。所有的模糊intent都是默认CATEGORY_DEFAULT 定义。

For example, here’s an activity with an intent filter that handles the ACTION_SEND intent when the data type is either text or an image:

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
        <data android:mimeType="image/*"/>
    </intent-filter>
</activity>

每个<intent-filter>只能有一个action和数据类型。但是可以在多个<intent-filter>中定义多个。

如果两个操作的数据和action是互斥的,我们需要创建两个<intent-filter>

例如,假设您的活动同时处理ACTION_SEND和ACTION_SENDTO意图的文本和图像。在这种情况下,您必须为两个操作定义两个独立的intent过滤器,因为ACTION_SENDTO intent必须使用数据Uri来指定收件人的地址,使用send或sendto Uri方案。例如:

<activity android:name="ShareActivity">
    <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->
    <intent-filter>
        <action android:name="android.intent.action.SENDTO"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="sms" />
        <data android:scheme="smsto" />
    </intent-filter>
    <!-- filter for sending text or images; accepts SEND action and text or image data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

为了处理模糊intent,必须设置category为 CATEGORY_DEFAULT,不然不会有反应。

3.2 handle the intent in your activity

为了知道activity需要执行什么操作,可以读取intent。启动你的app后,你可以通过getIntent()来得到启动app的intent,可以在生命周期的任何时候,但是普遍在早期,比如oncreate()或者onstart()。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    // Get the intent that started this activity
    Intent intent = getIntent();
    Uri data = intent.getData();

    // Figure out what to do based on the intent type
    if (intent.getType().indexOf("image/") != -1) {
        // Handle intents with image data ...
    } else if (intent.getType().equals("text/plain")) {
        // Handle intents with text ...
    }
}

3.3 return a result

调用setResult()来指明结果代码和intent。并且可以通过finish()来关闭activity。

// Create intent to deliver some kind of result data
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri"));
setResult(Activity.RESULT_OK, result);
finish();

结果代码要么是RESULT_OK和RESULT_CANCEL,并且默认是cancel。必要的时候可以在intent中添加数据。

如果你只是想要返回一个整形数据,那么你可以直接设置结果代码为你的大于0的任何值。这样就可以不用包括intent了。

setResult(RESULT_COLOR_RED);
finish();

没有必要检查activity是否被startActivity()或者startActivityForResult()得到的。都运行setResult()。如果要返回,那么会自动执行,如果不用返回就会自动忽略。

至此,结束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章