Flutter llama a métodos nativos (Android) y transmisión de datos

prefacio

Flutter es un marco de interfaz de usuario. Hay muchos métodos y funciones que solo pueden llamarse por sí mismos, pero ¿cómo los llamamos indirectamente a través de flutter? Oficialmente, hay dos métodos.

1. Pasar mensajes a través de los canales de la plataforma

1. Método de llamada unidireccional

Mensajería entre canales de plataforma:

Nota: Los mensajes y las respuestas se entregan de forma asíncrona para garantizar que la interfaz de usuario siga respondiendo.

Correspondencia de tipo de datos de plataforma

Lado de aleteo:

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  static const platform = MethodChannel('com.tdsss.get/battery');
  String _batteryLevel = "Unknown battery level";

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery Level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }
    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(onPressed: _getBatteryLevel,
              child: const Text('get Battery'),
            ),
            Text(_batteryLevel),
          ],
        ),
      ),// This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Luego busque el proyecto de Android, abra MainActivity, haga clic en Abrir para editar en Android Studio en la esquina superior derecha y abra una nueva ventana para editar en el entorno Andorid

Lado Android:

import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build;

import androidx.annotation.NonNull;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity {

    private static final String CHANNEL = "com.tdsss.get/battery";

    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),CHANNEL)
                .setMethodCallHandler(
                        (call,result) -> {
                            if (call.method.equals("getBatteryLevel")){
                                int batteryLevel = getBatteryLevel();
                                if (batteryLevel != -1){
                                    result.success(batteryLevel);
                                }else{
                                    result.error("UNAVAILABLE", "Battery level not available.", null);
                                }
                            }else{
                                result.notImplemented();
                            }
                        }
                );
    }
    private int getBatteryLevel() {
        int batteryLevel = -1;
        BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
        batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
        return batteryLevel;
    }
}

¡Aviso! ¡Aviso! ¡Aviso! El nombre del canal de la plataforma aquí debe ser coherente en ambos lados, es decir, "com.tdsss.get/battery"

Este nombre de canal es personalizado.

Luego regrese al proyecto flutter y ejecútelo en el teléfono móvil como se muestra en la figura a continuación:

 

 2. Pase el flujo de eventos a través de EventChannel y continúe la comunicación de datos

        En la lógica empresarial, a veces no se puede resolver simplemente llamando a un método una vez. Hay algunas funciones que requieren que monitoreemos continuamente algunos datos nativos. En este momento, podemos usar Stream para enviar eventos. Siempre que el final nativo se active los datos requeridos, Flutter puede monitorearlos.

Por ejemplo, en el lado del flutter, el sensor del podómetro en el lado de Android y el evento de hacer clic en el botón de volumen se monitorean continuamente Ambos canales se usan aquí, y el methodChannel solo se usa para verificar el permiso del podómetro de Android 10 y superior.

lado del dardo:


import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class CountStepPage extends StatefulWidget {
  const CountStepPage({Key? key}) : super(key: key);

  @override
  State<CountStepPage> createState() => _CountStepPageState();
}

class _CountStepPageState extends State<CountStepPage> {

  int steps = 0;
  int time = 0;
  int count = 0;
  var nowVoice;
  //method通信
  static const _permissionsChannel = MethodChannel('com.tdsss.permissions');

  //event通信
  static const _eventChannel = EventChannel('com.tdsss.step');

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    //初始化通信
    try {
      //请求权限
      _permissionsChannel.invokeMethod('getStepPermission');
    } on PlatformException catch (e) {
      print(e);
    }
    _eventChannel.receiveBroadcastStream().listen((event) {
      print('event : $event');
      print('event type: ${event is Map}');

      final Map<dynamic,dynamic> map = event;

      for(String key in map.keys){
        switch(key){
          case 'changeVoice':
            setState(() {
              nowVoice = map["changeVoice"];
            });
            break;
          case 'step':
            setState(() {
              steps = map["step"];
            });
            break;
          case 'time':
            setState(() {
              time = map["time"];
            });
            break;
        }
      }
    },onError: onError,onDone:onDone,);
  }
  void onError(error){
    print('event error: $error');
  }
  void onDone(){
    print('event done:');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            Text('times : $time'),
            Text('steps : $steps'),
            Text('voice : $nowVoice'),
          ],
        ),
      ),
    );
  }
}

Lado Android:

