LiveData не наблюдается после первого звонка - PullRequest
0 голосов
/ 30 января 2019

Я реализовал LiveData & ViewModel для имитации AsyncTaskLoader.

Я загружаю имена файлов из каталога камеры в DCIM, а затем прикрепляю fileObserver, чтобы Наблюдать, когда файл (изображение) удаляется, а затем обратный вызов сообщает LiveData о необходимости повторного получения fileNames при удалении события.происходит

Проблема:

Приведенный ниже код должен извлекать имена файлов из DCIM / Pictures асинхронно с помощью LiveData, а затем к каталогу присоединяется FileObserver (DCIM / Pictures), чтобы отслеживать, когда файл удален и реализован обратный вызов с подклассом LiveData для перезагрузки файлов, как показано в коде.

хорошо, это работает в первый раз, то естьфайлы загружаются в первый раз, вызывая setValue() и передавая fileNames, вызванные onChange, для вызова в наблюдающей операции / фрагменте.Но когда файл удален, функция обратного вызова вызывает функцию loadFiles () для повторной загрузки файлов, но при вызове setValue и передаче в FileNames не запускается OnChange в наблюдаемой операции / фрагменте на этот раз.

В соответствии с официальной документацией LiveData

Вы должны вызвать метод setValue (T) для обновления объекта LiveData из основного потока.

Мне любопытно узнать, почему LiveData не обновляет свое значение после первого вызова.


Код

MyLiveData

class MyLiveData() : MutableLiveData<MutableList<String>>(), PictureDelete {
    override fun onPicDelete() {
        loadFileNames()
    }

    val TAG = "MyLiveData"
    val fileNamesList: MutableList<String> = ArrayList()
    val fileWatcher : MyFileWatcher

    init {
        loadFileNames()
        val pathToWatch = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/Camera").getAbsolutePath()
        fileWatcher = MyFileWatcher(pathToWatch, this)
        fileWatcher.startWatching()
    }

    private fun loadFileNames() {
        val fileDir: File

        try {
            fileDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/Camera")
        } catch (e: Exception) {
            Log.e(TAG, e.message)
            return
        }

        Log.d(TAG, "Actively Loading Files in Status LiveData")

        val arrayOfFiles = fileDir.listFiles()

        if (arrayOfFiles == null || arrayOfFiles.size < 1) return

        Log.d(TAG, "Actively Loading Files. Size: ${arrayOfFiles.size}")

        setValue(fileNamesList)
    }

}

MyViewModel

class MyViewModel() : ViewModel() {
    val myLiveData: MyLiveData
    val TAG = "WhatsAppFragment-VModel"


    init {
        myLiveData = MyLiveData()
    }
}

MyFragment

class MyFragment : Fragment() {

    private val TAG = "MyFragment"

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_layout, container, false)
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
        viewModel.myLiveData.observe(this, androidx.lifecycle.Observer { fileNames ->
            Log.d(TAG, "New Live Data Dispatch")
            for ((index, name) in fileNames.withIndex()) {
                Log.d(TAG, "the element at $index is $name")
            }
        })
    }
}

MyFileObserver

class MyFileWatcher(pathToWatch: String, val picDelete: PictureDelete) : FileObserver(pathToWatch, DELETE) {

    val TAG = "MyFileWatcher"

    init {
        Log.d(TAG, "Initialization")
    }

    override fun onEvent(event: Int, path: String?) {
        if (event = FileObserver.DELETE) { // EventCode 512 == Delete
            Log.d(TAG, "OnEvent. Event: $event Path: $path")
            picDelete.onPicDelete()
        }
    }
}

Интерфейс PictureDelete

interface PictureDelete {
    fun onPicDelete()
}

Что не так с моей реализацией?

1 Ответ

0 голосов
/ 02 февраля 2019

У меня есть пример @Micklo_Nerd, но он не использует вашу проблему удаления файлов, но дает вам представление о том, что вам нужно сделать.В моем примере пользователь вставляет имя и после нажатия кнопки список меняется.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

<Button
        android:text="Add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/buttonAdd"
        app:layout_constraintStart_toStartOf="@+id/filename"
        app:layout_constraintEnd_toEndOf="@+id/filename"
        android:layout_marginTop="24dp"
        app:layout_constraintTop_toBottomOf="@+id/filename"/>
<EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        android:ems="10"
        android:id="@+id/filename"
        android:layout_marginStart="8dp"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="32dp"
        app:layout_constraintTop_toTopOf="parent"/>
<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        app:layout_constraintStart_toStartOf="parent"
        android:layout_marginStart="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginEnd="8dp"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@+id/buttonAdd"/>

 </android.support.constraint.ConstraintLayout>

MyRepository (в вашемпример - MyLiveData)

Здесь вы должны выполнить работу по получению имен файлов в папке и поместить их в MutableLiveData.

class MyRepository {

    fun loadFileNames(liveData : MutableLiveData<MutableList<String>>, filename: String){

       var fileList : MutableList<String>? = liveData.value

       if(test == null)
           fileList = MutableList(1){ filename }
       else
          fileList.add(filename)

       liveData.value = fileList
     }

}

MyViewModel

Здесь у меня есть два метода: один для обновления списка, когда я нажимаю кнопку, и другой, чтобы получить список имен файлов.Вероятно, вам нужен только тот, который получает список

class MyViewModel : ViewModel() {
    val repo: MyRepository
    var mutableLiveData : MutableLiveData<MutableList<String>>


    init {
       repo = MyRepository()
       mutableLiveData = MutableLiveData()
    }

    fun changeList(filename: String){

       repo.loadFileNames(mutableLiveData, filename)
    }

    fun getFileList() : MutableLiveData<MutableList<String>>{

      return mutableLiveData
    }
}

MainActivity

Здесь вы видите, что я наблюдаю метод, который возвращает список имен файлов,это то, что вам нужно сделать, потому что это то, что изменится.

class MainActivity : AppCompatActivity(), View.OnClickListener {

   private val TAG = "MyFragment"

   private lateinit var viewModel: MyViewModel

   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_main)

      viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)

      viewModel.getFileList().observe(this, Observer<MutableList<String>> { fileNames ->
         Log.d(TAG, "New Live Data Dispatch")

         textView.text = ""

         for ((index, name) in fileNames!!.withIndex()) {
            textView.append("the element at $index is $name\n")
         }
      })

      buttonAdd.setOnClickListener(this)
   }

   override fun onClick(v: View?) {
      when(v!!.id){
        R.id.buttonAdd -> {

            viewModel.changeList(filename.text.toString())
            filename.text.clear()
        }
      }
   }
}

Надеюсь, это поможет.

...