您的位置:首页 > 移动开发 > Android开发

在不同的Activity之间传递数据--摘自《android开发权威指南.pdf》

2015-11-03 10:46 399 查看
在学习android的过程中,一定会设及到Activity之间的跳转,既然跳转那么已经回碰到数据的传递,在起初学习这一块的时候自己也是摸了很久,今天从新回顾下,也让后来者能更清晰的理解到activity之间的数据传递的用法!

在Android中传递数据的方法非常多!目前我学习到的也算是比较常用的四种方法,如下:

1、通过Intent传递数据(用的最多的)
2、通过静态(static)变量传递数据(java的基础)
3、通过剪切板(Clipboard)传递数据(使用已经封装好的对象来处理)
4、使用全局变量传递

1、使用Intent传递数据

最常用的一种数据传递的方法。通过Intent类的.putExtra方法可以将简单类型的数据或可系列化的对象保存到Intent对象中,然后在目标Activity中使用getXXX(getInt/getString等)方法获得这些数据。将数据保存到Intent对象的代码如下;
<pre name="code" class="java">	intent = new Intent(this, MyActivity1.class);
intent.putExtra("intent_string", "通过Intent传递的字符串");//保存String类型的值
intent.putExtra("intent_integer", 300);//保存integer类型的值
Data data = new Data();
data.id = 1000;
data.name = "Android";
intent.putExtra("intent_object", data);//保存可序列化的对象
startActivity(intent);<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">	</span>



上面的代码中涉及到一个Data类,这个类是可序列化的,也就是实现java.io.Serializable接口。Data类的代码如下:

import java.io.Serializable;

public class Data implements Serializable{
public int id;
public String name;
}
在MyActivity1类中获得上面保存的3个值(String、Integer和Data类型的值)的代码如下:

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MyActivity1 extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState){
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.myactivity);
TextView textView = (TextView)findViewById(R.id.textview);

String intentString =  getIntent().getStringExtra("intent_string");//获得String类型的值
int intentInteger = getIntent().getExtras().getInt("intent_integer");//获得Integer类型的值
Data data = (Data)getIntent().getExtras().get("intent_object");//获得Data类型的值

StringBuffer sb = new StringBuffer();
sb.append("intent_string:");
sb.append(intentString);
sb.append("\n");
sb.append("intent_integer:");
sb.append(intentInteger);
sb.append("\n");
sb.append("data.id:");
sb.append(data.id);
sb.append("\n");
sb.append("data.name:");
sb.append(data.name);
sb.append("\n");

textView.setText(sb.toString());
}

}


注意:在使用Intent传递对象时必须要序列化,序列化在这里不重点赘述,Android实现序列化的接口有个两种;一种为上面介绍的Serializable接口(javaSE本身支持的),;另一个是实现Parcelable接口(Android特有的功能,效率比前者高,也可用于Intent数据传递,也可用于京城见的通信(IPC))。当然实现Serializable接口相对简单点,声明下就可以,后者就相对复杂点,但效率会更高!

注:Android中Intent传递对象有两种方法:一是Bundle.putSerializable(Key,Object),另一种是Bundle.putParcelable(Key,Object)。当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable接口。(这里用到了Bundle对象来帮助我们传递对象)

2、使用静态变量传递数据

前面介绍的Intent对象可以很方便的在Activity之间传递数据,当然这也是官方推荐的数据传递方式。但是Intent也是有其局限性的,比如无法传递不能序列化的对象,(如:Canvas对象),如果传递自定义的对象也是没有办法传递这类对象的!
注:在Activity传值中可能会遇到两个Activity不存在跳转关系就能进行数据的传递,这里也会用到静态变量传递,当然静态变量也是有其弊端的这个在这节的后面会介绍到!
例如,要向MyActivity2传递三个值:id、name和data,就需要在MyActivity2类中定义3咯public的静态变量(在其他类中单独定义都是可以的,因为静态属性直接用类名点属性就可取到)代码如下:
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MyActivity2 extends Activity{
public static String name;
public static int id;
public static Data data;
@Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.myactivity);
TextView textView = (TextView)findViewById(R.id.textview);

