向下一个Activity对象传递数据
场景:用户从activity1跳转到activity2,并将activity1的数据传递给activity2中。主要步骤如下:
- 在Intent对象中添加要传递的参数;
startActivity(Intent)
方式启动Activity对象;- 在被启动的Activity对象中通过Intent对象获取数据。
分解步骤如下:
1.在Intent对象中添加数据
Intent简介
在 Android 中,不同的 Activity 实例可能运行在一个进程中,也可能运行在不同的进程中。因此我们需要一种特别的机制帮助我们在 Activity 之间传递消息。Android 中通过 Intent 对象来表示一个意图,一个 Intent 对象不仅包含有这个意图的目的地,还可以包含意图的内容,这好比一封 Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象,意图“目的地”是必须的,而内容则是可选项。具体详情请见我的另一篇1。
添加数据
Intent对象可以添加八大基本数据类型,String,CharSequence及对应的数组,也可以添加Bundle对象,Serializable实例,Parcelable实例。Serializable和Parcelable可用于实现传递自定义对象类型。
下面具体以代码演示如何传递不同的数据类型。
传递非对象数据
使用Intent传递数据包括以下两种:
- 传递Bundle对象
Bundle维护了一个
HashMap<String,Object>
对象,将我们的数据存在 HashMap 中来进行传递; - 直接传递键值对
这种方式也是通过Bundle传递,只是Bundle由系统管理.
这两种实现代码如下:
/**
* 第一种方式:传递Bundle对象
*/
Bundle bundle = new Bundle();
//名字,值
bundle.putString("name","sywyg");
bundle.putInt("age",26);
intent.putExtra("person",bundle);
/**
* 第二种方式:直接传递键值对
* 内部也是通过Bundle实现的
*/
intent.putExtra("sex","男");
传递对象
包括以下两种方法
Serializable序列化方式
Serializable读作[s??r??la?’z?bl],表示序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上传输,也可以存储在本地。只要将准备序列化的对象实现Serializable接口即可,例如要实现对Cat的序列化,代码如下:
package com.sywyg.activity_test;
import java.io.Serializable;
/**
* Created by sywyg on 2015/4/21.
*/
public class Cat implements Serializable{
//为了方便描述就不封装了
String name;
int age;
@Override
public String toString() {
return "Cat[name:" + name + "age:" + age + "]";
}
}
上述代码完成了对Cat类的序列化。下面在MainActivity(第一个Activity)中设置传送Cat类对象到MainActivity2(第二个Activity)中,主要代码如下:
/**
* 通过Serializable接口传递对象
*/
Cat cat = new Cat();
cat.name = "小猫";
cat.age = 3;
//参数为:对象名,Serializable引用变量
intent.putExtra("cat",cat);
接下来在MainActivity2中获取对象的主要代码如下:
//getIntent()返回Intent对象
Cat cat = (Cat)getIntent().getSerializableExtra("cat");
Parcelable方式
Parcelable方式的实现原理是将一个完整的对象进行分解,分解后的每一部分都是Intent所支持的数据类型,这样就可以传输对象了。
Parcelable接口主要代码如下:
package android.os;
public interface Parcelable
{
//内容描述
public int describeContents();
//写入接口方法,打包
public void writeToParcel(Parcel dest, int flags);
//读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板参数传入
//为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数分别返回单个和多个继承类实例
public interface Creator<T>
{
public T createFromParcel(Parcel source);
public T[] newArray(int size);
}
}
假设要传输Dog类对象,则需实现Parcelable接口,具体代码如下:
package com.sywyg.activity_test;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by sywyg on 2015/4/21.
*/
public class Dog implements Parcelable{
String name;
int age;
public Dog(){}
//或放在createFromParcel(Parcel source)方法里直接new Dog()然后在调用readXXX(),不用定义构造器
private Dog(Parcel in){
//写
name = in.readString();
age = in.readInt();
}
//匿名内部类实现接口实例
/**其中public static final一个都不能少,内部对象CREATOR的名称也不能改变,必须全部大写。需重写本接口中的两个方法:createFromParcel(Parcel in) 实现从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层,newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话即可(return new T[size]),供外部类反序列化本类数组使用。
*/
public static final Parcelable.Creator<Dog> CREATOR = new Parcelable.Creator<Dog>() {
@Override
public Dog createFromParcel(Parcel source) {
return new Dog(source);
}
@Override
public Dog[] newArray(int size) {
return new Dog[size];
}
};
/**
*重写writeToParcel方法,将你的对象打包为一个Parcel对象,即:将类 的数据写入外部提供的Parcel中,打包需要传递的数据到Parcel容器保存,以便从 Parcel容器获取数据
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
//读
dest.writeString(name);
dest.writeInt(age);
}
//内容描述接口,返回0即可
@Override
public int describeContents() {
return 0;
}
@Override
public String toString() {
return "Dog[name:" + name + "age:" + age + "]";
}
}
简而言之:通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射成你的对象。也可以将Parcel看成是一个流,通过writeToParcel把对象写到流里面,在通过createFromParcel从流里读取对象,只不过这个过程需要你来实现,因此写的顺序和读的顺序必须一致。
在MainActivity中使用相同的代码传递Dog类对象,在MainActivity2中获取对象的代码如下:
Dog dog = (Dog)getIntent().getParcelableExtra("dog");
性能:
- Serializable要对整个对象序列化,因此性能要低于Parcelable,所以通常情况下推荐使用Parcelable的方式实现Intent传递对象的功能。
- Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
- Parcelable不能使用在要将数据存储在磁盘上的情况,因为在外界有变化的情况下Parcelable不能很好的保证数据的持续性。尽管Serializable效率低点, 也不提倡用,但在这种情况下,还是建议你用Serializable 。
注意:关于以上内容还要继续了解,这里只是简单的使用一下(how),至于what和why还需要进一步了解。
2. 启动另一个Activity对象
代码如下:
//this表示当前Activity,MainActivity2.class表示要跳转到的Activity
//MainActivity2需要在AndroidManifest.xml中声明。
Intent intent = new Intent(this,MainActivity2.class);
intent.putExtra("name",XXX);
startActivity(intent);
3. 获得数据
在启动的Activity通过getXXXExtra()方法获取数据,代码如下:
//获取第一种方式传递过来的数据
Bundle bundle = intent.getBundleExtra("person");
String name = bundle.getString("name");
int age = bundle.getInt("age");
//获取第二种方式传递过来的数据
String sex = intent.getStringExtra("sex");
//第三种方式传递数据
Cat cat = (Cat)intent.getSerializableExtra("cat");
//第四种方式传递数据
Dog dog = (Dog)intent.getParcelableExtra("dog");
返回数据给启动该Activity的Activity
场景:用户从activity1跳转到activity2,将activity2的结果返回到activity1中。主要步骤如下:
- 使用startActivityForResult()方法启动一个Activity
- 重写onActivityResult()事件方法,处理返回结果
- 在带放回结果的Activity里,使用setResult()设置返回结果。
下面详解:
1. 启动另一个Activity
代码如下:
//启动一个带返回结果的Activity,返回结果给当前Activity
Intent intent = new Intent(this, MainActivity32Activity.class);
//参数为:Intent对象,参数编码
//参数编码(对应设置它的Activity)用于判断哪个Activity响应的,REQUESTCODE大于等于0就会返回结果
startActivityForResult(intent,REQUESTCODE);
2. 启动带返回结果的Activity
下面的Activity通过按钮启动带返回结果的Activity,具体代码如下:
package com.sywyg.activity_test;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
/**
* 启动一个带返回结果的Activity
* @author sywyg
* @since 2015/4/22
*/
public class MainActivity3Activity extends Activity implements View.OnClickListener {
private TextView tv;
private Button btn_result;
private static final int REQUESTCODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_activity3);
tv = (TextView)findViewById(R.id.tv);
btn_result = (Button)findViewById(R.id.btn_result);
btn_result.setOnClickListener(this);
}
@Override
public void onClick(View view){
//启动一个带返回结果的Activity,返回结果给该Activity
Intent intent = new Intent(this, MainActivity32Activity.class);
//REQUESTCODE大于等于0就会返回结果
startActivityForResult(intent,REQUESTCODE);
}
/**
* 处理返回过来的结果,系统调用
* @param requestCode,请求编码,对应传递的REQUESTCODE,一般有多个,注意区分并要进行判断响应的是哪个Activity
* @param resultCode 返回编码,系统有固定值
* @param data 返回的Intent,含有返回结果数据
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case REQUESTCODE:
if (resultCode == RESULT_OK){
String result = data.getStringExtra("result");
tv.setText("返回结果为:" + result);
}
break;
}
}
}
其中,onActivityResult()方法用于处理返回的数据,由系统自行调用。一种有三个参数,详情见代码。这里特别说明一下:由于在一个Activity中可能通过调startActivityForResult()方法去启动多个不同的Activity,每一个返回的数据都是通过onActivityResult()方法处理,因此需要通过requestCode判断数据来源,然后再resultCode判断返回结果是否成功,最后将data中的数据显示出来。
关于startActivityForResult()启动singleTask的Activity,则onActivitResult()立即回调且resultCode为RESULT_CANCELED问题请参见2。
3. 带返回结果的Activity
下面的Activity为带返回结果的Activity,具体代码如下:
package com.sywyg.activity_test;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
/**
* 带返回结果的Activity把结果返回给调用它的Activity
* @author sywyg
* @since 2015/4/22
*/
public class MainActivity32Activity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_activity32);
//获得当前Intent对象,也可以自定义一个。不需指定意图,即来源和去向(intent.setClass())
Intent intent = getIntent();
//Intent intent = new Intent();
//添加需传递的数据
intent.putExtra("result","这是第二个Activity返回的结果");
/**
* 设置结果
* 参数为:结果编码(系统值),当前Intent对象
*/
setResult(RESULT_OK,intent);
//销毁该Activity
//finish();
}
}
其中,Intent对象可以由该Activity调用getIntent()方法获得一个Intent对象,或者重新构建一个,但是无论如何这里的Intent对象只是传递数据,没有任何的意图,即来源和去向。setResult()方法用于向上一个Activity返回数据,第一个参数用于向上一个Activity返回结果,一般使用RESULT_OK或RESULT_CANCELED这两个值,第二个参数是带有数据的Intent对象。
运行结果
运行结果如下:
可以看到直接按BACK键就能把数据传给上一个Activity中,当然设置按钮也一样,
需要注意一下几点:
- 在带返回结果的Activity中的Intent对象不能设置意图(即使设置了,没有startActivity也不会响应意图)。
- 有时在复杂的业务逻辑中,可能存在A startActivityForResult 到B,同时C也startActivityForResult 到B,且requestCode可能相同(以表示同意业务请求),这时可能需要在B中针对性的判断此请求来源(来自于A还是C)。此时,可以通过intent传参形式。相信大家都比较熟悉,其实Activity类中也提供了相应的函数可以获取到来源Activity的类型函数:getCallingActivity()。但需要注意此函数仅针对startActivityForResult有效,返回的结果中包含完成包名(Android总结篇系列:Activity中几个主要函数详解)。
- 前一个活动的回调方法在后一个活动的onPause()方法之后,该活动的onRestart()方法之前(如果调用该方法的话,注意若后一个活动以Dialog方式显示的或不会onStop()因此也没有onRestart()),且一定在onResume()方法之前。
威哥视频
郭霖 第一行android
- http://blog.csdn.net/wangyongge85/article/details/45307337 ?
- http://blog.csdn.net/sodino/article/details/22101881 ?
版权声明:本文为博主原创文章,未经博主允许不得转载。