3

我有一个简单的活动,它启动一个文件选择器,然后通过 Android Beam 发送文件,如下所示:

@Override
protected void onActivityResult(int requestCode, int resultCode,
                                Intent data) {
    if (requestCode==0 && resultCode==RESULT_OK) {
        adapter.setBeamPushUris(new Uri[] {data.getData()}, this);
        Button btn=new Button(this);
        btn.setText("Done");
        btn.setOnClickListener(this);
        setContentView(btn);
    }
}

该活动能够正确传送图像和 .txt 文件。但是,当我发送 .json 文件时,我得到“发送未完成”。

我认为这是因为接收器上没有查看 json 文件的应用程序,所以我创建了另一个版本来查看接收到的 txt 文件。清单有

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/*" />
</intent-filter>

当我将 json 文件的扩展名更改为 .txt 时,文件会正确发送并启动接收应用程序。但是,当我将扩展名更改回 .json 并将接收器的 mimetype 更改为“application/json”时,我仍然收到“Beam 未完成”消息。

任何想法为什么?

谢谢!

4

1 回答 1

6

任何想法为什么?

Android Beam使用 Beam数组中第一个文件Uri的文件扩展名在内部映射中查找相应的 MIME 类型,然后Intent通过蓝牙对象推送配置文件 (OPP) 将其与启动文件传输的 一起发送。

如果未找到文件扩展名或匹配的 MIME 类型,则将Intent的 MIME 类型设置为null,并且根本不会启动蓝牙 OPP 文件传输。

解决方法

发送扩展名未在 中列出的文件时MimeUtils,请使用二元素 BeamUri数组:

uris[0]:带有.txt扩展名的虚拟文本文件,(稍后将删除)
uris[1]:您要传输的文件(带有无法识别的扩展名)

在您的具体情况下:

        adapter.setBeamPushUris(
                new Uri[] { dummyTxtFileUri, data.getData() },
                this);

Android Beam 将向蓝牙发送一个 MIME 类型为 的意图text/plain,以及Uri两个文件的 s,蓝牙 OPP 文件传输将正常进行。请注意,当一次发送多个文件时,接收设备会将文件存储在 中的子目录中beam/,通常命名为beam-YYYY-MM-DD/.

背景

我比较了发送设备上发送带有扩展名的文件和带有.json扩展名的文件副本之间的日志.txt。第一个显着的区别在这里:

日志:喜气洋洋test.json

03-02 13:19:34.665: D/BluetoothOppHandover(32332): Handing off outging transfer to BT

日志:喜气洋洋test.txt

03-02 15:32:19.437: D/BluetoothOppHandover(3268): Handing off outging transfer to BT
03-02 15:32:19.445: D/BluetoothOppUtility(3309): putSendFileInfo: uri=file:///storage/emulated/0/Download/test.txt@2cb672fa sendFileInfo=com.android.bluetooth.opp.BluetoothOppSendFileInfo@2cb672fa

在 AOSP 中搜索“Handing off outging transfer to BT”:

平台/包/应用程序/Nfc/src/com/android/nfc/handover/BluetoothOppHandover.java

void sendIntent() {
    Intent intent = new Intent();
    intent.setPackage("com.android.bluetooth");
    String mimeType = MimeTypeUtil.getMimeTypeForUri(mContext, mUris[0]);
    intent.setType(mimeType);
    // ...
    if (DBG) Log.d(TAG, "Handing off outging transfer to BT");
    mContext.sendBroadcast(intent);

    complete();
}

在继续之前,请注意Uri数组中第一个的 MIME 类型在Intent. 以下MimeTypeUtil.getMimeTypeForUri()

平台/包/应用程序/Nfc/src/com/android/nfc/handover/MimeTypeUtil.java

    public static String getMimeTypeForUri(Context context, Uri uri) {
        // ...
            String extension = MimeTypeMap.getFileExtensionFromUrl(uri.getPath()).toLowerCase();
            if (extension != null) {
                return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
            } else {
                return null;
            }
        // ...

因此,如果它没有识别扩展名,它会null作为 MIME 类型返回。继MimeTypeMap.getSingleton().getMimeTypeFromExtension()...

框架/基础/核心/java/android/webkit/MimeTypeMap.java

    public String getMimeTypeFromExtension(String extension) {
        return MimeUtils.guessMimeTypeFromExtension(extension);
    }

平台/libcore/luni/src/main/java/libcore/net/MimeUtils.java

public final class MimeUtils {
    private static final Map<String, String> mimeTypeToExtensionMap = new HashMap<String, String>();

    private static final Map<String, String> extensionToMimeTypeMap = new HashMap<String, String>();

    // ...

    public static String guessMimeTypeFromExtension(String extension) {
        if (extension == null || extension.isEmpty()) {
            return null;
        }
        return extensionToMimeTypeMap.get(extension);
    }

在继续之前,请注意此类MimeUtils包含Android 可识别的 MIME 类型列表。这是一个很好的参考。

我们已经到达堆栈的末尾extensionToMimeTypeMap.get()

平台/libcore/luni/src/main/java/java/util/HashMap.java

    /**
     * Returns the value of the mapping with the specified key.
     *
     * @param key
     *            the key.
     * @return the value of the mapping with the specified key, or {@code null}
     *         if no mapping for the specified key is found.
    */
    public V get(Object key) {

因此,如果未找到匹配项,则 MIME 类型最终将返回为null. 更多挖掘表明这很重要:

平台/包/应用程序/蓝牙/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java

    @Override
    public void onReceive(Context context, Intent intent) {
        // ...
        if (action.equals(Constants.ACTION_HANDOVER_SEND)) {
            String type = intent.getType();
            Uri stream = (Uri)intent.getParcelableExtra(Intent.EXTRA_STREAM);
            if (stream != null && type != null) {
                // Save type/stream, will be used when adding transfer
                // session to DB.
            BluetoothOppManager.getInstance(context).saveSendingFileInfo(type,
                    stream.toString(), true);
            } else {
                if (D) Log.d(TAG, "No mimeType or stream attached to handover request");
            }
        // ...
        // we already know where to send to
        BluetoothOppManager.getInstance(context).startTransfer(device);

由于在保存文件信息和开始传输之前对 MIME 类型进行了空检查,因此蓝牙 OPP 文件传输永远不会启动。请注意,return当存在 a 时,其他两个条件块,因此在调用之后null这个块缺少 a 似乎是一个错误。returnLog.d()

于 2015-03-02T09:11:18.890 回答