此文章为个人的知识总结,如有错误,欢迎指正.
作用
1.Intent(意图,目的)是android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据.
2.Intent一般可以被用于启动活动,启动服务,发送广播等场景.
3.Intent可以分为两种:显式Intent和隐式Intent.
显式Intent
显式Intent是程序内部跳转活动最常用的一种方式,其用法也很简单:
Intent intent = new Intent(PackageContext , Class);
startActivity(intent);
其中PackageContext参数为本活动所能提供的上下文,如this , MainActivity.this , Class参数为待启动的活动类,如Main2Activity.class
隐式Intent
隐式Intent相对于显式Intent来说就比较复杂,它不仅能够在程序内部跳转活动,还可以在程序间跳转活动,我们需要掌握的就是程序间跳转的规则.
一.先来看看基本的用法:
Intent intent = new Intent();
intent.setAction("android.intent.action.XX");
intent.addCategory("android.intent.category.xx");
startActivity(intent);
可以看到,我们调用了Intent类的两个方法,setAction()和addCategory(),我们知道,Intent是用来跳转活动的,所以不难看出setAction()方法是用来指向名称为android.intent.action.XX的活动,那addCategory()又是什么?
Category的中文意思种类,范畴的意思,你可以把它当作是某个活动的所属类别,我们在利用startActivity()方法启动Intent时,会先在系统里找寻类别为android.intent.category.xx,名字为android.intent.action.XX的活动,只有系统存在满足这两个条件的活动,才可以正常的跳转,否则系统将报异常.
需要指出的是,一个活动可以有多个Category,但是只能有一个Action名,并且在程序内外部,都可以存在名字,类别相同的活动,那你可以会有问,如果存在相同的名字的活动名,那程序在跳转活动时该选择哪一个呢?答案是系统会给你自己选,我们应该都有遇到下图的这种情况:
二.那如果我们自己创建的活动,该去哪里设置活动的名称和类别呢?答案在AndroidManifest.xml中,找到活动对应的activity标签:
<activity
android:name=".Main2Activity"
android:label="@string/title_activity_main2">
<intent-filter>
<action android:name="com.example.krehizilin.myapplication.Main2Activity"/>
<category android:name="com.example.krehizilin.myapplication.MY_CATEGORY" />
</intent-filter>
</activity>
action和category标签一定是放在intent-filter标签中的,其android:name属性分别就是活动名和类名,其值是可以随便取的,当是,为了统一规范,我们一般都是以活动所在的包名来开头的.
action标签只能有一个,category标签可以多个.
注意:
android.intent.category.DEFAULT是一种默认的category,我们在调用startActiivity()方法隐式启动活动时,会类似的默认添加一行intent.addCategory("android.intent.category.DEFAULT");代码,这意味着,我们需要在启动的活动添加<category android:name="android.intent.category.DEFAULT"/>,否则就会报异常.
Intent intent = new Intent();
intent.setAction("android.intent.action.XX");
intent.addCategory("android.intent.category.xx");
intent.addCategory("android.intent.category.DEFAULT");//默认添加此行代码,实际上是没有的
startActivity(intent);
<activity
android:name=".Main2Activity"
android:label="@string/title_activity_main2">
<intent-filter>
<action android:name="com.example.krehizilin.myapplication.Main2Activity"/>
<category android:name="com.example.krehizilin.myapplication.MY_CATEGORY" />
<!--添加此行代码,否则将会报异常-->
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
此外还有一点比较特殊,就是我们隐式跳转到主活动(<action android:name="android.intent.action.MAIN" />)时,并不需要为主活动添加<category android:name="android.intent.category.DEFAULT"/>,同样可以正常跳转.
典例探究
我们应该都有用过以下代码来实现拨打电话,那它具体是怎么实现的呢?
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri data = Uri.parse("tel:110");
intent.setData(data);
startActivity(intent);
以上代码的主要问题就是setData()方法,其有两个作用,一是类似Category用来匹配对应的活动(过程比Category稍微复杂),二是可以用来传递数据.
如果你不懂Uri和此方法的使用,请阅读此博客的进阶部分:https://blog.csdn.net/Krehizi/article/details/81842608
--------------------------------------------------------------------------------------------------------------------------------------------------------------
有了以上知识,我们便可以继续研究,首先,Intent.ACTION_DIAL的真实值为android.intent.action.DIAL,所以此意图指向的是名为android.intent.action.DIAL的活动,然后通过此setDate()方法再次约束此意图,使之指向符合Uri为tel:110的活动.
由于获取系统电话软件的源代码比较麻烦,我们自己创建一个活动来代替系统电话软件的拨打界面,其活动清单为:
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel"/>
</intent-filter>
</activity>
可以看到,action,category,data都能够匹配上,所以此活动我们能够正常打开,并且对应的就是打开拨打电话的界面.
之后便在我们代替系统电话软件的活动中,编写逻辑代码,获取到传递过来的Uri的中的电话号码,然后实现拨打电话的过程(拨打电话的实现代码省略,只模拟获取到电话号码):
Intent intent = getIntent();
if (intent != null){
Uri uri = intent.getData();
Toast.makeText(Main2Activity.this, uri.getSchemeSpecificPart(),Toast.LENGTH_SHORT).show();
}
uri.getSchemeSpecificPart()方法获取到的便是110了(此方法的用法在上面提到的博客上有讲解).
so,这样我们利用隐式启动的知识,弄清楚在别的软件上调用系统的电话软件拨打电话的过程了.
传递数据
向下个活动传递数据
活动MainActivity传递:
Button transferData = (Button) findViewById(R.id.transferData);
transferData.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
String data = "this is data";
Intent intent = new Intent(MainActivity.this , SecondActivity.class);
intent.putExtra("extra_data" , data);
MainActivity.this.startActivity(intent);
}
});
活动SecondActivity接收:
Button gettingData = (Button) findViewById(R.id.gettingData);
gettingData.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Toast.makeText(SecondActivity.this , "值为 : " + data , Toast.LENGTH_LONG).show();
}
});
向上个活动传递数据
MainActivity活动启动Main2Activity活动:
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this , Main2Activity.class);
startActivityForResult(intent , 1); //启动
}
});
Main2Activity活动返回数据:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("key" , "123456789");
setResult(RESULT_OK , intent); //返回
finish();
}
});
当使用setResult()方法时,MainActivity的onActivityResult()方法被回调:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode){
case 1 :
if (resultCode == RESULT_OK){
Log.d(TAG, "onActivityResult: " + data.getStringExtra("key"));
}
}
}
Bundle
Bundle主要用于传递数据(也可以是对象,对象数组),它保存的数据是以key-value(键值对)的形式存在的。
当Bundle传递的是对象或对象数组时,必须实现Serializable 或Parcelable接口。
Bundle提供了各种常用类型的putXxx()/getXxx()方法,用于读写基本类型的数据。
Intent intent = new Intent(PackageContext , Class);
Bundle bundle = new Bundle(); //得到bundle对象
bundle.putString("key1", "value"); //存入数据,String类型
bundle.putInt("key2", 175); //存入数据,int类型
bundle.putSerializable("object",object); //存入对象
intent.putExtras(bundle); //利用Intent的putExtras方法携带此Bundle
startActivity(intent);
-----------------------------------------------------------------------------------
在下个活动中获取数据:
Bundle bundle = getIntent().getExtras(); //读取intent的数据给Bundle对象
String str1 = bundle.getString("key1"); //通过key得到value
int int1 = bundle.getInt("key2"); //通过key得到value
Object object = (Object) bundle.getSerializable("object"); //通过key得到object
Intent本身就可以传递参数为何还要用Bundle呢?
Bundle只是一个信息的载体,内部其实就是维护了一个Map<String,Object>。
Intent负责Activity之间的交互,内部是持有一个Bundle的,Intent的putExtra()方法的源码:
public Intent putExtra(String name, boolean value) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putBoolean(name, value);
return this;
}
应用场景:
-
例1:
从A界面跳转到B界面或者C界面
这样的话 我就需要写2个Intent 如果你还要涉及的传值的话 你的Intent就要写两遍添加值的方法。那么,如果我用1个Bundle,直接先存值,然后再存到Intent中 不就更简洁吗? -
例2:
现在要把值通过Activity A经过Activity B传给Activity C。如果用Intent的话,A-B先写一遍,再在B中都取出来 然后在把值塞到Intent中,再跳到C。
如果在A中用了 Bundle 的话,把Bundle传给B,在B中再转传到C,C就可以直接去取了。
具体看 : https://www.jianshu.com/p/e9db0797293b