Parcelable中的标志位有什么用途?

17

我一直在将 Parcelable 写入 Parcel 中,没有关注方法签名中的参数 flags 字段,这一切都运行良好,但现在我遇到了一个实现,无法再忽略它们:

public static <K extends Parcelable, V extends Parcelable> void write(Parcel dest,
                                                    Map<K, V> map, int flags) {
        if (map == null) {
            dest.writeInt(-1);
        } else {
            Set<Map.Entry<K, V>> entrySet = map.entrySet();
            dest.writeInt(entrySet.size());
            for (Map.Entry<K, V> entry : entrySet) {
                dest.writeParcelable(entry.getKey(), flags);
                dest.writeParcelable(entry.getValue(), flags);
            }
        }
    }
我写了一个实用程序,用于将Map转换为Parcelable,我想知道在编写时是否应该将标志原样传递给KeyValue,还是应该对于Key传递0,对于Value传递flags。我阅读了文档中关于标志的定义:

PARCELABLE_WRITE_RETURN_VALUE

自API等级1起添加

int PARCELABLE_WRITE_RETURN_VALUE

标志用于在 writeToParcel(Parcel, int) 中使用:要写入的对象是函数返回的结果,例如 "Parcelable someFunction()""void someFunction(out Parcelable)" 或者 "void someFunction(inout Parcelable)"。一些实现可能希望在此时释放资源。

常量值:1 (0x00000001)

但我无法理解它。有人能简单地解释一下什么是 Parcelable 标志以及应该如何使用吗?


一个返回值,即函数的结果,非常容易理解。 - OneCricketeer
我有一个void方法?但那是一个静态方法?这与什么有关?如果你引用了entry.getKey()方法,它确实返回一个Parcelable。标志不应该是1吗? - Manish Kumar Sharma
void write 是你的方法,entry.getKey() 定义在 Map 接口中,我不明白你的意思。 - OneCricketeer
你只有两个选项,那么在发问之前你尝试了多少个呢? - OneCricketeer
2个回答

7

目前唯一存在的标志 (PARCELABLE_WRITE_RETURN_VALUE) 旨在用于 AIDL 接口。它被设计为暗示某些 Parcelable 对象,这些对象正在从 IPC 方法中返回,因此它们关联的资源可以被释放。例如,ContentProvider 内部包含类似以下 AIDL 方法:

ParcelFileDescriptor openFile(String path, int flags);

当您在自定义ContentProvider中覆盖openFile时,您的方法会返回一个打开的ParcelFileDescriptor……您不需要自己关闭它,在进程间传输期间也不会自动关闭(在Linux中,传递描述符并不意味着关闭它们)。但是,该描述符不会泄漏!相反,当写入Parcel时,ParcelFileDescriptor会自行关闭

@Override
public void writeToParcel(Parcel out, int flags) {
    if (mWrapped != null) {
        try {
            mWrapped.writeToParcel(out, flags);
        } finally {
            releaseResources();
        }
    } else {
        if (mCommFd != null) {
            out.writeInt(1);
            out.writeFileDescriptor(mFd);
            out.writeFileDescriptor(mCommFd);
        } else {
            out.writeInt(0);
            out.writeFileDescriptor(mFd);
        }
        if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
            // Not a real close, so emit no status
            closeWithStatus(Status.SILENCE, null);
        }
    }
}

由于ParcelFileDescriptor只是普通类,使用Binder/Parcel的功能在进程之间传递FileDescriptor,你可以想象存在类似的类,它们持有本地资源(内存、文件描述符)并在从openFile等方法返回时有条件地释放它们。

同样,其他标志可能被用于在Parcelable matryoshka中深入传播类似的有条件行为。不幸的是,Android开发人员没有定义引入这种自定义标志的合理规则(不像例如IBinder#FIRST_CALL_TRANSACTIONIBinder#LAST_CALL_TRANSACTION),而AIDL在实践中除了Android内部外并不广泛使用,所以我不知道任何关于这种标志的例子。


好的,请告诉我是否我理解正确。open() 方法会导致 IPC,另一侧(另一个进程)会创建一个 FileDescriptor 最终返回给我们,因为 FileDescriptor 可能已经在那一侧打开,所以检查后才将其发送给我们。此外,在通过 IPC 调用发送之前关闭资源是一个非常特殊的情况,我可能只需要在 IPC 期间查找它。 - Manish Kumar Sharma
1
@pulp_fiction 是的,你全都说对了。此外,这个答案只是“大致”正确,因为描述符实际上并不是“在发送之前关闭”,而是被复制(在writeFileDescriptor内部),原始的被“关闭”(实际上并没有,因为描述符是引用计数的),然后副本真正地被关闭(可能是在传输过程中,与我先前的说法相矛盾,即在传输期间描述符不会关闭)。所有这一切的疯狂都隐藏在ParcelFileDescriptor抽象之后,使其看起来像是“在发送之前关闭”。他们当时在想什么…… - user1643723
1
@pulp_fiction 简而言之:不要随意将文件描述符存储在其他对象中,否则 Dianne Hackborn 就会来杀你的狗。有一个方便的方法专门用于此目的(确定 Parcel 是否包含文件描述符)。 - user1643723

0

你只能提供标志零或一。

你有一个void方法,因此你没有从函数返回Parcelable,也没有一个参数是Parcelable,正如文档所说,因此标志应该为零。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接