在Flutter应用中使用Android NDK中的AssetManager类 [英] Using AssetManager class from Android NDK in a Flutter app
问题描述
我一直在尝试使用Android NDK的 AssetManager 我的Flutter应用中的类,可与 Google Oboe 一起使用,以访问音频文件.在Oboe存储库中的此示例之后,我了解到他们获得了来自Java的 AssetManager
:
JNIEXPORT无效的JNICALLJava_com_google_oboe_sample_rhythmgame_MainActivity_native_1onStart(JNIEnv * env,jobject实例,jobject jAssetManager){AAssetManager * assetManager = AAssetManager_fromJava(env,jAssetManager);如果(assetManager == nullptr){LOGE(无法获得AAssetManager");返回;}game = std :: make_unique< Game>(* assetManager);game-> start();}
基本上,它们使用参数 jAssetManager
通过JNI接口从Java传递到C ++函数.现在,我不使用JNI,因为我正在使用Flutter和Dart,并且Dart中与C ++函数进行通信的方式是通过 dart:ffi ,但是由于我创建 AssetManager
的唯一方法是使用 AAssetManager_fromJava(env,jAssetManager)
,所以我需要这两个参数我找不到用Flutter和Dart取代的方法.
我做了一些研究,当我创建Flutter FFI插件时,显然Dart代码与Kotlin代码进行通信,然后又调用了本机C ++函数.
这是我的C ++函数:
EXTERNC void * engine_create(void){AAssetManager * assetManager = AAssetManager_fromJava(env,jAssetManager);//错误:如何获取这些?如果(assetManager == nullptr){LOGE(无法获得AAssetManager");返回nullptr;}返回新的DSPAudioEngine(* assetManager);}
以下是该功能的Dart包装器:
import'dart:ffi';导入'dart:typed_data';导入'package:ffi/ffi.dart';导入'package:flutter/services.dart';typedef oboe_engine_init =指针< Void>功能();typedef OboeEngineInit = Pointer< Void>功能();FfiGoogleOboe类{静态const MethodChannel _channel =const MethodChannel('ffi_google_oboe');静态Future< String>获取platformVersion异步{最终的字符串版本=等待_channel.invokeMethod('getPlatformVersion');返回版本;}静态FfiGoogleOboe _instance;工厂FfiGoogleOboe(){如果(_instance == null){_instance = FfiGoogleOboe ._();}返回_instance;}OboeEngineInit _engineInit;FfiGoogleOboe ._(){最终的oboeLib = DynamicLibrary.open('libffi_google_oboe.so');_engineInit = oboeLib.lookup< NativeFunction< oboe_engine_init>>('engine_create').asFunction();}}
这是我在FFI插件实现中找到的Kotlin代码:
程序包g1_assd_2020.ffi_google_oboe导入androidx.annotation.NonNull;导入io.flutter.embedding.engine.plugins.FlutterPlugin导入io.flutter.plugin.common.MethodCall导入io.flutter.plugin.common.MethodChannel导入io.flutter.plugin.common.MethodChannel.MethodCallHandler导入io.flutter.plugin.common.MethodChannel.Result导入io.flutter.plugin.common.PluginRegistry.Registrar导入android.content.res.AssetManager/** FfiGoogleOboePlugin */公共类FfiGoogleOboePlugin:FlutterPlugin,MethodCallHandler {///将在Flutter和本机Android之间进行通信的MethodChannel//////此本地参考用于在Flutter Engine中注册插件并取消注册///当Flutter Engine与Activity分离时私人lateinit var频道:MethodChannel重写onAttachedToEngine(@NonNull flutterPluginBinding:FlutterPlugin.FlutterPluginBinding)的乐趣{channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(),"ffi_google_oboe")channel.setMethodCallHandler(this);}//此静态函数是可选的,等效于onAttachedToEngine.它支持旧的//扑扑1.12之前的Android项目.鼓励您继续支持//在应用迁移为使用新的Android API时通过此功能注册插件//通过https://flutter.dev/go/android-project-migration在Flutter 1.12之后发布.////鼓励在onAttachedToEngine和registerWith之间共享逻辑以保持//它们在功能上是等效的.onAttachedToEngine或registerWith中只有一个会被调用//视使用者的专案而定.必须同时定义onAttachedToEngine或registerWith//在同一个班.伴随对象{@JvmStaticfun registerWith(registrar:Registrar){val channel = MethodChannel(registrar.messenger(),"ffi_google_oboe")channel.setMethodCallHandler(FfiGoogleOboePlugin())}}重写fun onMethodCall(@NonNull调用:MethodCall,@NonNull结果:结果){如果(call.method =="getPlatformVersion"){result.success("Android $ {android.os.Build.VERSION.RELEASE}")} 别的 {result.notImplemented()}}重写fun onDetachedFromEngine(@NonNull绑定:FlutterPlugin.FlutterPluginBinding){channel.setMethodCallHandler(null)}}
最后,这是来自Oboe的人们使用JNI和Java处理它的方式:
package com.google.oboe.sample.rhythmgame;导入android.content.Context;导入android.content.res.AssetManager;导入androidx.appcompat.app.AppCompatActivity;导入android.media.AudioManager;导入android.os.Build;导入android.os.Bundle;导入android.view.WindowManager;公共类MainActivity扩展了AppCompatActivity {//用于在应用程序启动时加载"native-lib"库.静态的 {System.loadLibrary("native-lib");}@Override受保护的void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);setDefaultStreamValues(this);}受保护的void onResume(){super.onResume();native_onStart(getAssets());}受保护的void onPause(){super.onPause();native_onStop();}static void setDefaultStreamValues(Context context){如果(Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1){AudioManager myAudioMgr =(AudioManager)context.getSystemService(Context.AUDIO_SERVICE);字符串sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);int defaultSampleRate = Integer.parseInt(sampleRateStr);字符串framePerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);native_setDefaultStreamValues(defaultSampleRate,defaultFramesPerBurst);}}私有原生void native_onStart(AssetManager assetManager);私有原生void native_onStop();私有静态本机无效native_setDefaultStreamValues(int defaultSampleRate,int defaultFramesPerBurst);}
基本上,您需要将AssetManager引用从插件的Kotlin文件传递到C ++库.此答案说明了如何使Kotlin文件调用C ++代码: Android:如何从Kotlin调用ndk函数?
您将要使用methodChannel调用来触发此操作.您可以从 flutterPluginBinding.applicationContext.assets
中的onAttachedToEngine方法中获取AssetManager参考.
下面是Flutter插件的示例,该插件读取C ++库中的资产: https://github.com/mikeperri/ndk_asset_manager_example/commit/533d28b > I've been trying to use the Android NDK's AssetManager class in my Flutter app, that works with Google Oboe, to access to audio files. Following this example in the Oboe repository, I learned that they obtain the Basically with the argument I did some research and when I created the Flutter FFI plugin, apparently the Dart code communicates with Kotlin code, which then calls the native C++ functions. Here's my C++ function: Here's the Dart wrapper for that function: And here's the Kotlin code I found in the FFI plugin implementation: Finally, here's how the people from Oboe handle it using JNI and Java:
Basically, you need to pass the AssetManager reference from your plugin's Kotlin file to the C++ library. This answer explains how to make the Kotlin file call C++ code: Android: How to call ndk function from Kotlin? You'll want to use a methodChannel call to trigger this. You can get the AssetManager reference in the onAttachedToEngine method from Here's an example Flutter plugin that reads an asset in a C++ library:
https://github.com/mikeperri/ndk_asset_manager_example/commit/533d28b33c1d22f89028f89691f78e907bf19db3 这篇关于在Flutter应用中使用Android NDK中的AssetManager类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!AssetManager
from Java like this:JNIEXPORT void JNICALL
Java_com_google_oboe_sample_rhythmgame_MainActivity_native_1onStart(JNIEnv *env, jobject instance,
jobject jAssetManager) {
AAssetManager *assetManager = AAssetManager_fromJava(env, jAssetManager);
if (assetManager == nullptr) {
LOGE("Could not obtain the AAssetManager");
return;
}
game = std::make_unique<Game>(*assetManager);
game->start();
}
jAssetManager
they pass from Java to the C++ functions, through the JNI interface. Now I'm not working with JNI because I'm using Flutter and Dart, and the way in Dart for communicating with C++ functions is through dart:ffi, but since the only way I can create an AssetManager
is with AAssetManager_fromJava(env, jAssetManager)
, I need those two arguments that I can't find a way to replace with Flutter and Dart.EXTERNC void *engine_create(void) {
AAssetManager *assetManager = AAssetManager_fromJava(env, jAssetManager); // ERROR: How do I get these?
if (assetManager == nullptr) {
LOGE("Could not obtain the AAssetManager");
return nullptr;
}
return new DSPAudioEngine(*assetManager);
}
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:flutter/services.dart';
typedef oboe_engine_init = Pointer<Void> Function();
typedef OboeEngineInit = Pointer<Void> Function();
class FfiGoogleOboe {
static const MethodChannel _channel =
const MethodChannel('ffi_google_oboe');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
static FfiGoogleOboe _instance;
factory FfiGoogleOboe() {
if (_instance == null) {
_instance = FfiGoogleOboe._();
}
return _instance;
}
OboeEngineInit _engineInit;
FfiGoogleOboe._() {
final oboeLib = DynamicLibrary.open('libffi_google_oboe.so');
_engineInit = oboeLib
.lookup<NativeFunction<oboe_engine_init>>('engine_create')
.asFunction();
}
}
package g1_assd_2020.ffi_google_oboe
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import android.content.res.AssetManager
/** FfiGoogleOboePlugin */
public class FfiGoogleOboePlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "ffi_google_oboe")
channel.setMethodCallHandler(this);
}
// This static function is optional and equivalent to onAttachedToEngine. It supports the old
// pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
// plugin registration via this function while apps migrate to use the new Android APIs
// post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
//
// It is encouraged to share logic between onAttachedToEngine and registerWith to keep
// them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
// depending on the user's project. onAttachedToEngine or registerWith must both be defined
// in the same class.
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "ffi_google_oboe")
channel.setMethodCallHandler(FfiGoogleOboePlugin())
}
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
package com.google.oboe.sample.rhythmgame;
import android.content.Context;
import android.content.res.AssetManager;
import androidx.appcompat.app.AppCompatActivity;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.view.WindowManager;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setDefaultStreamValues(this);
}
protected void onResume(){
super.onResume();
native_onStart(getAssets());
}
protected void onPause(){
super.onPause();
native_onStop();
}
static void setDefaultStreamValues(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int defaultSampleRate = Integer.parseInt(sampleRateStr);
String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);
native_setDefaultStreamValues(defaultSampleRate, defaultFramesPerBurst);
}
}
private native void native_onStart(AssetManager assetManager);
private native void native_onStop();
private static native void native_setDefaultStreamValues(int defaultSampleRate,
int defaultFramesPerBurst);
}
flutterPluginBinding.applicationContext.assets
.