Android之一窥究竟Activity间的数据传递以及Intent的用处

转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/50775044
本文出自:【顾林海的博客】

何为Intent

Intent是一种消息传递机制,可以在应用程序内使用,也可以在应用程序间使用。Intent可以用于:
  • 使用类名显式启动一个特定的Service或Activity。
  • 启动Activity或Service来执行一个动作的Intent,通常需要使用特定的数据,或者对特定的数据执行动作。
  • 广播某个事件已经发生。

Intent支持Android设备上安装的任意应用程序组件之间的交互,不管它们哪个应用程序的一部分都是如此。这就把设备从一个包含相互独立的组件集合的平台变成了一个互联的系统。

Intent最常见的一个用法是显式地(通过指定要装载的类)或者隐式地(通过请求对一条数据执行某个动作)启动新的Activity。在后一种情况中,动作不一定由调用应用程序中的Activity执行。

Intent也可以用来在系统范围内广播消息。应用程序可以注册一个Broadcast Receiver来监听和响应这些广播的Intent。这样可以基于内部的、系统的或者第三方应用程序的事件创建事件驱动的应用程序。

Android通过广播Intent来公布系统事件。比如网络连接状态或者电池电量的改变。本地Android应用程序(如拨号软件)简单地注册监听特定的广播Intent(列如“来电”)并作出相应的动作。

Intent的用武之地

使用Intent来启动Activity

Intent最常见的用途是绑定应用程序组件,并在应用程序之间进行通信。Intent用来启动Activity,允许创建不同屏幕的一个工作流。

通过显式地启动一个Activity:

 Intent intent = new Intent(this, ActivityB.class);
 intent.putExtra("key_1", "value_1");
 intent.putExtra("key_2", false);
 startActivity(intent);

通过隐式地启动一个Activity:

Intent intent=new Intent(Intent.ACTION_DIAL,   Uri.parse("tel:12345678"));
startActivity(intent);

通过隐式启动Activity时,Android会解析这个Intent,并启动一个新的Activity,该Activity会提供这个电话号码进行拨号的动作。
构建一个新的隐式Intent时,需要指定一个要执行的动作,另外,也可以提供执行那个动作需要的数据的URL。还可以通过向Intent添加extra来向目标Activity发送额外的数据。

Activity间的数据传递

我们知道Activity间数据传递是通过Intent实现的。

private void init() {
        Intent intent = new Intent(this, ActivityB.class);
        intent.putExtra("key_1", "value_1");
        intent.putExtra("key_2", false);
        startActivity(intent);
}

上面是我们平时开发中常用的手段,以下是获取数据的代码段:

private void init() {
        Intent intent = getIntent();
        String strValue = intent.getStringExtra("key_1");
        Boolean blValue = intent.getBooleanExtra("key_2", false);
}

Intent intent = new Intent(this, ActivityB.class);

这段代码是Intent与Activity的一个绑定。重点看intent.putExtra方法,进入到putExtra方法中,看看"value_1"是如何存入"key_1"这个key中的。

public Intent putExtra(String name, String value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putString(name, value);
        return this;
 }

这段代码是在Intent类源码中的,具体流程是先判断mExtras是否为空,为空就创建一个Bundle的实例,mExtras是Bundle实例,接着调用Bundle类的中putString方法,也就是说Activity间的数据传递是通过Bundle传递的,查看Bundle类中的putString方法:

Map<String, Object> mMap = null;
public void putString(String key, String value) {
        unparcel();
        mMap.put(key, value);
}

在这个方法中,我们最终传递的值是以键值对存储在Map中的。

数据存储

public Bundle() {
        mMap = new HashMap<String, Object>();
        mClassLoader = getClass().getClassLoader();
}

mMap的初始化时机是在Intent中创建Bundle对象时进行了初始化。

回到Bundle类中的putString方法中,mMap在以键值对存储数据之前调用了unparcel()方法。

Bundle类中putString方法

synchronized void unparcel() {
        if (mParcelledData == null) {
            return;
        }

        int N = mParcelledData.readInt();
        if (N < 0) {
            return;
        }
        if (mMap == null) {
            mMap = new HashMap<String, Object>();
        }
        mParcelledData.readMapInternal(mMap, N, mClassLoader);
        mParcelledData.recycle();
        mParcelledData = null;
}

看到这里,这个mParcelledData是个什么鬼?原来是个Parcel对象,既然unparcel方法第一步是判断mParcelledData是否为空,那肯定是在某个地方进行了初始化,于是乎查遍Bundle类中所有地方,发现Bundle这个类实现了Parcelable接口。

public final class Bundle implements Parcelable, Cloneable{
}

Parcelable是Android特有的功能,效率比实现Serializable接口高,可以用在进程间通信(IPC)。
实现Serializable接口非常简单,声明一下就可以了。而实现Parcelable接口稍微复杂一些,但效率更高,推荐用这种方法提高性能。
android 中自定义的对象序列化的问题有两个选择一个是Parcelable,另外一个是Serializable。

实现Parcelable接口需要实现Parcelable接口中的describeContents和writeToParcel方法,并且需要自定义类型名称为CREATOR的静态成员,该成员对象需要实现Parcelable.Creator接口及其方法。

查看Bundle类实现Parcelable接口中的几个方法。

public void writeToParcel(Parcel parcel, int flags) {
        final boolean oldAllowFds = parcel.pushAllowFds(mAllowFds);
        try {
            if (mParcelledData != null) {
                int length = mParcelledData.dataSize();
                parcel.writeInt(length);
                parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L'
                parcel.appendFrom(mParcelledData, 0, length);
            } else {
                parcel.writeInt(-1); // dummy, will hold length
                parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L'

                int oldPos = parcel.dataPosition();
                parcel.writeMapInternal(mMap);
                int newPos = parcel.dataPosition();

                // Backpatch length
                parcel.setDataPosition(oldPos - 8);
                int length = newPos - oldPos;
                parcel.writeInt(length);
                parcel.setDataPosition(newPos);
            }
        } finally {
            parcel.restoreAllowFds(oldAllowFds);
        }
}


public int describeContents() {
        int mask = 0;
        if (hasFileDescriptors()) {
            mask |= Parcelable.CONTENTS_FILE_DESCRIPTOR;
        }
        return mask;
 }


public static final Parcelable.Creator<Bundle> CREATOR =
        new Parcelable.Creator<Bundle>() {
        public Bundle createFromParcel(Parcel in) {
            return in.readBundle();
        }

        public Bundle[] newArray(int size) {
            return new Bundle[size];
        }
 };

这里最有用的是以下writeToParcel方法中标红的,通过writeToParcel将Map映射成Parcel对象,在通过createFromParcel将Parcel对象映射成Bundle对象。

map数据的序列化

映射成Bundle对象

总结:通过Intent携带的Bundle进行Activity间的数据传递,Bundle内部实现了Parcelable接口,通过Parcelable接口的实例将自身的状态信息(这里是mMap 对象)写入Parcel,最后通过目标Activity从Parcel中恢复状态信息,这里面的Parcel完成了数据的序列化传递。

©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页