import android.Manifest;
import android.app.Service;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity {

    public static final String[] STEP_PERMISSION = {Manifest.permission.ACTIVITY_RECOGNITION};

    //事件派发对象
    private EventChannel.EventSink eventSink = null;
    //事件派发流
    private EventChannel.StreamHandler streamHandler = new EventChannel.StreamHandler() {
        @Override
        public void onListen(Object arguments, EventChannel.EventSink events) {
            eventSink = events;
        }

        @Override
        public void onCancel(Object arguments) {
            eventSink = null;
        }
    };

    SensorManager sensorManager;
    Sensor stepSensor;
    boolean isRegistered = false;
    int steps;
    int time = 0;
    AudioManager audioManager;
    int mediaVoice;
    Map map = new HashMap();
    String key = "changeVoice";
    String stepKey = "step";
    String timeKey = "time";
    String TAG = "flutter";
    MethodChannel permissionChannel;
    MethodChannel.MethodCallHandler permissionHandler = new MethodChannel.MethodCallHandler() {
        @Override
        public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
            if (call.method.equals(Config.getPerMethodName)){
                checkPermission(result);
            }
        }
    };
    EventChannel eventChannel;
    SensorEventListener sensorEventListener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            steps = (int) event.values[0];
            map.put(stepKey,steps);
            sendEventData(map);
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    };


    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        permissionChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),"com.tdsss.permissions");
        permissionChannel.setMethodCallHandler(permissionHandler);
        eventChannel = new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),"com.tdsss.step");
        eventChannel.setStreamHandler(streamHandler);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode){
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_DOWN:
                mediaVoice = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
                map.put(key,mediaVoice);
                sendEventData(map);
                Log.e("flutter", "onKeyDown mediaVoice: "+mediaVoice );
                return false;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        audioManager = (AudioManager) getSystemService(Service.AUDIO_SERVICE);
        mediaVoice = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        map.put("changeVoice",mediaVoice);
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                time++;
                map.put(timeKey,time);
                //sendEventData(map);
            }
        },0,1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isRegistered){
            sensorManager.unregisterListener(sensorEventListener);
        }
    }

    private void sendEventData(Object data){
        if (eventSink != null){
            Log.i(TAG, "sendEventData: ");
            eventSink.success(data);
        }
    }
    private void checkPermission(@NonNull MethodChannel.Result result){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // 检查该权限是否已经获取
            int get = ContextCompat.checkSelfPermission(getApplicationContext(), STEP_PERMISSION[0]);
            // 权限是否已经 授权 GRANTED---授权  DINIED---拒绝
            if (get != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(STEP_PERMISSION,101);
                result.success(1);
            }else{
                result.success(0);
                initSensor();
                //result.error("STEP_PERMISSION","isGranted",0);
            }
        }
    }
    private void initSensor(){
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        stepSensor =sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
        if (stepSensor != null) {
            isRegistered = sensorManager.registerListener(sensorEventListener,stepSensor,SensorManager.SENSOR_DELAY_FASTEST);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 101:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    initSensor();
                    Log.i(TAG, "onRequestPermissionsResult: allowed");
                }else{
                    Log.i(TAG, "onRequestPermissionsResult: false");
                }
                return;
        }
    }
}

El resultado final es el siguiente:

 

 En segundo lugar, use el complemento Pigeon

Este método se recomienda oficialmente, porque el uso de este complemento es de tipo seguro. En  Pigeon  , la interfaz de mensajes se define en Dart, y luego generará el código de Android e iOS correspondiente.

Primero, agregue dependencias de complementos al archivo de dependencias del proyecto pubspec.yaml:

dependencias: 
  #Llamar a la paloma del complemento API de plataforma dual 
  : ^9.0.1

Los pasos de este método son engorrosos y el ejemplo oficial no es muy claro.Aquí uso flutter para simplemente obtener una cadena en Android como ejemplo.

Primero, escriba una nueva clase abstracta en el proyecto flutter y márquela como @HostApi, lo que indica que este es un método para llamar a Android desde Flutter y debe implementarse en el lado de Android.

pigeon_get.dardo:

import 'package:pigeon/pigeon.dart';

@HostApi()
abstract class AndroidApi {
  String getString();
}

 Luego abra la consola en el directorio raíz del proyecto, o abra la terminal en el IDE, ejecute el comando y genere el código automáticamente.

El archivo de entrada es el archivo dart que acabamos de crear, que contiene el método de construcción AndroidApi

dart_out es donde el código en el lado del dardo se genera y nombra automáticamente

java_out es la ubicación y el nombre del método de construcción generado en el lenguaje java en el lado de Android

java_package es el nombre del paquete de la aplicación

flutter pub run pigeon --input lib/pigeon_get.dart --dart_out lib/pigeon_g.dart --java_out android/app/src/main/java/com/tdsss/found/food/found_food/StepPigeon.java --java_package " com.tdsss.found.food.found_food"

El directorio del proyecto es el siguiente:

Puede ver que los dos archivos que generamos ya han aparecido. Abra el archivo StepPigeon.java y puede ver que la clase abstracta que creamos en el lado del dardo ahora se ha convertido en una interfaz.

Ahora vayamos al lado de Android para implementar este AndroidApi

Actividad principal.java:

public class MainActivity extends FlutterActivity {

    private class MyApi implements StepPigeon.AndroidApi{
        @NonNull
        @Override
        public String getString() {
            return "this is Android String!!";
        }
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        StepPigeon.AndroidApi.setup(
            getFlutterEngine().getDartExecutor().getBinaryMessenger(),new MyApi()
                );
    }
}

Finalmente, regrese al lado de aleteo y busque una página para llamar a este método.

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:found_food/pigeon_g.dart';

class CountStepPage extends StatefulWidget {
  const CountStepPage({Key? key}) : super(key: key);

  @override
  State<CountStepPage> createState() => _CountStepPageState();
}

class _CountStepPageState extends State<CountStepPage> {
  String androidS = '';
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            ElevatedButton(onPressed: _getString, child: Text('string : $androidS')),
          ],
        ),
      ),
    );
  }
  _getString() async{
    AndroidApi api = AndroidApi();
    String s = await api.getString();
    setState(() {
      androidS = s;
    });
    print(androidS);
  }
}

El resultado de la operación es el siguiente: 

¡Hasta ahora! Hemos completado los dos métodos nativos para llamar a flutter.

referencia

Escribir código de plataforma de dos extremos (escritura e implementación de complementos) - Documentación en chino de Flutter - Sitio web para desarrolladores de Flutter en chino - Flutter

 Uso simple de llamar a EventChannel basado en el complemento flutter (3) - Blog de XiaoBaiGeYHS - Blog de CSDN

Supongo que te gusta

Origin blog.csdn.net/TDSSS/article/details/129248396
Recomendado
Clasificación