StringBuffer sb = new StringBuffer();
sb.append("name:");
sb.append(name);
sb.append("\n");
sb.append("id:");
sb.append(id);
sb.append("\n");
sb.append("data.id:");
sb.append(data.id);
sb.append("\n");
sb.append("data.name:");
sb.append(data.name);
sb.append("\n");

textView.setText(sb.toString());
}

}

当然想要在MyActivity2中获得这些静态变量的值,就需要在显示MyActivity2之前(调用startActivity方法之前)为这些静态变量赋值,代码如下:
intent = new Intent(this, MyActivity2.class);
MyActivity2.id = 3000;
MyActivity2.name = "保时捷";
MyActivity2.data = new Data();
MyActivity2.data.id = 1000;
MyActivity2.data.name = "Android";
startActivity(intent);

到这里静态变量传递数据就完成了,但是可以看到的是,当我的Activity退出后,我们的值是均会重置的,这算是一个项目中可能会遇到的,就是我需要保存我的数据,当然我们这里需要理解的仅仅是如何传值,如果想要保存它的值的 话就需要用到存储了,在界面直接传递的值保存我们一般会用到五大存储之一的 SharePreference,很好用,真的!用过的人都说好...嘿嘿~
     其实可以说,使用上面的两种基本上都能满足传值的需求了,但是学海无涯,能多一点技术傍身我们也是不会拒绝的,下面我们开始介绍另外两种传递数据的方法,当然下面的两种相对来说用的会比较少一点。

3、使用剪切板传递数据

在Activity之间传递数据还可以利用一些技巧。例如,不管是Windows,还是Linux(Android、Meego或者其他机遇Linux类和的操作系统),都会支持一个叫做剪切板的技术(这些在使用手机的过程中及应该都有发现)。也就是某一个程序将一些数据复制到剪切板上,然后其他的任何程序都可以从剪切板中获得这些数据。那么在Activity之间传递数据也可以利用这种技术,将支付穿保存到剪切板的代码如下:
intent = new Intent(this, MyActivity3.class);
//获得管理剪切板的对象ClipboardManager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText("通过Clipboard传递的数据");//向剪切板保存字符串
startActivity(intent);
      上面使用到了getSystemService方法获得一个系统服务对象,也就是ClipboardManager对象,该对象用于管理系统剪切板,并使用ClipboardManager.setText方法向剪切板保存一个字符串。下面的代码从剪切板中获得这个字符串。
<span style="white-space:pre">	</span>    //获得管理剪切板的对象ClipboardManager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);<pre name="code" class="java"><span style="white-space:pre">	</span>    String text = clipboard.getText().toString();
上例是传递简单的字符串,很简单,但是很遗憾的ClipboardManager对象只支持向剪切板读写字符串,并不支持其它类型的数据,更不不要说我们前面说过的传对象。当然这是难不倒我们的,简单点的数据类型如int、boolean等等,都是可以将其转化我字符串进行传递,那么如果是对象呢!当然答案是肯定的,没有任何问题!
由于Data是可序列化的对象,因此,完全可以将Data转化为byte[]类型的数据,然后将byte[]类型的数据在进行Base64编码(通过E-mail发送附件就是讲附件转换成Base64格式的字符串发送的)转化为字符串,这样就可以使用剪切板进行数据传递了(通过Intent对象也可以利用这种方法来传递可序列化的对象)。下面的代码利用剪切板传递一个Data对象,当然其实也可以将其封装起来写一个通用的方法类(如果你使用比 剪切板比较多的话)将可序列化的对象转化成字符串!
</pre><pre name="code" class="java"><pre name="code" class="java">			    intent = new Intent(this, MyActivity3.class);
//获得管理剪切板的对象ClipboardManager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
// clipboard.setText("通过Clipboard传递的数据");
Data clipboardData = new Data();
clipboardData.id = 6666;
clipboardData.name = "通过Clipboard传递的数据";
ByteArrayOutputStream baos = new ByteArrayOutputStream();

String base64Str = "";
try
{
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(clipboardData);

base64Str = Base64.encodeToString(baos.toByteArray(),
Base64.DEFAULT);
oos.close();
}
catch (Exception e)
{
// TODO: handle exception
}
clipboard.setText(base64Str);
startActivity(intent);



    
      在MyActivity3中将Base64编码的字符串进行解码,并还原成Data对象
