一人一猫旅行记之Intent传递数据原理

安卓提供了Intent机制来实现应用间的通信,可以在Activity之间、Service、BroadCast中传递数据。提起Intent,我们最熟悉的可能就是在启动Activity的时候携带数据,使用非常简单:

Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("param1","我是参数1");
startActivity(intent);

在新的Activity,也就是Activity中获取数据也是非常的简单:

String param1 = getIntent().getStringExtra("param1");

我们可以看到在传值的时候,调用了Intent的putExtra方法,我们查看Intent的源码,发现Intent中定义了许多的putExtra方法,以咱们的上面的调用为例,实际上是调用了如下的方法:

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

从上面的代码我们可以明白,在Intent中定义了一个Bundle类型的变量mExtras,当调用Intent的putExtra时,会根据第二个参数类型的不同,调用Bundle的对应方法。我们跑到Bundle会惊喜的发现没有putString方法,所以只能到它的父类BaseBundle中寻找,结果如下:

ArrayMap<String, Object> mMap = null;
<-省略部分代码->
public void putString(@Nullable String key, @Nullable String value) {
        unparcel();
        mMap.put(key, value);
    }

我们接着看看unparcel这个方法

synchronized void unparcel() {
        synchronized (this) {
            final Parcel parcelledData = mParcelledData;
            if (parcelledData == null) {
                if (DEBUG) Log.d(TAG, "unparcel "
                        + Integer.toHexString(System.identityHashCode(this))
                        + ": no parcelled data");
                return;
            }

            if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
                Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
                        + "clobber all data inside!", new Throwable());
            }

            if (isEmptyParcel()) {
                if (DEBUG) Log.d(TAG, "unparcel "
                        + Integer.toHexString(System.identityHashCode(this)) + ": empty");
                if (mMap == null) {
                    mMap = new ArrayMap<>(1);
                } else {
                    mMap.erase();
                }
                mParcelledData = null;
                return;
            }

            int N = parcelledData.readInt();
            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                    + ": reading " + N + " maps");
            if (N < 0) {
                return;
            }
            ArrayMap<String, Object> map = mMap;
            if (map == null) {
                map = new ArrayMap<>(N);
            } else {
                map.erase();
                map.ensureCapacity(N);
            }
            try {
                parcelledData.readArrayMapInternal(map, N, mClassLoader);
            } catch (BadParcelableException e) {
                if (sShouldDefuse) {
                    Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
                    map.erase();
                } else {
                    throw e;
                }
            } finally {
                mMap = map;
                parcelledData.recycle();
                mParcelledData = null;
            }
            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                    + " final map: " + mMap);
        }
    }

从上面的代码我们可以看到一个Parcel对象mParcelledData,从代码看来是对该对象进行处理,实际上就是将Parcel数据迁移至mMap对象中。这样就可以保证我们从mMap中获取Bundle携带的数据。

@Nullable
    public String getString(@Nullable String key) {
        unparcel();
        final Object o = mMap.get(key);
        try {
            return (String) o;
        } catch (ClassCastException e) {
            typeWarning(key, o, "String", e);
            return null;
        }
    }

也可以把unparcel这个方法看成一个预处理,就是把已经序列化的数据反序列化后放在mMap中,之后Bundle就可以通过key从mMap中读取数据。但是有一个问题,mParcelledData这个对象是从哪里来的呢?
我们以startActivity为例,最终会调走到ActivityManagerNative中的onTransact函数,我们可以看到如下一段代码:

Bundle options = data.readInt() != 0
  ? Bundle.CREATOR.createFromParcel(data) : null;

我们回到Bundle.java看一下CREATOR

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

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

从上面这段代码,可以看出createFromParcel实际调用的是Parcel的readBundle
继续往下走的话,我们会走到如下方法:

public final Bundle readBundle(ClassLoader loader) {
        int length = readInt();
        if (length < 0) {
            if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
            return null;
        }

        final Bundle bundle = new Bundle(this, length);
        if (loader != null) {
            bundle.setClassLoader(loader);
        }
        return bundle;
    }

从Bundle的构造函数继续阅读代码

BaseBundle(Parcel parcelledData, int length) {
        readFromParcelInner(parcelledData, length);
    }
    private void readFromParcelInner(Parcel parcel, int length) {
        if (length < 0) {
            throw new RuntimeException("Bad length in parcel: " + length);

        } else if (length == 0) {
            // Empty Bundle or end of data.
            mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
            return;
        }

        final int magic = parcel.readInt();
        if (magic != BUNDLE_MAGIC) {
            throw new IllegalStateException("Bad magic number for Bundle: 0x"
                    + Integer.toHexString(magic));
        }

        // Advance within this Parcel
        int offset = parcel.dataPosition();
        parcel.setDataPosition(MathUtils.addOrThrow(offset, length));

        Parcel p = Parcel.obtain();
        p.setDataPosition(0);
        p.appendFrom(parcel, offset, length);
        if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
                + ": " + length + " bundle bytes starting at " + offset);
        p.setDataPosition(0);

        mParcelledData = p;
    }

所以我们也可以知道mParcelledData实际就是传过来的Bundle序列化后的数据

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页