ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Android 使用 NFC 分享文件:Android Beam 文件传输完整实战指南

Android 使用 NFC 分享文件:Android Beam 文件传输完整实战指南 文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载本指南基于《Android 官方培训课程中文版》仓库的 使用NFC分享文件 及其两个子课程 发送文件给其他设备 与 接收其他设备的文件 编写。Android 允许开发者通过Android Beam 文件传输Android Beam File Transfer功能在设备之间传送大文件。它提供了简单的 API用户只需将两台支持 NFC 的设备背部相触即可触发文件拷贝流程Android Beam 会自动把文件从一台设备复制到另一台设备并在传输完成后通知用户。本文完整讲解发送端与接收端的配置与实现覆盖 Manifest 权限声明、设备能力检测、URI 回调、接收目录解析等全部实战环节读完即可在自己的应用中接入基于 NFC 的设备间文件传输能力。一、Android Beam 文件传输简介大文件与小数据的两种 API在动手编码之前需要先厘清 Android NFC 框架提供的两种Beam能力能力适用数据规模引入版本说明Android Beam 文件传输 API较大的文件Android 4.1API Level 16将实际文件从一台设备拷贝到另一台设备完成后发布通知Android Beam NDEF 传输 API较小的数据URI、消息等Android 4.0API Level 14用于传递 NDEF 消息如分享 URL 或短信文本在content-sharing/beam-files/index.md中明确说明Android Beam 文件传输 API 用来处理规模较大的数据而 Android 4.0 引入的 NDEF 传输 API 用来处理规模较小的数据如 URI 或消息。此外Android Beam 只是 Android NFC 框架的众多特性之一NFC 框架还允许应用从 NFC 标签读取 NDEF 消息。在应用层开发者与 NFC 硬件通信的统一入口是NfcAdapter。通过NfcAdapter.getDefaultAdapter(Context)获取控制器实例后即可注册 Beam 回调或直接指定待推送的 URI 列表。Android 平台把 NFC 硬件能力声明为android.hardware.nfc特性见 tv/start/hardware.md 中的硬件特性清单应用可通过PackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)在运行时检测设备是否具备该硬件。本文聚焦大文件场景以下分别讲解发送端发送文件给其他设备与接收端接收其他设备的文件的完整实现。二、发送端将大文件 Beam 给其他设备1. 发送文件的四大硬性前提使用 Android Beam 文件传输发送文件必须同时满足以下四个条件见 发送文件给其他设备系统版本Android Beam 文件传输必须在 Android 4.1API Level 16及以上版本使用。存储位置待传输文件必须放置于外部存储external storage可通过Context.getExternalFilesDir(null)等 API 获取应用专属外部存储目录。读权限文件必须是全局可读的通过File.setReadable(true, false)设置第二个参数false表示仅所有者可读即对 Android Beam 进程授予读取权限。URI 类型必须提供File URIscheme 为file的 URI。Android Beam 文件传输无法处理由FileProvider.getUriForFile()生成的 Content URI——这一点对习惯使用 FileProvider 做安全分享的开发者尤其重要两种分享机制的 URI 约束完全不同。2. 在 Manifest 清单文件中声明权限与功能声明权限发送端需要在manifest标签下添加两个权限!-- 允许应用通过 NFC 发送数据 -- uses-permission android:nameandroid.permission.NFC / !-- 允许应用读取外部存储 -- uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE /关于READ_EXTERNAL_STORAGE有一个重要的版本细节对于 Android 4.2.2API Level 17及之前版本该权限并非必需后续版本若应用需要读取外部存储则可能需要声明。为了保证程序在未来的稳定性建议在权限声明变成必需之前就提前在清单文件中声明。指定 NFC 功能通过uses-feature声明应用使用 NFC并将android:required设为true使应用只在 NFC 可用时才能安装运行uses-feature android:nameandroid.hardware.nfc android:requiredtrue /如果应用将 NFC 视为可选功能希望在无 NFC 设备上继续运行则应将android:required设为false并在代码中自行测试 NFC 可用性下一节详述。指定最低 SDK 版本由于 Android Beam 文件传输要求 Android 4.1API Level 16及以上若该功能是应用不可缺少的核心模块应在uses-sdk中指定android:minSdkVersion16否则可将minSdkVersion设为更低值并在代码中检测平台版本后降级处理。3. 代码中测试设备是否支持 Android Beam 文件传输当清单中android:requiredfalse时必须在运行时检测能力。检测分两步先用PackageManager.hasSystemFeature()配合FEATURE_NFC判断硬件是否支持 NFC再通过Build.VERSION.SDK_INT与Build.VERSION_CODES.JELLY_BEAN_MR1即 Android 4.2API Level 17Java 代码中该常量的语义对应支持文件传输的系统版本基线比较判断系统版本是否支持文件传输。只有两者都满足时才获取NfcAdapter实例public class MainActivity extends Activity { ... NfcAdapter mNfcAdapter; // Flag to indicate that Android Beam is available boolean mAndroidBeamAvailable false; ... Override protected void onCreate(Bundle savedInstanceState) { ... // NFC isnt 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 isnt supported } else if (Build.VERSION.SDK_INT Build.VERSION_CODES.JELLY_BEAN_MR1) { // If Android Beam isnt available, dont continue. mAndroidBeamAvailable false; /* * Disable Android Beam file transfer features here. */ ... // Android Beam file transfer is available, continue } else { mNfcAdapter NfcAdapter.getDefaultAdapter(this); ... } } ... }说明原文示例中PackageManager.hasSystemFeature(...)为描述性写法实际调用应通过getPackageManager().hasSystemFeature(...)进行此处已按正确 API 形式给出。4. 创建提供文件的回调函数确认设备支持后需要实现NfcAdapter.CreateBeamUrisCallback接口重写其createBeamUris(NfcEvent event)方法。当 Android Beam 文件传输检测到用户希望向另一台支持 NFC 的设备发送文件时系统会调用该回调回调返回的Uri[]数组中的每个 URI 对应的文件将被依次拷贝到接收设备public class MainActivity extends Activity { ... // List of URIs to provide to Android Beam private Uri[] mFileUris 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 mFileUris; } } ... }实现接口后通过NfcAdapter.setBeamPushUrisCallback()将回调注册给 Android Beam 文件传输通常在onCreate()中完成public class MainActivity extends Activity { ... // Instance that returns available files from this app private FileUriCallback mFileUriCallback; ... Override protected void onCreate(Bundle savedInstanceState) { ... // Android Beam file transfer is available, continue ... mNfcAdapter NfcAdapter.getDefaultAdapter(this); /* * Instantiate a new FileUriCallback to handle requests for * URIs */ mFileUriCallback new FileUriCallback(); // Set the dynamic callback for URI requests. mNfcAdapter.setBeamPushUrisCallback(mFileUriCallback,this); ... } ... }替代方案静态 URI如果能在 NFC 触碰事件发生之前确定要分享的 URI 集合也可以不注册回调而是直接通过NfcAdapter.setBeamPushUris(Uri[], Activity)把 URI 数组交给 NFC 框架。动态回调适合用户操作后才确定文件的场景静态方式适合文件预先已知的场景二者择一即可。5. 指定要发送的文件构建 File URI要发送一个或多个文件需要为每个文件获取 File URIscheme 为file并确保拥有该文件的读权限然后将其加入Uri[]数组/* * Create a list of URIs, get a File, * and set its permissions */ private Uri[] mFileUris 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) { mFileUris[0] fileUri; } else { Log.e(My Activity, No File URI available for file.); }关键点总结文件必须位于外部存储本例使用getExternalFilesDir(null)获得应用专属外部目录无需额外写权限即可创建requestFile.setReadable(true, false)确保 Beam 传输进程能读取该文件Uri.fromFile()生成file://scheme 的 File URI这是 Android Beam 文件传输唯一接受的 URI 形式数组大小示例为 10应按业务需要调整避免越界。三、接收端响应传输并定位拷贝的文件1. 传输完成后的系统行为Android Beam 文件传输把文件拷贝到接收设备的特殊目录应用专属外部目录后会执行两项收尾工作使用Android Media Scanner扫描拷贝的文件并在MediaStoreProvider 中为媒体文件添加条目记录发布一个包含Intent的通知该 Intent 带有ACTION_VIEWaction、第一个被传输文件的 MIME 类型以及指向该文件的 URI。用户点击通知后该 Intent 被发送到系统交由能够响应的 Activity 处理。2. 通过 intent-filter 响应 VIEW Intent为了让应用响应上述通知需要在目标 Activity 的activity标签内添加intent-filter并包含三个子标签activity android:namecom.example.android.nfctransfer.ViewActivity android:labelAndroid Beam Viewer ... intent-filter action android:nameandroid.intent.action.VIEW/ category android:nameandroid.intent.category.DEFAULT/ ... /intent-filter /activity三个子标签的职责分别是标签作用action android:nameandroid.intent.action.VIEW /匹配通知发出的、带ACTION_VIEW的 Intentcategory android:nameandroid.intent.category.DEFAULT /匹配不包含显式 Category 的 Intent隐式 Intent 默认要求 DEFAULT categorydata android:mimeTypemime-type /匹配指定 MIME 类型仅声明应用能处理的类型注意Android Beam 文件传输并不是ACTION_VIEWIntent 的唯一发送者接收设备上的其他应用也可能发送带该 action 的 Intent。因此接收端必须对 Intent 的来源加以甄别详见下文获取拷贝文件的目录避免误处理其他来源的数据。3. 请求文件读权限读取 Beam 传输到设备上的文件需要READ_EXTERNAL_STORAGE权限uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE /若希望文件被拷贝到应用自己的存储区则需要WRITE_EXTERNAL_STORAGE权限——注意WRITE_EXTERNAL_STORAGE权限包含了READ_EXTERNAL_STORAGE。另外两个要点Android 4.2.2API Level 17及之前READ_EXTERNAL_STORAGE仅在用户主动选择读取文件时才强制要求后续版本会在所有读取场景下要求因此建议提前声明以保证兼容性应用对自己的内部存储区域拥有完全控制权因此文件拷贝到应用自身内部存储时不需要声明写权限。4. 获取拷贝文件的目录检查 Scheme 与 AuthorityAndroid Beam 文件传输会一次性把所有文件拷贝到接收设备上的同一个目录通知中的 Intent 只携带第一个文件的 URI。由于接收到的ACTION_VIEWIntent 可能来自其他应用接收端必须通过检查 URI 的Scheme与Authority来区分处理策略。首先用Uri.getScheme()判断 URI 类型再分别处理public class MainActivity extends Activity { ... // A File object containing the path to the transferred files private File mParentPath; // Incoming Intent private Intent mIntent; ... /* * Called from onNewIntent() for a SINGLE_TOP Activity * or onCreate() for a new Activity. For onNewIntent(), * remember to call setIntent() to store the most * current Intent * */ private void handleViewIntent() { ... // Get the Intent action mIntent getIntent(); String action mIntent.getAction(); /* * For ACTION_VIEW, the Activity is being asked to display data. * Get the URI. */ if (TextUtils.equals(action, Intent.ACTION_VIEW)) { // Get the URI from the Intent Uri beamUri mIntent.getData(); /* * Test for the type of URI, by getting its scheme value */ if (TextUtils.equals(beamUri.getScheme(), file)) { mParentPath handleFileUri(beamUri); } else if (TextUtils.equals( beamUri.getScheme(), content)) { mParentPath handleContentUri(beamUri); } } ... } ... }代码注释中已提示一个重要实践若 Activity 使用singleTop启动模式后续的 Beam Intent 会通过onNewIntent()送达此时务必调用setIntent()保存最新的 Intent再执行上述处理逻辑首次启动时则走onCreate()。5. 从 File URI 获取目录如果 Intent 携带的是 File URI该 URI 包含文件的绝对路径完整路径 文件名。对 Android Beam 文件传输而言该文件所在目录正是其他被传输文件若有的位置。取得 URI 路径部分去掉file:前缀、构造File对象并取其父目录即可... public String handleFileUri(Uri beamUri) { // Get the path part of the URI String fileName beamUri.getPath(); // Create a File object for this filename File copiedFile new File(fileName); // Get a string containing the files parent directory return copiedFile.getParent(); } ...6. 从 Content URI 获取目录查询 MediaStore如果 Intent 携带的是 Content URI情况稍复杂。此类 URI 可能指向MediaStoreContent Provider 中记录的目录与文件名既可能来自 Android Beam 文件传输也可能来自其他应用也可能是针对其他 Content Provider 的 URI。关键背景对 Android Beam 文件传输而言当接收的第一个文件 MIME 类型为audio/*、image/*或video/*时系统会在存储传输文件的目录内运行 Media Scanner 建立索引并把结果写入MediaStoreContent Provider随后将第一个文件的Content URI回传给 Android Beam 文件传输——通知 Intent 中携带的正是这个 Content URI。因此接收端需要先通过Uri.getAuthority()判断 URI 关联的 Content ProviderAuthority 值含义与处理策略MediaStore.AUTHORITYURI 关联的是被MediaStore记录的一个或多个文件可从MediaStore查询出文件全名含目录进而获得目录其他值来自其他 Content Provider 的 Content URI可以展示关联数据但不要尝试获取文件目录对于MediaStore的 Content URI执行一次查询将收到的 Content URI 作为Uri参数投影projection指定为MediaStore.MediaColumns.DATA列。返回的Cursor中即包含 URI 所代表文件的完整路径与文件名该目录路径下还包含 Android Beam 文件传输传送过来的其他文件... public String handleContentUri(Uri beamUri) { // Position of the filename in the query Cursor int filenameIndex; // File object for the filename File copiedFile; // The filename stored in MediaStore String fileName; // Test the authority of the URI if (!TextUtils.equals(beamUri.getAuthority(), MediaStore.AUTHORITY)) { /* * Handle content URIs for other content providers */ // For a MediaStore content URI } else { // Get the column that contains the file name String[] projection { MediaStore.MediaColumns.DATA }; Cursor pathCursor getContentResolver().query(beamUri, projection, null, null, null); // Check for a valid cursor if (pathCursor ! null pathCursor.moveToFirst()) { // Get the column index in the Cursor filenameIndex pathCursor.getColumnIndex( MediaStore.MediaColumns.DATA); // Get the full file name including path fileName pathCursor.getString(filenameIndex); // Create a File object for the filename copiedFile new File(fileName); // Return the parent directory of the file return new File(copiedFile.getParent()); } else { // The query didnt work; return null return null; } } } ...至此无论 URI 是filescheme 还是指向MediaStore的contentscheme接收端都能定位到 Beam 传输文件的目录而针对其他 Content Provider 的 URI 则只做展示、不解析目录这正是对非 Beam 来源的 VIEW Intent的兼容处理。四、发送/接收两端配置总览环节发送端接收端权限NFC、READ_EXTERNAL_STORAGEREAD_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE功能声明uses-feature android:nameandroid.hardware.nfc可选核心 APINfcAdapter.getDefaultAdapter()、setBeamPushUrisCallback()intent-filterACTION_VIEWCATEGORY_DEFAULTmimeTypeURI 类型file://File URI禁止 FileProvider Content URIfile://或MediaStore的content://目录定位无需handleFileUri()/handleContentUri()查MediaStore.MediaColumns.DATA五、课程定位与延伸阅读本指南属于仓库内容分享课程系列的第三部分。整个系列在 building-content-sharing.md 中有总览分享简单的数据通过 ACTION 与 Intent 与其他应用分享/接收简单数据分享文件FileProvider使用 URI 与临时访问权限提供安全的文件访问——注意该课程中的 Content URI 方案与本文的 File URI 方案适用场景不同二者形成互补使用 NFC 分享文件本文使用 NFC 实现设备间的文件传递。从仓库目录结构可见content-sharing/beam-files/下的index.md、sending-files.md、receive-files.md本文涉及的发送、接收两节分别对应两个独立文档SUMMARY.md 也以同样的层级组织该系列便于按需查阅。需要说明的是Android Beam 文件传输 API 发布于 Android 4.1 时代其 File URI 全局可读权限的模型与后来的分区存储、FileProvider 安全模型存在差异。在实际项目中应在满足兼容性要求的前提下文件置于外部存储、File.setReadable(true, false)、File URI结合目标系统版本的存储权限策略合理取舍例如在 Android 10 及以上的分区存储环境中优先考虑系统级分享组件或 Beam 能力降级方案。本文的 API 细节与代码均以本仓库文档原文为准可作为理解 NFC 点对点文件传输机制的权威参考。赞分享文档教程移动开发【免费下载链接】android-training-course-in-chineseAndroid官方培训课程中文版项目地址https://gitcode.com/gh_mirrors/an/android-training-course-in-chinese点击查看免费下载相关推荐Laravel Comments与前端框架集成Vue.js和React的最佳实践Laravel Comments与前端框架集成Vue.js和React的最佳实践 Laravel Comments是一款专为Laravel应用设计的原生评论系如何在Mac上使用NearDrop实现跨设备文件共享Mac与Android无缝传输完整指南如何在Mac上使用NearDrop实现跨设备文件共享Mac与Android无缝传输完整指南 想要在Mac和Android设备之间快速共享文件吗厌倦了使用数据桌面应用通信终极Android文件传输方案OpenMTP在macOS上的完整使用指南还在为macOS与Android设备间的文件传输而烦恼吗传统MTP工具传输慢、稳定性差、大文件限制等问题现在有了完美的解决方案。OpenMTP作为专为mac桌面应用上一篇企业数据主权革命WeKnora如何重新定义本地化智能文档处理下一篇5步实操指南用OpenCore Legacy Patcher让旧款Mac免费升级最新macOS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表