使用 NFC 将文件发送到其他设备

本课介绍了如何设计应用,以使用 Android Beam 文件传输功能将大型文件发送到其他设备。如需发送文件,您需要请求使用 NFC 和外部存储空间的权限,进行测试以确保您的设备支持 NFC,然后向 Android Beam 文件传输功能提供相应 URI。

使用 Android Beam 文件传输功能的要求如下:

  1. 仅 Android 4.1(API 级别 16)及更高版本支持使用 Android Beam 文件传输功能传输大型文件。
  2. 您要传输的文件必须位于外部存储空间中。如需详细了解如何使用外部存储空间,请阅读使用外部存储空间
  3. 您要传输的每个文件都必须为全局可读文件。您可以通过调用 File.setReadable(true,false) 方法来设置此权限。
  4. 您必须提供待传输文件的 URI。Android Beam 文件传输功能无法处理由 FileProvider.getUriForFile 生成的内容 URI。

在清单中声明功能

首先,修改您的应用清单,以声明您的应用所需的权限和功能。

请求权限

如需允许您的应用使用 Android Beam 文件传输功能通过 NFC 从外部存储空间发送文件,您必须在应用清单中请求以下权限:

NFC
允许您的应用通过 NFC 发送数据。如需指定此权限,请将以下元素添加为 <manifest> 元素的子级:
        <uses-permission android:name="android.permission.NFC" />
    
READ_EXTERNAL_STORAGE
允许您的应用从外部存储空间读取数据。如需指定此权限,请将以下元素添加为 <manifest> 元素的子级:
        <uses-permission
                android:name="android.permission.READ_EXTERNAL_STORAGE" />
    

注意:从 Android 4.2.2(API 级别 17)开始,系统不会强制执行此权限。对于要从外部存储空间读取数据的应用,该平台的未来版本可能需要此权限。为了确保向前兼容性,请立即请求此权限(在需要使用它之前)。

指定 NFC 功能

通过将 <uses-feature> 元素添加为 <manifest> 元素的子级,指定您的应用要使用 NFC。将 android:required 属性设置为 true,以指明您的应用只有在存在 NFC 时才会正常工作。

以下代码段展示了如何指定 <uses-feature> 元素:

    <uses-feature
        android:name="android.hardware.nfc"
        android:required="true" />

请注意,如果您的应用仅将 NFC 作为一个选项,但在不存在 NFC 时仍然正常工作,则应将 android:required 设置为 false,并在代码中测试 NFC。

指定 Android Beam 文件传输功能

由于仅 Android 4.1(API 级别 16)及更高版本支持 Android Beam 文件传输功能,如果您的应用将 Android Beam 文件传输作为其关键功能,则您必须指定具有 android:minSdkVersion="16" 属性的 <uses-sdk> 元素。否则,您可能需要在必要时将 android:minSdkVersion 设置为其他值,并在代码中测试平台版本,如下文所述。

测试是否支持 Android Beam 文件传输功能

如需在应用清单中指定 NFC 是可选项,您可以使用以下元素:

    <uses-feature android:name="android.hardware.nfc" android:required="false" />

如果您设置属性 android:required="false",则必须在代码中测试是否支持 NFC 和 Android Beam 文件传输功能。

如需在代码中测试是否支持 Android Beam 文件传输功能,请先通过调用具有 FEATURE_NFC 参数的 PackageManager.hasSystemFeature() 来测试设备是否支持 NFC。然后,通过测试 SDK_INT 的值来检查 Android 版本是否支持 Android Beam 文件传输功能。如果支持 Android Beam 文件传输功能,则获取一个 NFC 控制器实例,您可以通过该实例与 NFC 硬件通信。例如:

Kotlin

    class MainActivity : Activity() {
        ...
        private lateinit var nfcAdapter: NfcAdapter
        // Flag to indicate that Android Beam is available
        private var androidBeamAvailable = false
        ...
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            androidBeamAvailable = if (!packageManager.hasSystemFeature(PackageManager.FEATURE_NFC)) {
                // NFC isn't available on the device
                /*
                  * Disable NFC features here.
                  * For example, disable menu items or buttons that activate
                  * NFC-related features
                  */
                false
            // Android Beam file transfer isn't supported
            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
                // If Android Beam isn't available, don't continue.
                androidBeamAvailable = false
                /*
                 * Disable Android Beam file transfer features here.
                 */
                ...
                false
            } else {
                // Android Beam file transfer is available, continue
                nfcAdapter = NfcAdapter.getDefaultAdapter(this)
                ...
                true
            }
        }
        ...
    }
    

Java

    public class MainActivity extends Activity {
        ...
        NfcAdapter nfcAdapter;
        // Flag to indicate that Android Beam is available
        boolean androidBeamAvailable  = false;
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
            // NFC isn't available on the device
            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
                /*
                 * Disable NFC features here.
                 * For example, disable menu items or buttons that activate
                 * NFC-related features
                 */
                ...
            // Android Beam file transfer isn't supported
            } else if (Build.VERSION.SDK_INT <
                    Build.VERSION_CODES.JELLY_BEAN_MR1) {
                // If Android Beam isn't available, don't continue.
                androidBeamAvailable = false;
                /*
                 * Disable Android Beam file transfer features here.
                 */
                ...
            // Android Beam file transfer is available, continue
            } else {
                androidBeamAvailable = true;
                nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            ...
            }
        }
        ...
    }
    

