Service / Background Task pada Flutter

Implementasi service di Flutter dapat dilakukan melalui beberapa cara. Di antaranya adalah dengan menambahkan package/library, misalnya menggunakan package flutter_background_service https://pub.dev/packages/flutter_background_service/example

atau menggunakan package get_it https://pub.dev/packages/get_it,

contoh penggunaan library get_it ini dapat dibaca pada https://medium.com/flutter-community/creating-services-to-do-the-work-in-your-flutter-app-93d6c4aa7697.

Pada tutorial ini akan dibahas implementasi background task di Flutter melalui mekanisme isolate. Isolate adalah model Dart untuk membuat progran multithread, code yang dijalankan melalui isolate tidak berbagi memory dengan program utama.

Cara paling sederhana untuk mengimplementasikan isolate dalam program Dart adalah dengan menggunakan fungsi compute, pada tutorial berikut ini akan diuraikan contoh programnya.

Intro

Secara default, aplikasi yang dibangun menggunakan bahasa Dart bekerja pada single thread. Pada sebagian besar kasus, model single thread cara codingnya sederhana dan program dapat dijalankan dengan cepat tanpa mengalami penurunan kinerja aplikasi atau adanya tampilan UI yang tersendat-sendat (sering disebut sebagai "jank").

Namun pada kasus tertentu, sebuah aplikasi mungkin membutuhkan adanya komputasi yang cukup berat, misalnya melakukan parsing sebuah dokumen dalam format JSON yang berukuran besar. Jika operasi ini dilakukan lebih dari 16 milidetik, pengguna aplikasi akan mengalami jank.

Untuk mencegah terjadinya jank, developer aplikasi perlu memindahkan komputasi yang berat tersebut ke background. Pada Android, hal ini artinya menjadwalkan pekerjaan pada thread yang terpisah. Pada Flutter ini akan diimplementasikan menggunakan isolate. 

Apa yang akan dibuat?

Pada tutorial ini akan dibuat sebuah program yang memproses data dalam bentuk json. File json yang diproses berukuran 1,1 MB dan berisi data 5.000 foto. Setiap record data berisi id foto, id album, dan url fotonya. 

Penerapan isolate pada tutorial ini akan dilakukan melalui langkah-langkah berikut :
1. Buat project Flutter di VS Code

2. Tambahkan package http
Tambahkan packe dengan mengeksekusi perintah melalui command line

flutter pub add http

3. Buat class Photo
Class ini dibuat untuk memudahkan proses parsing data JSON menjadi object Photo.

class Photo {
  final int albumId;
  final int id;
  final String title;
  final String url;
  final String thumbnailUrl;

  const Photo({
    required this.albumId,
    required this.id,
    required this.title,
    required this.url,
    required this.thumbnailUrl,
  });

  factory Photo.fromJson(Map<String, dynamic> json) {
    return Photo(
      albumId: json['albumId'] as int,
      id: json['id'] as int,
      title: json['title'] as String,
      url: json['url'] as String,
      thumbnailUrl: json['thumbnailUrl'] as String,
    );
  }
} 
4. Buat method parsePhotos untuk mengubah response dari data JSON yang didapat ke bentuk List<Photo>;

// A function that converts a response body into a List<Photo>.
List<Photo> parsePhotos(String responseBody) {
  final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();

  return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
5. Buat method fetchPhotos untuk (a) melakukan request dan mendapatkan response berupa data json dan (b) melakukan parsing data di background
Future<List<Photo>> fetchPhotos(http.Client client) async {
  final response = await client
      .get(Uri.parse('https://jsonplaceholder.typicode.com/photos')); // (a) melakukan request dan mendapatkan response berupa data json

  // Use the compute function to run parsePhotos in a separate isolate.
  return compute(parsePhotos, response.body); // (b) melakukan parsing data di background
}
6. Tambahkan permission untuk mengakses internet dengan mengetikkan beris berikut di file
android/src/main/AndroidManifest.xml 

tambahkan baris tersebut sebelum tag <application

Source code lengkap lib/main.dart dapat dilihat sebagai berikut
import 'dart:async';
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future<List<Photo>> fetchPhotos(http.Client client) async {
  final response = await client
      .get(Uri.parse('https://jsonplaceholder.typicode.com/photos'));

  // Use the compute function to run parsePhotos in a separate isolate.
  return compute(parsePhotos, response.body);
}

// A function that converts a response body into a List<Photo>.
List<Photo> parsePhotos(String responseBody) {
  final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();

  return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}

class Photo {
  final int albumId;
  final int id;
  final String title;
  final String url;
  final String thumbnailUrl;

  const Photo({
    required this.albumId,
    required this.id,
    required this.title,
    required this.url,
    required this.thumbnailUrl,
  });

  factory Photo.fromJson(Map<String, dynamic> json) {
    return Photo(
      albumId: json['albumId'] as int,
      id: json['id'] as int,
      title: json['title'] as String,
      url: json['url'] as String,
      thumbnailUrl: json['thumbnailUrl'] as String,
    );
  }
}

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    const appTitle = 'Isolate Demo';

    return const MaterialApp(
      title: appTitle,
      home: MyHomePage(title: appTitle),
    );
  }
}

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

  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: FutureBuilder<List<Photo>>(
        future: fetchPhotos(http.Client()),
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return const Center(
              child: Text('An error has occurred!'),
            );
          } else if (snapshot.hasData) {
            return PhotosList(photos: snapshot.data!);
          } else {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
    );
  }
}

class PhotosList extends StatelessWidget {
  const PhotosList({super.key, required this.photos});

  final List<Photo> photos;

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
      ),
      itemCount: photos.length,
      itemBuilder: (context, index) {
        return Image.network(photos[index].thumbnailUrl);
      },
    );
  }
}
Referensi :
https://docs.flutter.dev/cookbook/networking/background-parsing
https://docs.flutter.dev/development/packages-and-plugins/background-processes
https://blog.logrocket.com/multithreading-flutter-using-dart-isolates/
source code https://github.com/ardhiesta/flutter_json_isolate

Comments

Popular posts from this blog

Contoh Inheritance (Pewarisan) di Java

Review Singkat Pilihan Transportasi Umum Rute Solo - Wonosobo

Physical address dan Logical Address dalam Jaringan Komputer