//获得管理剪切板的对象ClipboardManager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);

String base64Str = clipboard.getText().toString();

byte[] buffer = Base64.decode(base64Str, Base64.DEFAULT);

ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
try
{
ObjectInputStream ois = new ObjectInputStream(bais);
Data data = (Data) ois.readObject();
textView.setText(base64Str + "\n\ndata.id:" + data.id
+ "\ndata.name" + data.name);
}
catch (Exception e)
{
// TODO: handle exception
}

注意:由于Base64类是Android2.2开始支持的,因此Android2.1及更低版本的Android无法通过Android SDK API进行Base64编码和解码,因此,需要使用第三方的类库(如,common httpclient)才可以进行Base64编码。

4、使用全局对象传递数据

虽然使用静态变量可以传递任何类型的数据,但是官方并不建议那样做,如果在类中大量使用静态变量(尤其是使用很占资源的变量,例如Bitmap对象)可能会造成内存溢出的异常,而且还可能因为静态变量在很多类中出现而造成代码难以维护和混乱,因此就出现了我们现在要介绍的一种更优雅的数据传递方式,这就是全局对象。由全局对象来取代静态变量,这里的全局的意思指的是整个应用的全局对象。
在java EE中java Web有四个作用域,从小到大分别是Page、Request、Session和Application,其中Application域在应用程序的任何地方都可以访问,除非Web服务器停止,否则这个Application域将一直存在。Android中的全局对象非常类似与java Web中的这个Application域,除非Android应用程序彻底清除出内存,否则全局对象将一直可以访问。当然全局对象需要继承Application,必须是android.app.Application的子类。全局类代码如下:
import android.app.Application;

public class MyApp extends Application
{
public String country;
public Data data = new Data();
}
注意:全局类中不需要静态变量,只需要定义成员变量即可,而且全局类中必须要有一个无参的构造方法,这个类和Activity类一样的,由系统自动创建MyApp对象。因此,必须要有一个无参数的构造方法。
只编写一个全局类是不会自动创建全局类对象的,因此Android系统并不知道哪个类是全局类,因此,需要在AndroidManifest.xml清单文件中通过<application>标签的android:name属性指定这个类,代码如下;
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="mobile.android.ch04.transmit.data" android:versionCode="1"
android:versionName="1.0">
<application android:name=".MyApp" android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".Main" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

指定全局类后,值程序运行后,MyApp对象就会自动被创建,而且会一直在内存中驻留知道应用程序彻底退出内存。
下面的代码首先获得MyApp对象,然后为MyApp对象中的字段赋值。
MyApp myApp = (MyApp) getApplicationContext();
myApp.country = "美国";
myApp.data.id = 1234;
myApp.data.name = "飞碟";
intent = new Intent(this, MyActivity4.class);
startActivity(intent);
在MyActivity4中获得MyApp对象,并在屏幕上输出MyApp对象中字段的值。
MyApp myApp = (MyApp) getApplicationContext();
textView.setText("MyApp.country:" + myApp.country + "\nMyApp.data.id:"
+ myApp.data.id + "\nMyApp.data.name" + myApp.data.name);


总结:方法有很多,无论难易,我们的目的是解决问题,在用的过程中我们需要考虑到的是需要使我们的代码更加有利于维护,更加健壮,从而采用不同的方法。对于向其他Activity传递简单类型(int、String、short、boolean等)或可序列化的对象时,建议使用Intent对象进行数据传递,如果传递不可序列化对象,可以采用静态变量或者全局对象的方式,不过按照官方的建议,最好采用全局对象的方式。另外,如果想使某些数据长时间驻留内存,以便程序随时采用,最好采用全局对象的形式,当然,如果数据不复杂,也可以采用静态变量。至于剪切板,如果不是非常特殊的情况,并不建议使用。因为这可能会影响到其它的程序(因为其他的程序可能会用到剪切板)。

此文摘自Android开发权威指南一书,其中也有部分是自己的理解,在这里与大家共同学习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息