什么是Intent
Intent是Android程序总各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以再不同组件之间传递数据。Intent一般可用于启动活动、启动服务以及发送广播等场景。
Intent分类
- 显示Intent
Intent有很多构造函数重载,其中一个是Intent(Context packageContext, Class<?>cls)。该函数接受两个参数,第一个参数是Context要求提供一个启动活动的上下文,第二个参数Class则是指定想要启动的目标活动,通过这个构造函数就可以创建出Intent的“意图”。再使用startActivity()方法启动活动,该方法接收一个Intent参数,只需要将上边创建好的Intent传入该函数即可
创建两个Activity,FirstActivity(主活动)和SecondActivity,在FirstActivity中创建一个按钮,点击之后进入SecondActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_layout);
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
// 构建Intent,传入FirstActivity.this作为上下文,传入SecondActivity.class作为目标活动
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
// 这样"意图"就比较明显了,即在FirstActivity这个活动的基础上打来SecondActivity这个活动,执行Intent
startActivity(intent);
}
});
}
运行后就可以成功启动SecondActivity了,如果想回到上一个活动,只要按下Back键就可以销毁当前活动,回到上一个活动了
- 使用隐式Intent
相比于显式Intent,隐式Intent并不明确指出想要启动哪一个活动,而是指定一系列更为抽象的action和category等信息,然后交由系统去分析这个Intent,并找出合适的活动去启动
那么什么是合适的活动呢?简答来说就是可以响应这个隐式Intent的活动。对于SecondActivity可以响应什么样的隐式Intent呢?通过在<activity>标签下配置<intent-filter>的内容,可以指定当前活动能够响应的action和category,打开AndroidMainfest.xml,添加如下代码
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.znty.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
在<action>标签中,指明了当前活动可以响应com.znty.xietao.activitytest.ACTION_START这个action,而<category>标签则包含了一些附加信息,更明确地指明了当前的活动能够响应的Intent中还可能带有category。只有<action>和<category>中的内容同时能够匹配上Intent中指定的action和category时,这个活动才能响应该Intent(就是起个名和分个类)。
回到FirstActivity中的按钮事件,修改为:
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent("com.znty.activitytest.ACTION_START");
startActivity(intent);
}
});
可以看到使用了Intent的另外一个构造函数,直接将action的字符串传进去,表明想要启动能够响应“com.znty.xietao.activitytest.ACTION_START”这个action的活动,由于category设置的是DEFAULT,这是一种默认的category,在调用startActivity()方法的时候会自动将这个category添加到Intent中。
重新运行程序,可以得到和显示Intent使用一样的效果,这里使用的是隐式Intent,这就说明了在<activity>标签下设置的action和category的内容生效了
每个Intent只能指定一个action,但却能指定多个category。现在在Intent中除了默认的category,再添加一个category。调用Intent的addcategory()方法来添加一个category
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent("com.znty.activitytest.ACTION_START");
intent.addCategory("com.zntq.activitytest.MY_CATEGORY");
startActivity(intent);
}
});
同时需要在AndroidManifest.xml中进行设置,否则的话整个程序就会直接闪退
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.znty.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.zntq.activitytest.MY_CATEGORY" />
</intent-filter>
</activity>
这样再一次运行之后,就会发现一切都正常了
更多隐式Intent的用法
使用隐式Intent不仅可以启动自己程序内的活动,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享成为了可能。比如在应用程序中需要展示一个网页,只需要调用系统的浏览器来打开这个网页就行
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
// Intent.ACTION_VIEW是一个Android系统内置的动作,它的常量值是"android.intent.action.VIEW"
intent.setData(Uri.parse("http://www.baidu.com"));
// 通过Uri.parse()方法,将一个网址字符串解析成一个UR对象,再调用Intent的setData()方法将这个Uri对象传进去
// setData()方法,它接收一个Uri对象,主要用于指定当前Intent正在操作的数据,而这些数据通常都是以字符串的形式传入到Uri.parse()方法中进行解析产生的。
startActivity(intent);
}
});
与上边的对应,还可以再<intent-filter>标签中再配置一个<data>标签,用于更精确地指定当前活动能够响应什么类型的数据。<data>标签中主要可以配置以下内容
- android:scheme。用于指定数据的协议部分,如上例中的http部分。
- android:host。用于指定数据的主机名部分,如上例中的www.baidu.com部分。
- android:port。用于指定数据的端口部分,一般紧随在主机名之后。
- android:path。用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的部分。
- android:mineType。用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
只有<data>标签中指定的内容和Intent中携带的Data完全一致时,当前活动才能够响应该Intent。不过<data>标签都不会指定过多的内容,如上边例子中,其实只需要指定android:scheme为http,就可以响应所有的http协议的Intent了。
调用系统打电话功能,改变FirstActivity中按钮想打电话的“意图”,data部分指定的协议时10086,当然还可以指定其他很多协议,如geo表示显示地理位置等
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
// 响应打电话功能
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
});
向下一个Intent传递数据
Intent不仅能启动Activity,还可以再启动Activity的时候传递数据。它的思路很简单,Intent提供了一系列putExtra()方法的重载,可以把想要传递的数据暂存在Intent中,启动了另一个活动之后,只需要把这些数据再从Intent中取出来就可以了。
比如FirstActivity中有一个字符串,现在想把这个字符串传递到SecondActivity中,可以这样编写
在FirstActivity中:
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
String data = "Hello SecondActivity";
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("extra_data", data);
// 第一个参数是键,用于后边的取值,第二个参数是真正的数据
startActivity(intent);
}
});
在SecondActivity中接收数据:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
// 通过getIntent()方法获取到用于SecondActivity的Intent
Intent intent = getIntent();
// 调用getStringExtra()方法来获取传递的数据,用键来取
// 如果传递的是整型数据,则使用getIntExtra()方法;传递的是布尔型数据getBoolExtra()方法
String data = intent.getStringExtra("extra_data");
System.out.println(data);
Log.d("SecondActivity", data);
}
}
返回数据给上一个Activity
返回上一个活动只需要按一下Back键就可以了,并没有一个用于启动活动Intent来传递数据。Activity还有一个startActivityForResult()方法也适用于启动活动的,但是这个方法期望在活动销毁的时候能够返回一个结果给上一个活动,这就是我们所需要的。
startActivityForResult()方法接收两个参数,第一个参数还是Intent,第二个参数是请求码,用于之后的回调中判断数据的来源
修改FirstActivity中按钮的点击事件
Button button = (Button)findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
// 使用startActivityForResult()方法来启动Activity,请求码只要是一个唯一值就可以了,这里传入1
startActivityForResult(intent, 1);
}
});
在SecondActivity中给按钮注册事件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_layout);
// 给按钮注册点击事件,并在点击事件中添加返回数据的逻辑
Button button2 = (Button)findViewById(R.id.button_2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建一个Intent,该Intent仅仅用于传递数据,没有指定任何的"意图"
Intent intent = new Intent();
// 把数据存放在Intent中
intent.putExtra("data_return", "Hello FirstActivity");
// 调用setresult()方法,该方法是专门用于向上一个页面传递数据的。接收两个参数,第一个参数
// 用于向上一个活动返回处理结果,一般只使用RESULT_OK或RESULT_CANCELED这两个值,第二个参数则把
// 带有数据的Intent传递回去
setResult(RESULT_OK, intent);
// 调用finish()方法来销毁当前活动
finish();
}
});
}
在SecondActivity销毁之后会回调上一个活动的onActivityResult()方法,再回到FirstActivity
// 由于使用 startActivityForResult()方法来启动SecondActivity的,在SecondActivity被销毁之后会回调
// 上一个活动也就是FirstActivity的onActivityResult()方法,因此重写该方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 该方法接收三个参数,第一个参数requestCode,就是启动活动时传入的请求码,第二个参数resultCode,即返回数据时
// 传入的处理结果,第三个参数就是携带者返回数据的Intent
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String returnedData = data.getStringExtra("data_return");
Log.d("FirstActivity", returnedData);
}
break;
default:
}
}
由于在一个Activity中有可能调用startActivityForResult()方法去启动很多不同的Activity,每一个Activity返回的数据都会回调到onActivityResult()这个方法中,因此首先要通过检查requestCode的值来判断数据源。确定数据是从SecondActivity返回的之后,再通过resultCode的值来判断处理结果是否成功,最后从data中取值并打印,这样就完成了向上一个Activity返回数据的工作。
如果用户在SecondActivity中并不是通过点击按钮而是通过按下Back键回到FirstActivity,这时候需要在SecondActivity中重写onBackPressed()方法来解决:
@Override
public void onBackPressed() {
super.onBackPressed();
Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");
setResult(RESULT_OK, intent);
finish();
}
这样当用户按下Back键,就会执行onBackPressed()方法中的代码,从而在这里添加返回数据的逻辑就行。