创建可提供文件的回调方法

确认设备支持 Android Beam 文件传输功能后,添加一个回调方法,系统会在 Android Beam 文件传输功能检测到用户要将文件发送到其他支持 NFC 的设备时调用该方法。此回调方法会返回 Uri 对象的数组。Android Beam 文件传输功能会将由这些 URI 表示的文件复制到接收设备。

如需添加回调方法,请实现 NfcAdapter.CreateBeamUrisCallback 接口及其方法 createBeamUris()。以下代码段展示了如何执行此操作:

Kotlin

    public class MainActivity : Activity() {
        ...
        // List of URIs to provide to Android Beam
        private val fileUris = mutableListOf<Uri>()
        ...
        /**
         * Callback that Android Beam file transfer calls to get
         * files to share
         */
        private inner class FileUriCallback : NfcAdapter.CreateBeamUrisCallback {
            /**
             * Create content URIs as needed to share with another device
             */
            override fun createBeamUris(event: NfcEvent): Array<Uri> {
                return fileUris.toTypedArray()
            }
        }
        ...
    }
    

Java

    public class MainActivity extends Activity {
        ...
        // List of URIs to provide to Android Beam
        private Uri[] fileUris = new Uri[10];
        ...
        /**
         * Callback that Android Beam file transfer calls to get
         * files to share
         */
        private class FileUriCallback implements
                NfcAdapter.CreateBeamUrisCallback {
            public FileUriCallback() {
            }
            /**
             * Create content URIs as needed to share with another device
             */
            @Override
            public Uri[] createBeamUris(NfcEvent event) {
                return fileUris;
            }
        }
        ...
    }
    

实现该接口后,通过调用 setBeamPushUrisCallback() 提供对 Android Beam 文件传输功能的回调。以下代码段展示了如何执行此操作:

Kotlin

    class MainActivity : Activity() {
        ...
        private lateinit var nfcAdapter: NfcAdapter
        // Flag to indicate that Android Beam is available
        private var androidBeamAvailable = false
        ...
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            // Android Beam file transfer is available, continue
            nfcAdapter = NfcAdapter.getDefaultAdapter(this).apply {

                /*
                 * Instantiate a new FileUriCallback to handle requests for
                 * URIs
                 */
                fileUriCallback = FileUriCallback()
                // Set the dynamic callback for URI requests.
                nfcAdapter.setBeamPushUrisCallback(fileUriCallback, this@MainActivity)
            }
            ...
        }
        ...
    }
    

Java

    public class MainActivity extends Activity {
        ...
        // Instance that returns available files from this app
        private FileUriCallback fileUriCallback;
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
            // Android Beam file transfer is available, continue
            ...
            nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            /*
             * Instantiate a new FileUriCallback to handle requests for
             * URIs
             */
            fileUriCallback = new FileUriCallback();
            // Set the dynamic callback for URI requests.
            nfcAdapter.setBeamPushUrisCallback(fileUriCallback,this);
            ...
        }
        ...
    }
    

注意:您还可以通过应用的 NfcAdapter 实例将 Uri 对象的数组直接提供给 NFC 框架。如果您可以定义在 NFC 触摸事件发生之前传输的 URI,请选择此方法。如需详细了解此方法,请参阅 NfcAdapter.setBeamPushUris()

指定要发送的文件

如需将一个或多个文件传输到其他支持 NFC 的设备,请获取每个文件的 URI(架构为 file 的 URI),然后将相应的 URI 添加到 Uri 对象的数组中。如需传输文件,您还必须具有该文件的永久读取权限。例如,以下代码段展示了如何从文件名中获取文件 URI,然后将该 URI 添加到数组中:

Kotlin

            /*
             * Create a list of URIs, get a File,
             * and set its permissions
             */
            val fileUris = mutableListOf<Uri>()
            val transferFile = "transferimage.jpg"
            val extDir = getExternalFilesDir(null)
            val requestFile = File(extDir, transferFile).apply {
                setReadable(true, false)
            }
            // Get a URI for the File and add it to the list of URIs
            Uri.fromFile(requestFile)?.also { fileUri ->
                fileUris += fileUri
            } ?: Log.e("My Activity", "No File URI available for file.")
    

Java

            /*
             * Create a list of URIs, get a File,
             * and set its permissions
             */
            private Uri[] fileUris = new Uri[10];
            String transferFile = "transferimage.jpg";
            File extDir = getExternalFilesDir(null);
            File requestFile = new File(extDir, transferFile);
            requestFile.setReadable(true, false);
            // Get a URI for the File and add it to the list of URIs
            fileUri = Uri.fromFile(requestFile);
            if (fileUri != null) {
                fileUris[0] = fileUri;
            } else {
                Log.e("My Activity", "No File URI available for file.");
            }
    

如需了解其他相关信息,请参阅存储选项

如需查看与此网页相关的示例代码,请参阅 Android BeamLargeFiles 示例