hpoul / file_picker_writable

Flutter plugin to choose files which can be read, referenced and written back at a later time.
https://pub.dev/packages/file_picker_writable
MIT License
17 stars 13 forks source link

Not working on Android when the activity is forcibly restarted by the Android system #27

Open yegor-pelykh opened 2 years ago

yegor-pelykh commented 2 years ago

Hi!

There is a some kind of bug, that is relevant for me only on real device.

What I have: 1) Real device with stock Android 10. 2) Application using simple function FilePickerWritable().openFile():

final fileInfo = await FilePickerWritable().openFile((fileInfo, file) async {
      return fileInfo;
});

What happens further: 1) The file open dialog opens normally 2) I find my file 3) I click on it 4) The dialog freezes for a while, and then simply disappears / closes. Returning nothing! My debugger cannot catch the moment when the value is returned, it seems to be detached from the process and no longer stops at a breakpoint.

There are no errors in the log, but there are some messages:

D/FilePickerWritable(30527): Got method call: openFilePicker
I/Choreographer(30527): Skipped 76 frames!  The application may be doing too much work on its main thread.
W/ActivityThread(30527): handleWindowVisibility: no activity for token android.os.BinderProxy@b858bba
D/FilePickerWritable(30527): onNewIntent(null)
D/PathProviderPlugin(30527): Use TaskQueues.
D/FilePickerWritable(30527): We have no active result, so activity result was not for us.

The last one is most interesting. It is hardcoded in your FilePickerWritableImpl.kt:

val result = filePickerResult ?: return false.also {
  plugin.logDebug("We have no active result, so activity result was not for us.")
}

We come here because we really have filePickerResult = null. But in openFilePicker() method it is aclually set to not null result, I checked this:

fun openFilePicker(result: MethodChannel.Result) {
  ...
  this.filePickerResult = result
  ...
}

It is strange... It looks like setting this value and then checking this value happen on different threads... Or some other reason, I don't know.

Another strange thing is that on emulator it is always working correctly!

I thought there might be some problem in my application. But I tried a simple application that just opens a file open dialog and then shows the returned file identifier in the dialog. It is relevant also for this simplest app. To recreate the simplest application, just place the main.dart file from the archive into the lib folder of the created template application (and add a dependency on file_picker_writable).

Please take a look at this issue! Try to reproduce this. Perhaps this is not reproduced on every device, but I have a stock Android on my phone, without third-party launchers, etc. That is, the problem may not be that rare. As a last resort, please try to think theoretically what could be the problem and try to fix it. Thanks in advance!

bradyt commented 2 years ago

I am curious if your issue still arises with the the example app.

Can you clarify which Document Provider you are selecting from in the System UI file picker? I expect Downloads or Google Drive to work, but I've had less success with Dropbox, etc.

yegor-pelykh commented 2 years ago

Hi @bradyt

I am curious if your issue still arises with the the example app.

I had a hard time getting your example running because it is outdated. But anyway this example works with the same error mentioned above.

First time when I ran into this error was the case of opening files from Google Drive. But then I tried the simplest case, trying to open a file from the "downloads" folder. The same result. So the reason is not the Document Provider, but the file picker itself.

bradyt commented 2 years ago

I don't have a physical Android. So I would find it difficult to help here. But for what it's worth, I installed the example app, tried Create New File, selected the Downloads location in System UI picker, and pressed SAVE, which created a file like newfile.<some integer>.codeux, removed the file in app UI, pressed Open File Picker, and was able to select the recently created file successfully. If I understand correctly, you are saying that doesn't work on your physical Android device.

This was with flutter channel stable, 2.8.1, by the way. I didn't notice any issues with the app being outdated, but I was only able to test on a physical iPhone, an iPhone simulator, and an Android emulator.

EDIT: file_picker_writable: version: 2.0.1

bradyt commented 2 years ago

I found an Android device, the example app worked fine here. But it's Android 8.1.

hpoul commented 2 years ago

Did you already try the provided example? I'm using this plugin in AuthPass and haven't heard of any problems...

yegor-pelykh commented 2 years ago

@bradyt @hpoul

Now this is starting to clear up.

The thing is, some time ago I was playing with the Android settings in the "Developer options" section on my device. There I changed the setting "Don't keep activities" ("Destroy every activity as soon as the user leaves it"), setting it to On.

After that, this behavior began to reproduce. This can be reproduced on any device now, even on an emulator, by setting this option.

That is, during normal use of the device, this behavior, it seems, should not be relevant. So maybe it is not the bug.

On the other hand, if you want, you could improve it. Up to you.

The reason, as I understand it now, is that when this option is turned On, activities can be unloaded by Android itself unexpectedly for the application. And then, can be restarted. Which is what happens in this case... Method openFilePicker() is called from existing instance. But when we get the result in onActivityResult() method, it checks filePickerResult field in another instance. So it is uninitialized and equals to null, so we have an error.

As far as I understand, applications can be prepared to save and load their state when forced to be unloaded by Android. I haven't read much about this, but it seems to be done using methods onSaveInstanceState() / onRestoreInstanceState() that allow an application to save the data it needs and restore it to a new instance.

But again, since this can hardly be called very critical, it is up to you.

hpoul commented 2 years ago

Thanks for the update. Yes, that's a problem especially on memory constraint devices, but flutter support for state restoration is still very limited: https://github.com/flutter/flutter/pull/60375

I honestly didn't look too much into it. I see this as a minor problem, I'll leave the issue open.. but i don't plan to work on it any time soon.. but if anyone would be interested in working on it, I'd be happy to review and merge PRs