CategoriesAndroidFlutteriOSProgramming

Threading di Flutter dengan Isolate

Pernahkan device kalian terasa berat atau mungkin sampai aplikasinya not responding saat menjalankan suatu method atau function dari kodingan flutter kalian ? kenapa bisa terasa berat padahal sudah dijalankan menggunakan async/await ? itu karena async/await berjalan di thread utama yang bertugas untuk memproses UI / Tampilan atau bisa disebut dengan UI Thread. jika method/function yang membutuhkan waktu lama untuk diproses dijalankan di UI Thread akan menyebabkan Not Responding. biasanya method/function yang berhubungan dengan ImageProcessing yang membutuhkan waktu lama untuk memproses. solusinya agar tidak Not Responding adalah memproses method/function tersebut di Thread lain agar tidak mengganggu UI Thread. salah satu metode Threading di Flutter adalah menggunakan Isolate.

Isolate adalah unit pemrosesan / Thread ringan yang beroperasi secara mandiri, memungkinkan Flutter untuk menjalankan kode secara asinkron tanpa menghambat proses pada UI. Dengan kata lain, Isolate memungkinkan Flutter untuk menjalankan tugas-tugas berat tanpa membuat UI menjadi tidak responsif.

sebagai contoh kita mempunyai method untuk memproses gambar menjadi grayscale.

sebelum mencoba, install terlebih dahulu library untuk menghandle gambar di Flutter

dependencies:
  image: ^3.0.1

setelah itu isi kode dengan koding sebagai berikut :

import 'dart:io';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'dart:async';
import 'package:image/image.dart' as img;

// Fungsi untuk mengonversi gambar ke grayscale secara asynchronous
Future<Uint8List> convertToGrayscaleAsync(Uint8List imageData) async {
  img.Image image = img.decodeImage(imageData);
  img.grayscale(image); // Mengonversi gambar ke grayscale
  return img.encodePng(image);
}

// Fungsi untuk menyimpan gambar ke file secara asynchronous
Future<File> saveImageToFileAsync(Uint8List imageBytes, String outputPath) async {
  File outputFile = File(outputPath);
  await outputFile.writeAsBytes(imageBytes);
  return outputFile;
}

// Fungsi untuk mengonversi gambar ke grayscale dan menyimpannya ke file
Future<File> convertAndSaveImageToGrayscale(File inputFile, String outputPath) async {
  Uint8List imageData = await File(inputFile.path).readAsBytes();
  Uint8List grayscaleImage = await convertToGrayscaleAsync(imageData);
  return await saveImageToFileAsync(grayscaleImage, outputPath);
}

// Contoh penggunaan
void main() async {
  // Lokasi file input dan output
  File inputFile = File('path/to/input/image.jpg');
  String outputPath = 'path/to/output/grayscale_image.png';

  // Mengonversi gambar ke grayscale dan menyimpannya ke file secara asynchronous
  File grayscaleImageFile = await convertAndSaveImageToGrayscale(inputFile, outputPath);

  // Gunakan hasil konversi gambar sesuai kebutuhan
  // (Misalnya, menampilkan path file grayscale atau menampilkan di antarmuka pengguna)
  print('Grayscale image saved to: ${grayscaleImageFile.path}');
}

coba ketik kodingan sesuai instruksi dan jalankan.
pastinya akan lama sekali waktu pemrosesannya jika anda menggunakan gambar berukuran besar, dan bisa saja aplikasi anda Not Responding atau Force Close karena beban pemrosesan yang terlalu berat.

sekarang coba proses dengan Isolate dengan koding sebagai berikut :

import 'dart:io';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'dart:async';
import 'package:image/image.dart' as img; // Import paket image untuk manipulasi gambar

// Fungsi untuk memproses gambar secara asynchronous di Isolate
void imageProcessing(SendPort sendPort) {
  ReceivePort receivePort = ReceivePort();
  sendPort.send(receivePort.sendPort);

  receivePort.listen((dynamic data) {
    if (data is List<dynamic>) {
      String action = data[0];
      Uint8List imageData = data[1];
      String outputPath = data[2];

      if (action == 'convertToGrayscale') {
        Uint8List grayscaleImage = convertToGrayscale(imageData);
        File outputFile = saveImageToFile(grayscaleImage, outputPath);
        sendPort.send(outputFile);
      }
    }
  });
}

// Fungsi untuk mengonversi gambar ke grayscale
Uint8List convertToGrayscale(Uint8List imageData) {
  img.Image image = img.decodeImage(imageData);
  img.grayscale(image); // Mengonversi gambar ke grayscale
  return img.encodePng(image);
}

// Fungsi untuk menyimpan gambar ke file
File saveImageToFile(Uint8List imageBytes, String outputPath) {
  File outputFile = File(outputPath);
  outputFile.writeAsBytesSync(imageBytes);
  return outputFile;
}

// Fungsi untuk memulai proses konversi gambar ke grayscale
Future<File> convertImageToGrayscale(File inputFile, String outputPath) async {
  // Mendapatkan byte data dari gambar
  Uint8List imageData = await File(inputFile.path).readAsBytes();

  ReceivePort receivePort = ReceivePort();
  Isolate isolate = await Isolate.spawn(imageProcessing, receivePort.sendPort);

  Completer<File> completer = Completer<File>();

  receivePort.listen((dynamic data) {
    if (data is SendPort) {
      // Mengirim data gambar ke Isolate
      data.send(['convertToGrayscale', imageData, outputPath]);
    } else if (data is File) {
      // Menerima hasil konversi gambar dari Isolate
      completer.complete(data);
      isolate.kill();
      receivePort.close();
    }
  });

  return completer.future;
}

// Contoh penggunaan
void main() async {
  // Lokasi file input dan output
  File inputFile = File('path/to/input/image.jpg');
  String outputPath = 'path/to/output/grayscale_image.png';

  // Mengonversi gambar ke grayscale menggunakan Isolate
  File grayscaleImageFile = await convertImageToGrayscale(inputFile, outputPath);

  // Gunakan hasil konversi gambar sesuai kebutuhan
  // (Misalnya, menampilkan path file grayscale atau menampilkan di antarmuka pengguna)
  print('Grayscale image saved to: ${grayscaleImageFile.path}');
}

coba run dan rasakan hasilnya.

tidak akan ada lagi yang namanya not responding atau force close, karena UI Thread bebas dari proses berat. anda bisa melakukan hal lain sembari menunggu gambar selesai diproses.

lalu apakah bisa kita menjalankan lebih dari satu Isolate dalam satu waktu ? jawabannya adalah bisa. tinggal menambahkan ReceivePort, Isolate.spawn, Completer, dan listener untuk ReceivePort kita bisa menambahkan Isolate lain dengan method/function yang sama.

Kesimpulan

  • Isolate Memungkinkan pemrosesan paralel di luar thread utama aplikasi (UI thread).
  • Isolate adalah thread mandiri yang dapat berjalan secara paralel. Setiap isolate memiliki ruang memori terisolasi, dan komunikasi antar isolate terjadi dengan sistem send broadcast dan listen data.
  • Isolate Berguna untuk tugas-tugas berat yang dapat dijalankan secara mandiri tanpa menghambat UI Thread. Cocok untuk komputasi intensif, pengolahan data besar, dll.

Published by Ahmad Saifur Ridlo

Android Developer at Algostudio.net