如何在ARCore中从本地存储运行时导入并渲染SFB文件?
解决ARCore加载本地存储SFB模型的问题
我来帮你搞定这个在ARCore里加载本地Download文件夹中SFB模型的问题~首先先梳理下你代码里的几个小问题,然后给出修正后的方案:
原代码的核心问题
- File路径格式错误:你给
File构造器传了带file://前缀的路径,但File需要的是本地文件系统的绝对路径字符串(比如/storage/emulated/0/Download/andy.sfb),带file://的是Uri格式,会导致文件找不到。 - 不必要的线程操作:你用
FutureTask单独开线程获取InputStream其实没必要,因为ModelRenderable.build()本身就是异步执行的,会在后台线程处理流加载逻辑。
修正后的完整代码
首先要确保你的应用已经获取了外部存储读取权限:
- 在
AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- Android 6.0及以上系统,需要动态申请权限,确保用户授权后再执行模型加载逻辑。
然后是加载模型的代码:
// 获取Download文件夹下的andy.sfb文件,用系统API更稳妥 File modelFile = new File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "andy.sfb" ); ModelRenderable.builder() .setSource(this, () -> { try { // 直接返回文件输入流,ModelRenderable会在后台处理 return new FileInputStream(modelFile); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException("未找到目标模型文件", e); } }) .build() .thenAccept(renderable -> { andyRenderable = renderable; // 这里可以添加模型加载成功后的逻辑,比如绑定到AnchorNode上 Log.d("ModelLoad", "Andy模型加载成功"); }) .exceptionally(throwable -> { Log.e("ModelLoad", "模型加载失败", throwable); Toast toast = Toast.makeText( this, "加载Andy模型失败:" + throwable.getMessage(), Toast.LENGTH_LONG ); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); return null; });
针对Android 10+分区存储的补充说明
如果你的应用目标SDK版本是30及以上,直接用File访问外部存储可能会受到分区存储的限制,这时候可以通过MediaStore查询Download文件夹中的文件Uri,再通过ContentResolver获取输入流:
// 示例:通过MediaStore获取Download文件夹中andy.sfb的Uri Uri modelUri = null; String[] projection = {MediaStore.Files.FileColumns._ID}; String selection = MediaStore.Files.FileColumns.DISPLAY_NAME + " = ?"; String[] selectionArgs = {"andy.sfb"}; Cursor cursor = getContentResolver().query( MediaStore.Downloads.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null ); if (cursor != null && cursor.moveToFirst()) { long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)); modelUri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, id); cursor.close(); } if (modelUri != null) { ModelRenderable.builder() .setSource(this, modelUri) .build() .thenAccept(renderable -> { andyRenderable = renderable; Log.d("ModelLoad", "通过Uri加载模型成功"); }) .exceptionally(throwable -> { // 异常处理逻辑 return null; }); }
这种方式更符合Android 10+的存储规范,避免出现文件访问权限问题。
内容的提问来源于stack exchange,提问作者Jonas Kgomo
相关产品推荐
相关产品推荐

