@stepbystep
2015-01-28T05:35:17.000000Z
字数 16238
阅读 4458
Android
说明: Intent用于激活组件,包括Activity、Service-服务、BroadcastReceiver-广播接受者,即四大组件中除了内容提供者ContentProvider
通过Context.startActivity()
orActivity.startActivityForResult()
启动一个Activity;
通过 Context.startService()
启动一个服务,或者通过Context.bindService()
和后台服务交互;
通过广播方法(比如 Context.sendBroadcast()
,Context.sendOrderedBroadcast()
, Context.sendStickyBroadcast()
) 发给BroadcastReceiver。
使用Android系统内置的Intent来完成发送短信、拨打电话、浏览网页和查看地图等基本功能,利用其Data和Action属性有关
激活组件时,用Intent的Data属性或Extra属性传递数据
课前复习
Intent intent=new Intent(this,NewActivity.class);
this.startActivity(intent);
starActivityForResult(Intent intent, int requestCode)
方法来实现requestWindowFeature(Window.FEATURE_NO_TITLE);
去掉标题栏android:theme="@android:style/Theme.NoTitleBar"
去掉标题栏android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
设置全屏属性字段 | 名称 | 数据类型 | 获取方法 | 设置方法 |
---|---|---|---|---|
ComponentName | 组件名 | ComponentName | getComponent() | setComponetn() setClass() setClassName() |
Action | 动作 | String | getAction() | setAction() |
Data | 数据 | URL | getData() getType() | setData() setType() setDataAndType() |
Category | 分类 | String | getCategories() | addCategory() removeCategory() |
Extra | 额外信息 |
键值对形式<String, value> , 2. Bundle
|
get*Extra(); getExtras()->Bundle | put*Extra() putExtras() |
Action | 动作 | String | getAction() | setAction() |
Android.Intents.action.Main
Action值 | 目标组件 | 功能 |
---|---|---|
ACTION_CALL | Activity | 拨打Data属性指定的电话号码 |
ACTION_MAIN | Activity | 设置Activity做为程序的入口 |
ACTION_DIAL | Activity | 将Data属性中的电话号码放入拨打界面,显示给用户,供用户手动拨出这个号码 |
ACTION_View | Activity | 使用与Data匹配的应用程序显示Data指定的数据,如网页,播放mp3 |
ACTION_SEND | Activity | 发送数据 |
ACTION_SCREEN_ON | Broadcast Receiver | 屏幕被打开 |
当然,也可以自定义动作(自定义的动作在使用时,需要加上包名作为前缀,如"com.example.shiyanlou.SHOW_COLOR”),并可定义相应的Activity来处理我们的自定义动作。
设置了组件名的Intent叫做显式Intent,这种Intent在匹配时直接使用设置的参数。
// 第一种隐式
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
// 第二种方式
Intent intent = new Intent();
ComponentName componentName = new ComponentName(this, MainActivity.class);
intent.setComponent(componentName);
startActivity(intent);
不设置组件名的Intent叫做隐式Intent,这种Intent主要是通过查找已注册在AndroidManifest.xml中的所有IntentFilter及其中定义的Intent,最终找到匹配的Intent。在这个解析过程中,Android是通过Intent的action、type、category这三个属性来进行判断的。
Intent intent = new Intent();
intent.setAction("com.example.shiyanlou.test");
startActivity(intent);
使用场合:
显式意图比较适合于在用一个应用程序中,由于指定了目标组件,启动速度比较快
隐式意图即适合同一个应用和可以用在不同的应用程序之间,建议在不同的应用程序之间激活组件或者传递数据时使用隐式意图。
Data值 | 说明 | 示例 |
---|---|---|
file:/// | 本地文件数据,后接文件路径 | file:///mnt/sdcard/mp3/alone.mp3 |
http:// | 超文本链接,接网络资源的地址 | http://www.baidu.com |
smsto:// | 短信,后接目标号码 | smsto://182* |
tel:// | 电话,后接目标号码 | tel://182* |
mailto:// | 电子邮件,后接接收人地址 | mailto://193*@qq.com |
content:// | 内容,后接内容的定位 | content://contacts/people/* |
startActivity(new Intent(Intent.ACTION_VIEW, URI.parse("file:///mnt/adcard/mp3/alone.mp3")));
startActivity(new Intent(Intetn.ACTION_VIEW, URI.parse("http://www.baidu.com")));
startActivity(new Intent(Intetn.ACTION_CALL, URI.parse("tel://10086")));
startActivity(new Intent(Intetn.ACTION_SENDTO, URI.parse("smsto://10086")));
Category值 | 功能 |
---|---|
CATEGORY_BROWSABLE | 目标Activity可以被浏览器使用超链接触发,并能显示相应的数据 |
CATEGORY_HOME | 显示Home界面的Activity,可以用来显示Home界面 |
CATEGORY_LAUNCHER | 一个任务栈栈底的Activity,一般是程序的入口Activity |
CATEGORY_DEFAULT | 默认的,Android系统会给隐式Intent自动加上这个值 |
- Category主要被设置在IntentFilter中
- 对于显式Intent,如果不指定Category,则无论IntentFilter的内容是什么都是匹配的。
- 对于隐式Intent,Android会自动加上Category_DEFAULT,这样的话,IntentFilter中也必须加上此条目,否则匹配就会失败
// 方式一
// 发送方
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("name", "shiyanlou");
intent.putExtra("time", "2015-01-21 14:22");
intent.putExtra("lines", 1000);
startActiviy(intent);
// 接收方
Intent intent = getIntent();
String name = intent.getStringExtra("name");
String time = intetn.getStringExtra("time");
int lines = intent.getIntExtra("lines", 0);
// 方式二
// 发送方
Intent intent = new Intent(this, MainActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "shiyanlou");
bundle.putString("time", "2015-01-21 14:22");
bundle.putInt("lines", 1000);
intent.putExtras(bundle);
startActivity(intent);
// 接收方
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String name = bundle.getString("name");
String time = bundle.getString("time");
int lines = bundle.getInt("lines", 0);
本实验主要是为了验证Action与Data配合使用的实验,当然实验中也插入了一些其他的知识供大家学习。
MainActivity.java
package com.example.shiyanlou.intentdemo;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.telephony.SmsManager;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
public class MainActivity extends Activity{
private EditText editText;
private EditText editPhoneNumber;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 导航布局文件资源
setContentView(R.layout.activity_main);
// findView,给editText赋值等
editText = (EditText) findViewById(R.id.edit_text);
editPhoneNumber = (EditText) findViewById(R.id.edit_phone_number);
// 注册点击事件,一个listenr变量可以给多个view使用,
// 因为在onClick事件中调用了switch case语句,通过View.getId()函数来判断执行
ClickHandleClass listener = new ClickHandleClass();
findViewById(R.id.btn_call).setOnClickListener(listener);
findViewById(R.id.btn_openg_url).setOnClickListener(listener);
findViewById(R.id.btn_pay_vedio).setOnClickListener(listener);
findViewById(R.id.btn_send_msg).setOnClickListener(listener);
}
/**
* 从raw文件拷贝数据到fullPath, 需要注册权限
* <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
* <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
*/
private void writeAssetsToSdcard(String fullPath){
File audioFile = new File(fullPath);
// 如果文件已经存在就不用拷贝了
if(audioFile.exists()){
return;
}
// 获取当前文件所在的目录函数用getParent()
File dir = new File(audioFile.getParent());
if (!dir.exists()) {
// 创建目录
if (!dir.mkdir()) {
// 创建目录失败, Toast吐司可以在手机底部弹出一个提示
Toast.makeText(MainActivity.this, "创建mp3目录失败", Toast.LENGTH_SHORT).show();
System.out.print("**"+dir.getAbsolutePath()+"**");
return;
}
}
// 下面执行拷贝数据的函数
try {
OutputStream myOutput = new FileOutputStream(fullPath);
InputStream myInput = getResources()
.openRawResource(R.raw.alone);
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myOutput.close();
myInput.close();
} catch (Exception e) {
/**
* 如果没有注册权限 java.io.FileNotFoundException: /storage/sdcard0/alone.mp3: open failed: EACCES (Permission denied)
*/
e.printStackTrace();
}
}
/**
* 打开手机存储卡中的文件, 内部自己创建了Intent,并启动了Intent
*/
private void openMp3OnSdcard(){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
//intent.setDataAndType(Uri.fromFile(new File("file:///mnt/sdcard/"+fullPath)), "audio/mp3"); //name.mp3值文件名
//使用api获得手机的sdcard路径适用于所有手机:/mnt/sdcard
String path= Environment.getExternalStorageDirectory().getAbsolutePath();
String fileName=new File(path+File.separator+ "raw/alone.mp3").getAbsolutePath(); //name.mp3值文件名
// 将raw文件写入目录
writeAssetsToSdcard(fileName);
// 此处设置Data和Type
/*
* setType()在设置type时会自动清空data,同理在setData()设置data时也会自动清空type,
* 因此如果想同时设置data和type则只能使用setDataAndType(data,type);
*/
//Type表示类型,如"audio/mp3",系统根据此来转换资源,如果省略,系统会根据URI的内容去添加Type的值
intent.setDataAndType(Uri.parse("file://" + fileName),"audio/mp3");
//隐式意图使用这个方法启动Activity时会自动添加一个Category:android.intent.category.DEFAULT
startActivity(intent);
}
/**
* 拨打电话的程序
* @param phoneNumber 电话号码
*/
private void call(String phoneNumber){
/*
com.example.shiyanlou.intentdemo E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.SecurityException: Permission Denial: starting Intent
{ act=android.intent.action.CALL dat=tel:xxxxxxxxxxxxx cmp=com.android.phone/.OutgoingCallBroadcaster }
from ProcessRecord{40fe1510 866:com.example.shiyanlou.intentdemo/u0a10047}
(pid=866, uid=10047) requires android.permission.CALL_PHONE
*/
Intent intent = new Intent();
//指定隐式意图的动作:拨打电话(转到电话拨号器的界面并填充号指定的号码)
//intent.setAction(Intent.ACTION_DIAL);
//指定拨打电话的数据(模式+电话号码)
intent.setAction(Intent.ACTION_CALL);
// 设置intent的Data属性
Uri data=Uri.parse("tel://"+phoneNumber);
intent.setData(data);
// 拨打电话
startActivity(intent);
}
/**
* 短信发送函数
* @param phoneNumber 电话号码
* @param text 短信内容
*/
private void sendMsg(String phoneNumber, String text){
/*
*
* com.example.shiyanlou.intentdemo E/AndroidRuntime﹕ FATAL EXCEPTION: main
* java.lang.SecurityException: Sending SMS message: uid 10047 does not have android.permission.SEND_SMS
* 如果报上面的错,则说明没有注册发送短信的权限
* SmsManager采用单例设计模式
*/
SmsManager smsManager=SmsManager.getDefault();
/*
* 进行普通短信的发送,如果短信内容太长,超过160字母后就会发生异常:
* E/AndroidRuntime(439): java.lang.NullPointerException
* at android.telephony.SmsManager.sendTextMessage(SmsManager.java:87),
* 解决方式为将短信内容进行拆分,SmsManager内部有封装好的方法: divideMessage(String content);
*/
if(text.length()>70){
//进行短信的拆分
ArrayList<String> msgs= smsManager.divideMessage(text);
for(String msg:msgs){
smsManager.sendTextMessage(phoneNumber, null, msg, null, null);
}
}else{
smsManager.sendTextMessage(phoneNumber, null, text, null, null);
}
// Toast吐司可以在手机底部弹出一个提示
Toast.makeText(MainActivity.this, "短信发生成功! ", Toast.LENGTH_SHORT).show();
}
/**
* 利用Intent打开一个网页
* @param url 网页地址
*/
private void opengWebByUrl(String url){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
}
// 定义内部类,实现点击事件
class ClickHandleClass implements View.OnClickListener{
@Override
public void onClick(View v) {
String str = editText.getText().toString().trim();
String phone = editPhoneNumber.getText().toString().trim();
switch (v.getId()){
case R.id.btn_call:
if(TextUtils.isEmpty(phone)){
// 使editPhoneNumber获取焦点
editPhoneNumber.requestFocus();
editPhoneNumber.setError("拨打号码不能为空!");
return;
}
// 调用拨打电话的程序
call(phone);
break;
case R.id.btn_openg_url:
if(TextUtils.isEmpty(str)){
// editText获取焦点
editText.requestFocus();
editText.setError("网址不能为空!");
editText.setHint("示例:http://www.baidu.com");
return;
}
try {
opengWebByUrl(str);
}catch (Exception e){
Toast.makeText(MainActivity.this, "网址解析异常", Toast.LENGTH_SHORT).show();
editText.setError("示例:http://www.baidu.com");
}
break;
case R.id.btn_pay_vedio:
// 播放sdcard根目录的alone.mp3文件, 为了防止没有这样的文件,本工程会将raw文件中带的alone.mp3文件拷贝过去!
openMp3OnSdcard();
break;
case R.id.btn_send_msg:
if(TextUtils.isEmpty(phone)){
editPhoneNumber.requestFocus();
editPhoneNumber.setError("短信号码不能为空!");
return;
}
if(TextUtils.isEmpty(str)){
editText.requestFocus();
editText.setError("短信内容不能为空!");
return;
}
// 调用发送短信的函数
sendMsg(phone, str);
break;
}
}
}
}
activity_main.xml
<!-- 外面用ScrollView的原因是有的屏幕太小了... 显示不下这么多控件,他可以在竖直方向是滚动内容
注意,它只能有一个子组件,该布局文件中就是LinearLayout
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<!-- 记得设置android:orientation="vertical"-->
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical">
<!--EditText可以输入文本的控件,inputType表示输入的类型,设置为phone,number等-->
<!--android:textStyle="bold":改变字体样式(normal(正常),bold(加粗),italic(倾斜))-->
<!--textColor #AA2988e4":设置字体颜色颜色,8位模式时前两位表示透明度-->
<!--A(Alpha:透明度;00完全透明;FF:完全不透明)RGB-->
<EditText
android:id="@+id/edit_phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入电话号码"
android:textColor="#AA2988e4"
android:textStyle="bold"
android:inputType="phone"
/>
<!--此处的android:singleLine="false"-->
<!--android:scrollbars="vertical"是为了防止短信内容过多,可以在竖直方向上滚动内容-->
<!--android:layout_marginTop="20dp" 表示距离上一个控件的间隔为20dp-->
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="20dp"
android:hint="请输入文本"
android:gravity="start"
android:textColor="#AA2988e4"
android:singleLine="false"
android:scrollbars="vertical"
/>
<Button
android:id="@+id/btn_call"
android:text="拨打电话"
android:background="#2988e4"
android:textColor="#fff"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn_send_msg"
android:text="发送短信"
android:background="#2988e4"
android:textColor="#fff"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn_openg_url"
android:text="打开网页"
android:background="#2988e4"
android:textColor="#fff"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn_pay_vedio"
android:text="播放多媒体"
android:background="#2988e4"
android:textColor="#fff"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
AndroidManefest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.shiyanlou.intentdemo" >
<!--发送短信权限-->
<uses-permission android:name="android.permission.SEND_SMS"/>
<!--拨打电话权限-->
<uses-permission android:name="android.permission.CALL_PHONE"/>
<!--读写储存卡权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.shiyanlou.intentdemo.MainActivity"
android:label="IntentDemo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
进入实验界面如下:
拨打号码测试
直接点击拨打电话会有如下提示:
为了测试拨打电话的功能,我们需要再创建并启动一个模拟器。
启动后的新模拟器如下图
我们可以看到,每个模拟器上面都有一个5554
,5556
的编号,这被定义为该手机的号码。拨打号码如下:
拨打成功界面如下: (可以看到,尾号5554就是拨打过去的电话号码)
发送短信测试
输入发送好5556,以及短信内容如下:
等待一会儿,短信就回成功发送,本次发送了两条。
打开网页测试
因为intent根据http://解析出为打开网页的动作,故而如果网址输入不是http://开头就会出现解析错误
错误如下:
输入正确的http://www.baidu.com
调用系统浏览器,正确跳转到相应页面。
打开mp3播放测试
点击播放多媒体按钮,会首先将raw目录下的alone.mp3拷贝到sdcard上,再启动系统对应的播放程序,播放音乐。
<intent-filter>
标签
IntentFilter filter = new IntentFilter();
filter.addAction("action_1");
filter.addAction("action_2");
<!-- manifest中实现 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
android.intent.action.MAIN
与 android.intent.category.LAUNCHER
android.intent.action.MAIN决定一个应用程序最先启动那个组件
android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里(说白了就是是否在桌面上显示一个图标) 这两个属性组合情况:
第一种情况:有MAIN,无LAUNCHER,程序列表中无图标
原因:android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里
第二种情况:无MAIN,有LAUNCHER,程序列表中无图标
原因:android.intent.action.MAIN决定应用程序最先启动的Activity,如果没有Main,则不知启动哪个Activity,故也不会有图标出现
所以这两个属性一般成对出现。
如果一个应用中有两个组件intent-filter都添加了android.intent.action.MAIN和
android.intent.category.LAUNCHER这两个属性, 则这个应用将会显示两个图标, 写在前面的组件先运行。
关于隐式intent
隐式 Intent 都至少有一个 category,就是 "android.intent.category.DEFAULT",所以只要是想接收一个隐式 Intent 的 Activity 都应该包括 "android.intent.category.DEFAULT" category,不然将导致 Intent 匹配失败.
比如说一个activity组件要想被其他组件通过隐式intent调用, 则其在manifest.xml中的声明如下:
<activity android:name="com.example.shiyanlou.MainActivity">
<intent-filter>
<action android:name="com.example.shiyanlou.test" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
intent-filter匹配优先级
Intent的过滤器(intent-filter),按照以下优先关系查找:action->data->category
流程如下:
隐式意图的匹配过程:通过查找功能清单文件中配置的意图过滤器(intent-filter)中的action,category,data和启动组件的意图(intent)进行匹配来决定是否实例化相应的组件.
1. 如果意图过滤器中没有配置任何内容,那么启动的意图中也没有配置任何内容,这时不会匹配成功.
2. 如果意图过滤器中的配置信息和启动这个Activity的意图中的字符串完全匹配,则隐式意图匹配成功(意图过滤器中至少有一个action).
3. 意图中有的配置信息,在意图过滤器中必须有才能匹配成功.意图过滤器中有的内容,意图中不一定都有,只要意图过滤器中的一个action或者category和意图中的action或者category匹配成功即可.总之:意图中有的内容,意图过滤器中必须有才能匹配成功。意图过滤器中有的内容,意图中可以只有部分匹配即可。
4. 一个android组件的配置中可以有多个意图过滤器,只有意图对象和其中任何一个意图过滤器匹配成功即可完成激活组件的工作