Intent主要用来启动其他的Activity、Service、Broadcast
显示Intent与隐式Intent
Intent intent = new Intent();
intent.setClass(Context packageContext, OtherActivity.class); //显式intent,指明确指出此intent是启动哪个activity.
startActivity(intent);
<span style="background-color: rgb(255, 255, 255);">Intent intent = new Intent();
intent.setAction(Intent.ACTION_NEW); //隐式intent是指并没有指出要启动哪个activity,而要系统自动去判断并启动匹配的activity
startActivity(intent);</span>
显式Intent
有两种方式来显示的指示要启动的Activity:
方式一:(通过setClassName)
Intent intent = new Intent();
//表示希望启动com.example.test_permission包中的com.example.test_permission.MainActivity
intent.setClassName("com.example.test_permission", "com.example.test_permission.MainActivity");
startActivity(intent);
方式二:(通过SetClass)
Intent intent = new Intent();
intent.setClass(Context packageContext, OtherActivity.class);
startActivity(intent);
注意:Context代表访问该应用环境信息的接口,而包名是应用的唯一标识,所以它们有一一对应的关系。
隐式intent
public class Intent implements Parcelable, Cloneable { //... private String mAction;
private Uri mData;
private String mType;
private String mPackage;
private ComponentName mComponent;
private int mFlags;
private HashSet<String> mCategories;
private Bundle mExtras;
private Rect mSourceBounds;
从上面源码可以看出,Intent对象包含Data、Type、Component、Flag、Category、Action、Extra
Action、Category
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Activity的intent-filter的子元素代表了该Activity所能响应的Intent,一旦匹配就可以激活该Activity。
3.intent-filter代表了该组件所能满足的要求,只要能满足的要求大于等于intent的要求,就可以启动
Action:该activity可以执行的一个“抽象”动作
该标识用来说明这个activity可以执行哪些动作,所以当隐式intent传递过来action时,如果跟这里<intent-filter>所列出的任意一个匹配的话,就说明这个activity是可以完成这个intent的意图的,可以将它激活
ACTION_CALL activity 启动一个电话.
ACTION_EDIT activity 显示用户编辑的数据.
<span style="color:#ff0000;">ACTION_MAIN</span> activity 作为Task中第一个Activity启动,即应用程序入口
ACTION_SYNC activity 同步手机与数据服务器上的数据.
ACTION_BATTERY_LOW broadcastreceiver 电池电量过低警告.
ACTION_HEADSET_PLUG broadcastreceiver 插拔耳机警告
ACTION_SCREEN_ON broadcastreceiver 屏幕变亮警告.
<span style="color:#ff0000;">ACTION_VIEW</span> Activity 查看指定数据
Category:指定当前动作(Action)被执行的环境,为Action增加额外的附加类别信息
即这个activity在哪个环境中才能被激活。不属于这个环境的,不能被激活。
常用category: <span style="color:#ff0000;">CATEGORY_DEFAULT</span>:Android系统中默认的执行方式,按照普通Activity的执行方式执行。表示所有intent都可以激活它
CATEGORY_HOME:设置该组件为Home Activity。
CATEGORY_PREFERENCE:设置该组件为Preference。
<span style="color:#ff0000;">CATEGORY_LAUNCHER</span>:设置该组件为在当前应用程序启动器中优先级最高的Activity,通常与入口ACTION_MAIN配合使用。
CATEGORY_BROWSABLE:设置该组件可以使用浏览器启动。表示该activity只能用来浏览网页。
CATEGORY_GADGET:设置该组件可以内嵌到另外的Activity中。
Intent intent=new Intent();
intent.setAction(Intent.<span style="color:#ff6600;">ACTION_MAIN</span>);
intent.addCategory(Intent.<span style="color:#ff6600;">CATEGORY_HOME</span>);
startActivity(intent);
注意:
如果该activity想要通过隐式intent方式激活,那么不能没有任何category设置,至少包含一个android.intent.category.DEFAULT,因为当Intent被创建时,默认Category属性值为Intent.CATEGORY_DEFAULT。
Data、Type
Data Action属性执行时要操作的数据
在目标<data/>标签中包含了以下几种子元素,他们定义了url的匹配规则:
- android:scheme 匹配url中的前缀,指定数据的协议部分,除了“http”、“https”、“tel”...之外,我们可以定义自己的前缀
- android:host 匹配url中的主机名部分,如“google.com”,如果定义为“*”则表示任意主机名
- android:port 匹配url中的端口
- android:path 匹配url中的路径
- android:mimeType Type属性,指定该Data属性所指定的Uri的数据类型(MIME类型),允许使用通配符,而且可以是自定义的MIME类型,只要符合abc/xyz的格式的字符串即可
<activity android:name=".TargetActivity">
<span style="white-space:pre"> </span><intent-filter>
<span style="white-space:pre"> </span><action android:name="com.scott.intent.action.TARGET"/>
<span style="white-space:pre"> </span><category android:name="android.intent.category.DEFAULT"/>
<span style="white-space:pre"> </span><data android:scheme="scott" android:host="com.scott.intent.data" android:port="7788" android:path="/target"/>
<span style="white-space:pre"> </span></intent-filter>
</activity>
注意:
1.只有<data.../>标签中指定的内容和Intent中携带的Data完全一致时,当前活动才能被激活。如果其它条件都足以激活该Activity,但intent却没有传进来指定类型的Data时,就不能激活该activity。所以想要启动上面的Activity,除了设置action外还要设置Data
2.Data属性与Type属性会相互覆盖,后这设置的会覆盖先设置的,如果想要同时存在,则要用setDataandType()方法
例子:打开浏览器,访问一个网页,action和data结合使用
Intent intent=new Intent();
intent.setAction(Intent.<span style="color:#ff6600;">ACTION_VIEW</span>);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
Extras
这个参数不参与匹配activity,而仅作为额外数据传送到另一个activity中,接收的activity可以将其取出来。这些信息并不是激活这个activity所必须的。也就是说激活某个activity与否只上action、data、catagory有关,与extras无关。而extras用来传递附加信息,诸如用户名,用户密码什么的。可通过putXX()和getXX()方法存取信息;也可以通过创建Bundle对象,再通过putExtras()和getExtras()方法来存取。
putXXX()方法的本质仍然是通过Bundle实现: public Intent putExtra(String name, long value) {
if (mExtras == null) {
mExtras = new <span style="color:#ff0000;">Bundle()</span>;
}
mExtras.<span style="color:#ff0000;">putLong</span>(name, value);
return this;
}
而传递一个Bundle:
public Intent putExtra(String name, Bundle value) {
if (mExtras == null) {
mExtras = new <span style="color:#ff0000;">Bundle</span>();
}
mExtras.<span style="color:#ff0000;">putBundle</span>(name, value);
return this;
}
归根结底都是通过Bundle来实现数据封装。而Bundle则是通过Map的数据结构来存储数据。
关于Bundle则是实现了Parcelable接口的类,通过上面提到的HashMap和一个Parcel来存储数据。
Intent源码6700多行代码,但真正核心代码 就那么几百行,大部分都用来定义常量字符串了
先来看一下
- public class Intent implements Parcelable, Cloneable
没错,它还实现了cloneable接口,但平常我们很少会用到它,其实现方法为:
- /**
- * Copy constructor.
- */
- public Intent(Intent o) {
- this.mAction = o.mAction;
- this.mData = o.mData;
- this.mType = o.mType;
- this.mPackage = o.mPackage;
- this.mComponent = o.mComponent;
- this.mFlags = o.mFlags;
- if (o.mCategories != null) {
- this.mCategories = new HashSet<String>(o.mCategories);
- }
- if (o.mExtras != null) {
- this.mExtras = new Bundle(o.mExtras);
- }
- if (o.mSourceBounds != null) {
- this.mSourceBounds = new Rect(o.mSourceBounds);
- }
- if (o.mSelector != null) {
- this.mSelector = new Intent(o.mSelector);
- }
- if (o.mClipData != null) {
- this.mClipData = new ClipData(o.mClipData);
- }
- }
- @Override
- public Object clone() {
- return new Intent(this);
- }
没错,就是复制了一个自己
Intent另一个功能,就是传参,这里只展示public IntentputExtra(String name, String value),其他的都是类似这个写法:
- public Intent putExtra(String name, String value) {
- if (mExtras == null) {
- mExtras = new Bundle();
- }
- mExtras.putString(name, value);
- return this;
- }
其中,mExtras是private Bundle mExtras;
那么,这里还得说一下public Intent putExtras(Bundle extras)方法:
- public Intent putExtras(Bundle extras) {
- if (mExtras == null) {
- mExtras = new Bundle();
- }
- mExtras.putAll(extras);
- return this;
- }
没错,bundle中放入一个bundle。。。
再看一下取出的方法:
- public String getStringExtra(String name) {
- return mExtras == null ? null : mExtras.getString(name);
- }
小结:所以如果是基本数据类型(Int,long,double,boolean,string)传参,没必要自己生成一个bundle对象,再传参到Intent中。
接下来,聊聊 parseUri 这个方法,里面传的参数是 uri,但到里面,基本就是按照字符串解析
随便摘取几段
- // simple case
- i = uri.lastIndexOf("#");
- if (i == -1) return new Intent(ACTION_VIEW, Uri.parse(uri));
- // old format Intent URI
- if (!uri.startsWith("#Intent;", i)) return getIntentOld(uri);
另一个被忽略的是toString()方法,Intent的重写了该方法,可以通过输出String查看Intnet里具体包含的信息内容
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder(128);
- b.append("Intent { ");
- toShortString(b, true, true, true, false);
- b.append(" }");
- return b.toString();
- }
那么toShortString()这个方法,到底输出了哪些内容?
可以通过传参的名字了解一二,
- public void toShortString(StringBuilder b, boolean secure, boolean comp, boolean extras,boolean clip) {
secure对应的是 uri的输出,
comp对应的是ComponentName 也就是要启动的类名,
extras对应的就是传参内容。
小结,如果在调试过程的时候,可以试着输出Intent内容,看看是否是传参错误导致的异常
Component
组件:指定Intent目标组件的类名称,通常intent会根据其他属性信息,如action,data,type,category进行查找,最终找到一个与之匹配的目标组件。但是,如果直接指定component的话,将直接使用它指定的组件,而不再执行上述查找过程;指定了这个属性,则Intent的其它所有属性都是可选的;
Flag
标识:为该Intent添加一些额外的标识。
FLAG_ACTIVITY_CLEAR_TOP:相当于启动模式中的singleTask
FLAG_ACTIVITY_BROUGHT_TO_FRONT:带到前台
FLAG_ACTIVITY_SINGLE_TOP:启动模式中的singletop模式
传递对象
Android中Intent传递类对象提供了两种方式一种是 通过实现Serializable接口传递对象,一种是通过实现Parcelable接口传递对象。
要求被传递的对象必须实现上述2种接口中的一种才能通过Intent直接传递。
Intent中传递这2种对象的方法:
- Bundle.putSerializable(Key,Object); //实现Serializable接口的对象
- Bundle.putParcelable(Key, Object); //实现Parcelable接口的对象
以下以最常用的Serializable方式为例 :
假设由登录界面(Login)跳转到主界面(MainActivity)传递的对象为登录的用户信息 User类
首先创建一个序列化类:User
- import java.io.Serializable;
- public class User implements Serializable {
- private int ID;
- private String UserName;
- private String PWD;
登录窗体登录后传递内容
- Intent intent = new Intent();
- intent.setClass(Login.this, MainActivity.class);
- Bundle bundle = new Bundle();
- bundle.putSerializable("user", user);
- intent.putExtras(bundle);
- this.startActivity(intent);
接收端
- Intent intent = this.getIntent();
- user=(User)intent.getSerializableExtra("user");
以上就可以实现对象的传递。
补充:
如果传递的是List<Object>,可以把list强转成Serializable类型,而且object类型也必须实现了Serializable接口
- Intent.putExtra(key, (Serializable)list)
接收
- (List<YourObject>)getIntent().getSerializable(key)
上面是通过将所有信息集成一个类,然后对这个类序列化来完成信息传递的,其实我们完全可以将每条信息逐个利用putExtras()逐个添加到里面,然后逐个取出。
如:
发送端:
- int postID=1;
- Intent intent=new Intent(getApplicationContext(), WriteStoryActivity.class);
- Bundle bundle = new Bundle();
- bundle.putSerializable("ACTION","PLUS");
- bundle.putSerializable("POSTID",postID);
- intent.putExtras(bundle);
- startActivityForResult(intent,REQUESTCODE );
接收端:
- Intent intent = this.getIntent();
- String action=(String)intent.getSerializableExtra("ACTION");
- String postID=(String)intent.getSerializableExtra("POSTID");
发现一个问题,在发送端发送的postID为int类型,而接收端不能将其强转成int类型,会报错,只能转成String类型。可见发送端发送与接收的类型是有限制的,现将目前所发现的接收后能强转的类型贴出来:hashMap;String;
除了上面方法以外,如果传送的信息全部是String类型,则可以用下面的方法;
发送端:
- Intent intent=new Intent(getApplicationContext(), WriteStoryActivity.class);
- Bundle bundle = new Bundle();
- bundle.putCharSequence("usr","harvic");
- bundle.putCharSequence("pwd","123456");
- bundle.putCharSequence("email","xxxxx@163.com");
- intent.putExtras(bundle);
- startActivity(intent);
- Intent intent = getIntent();
- Bundle bundle=intent.getExtras();
- String usr=bundle.getString("usr");
- String pwd=bundle.getString("pwd");
- String email=bundle.getString("email");
注意:如果通过Intent传递的数据量比较大(如不可控的List对象),就不要使用Intent传递,这样会造成Intent显示卡顿,对于大数据的Intent之间传递,建议使用EventBus通知机制。但这种方法只适用于,一个Activity在返回到其父Activity时的情况 。在新建Activity时,不要使用EventBus,因为这时Activity还没有被创建,使用EventBus去接的时候,根本接不到,而且还会报错。
Intent传递数据时,下列的数据类型可以被传递:A、Serializable
1.Serializable :将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用ObjectInputStream 和 ObjectOutputStream 进行对象的读写。
2.charsequence :在JDK1.4中,引入了CharSequence接口,实现了这个接口的类有:CharBuffer、String、StringBuffer、StringBuilder这个四个类。
CharBuffer为nio里面用的一个类,String实现这个接口理所当然,StringBuffer也是一个CharSequence,StringBuilder是Java抄袭C#的一个类,基本和StringBuffer类一样,效率高,但是不保证线程安全,在不需要多线程的环境下可以考虑。提供这么一个接口,有些处理String或者StringBuffer的类就不用重载了。但是这个接口提供的方法有限,只有下面几个:charat、length、subSequence、toString这几个方法,感觉如果有必要,还是重载的比较好,避免用instaneof这个操作符。
3.Parcelable
外,只有实现了Parcelable接口的类才能被放入Parcel中。是GOOGLE在安卓中实现的另一种序列化,功能和Serializable相似,主要是序列化的方式不同
4.Bundle:Bundle是将数据传递到另一个上下文中或保存或回复你自己状态的数据存储方式。它的数据不是持久化状态。
startActivityForResult()
请求码requestCode的作用:用于标识请求的来源。
结果码resultCode的作用:用于标识返回的数据来自于哪个新的Activity。
startActivityForResult();可以实现把activity A数据传送给activity B,也可以把activity B的数据返回来给activity A处理。
- public void startActivityForResult (Intent intent, int requestCode)
- requestCode : If >= 0, this code will be returned in onActivityResult() when the activity exits.
一般都是设置大于或者等于0,因为activity A可能不单单要跳转到activity B,也可能跳转到activity C,D,E……,这些activity返回来的数据都交由activity处理,那activity又通过什么来分辨返回来的数据到底是哪个activity回来的呢。
所以在activity A跳转到某一个activity时,要设定目标activity的requestCode,这个requestCode就唯一地标识了相对应的activity。
如下所示,当要跳转到activity B时,15表示是activity B的标识,
- IntentActivity.this.startActivityForResult(intent, 15);
- IntentActivity.this.startActivityForResult(intent, 25);
数据处理方法onActivityResult()
activity A通过复写onActivityResult方法来处理这些activity返回来的数据。当activity B销毁时(调用子finish())会回调activity A的onActivityResult:
- protected void onActivityResult (int requestCode, int resultCode, Intent data)
有三个参数,第一个requestCode是用来区分哪个activity回来的数据,可以通过swith语句来筛选。
代码如下:
- switch (requestCode) {
- case 15:
- switch (resultCode) {
- case 0:
- //do something
- break;
- case 1:
- //do something
- break;
- default:
- break;
- }
- break;
- case 25:
- switch (resultCode) {
- case 0:
- //do something
- break;
- default:
- break;
- }
- default:
- break;
- }
setResult()
当跳转到其他activity时,如activity B。需要知道的是,把activity B的数据返回去给activity A的处理方法是setResult (int resultCode, Intent data) ,可以看到它的第一个参数是resultCode,也就是对应着相应的处理方式,官方的推荐取值如下
- RESULT_CANCELED
- RESULT_OK
- RESULT_FIRST_USER
具体做法如下:如你可以在activity B做如下设置,两个button的监听器对应着两个resultCode,也就对应了两种处理方式,至于你要做什么事,在监听器定义即可。
- button1.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- //do something
- secondactivity.this.setResult(0, intent);
- secondactivity.this.finish();
- }
- });
- button2.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- //do something
- secondactivity.this.setResult(1, intent);
- secondactivity.this.finish();
- }
- });
第三个参数为Intent data,为activity返回来的数值,如通过data.getStringExtra("key");就可以取出来。
注意:调用setResult()方法跳回原来的activity时,一定要调用finish方法结束当前的activity