使用 Intent 启动 Activity和Activity之间的数据传递

2019-01-18 17:32:32 浏览数 (1)

在Android 程序之中,Activity 对象时和用户交互的唯一手段,几乎每个 Android 项目程序都有多个 Activity。因此,灵活的在屏幕上切换 Activity 尤为重要。下面让我们一起来看一下如何用 Intent 来启动 Activity 对象吧

首先,Intent 分为 显式 Intent 和隐式 Intent。显式 Intent 就是指定了要启动的 Activity 类,而隐式 Intent 就是不直接指定要启动的 Activity 类,通过一些动作和标志来筛选符合条件的 Activity 对象启动。 先来看一下显式 Intent 的构造方法:

代码语言:javascript复制
Intent(Context packageContext, Class<?> cls) ;

第一个参数为 Context 对象,要求传入启动 Activity 的对象的引用,第二个参数要求传入要启动的 Activity 对象的类(也可以传入其他的组件(Service等等),这里只讨论 Activity 对象)。 例:

代码语言:javascript复制
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);

这个 Intent 对象指定了要启动的 Activity 对象,为显式 Intent 对象。当然,我们也可以在创建Intent 对象时不传入要启动的 Activity 对象,即为隐式Intent。例:

代码语言:javascript复制
Intent intent = new Intent();   // 创建一个新的 Intent对象
intent.setAction(Intent.ACTION_VIEW);
intent.setData("http://www.baidu.com");
intet.addCategory("com.example.category.MY_CATEGORY");
startActivity(intent);
  第二行代码设置 Intent 启动的 Activity 对象可以执行的动作,动作可以自己定义,也可以使用Android自带的一些动作,此处使用了Android自带的访问网页的动作,那么系统将会寻找能够执行该动作的Activity并启动,如果有多个Activity,那么系统将会询问。
  第三行代码设置了Intent的数据,http:为网络协议,还有很多的协议,比如:tel: 为拨打电话的协议,此处设定为打开百度网页。
  第四行代码设置了 Intent 启动的Activity对象的标志,在这里可以把它看成一种筛选Activity的属性,只有和Intent对象的Action、Data、Category (如果Intent设置了这些属性)属性都相匹配的Activity,才会被该隐式Intent启动。

下面演示一些隐式Intent的用法:

创建一个新的Android工程 activity_main.xml:

代码语言:javascript复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <Button 
        android:id="@ id/startActivityButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="隐式 Intent 启动  Activity"/>
</LineaLayout>

MainActivity.java:

代码语言:javascript复制
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
    private Button button = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.startActivityButton);
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void OnClick(View v)
            {
                Intent intent = new Intent();
                intent.setAction(Intent.ACION_VIEW);
                startActivity(intent);
            }
        });
    }
}

第二个Activity在AndroidManifest中的配置:

代码语言:javascript复制
<activity android:name="SecondActivity">
            <intent-filter >
                <action <action android:name="android.intent.action.VIEW"/> <!-- 为活动增加访问网页的动作 -->
                <data android:scheme="http"/> <!-- 设置Activity的响应协议和Intent匹配-->
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

SecondActivity 使用默认自带的代码就可以了 运行程序,单击按钮之后出现系统的选择框:

在这里我们有两种选择,我们可以使用当前项目的应用响应这个Intent,但是并不能打开网页,因为我们并没有实现打开网页的功能,也可以使用系统自带的浏览器相应这个Intent,这样就可以打开百度网页。

接下来是借助Intent来进行Activity之间的数据传递,要借助Intent对象来进行Activity 之间的数据传递,要借助Intent类的putExtra方法:

我们可以看到,putExtra方法有很多重载的版本,分别用于储存传输不同类型的数据,这里我们看一下画重点的两个方法参数,一个是Bundle类型的参数,一个数Serializable类型的参数,Bundle类型其实就是一个数据块,里面可以储存很多数据,并且都是根据键值对的形式储存。而Serializable类型是一个接口,意为“序列化的数据”,这个接口使得类的对象能够变成序列化的数据,供传输使用。所以我们可以通过我们自定义的类型实现这个接口,然后通过这个参数使得我们的自定义类型的对象能够通过Intent对象来进行传递,接下来通过一个例子来试验: 新建一个Android工程: activity_main.xml:

