概要

在flutter写界面很快,但是他有些功能并没有,如果要调用拍照,路由等,就要调用android 原生的功能,

也可以使用插件,但是本章节是教如何于原生通信调用拍照功能,并将拍照结果返回到flutter中进行显示

整体架构流程

flutter于原生的通信,首先就是通信建立通道,传输消息句柄过去给anroid原生进行接收 

Future<void> _playMusic() async{
    await Plat.platform.invokeMethod("playMusic");
  }

//由于项目中很多地方需要调用android原生,索性写到了一个类中
class Plat{
  static const platform = MethodChannel("com.example.android_message");
}

 这里是android原生代码,通道名要一致不然找不到通道就通信不了,就比如你需要打电话,打电话需要什么呢?需要手机和手机卡这就是通道,而有了这个还要有手机号,这样就能通信,下面是原生代码

主Activity

//定义Flutter 与Android通信的通道名称
    private static final String CHANNEL = "com.example.android_message";


   //配置Flutter引擎
    @Override
    public void configureFlutterEngine(FlutterEngine flutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);

        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
                .setMethodCallHandler(
                        (call, result) -> {
                        //接收判断消息句柄
                 if (call.method.equals("takePhoto")) {
                                this.result = result;
                                takePhone();
                            }
        );
    }
    private void takePhone(){
       
        Intent intent = new Intent(this,TakePhotoActivity.class);
        //用于将子活动完成后获取返回的结果
        //REQUEST_CODE_SUB_ACTIVITY 标识Activity返回结果的请求码,
        // 当你使用Activity 并希望获取其返回结果时,你可以为启动子 Activity 的请求分配一个唯一的请求码。
        startActivityForResult(intent,REQUEST_CODE_SUB_ACTIVITY);
    }

拍照功能 由于项目可能需要调用不同的功能,所以都写在主Activity中不太友好,就分开了,
这边创建一个子Activity,然后使用该Intent启动该TakePhotoActivity的子活动,之后调用拍照功能将拍照的结果返回到flutter中,

package com.example.android_message;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Base64;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;

import io.flutter.embedding.android.FlutterActivity;

public class TakePhotoActivity extends FlutterActivity {

    protected static final int REQUEST_IMAGE_CAPTURE = 1;
    private String currentPhotoPath;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_take_photo);
        onJurisdiction();
    }

    //拍照功能
    // 实现拍照的方法
    private void dispatchTakePictureIntent() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
            ex.printStackTrace();
            backResult("");
            return;
        }
        if (photoFile != null) {
            //创建用于存储拍照结果的URI,并添加到拍照意图中
            Uri photoURI = FileProvider.getUriForFile(this,
                    "com.example.android_message.fileprovider",
                    photoFile);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
            if (currentPhotoPath != null) {
                // 将文件移动到指定位置作为永久文件
                File tempFile = new File(currentPhotoPath);
                //将图片保存到永久文件夹当中返回
                String permanentFilePath = savePermanentFile(tempFile);

                // 将拍照后的图像返回给 Flutter
                File permanentFile = new File(permanentFilePath);
                Bitmap bitmap = BitmapFactory.decodeFile(permanentFile.getAbsolutePath());
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
                byte[] byteArray = stream.toByteArray();
                String imageString = Base64.encodeToString(byteArray, Base64.DEFAULT);
                Intent intent = new Intent();
                intent.putExtra("path","");
                setResult(RESULT_OK, intent);
                backResult(currentPhotoPath);
            }
        }
    }
//当拍照将要返回的时候调用这个方法,再将结果返回的路径给主Activity
    private void backResult(String path){
        Intent intent = new Intent();
        intent.putExtra("path",path);
        setResult(RESULT_OK, intent);
        finish();
    }
    private String savePermanentFile(File tempFile) {
        // 获取永久文件存储目录
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);


        // 创建目标文件夹(如果不存在)
        if (!storageDir.exists()) {
            storageDir.mkdirs();
        }

        // 生成永久文件名
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
        String permanentFileName = "IMG_" + timeStamp + ".jpg";
        File permanentFile = new File(storageDir, permanentFileName);

        String permanentFilePath = permanentFile.getAbsolutePath();

        currentPhotoPath = permanentFilePath;

        // 将临时文件移动到永久文件
        try {
            //读写图像 将临时图片写入到永久文件夹中
            InputStream in = new FileInputStream(tempFile);
            OutputStream out = new FileOutputStream(permanentFile);

            byte[] buffer = new byte[1024];
            int length;
            while ((length = in.read(buffer)) > 0) {
                out.write(buffer, 0, length);
            }
            in.close();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        // 返回永久文件的路径字符串
        return permanentFilePath;
    }

    //创建保存拍照结果的临时文件
    private File createImageFile() throws IOException {
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        String imageFileName = "IMG_" + UUID.randomUUID().toString() + ".jpg";
        File imageFile = File.createTempFile(imageFileName, ".jpg", storageDir);
        imageFile.setReadable(true, false);
        currentPhotoPath = imageFile.getAbsolutePath();
        return imageFile;
    }

    // 添加动态权限
    private void onJurisdiction() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int checkCallPhonePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
            // 用户取消打开权限直接返回
            if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 0);
                return;
            } else {
                dispatchTakePictureIntent();
            }
        } else {
            dispatchTakePictureIntent();
        }
    }

    // 动态申请权限返回结果
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 0:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    dispatchTakePictureIntent();
                } else {
                    Toast.makeText(this, "用户没有同意开启这个权限", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

技术细节

需要提到的是,如果你要使用到文件的读取,拍照功能,是需要加权限,和xml文件

在android原生中的xml文件夹下创建一个file_paths,用于定义应用程序对特定文件路径的访问权限

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="my_images" path="Pictures" />
</paths>

在AndroidManifest.xml 中的manifest.xml里面添加

<!-- 打开互联网权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 这行代码声明了应用程序需要读取外部存储器的权限。这个权限允许应用程序读取设备上的照片、视频、音频等文件。-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!--    这行代码声明了应用程序需要写入外部存储器的权限。这个权限允许应用程序在设备上创建、修改和删除文件。-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--    这行代码表明了 应用程序需要使用相机的权限,这个权限允许应用程序访问设备的相机进行拍照或录像操作-->
    <uses-permission android:name="android.permission.CAMERA" />

 

小结

代码上有很多注释,所以这里就不多赘述了,关于flutter于android之间的通信,就说到这里

总之: 两个设备之间的通信,首先需要一个通道,在需要将消息句柄对上,然后android原生再调用相应功能,但是做这些的前提是,需要加权限,因为手机上访问拍照的时候是需要权限的

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