Sobes.tech
Middle

Jak poprawnie przesłać dużą ilość danych, na przykład obraz, między aktywnościami w Android?

sobes.tech AI

Odpowiedź od AI

Nie zaleca się przesyłania dużych ilości danych, takich jak obraz, bezpośrednio przez Intent ze względu na ograniczenie rozmiaru danych przesyłanych podczas serializacji (zwykle kilka setek kilobajtów lub megabajtów). Przesyłanie zbyt dużych danych może spowodować TransactionTooLargeException.

Poprawne podejścia:

  1. Zapisz dane do pliku i przekaż ścieżkę do pliku w Intent.

    • Zapisz obraz do tymczasowego pliku.
    • Przekaż URI lub ścieżkę do pliku w Extra intencji.
    • Odbierająca aktywność odczytuje plik z otrzymanej ścieżki.
    // W pierwszej aktywności
    val imageUri: Uri = saveImageToFile(bitmap) // Funkcja zapisująca Bitmap do pliku i zwracająca Uri
    val intent = Intent(this, SecondActivity::class.java)
    intent.putExtra("image_uri", imageUri.toString())
    startActivity(intent)
    
    // W drugiej aktywności
    val imageUriString = intent.getStringExtra("image_uri")
    if (imageUriString != null) {
        val imageUri = Uri.parse(imageUriString)
        val bitmap = loadImageFromFile(imageUri) // Funkcja ładująca Bitmap z Uri
        imageView.setImageBitmap(bitmap)
    }
    
  2. Użyj Singletona lub globalnego magazynu danych.

    • Umieść dane w Singletonie lub w osobnej klasie dostępnej z każdego miejsca w aplikacji.
    • Nie trzeba nic przekazywać między aktywnościami, wystarczy odwołać się do Singletona, aby uzyskać dane.
    // Globalny obiekt, np. w klasie Application lub w osobnym singletonie
    object DataHolder {
        var bitmap: Bitmap? = null
    }
    
    // W pierwszej aktywności
    DataHolder.bitmap = bitmap
    
    // W drugiej aktywności
    val bitmap = DataHolder.bitmap
    imageView.setImageBitmap(bitmap)
    DataHolder.bitmap = null // Czyszczenie po użyciu, jeśli konieczne
    

    Należy uwzględnić cykl życia aplikacji i aktywności, które mogą być odtwarzane.

  3. Użyj bazy danych (Room) lub innego magazynu danych (np. SharedPreferences, choć nie jest to idealne dla obrazów).

    • Zapisz obraz (może w formie tablicy bajtów) w bazie danych.
    • Przekaż identyfikator wpisu w bazie danych przez Intent.
    • Aktywność odbiorcza odczytuje obraz z bazy danych za pomocą identyfikatora.
    // Zapis w pierwszej aktywności (pseudokod dla Room)
    val imageEntity = ImageEntity(imageData = bitmapToByteArray(bitmap))
    val imageId = imageDatabase.imageDao().insertImage(imageEntity)
    val intent = Intent(this, SecondActivity::class.java)
    intent.putExtra("image_id", imageId)
    startActivity(intent)
    
    // Odczyt w drugiej aktywności (pseudokod dla Room)
    val imageId = intent.getLongExtra("image_id", -1)
    if (imageId != -1L) {
        val imageEntity = imageDatabase.imageDao().getImageById(imageId)
        if (imageEntity != null) {
            val bitmap = byteArrayToBitmap(imageEntity.imageData)
            imageView.setImageBitmap(bitmap)
        }
    }
    
  4. Użyj wspólnego ViewModel w Navigation Component.

    • Jeśli używasz Navigation Component, możesz użyć wspólnego ViewModel, powiązanego z grafem nawigacji lub aktywnością.
    • Dane są umieszczane w ViewModel jednej aktywności i dostępne w innej.
    // W ViewModel
    class SharedViewModel : ViewModel() {
        val imageBitmap = MutableLiveData<Bitmap>()
    }
    
    // W pierwszej aktywności (z Navigation Component)
    private val sharedViewModel: SharedViewModel by activityViewModels() // lub przez navGraphViewModels(R.id.nav_graph)
    sharedViewModel.imageBitmap.value = bitmap
    
    // W drugiej aktywności
    private val sharedViewModel: SharedViewModel by activityViewModels() // lub przez navGraphViewModels(R.id.nav_graph)
    sharedViewModel.imageBitmap.observe(viewLifecycleOwner) { bitmap ->
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap)
        }
    }
    

Wybór metody zależy od scenariusza użycia, rozmiaru danych i złożoności aplikacji. Dla obrazów, zapis do pliku (i przekazanie URI) lub użycie Singletona/ViewModel jest najczęściej najbardziej odpowiednie i wydajne.