代码语言:javascript复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <Button
        android:id="@ id/buttonSendTextData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="传送文本数据" />
    <Button 
        android:id="@ id/buttonSendImageData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="传送图片数据"/>
    <Button 
        android:id="@ id/buttonSendObjectData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="传送自定义对象数据"/>

</LinearLayout>

一个简单的布局:三个按钮竖向居中布局对应传输三种数据事件 接下来是MainActivity,java:

代码语言:javascript复制
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {

    private Button button = null;
    private Intent intent = null;
    public static final String TEXT_DATA = "send_text_data";
    public static final String IMAGE_DATA = "send_image_data";
    public static final String OBJECT_DATA = "send_object_data";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.buttonSendTextData);
        button.setOnClickListener(listener);
        button = (Button) findViewById(R.id.buttonSendImageData);
        button.setOnClickListener(listener);
        button = (Button) findViewById(R.id.buttonSendObjectData);
        button.setOnClickListener(listener);
    }

    private View.OnClickListener listener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch(v.getId())
            {
            case R.id.buttonSendTextData:
                sendTextData();
                break;
            case R.id.buttonSendImageData:
                sendImageData();
                break;
            case R.id.buttonSendObjectData:
                sendObjectData();
                break;
            }
        }
    };

    private void sendTextData()
    {
        intent = new Intent(this, SecondActivity.class);
        /*
         * 调用Intent的putExtra方法来储存要传输的数据,以 “键值-对”的形式储存数据
         */
        intent.putExtra(TEXT_DATA, "MainActivity传送的文本");
        startActivity(intent);
    }

    private void sendImageData()
    {
        /*
         * 这里新建了一个Bundle对象用于保存Intent要传输的数据,这里的数据是一个Bitmap类型的图片数据,
         * Bitmap 类型实现了接口Parcelable,因此它也是Parcelable类型的数据
         */
        Bitmap bitmap = BitmapFactory.decodeResource(
                getResources(), R.drawable.ic_launcher); // 通过BitmapFactory类的一些静态方法可以从工程文件、手机储存文件等等一些已经存在的图片加载到程序中使用
        Bundle bundle = new Bundle();
        bundle.putParcelable(IMAGE_DATA, bitmap);
        /*
         * 这里将intent要传输的数据设置为Bundle对象,其实即使我们直接通过Intent对象的putExtra方法
         * 来传送数据,在内部还是使用Bundle对象来存储这个数据,因此Intent其实是通过Bundle对象来储存
         * 并且传输数据的
         */
        intent = new Intent(this, SecondActivity.class);
        intent.putExtras(bundle);
        startActivity(intent);
    }

    private void sendObjectData()
    {
        Book book = new Book("C Language", "xiaoming", 42.6);
        intent = new Intent(this, SecondActivity.class);
        /*
         * 设置传输的数据对象为我们自定义的类Book对象,Book类实现了接口Serializable,
         * 因此Book类也是Serializable类型
         */
        intent.putExtra(OBJECT_DATA, book);
        startActivity(intent);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

在MainActivity.java里面自定义了三个方法分别实现传输三种数据,在第二个方法sendImageData方法中我们使用bundle.putParcelable方法来储存一个Bitmap对象,在上面提过了我们可以用Serializable接口来将我们要传递的自定义数据“序列化”,那么在这里,这个Parcelable接口的功能也是一样的,也是将一些复杂的数据序列化用于传输,两者的区别在于效率问题Parcelable接口的效率更高,但是使用起来更加复杂,要实现接口中的一系列抽象方法用于将复杂的类型序列化,而Serializable接口使用简单,复杂的数据类型只需要用使用这个接口就行了,不需要我们去对这个数据类型序列化,序列化的过程由系统完成。 我们可以看到Android官方文档给出的Bitmap:

确实是实现了Parcelable接口,既然如此,我们直接用就行了。 第三个方法sendObjectData中的Book类就是实现了serializable接口,之后通过intent的putExtra方法的重载实现的传递,我们来看一下Book.java:

代码语言:javascript复制
package com.example.ChangeDataInActivities;

import java.io.Serializable;

public class Book implements Serializable{

    private String name = null;
    private String author = null;
    private double price = 0;

    public Book(String name, String author, double price)
    {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    @Override 
    public String toString()
    {
        return "Bookname = "   name   ", author = "   author   ", price = "   price;
    }
}

虽然Book类实现了Serializable接口,但是却不需要重写Serializable接口中的任何方法,因为序列化的过程由系统实现。好了,下面是第二个Activity的一些信息: 首先是布局文件second_activity.xml:

代码语言:javascript复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical" >

    <TextView
        android:id="@ id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />

    <ImageView 
        android:id="@ id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>


</LinearLayout>

然后是SecondActivity.java:

代码语言:javascript复制
package com.example.ChangeDataInActivities;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.ImageView;
import android.widget.TextView;

public class SecondActivity extends Activity {

    private TextView textView = null;
    private ImageView imageView = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.second_activity);

        textView = (TextView) findViewById(R.id.textView1);
        imageView = (ImageView) findViewById(R.id.imageView1);

        /*
         * 创建三个对象用于获取从MainActivity中获得从MainActivity传输的数据
         */
        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        String text = bundle.getString(MainActivity.TEXT_DATA);
        Bitmap bitmap = bundle.getParcelable(MainActivity.IMAGE_DATA);
        Book book = (Book) bundle.getSerializable(MainActivity.OBJECT_DATA);

        /*
         * 分别判断三个对象是否为空来进行数据的显示
         * 如果text不为 null 且text不为空的时候证明传输的是文本,
         * TextUtils的静态方法isEmpty(CharSequence str)
         * 可以对String类型的变量进行双重检验:既不为null也不为空字符串的时候返回false
         */
        if(!TextUtils.isEmpty(text)) 
        {
            textView.setText(text);
        }
        else if(bitmap != null)
        {
            imageView.setImageBitmap(bitmap);
        }
        else if(book != null)
        {
            textView.setText(book.toString());
        }

    }

}

SecondActivity.java完成的任务是对传送过来的数据进行判断并且显示 别忘了在AndroidManifest.xml配置文件中注册SecondActivity:

代码语言:javascript复制
<activity android:name="SecondActivity"></activity>

来看一下运行结果:

单击“传送文本数据”按钮:

成功的显示,单击返回后再单击“传送图片数据”按钮:

good,图片成功的显示,再单击返回后单击“传送自定义数据”按钮:

Ok,完成了,把传送的book类的对象的信息显示了出来。

那么对于Activity之间传送的数据有没有大小限制呢?答案当然是有的,我们可以尝试一下 在MainActivity.java新建一个按钮用于实验传输大数据,并且在MainActivity.java中新建一个方法sendBigData:

代码语言:javascript复制
private void sendBigData()
{
        byte[] bigData = new byte[1024*1024];
        intent = new Intent(this, SecondActivity.class);
        intent.putExtra(BIG_DATA, bigData);
        startActivity(intent);
}

可以看到,我们在这个方法里面新建了大小为1Mb的数组用于Intent 的传输,运行程序:

单击“传送大数据”按钮,并不会启动SecondActivity,我们打开LogCat:

!!!failed binder transaction 导致这个发生异常的正是因为我们传输的数据过大,那么我们改下一点:改成0.5Mb再试试,没反应,但是LogCat也没有报这个异常,我们再改小一点,接近0.5Mb试试:

这一次成功启动了SecondActivity!这就证明0.5Mb是极限的传输大小,Activity之间可以通过Intent每次传输小于0.5Mb的数据。

如果博客中有什么不正确的地方,还请多多指点。 谢谢观看。。。

0 人点赞