Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for blocking platform message send #4358

Closed

Conversation

mravn-google
Copy link
Contributor

@mravn-google mravn-google commented Nov 14, 2017

Adds support for blocking message send/reply from platform code and into Flutter. This facility is needed in situations where platform-specific code receives a (synchronous) request for a value whose computation involves executing Dart code.

One example is saving UI view state on Android, cf. flutter/flutter#6827.

* bytes between position zero and current position, or null.
* @return a {@link ByteBuffer} containing the reply payload, or null.
*/
ByteBuffer sendSync(String channel, ByteBuffer message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps rename to sendBlocking?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@jakobr-google jakobr-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM. Not sure how we'd really test this, but could you add a simple round-trip test just to verify data comes through (over on flutter/flutter, I guess)?

@mravn-google
Copy link
Contributor Author

We already have a flutter/flutter test of channel communication. I'll add some roundtrip cases to that once the engine PR lands and rolls.

@mravn-google mravn-google changed the title Add support for synchronous platform message Add support for blocking platform message send Nov 15, 2017
Copy link
Contributor

@jakobr-google jakobr-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@eseidelGoogle
Copy link
Contributor

FYI @Hixie @abarth

@abarth
Copy link
Contributor

abarth commented Nov 15, 2017

Are you sure this feature doesn't introduce a deadlock? Which threads are allowed to block on which other threads?

@mravn-google
Copy link
Contributor Author

@abarth The platform thread is allowed to block waiting for the ui thread.

@abarth
Copy link
Contributor

abarth commented Nov 15, 2017

@mravn-google That's good to know. Is the blocking pattern between the threads documented somewhere? It's the kind of documentation I'd find useful to refer to in the future.

@mravn-google
Copy link
Contributor Author

@abarth It is explicitly documented (in the two platform-specific BinaryMessenger interfaces) that the blocking send is, in fact, blocking. I did not yet write which thread we are waiting for, just that we are waiting for an answer from the Dart side of things.

So: the wait is ended when Dart code responds to the platform message. What is the official term for the thread executing in the Dart isolate? I called it the ui thread above.

@Hixie
Copy link
Contributor

Hixie commented Nov 15, 2017

I'd call it the "thread on which the framework handles the message" or some such.

@Hixie
Copy link
Contributor

Hixie commented Nov 15, 2017

I'm very dubious about doing anything blocking. The way we've solved this problem in the past (e.g. in the many accessibility APIs that rely on synchronous response) is to precompute the data and have it around when it's necessary. Why can't we use that approach here?

@mravn-google
Copy link
Contributor Author

@Hixie In the particular case driving this PR, that would require the Dart code to keep available a serialized form of whatever view state, the app developer cares about saving and restoring. I figured that might be expensive.

@abarth
Copy link
Contributor

abarth commented Nov 15, 2017

It is explicitly documented (in the two platform-specific BinaryMessenger interfaces) that the blocking send is, in fact, blocking.

I guess I would expect the find the documentation in this class, where the threads are declared:

https://github.com/flutter/engine/blob/master/common/threads.h#L30

The import issue is the global pattern of which threads are allowed to block on which other threads, among all the features of the engine. If there's a cycle in that pattern, then there's the possibility of a deadlock. The trouble with documenting the blocking in the platform-specific BinaryMessenger interface, is that someone working on another feature might not know to look there to learn about this behavior.

@Hixie
Copy link
Contributor

Hixie commented Nov 15, 2017

@Hixie In the particular case driving this PR, that would require the Dart code to keep available a serialized form of whatever view state, the app developer cares about saving and restoring. I figured that might be expensive.

Don't you need to do that anyway to survive a crash without losing state?

It shouldn't be that expensive, surely. A few milliseconds whenever the view state changes, which is pretty rarely (when the user changes route, when they scroll maybe...).

I think it would be significantly preferable to introducing blocking APIs.

The problem with blocking APIs is that they're initially used only for those really hard cases where you have "no choice", and then they're used when you have a choice but it's "really hard" without blocking, then they're used because "well we use it over there", and before you know it, everything is blocking and you might as well only be using one core. Not to mention the imminent deadlock issues when someone starts waiting on something else and we eventually loop it all around, like @abarth is worrying about.

@krisgiesing
Copy link
Contributor

To offer additional perspective: hybrid HTML5 apps on Android have this same problem, since (as far as I know) there's no way to send a synchronous message from native to HTML5. (iOS has a synchronous API, but they discourage its use).

The way we dealt with this on past projects was to save the state at each point where it changed significantly. In our case this was a local save, although a cloud save would be similar. For performance reasons we batched state changes until execution reached the top of stack, then did the persistence. Since the persistence was asynchronous it didn't block UI.

One advantage of this approach over responding to lifecycle events is that it saves state in cases where the lifecycle events don't happen, which is rare but possible (e.g. sudden battery disconnect).

@chinmaygarde
Copy link
Member

I strongly prefer that we not introduce blocking APIs in the engine for the following reasons:

  • The behavioral implications @Hixie mentioned in his comment are extremely compelling.
  • Upcoming changes will allow the the engine to host multiple shells. Each of these shells will be configurable with their own task runners for core engine tasks (UI, IO, GPU, Platform). One of the implications of this flexibility is that the UI & Platform task runners may be serviced on the same thread.
    • In such cases, this API will deadlock.
    • Reworking this API to support such configurations will be complex and hard to test.
  • Threading and deadlock considerations add cognitive overhead to the design and implementation of platform plugins.
    • If misbehaving Dart code fails to complete the response due to exceptional circumstances, the platform thread is blocked indefinitely and will cause an ANR.
    • The Dart code may not know that the message must be responded to immediately and that user interactions may be blocked in case of delays. It may respond to the message after intervals that are signifcant portions of the frame interval. In such cases, touches and other platform events (which are processed and dispatched from the the platform thread) will be blocked.
  • There are alternatives to solving the "save UI state on Android" issue without introducing countdown latches:
    • Having the serialized form saved in Dart code with callbacks exposed to user code to load data on demand in case of resumption from restart.
    • Saving state at major checkpoints.

The code looks good but I am blocking the PR for these reasons. If the rest of the team decides otherwise, feel free to override.

@Takhion
Copy link

Takhion commented Nov 16, 2017

@mravn-google @Hixie @abarth @chinmaygarde
Whoops, sorry for joining late in the conversation.

I think that the importance of having synchronous methods has been underestimated: they are (at least on Android) very much necessary! I'm saying this not just because serializing the instance state at every change sounds far from an ideal solution, but because that's just one of literally a myriad of cases where the host system expects a synchronous response, as a contract, and the trick of pre-computing the response is almost never a possible solution. At the cost of stating the obvious, it's worth mentioning that Android apps currently do block the thread when performing these operations, so any code ported to Flutter would also transfer the concern of not blocking more than necessary. Keep in mind that this is something that is only going to be used in platform plugins and can potentially be enabled only on platforms that really need it, like Android.

We want Flutter to be able to fully integrate with the system reaching 100% feature parity, and I strongly believe this is one of the steps that needs to be taken.

@krisgiesing
Copy link
Contributor

This PR was a solution to a particular use case (state persistence). Perhaps it would be worth filing issues for the other use cases you have in mind?

@mravn-google
Copy link
Contributor Author

mravn-google commented Nov 16, 2017

@krisgiesing @Takhion Actually, this PR attempted to provide a general solution to involving Dart code execution in synchronous platform call contexts. But I agree it would be good to list a few other scenarios where having Dart code keep the (serialized) result up to date could be problematic.

@chinmaygarde
Copy link
Member

@Takhion: It is entirely possible that we will have to support synchronous platform calls. My objections to this patch are mainly centered around construction issues (deadlocks in case of task runner threads being the same, hangs with ANRs in case of dropped messages, performance guarantees of message responses, etc..).

it's worth mentioning that Android apps currently do block the thread when performing these operations

There is a distinction between performing operations on the current thread vs. waiting for another thread to perform tasks while the current thread idles on a countdown latch.

@mravn-google: I wonder if it is possible to support synchronous execution of the callback by running a Dart invocation on the platform thread itself. We would have to make sure all dart invocations are made after acquiring a binary semaphore. But it should be theoretically possible (and safer). We can make the runtime controller hold the semaphore. WDYT?

@Takhion: I want to re-iterate that I am not against supporting synchronous platform calls. But, we have to figure out a scheme that is safe against deadlocks and hangs. And, even if we do implement such a scheme, there will still be a performance overhead related to making such calls. So, I would like for us to resort to such schemes as a means of last resort.

@Hixie
Copy link
Contributor

Hixie commented Nov 16, 2017

FWIW, I am against us making it easy to perform synchronous calls from the Java thread to the dart:ui isolate, unless there's a really strong use case. I haven't seen a use case yet that is compelling enough to require that we support this.

@sethladd
Copy link
Contributor

@Takhion would it be too much trouble to help us see a list of the many sync APIs that might require interop'ing with? You are very experienced with Android, it would be a big help.

@Takhion
Copy link

Takhion commented Nov 18, 2017

@sethladd of course! Sorry if I'm available only intermittently but I can only work on this in my (little) spare time and we just moved to a new flat and still don't have an Internet connection at home... BUT I'm writing a proper reply with use-cases, stay tuned 💪

@NathanaelA
Copy link

@sethladd @Hixie -- Not sure if @Takhion got sidetracked, but to show some progress on this, I'll help fill in some of them that exist... And why we need some sort of Sync support...

Some SYNC functions:

  • onLowMemory
  • onTrimMemory
  • onCreate
  • onActivityCreated
  • onStart
  • onResume
  • onMultiWindowModeChanged
  • onPictureInPictureModeChanged
  • onConfigurationChanged
  • onCreateOptionsMenu
  • onPrepareOptionsMenu
  • onOptionsItemSelected
  • onContextItemSelected
  • onOptionsMenuClosed
  • onSaveInstanceState
  • onPause
  • onStop
  • onDestroyView
  • onDetach
  • onStartEnterTransition
  • onViewStateRestored
  • onHiddenChanged
  • onRestoreInstanceState
  • onPrepareDialog
  • onCreateDialog
  • onResume
  • onNewIntent
  • onUserLeaveHint
  • onCreateThumbnail
  • onCreateDescription
  • onProvideAssistData
  • onProvideKeyboardShortcuts
  • onStop
  • onDestroy
  • onMovedToDisplay
  • onRetainNonConfigurationInstance
  • onAttachFragment
  • onKeyDown
  • onKeyLongPress
  • onKeyUp
  • onKeyMultiple
  • onKeyShortcut
  • onTouchEvent
  • onTrackballEvent
  • onGenericMotionEvent
  • onUserInteraction
  • onWindowAttributesChanged
  • onContentChanged
  • onWindowFocusChanged
  • onWindowDismissed
  • onCreatePanelView
  • onPanelClosed
  • onPrepareOptionsMenu
  • onNavigateUp
  • onCreateNavigateUpTaskStack
  • onPrepareNavigateUpTaskStack
  • onNavigateUpFromChild
  • onCreateNavigateUpTaskStack
  • onSearchRequested
  • onApplyThemeResource
  • onProvideReferrer
  • onActivityResult
  • onActivityReenter
  • onTitleChanged
  • onChildTitleChanged
  • onCreateView
  • onTranslucentConversionComplete
  • onNewActivityOptions
  • onVisibleBehindCanceled
  • onBackgroundVisibleBehindChanged
  • onEnterAnimationComplete
  • onWindowStartingActionMode
  • onActionModeStarted
  • onActionModeFinished
  • onPostResume
  • onDump
  • onShouldSaveFragmentState
  • onGetLayoutInflater
  • onUseFragmentManagerInflaterFactory
  • onGetHost
  • onInvalidateOptionsMenu
  • onStartActivityFromFragment
  • onStartActivityAsUserFromFragment
  • onStartIntentSenderFromFragment
  • onRequestPermissionsFromFragment
  • onHasWindowAnimations
  • onGetWindowAnimations
  • onAttachFragment
  • onFindViewById
  • onHasView
  • onProvideAssistData

These are all on Activity, Application or Fragments callbacks. That doesn't even count any normal Listeners, which are key in Android for a huge number of events and a lot of them are syncronise.

Please note this is NOT a complete list; just some of them I found by looking quickly in the ASOP source. Some of these EXPECT a returned value which changes the rest of what the calling function will do when called. Some of these if they are ASYNC the code after the call (which expected you to do something) will then continue and so you never had time to do what you needed. For example; onStop, onDestroy, onPause, onLowMemory, onTrimMemory, onSaveInstanceState all run code AFTER they call you and so they expect you to have handled whatever they sent you first; and then they proceed to do the next step in their process which can be a bad thing if you weren't able to handle it. Some of these above I realize are moot in Flutter because they aren't ever triggered unless we are attempting to overlay native view over the flutter window...

The ones like onLowMemory, OnTrimMemory, onPause, onStop, onResume, onNewIntent, onStart, onDestroy, onKeyDown, onKeyUp, onCreateThumbnail probably should be translated into something flutter understands and handles properly as these are life cycle events that a lot of use is needed. I've used at least one or more of these in every single application I've done. Most the time I've used most of these in the application.

@NathanaelA
Copy link

NathanaelA commented Mar 7, 2018

Some use cases:

  1. OnKeyDown/Up has allowed me in several applications to capture keys (like volume keys) handle them and not allow the OS to handle them. (i.e. returning "true" is like the web, where it cancel's propagation of the key).

  2. OnLowMemory/onTrimMemory, allows you to eliminate any memory caches BEFORE the app is killed do to memory issues; this is needed to make sure the App isn't killed because of memory usage -- clearing any unused images you have cached can be the difference between the app remaining in memory vs it having to be restarted at a later point.

  3. onPause/onStop/onResume/onStart allows you to handle different activity issues; for example in VR we start/shutdown parts of the 3d rendering and 3d rendering loops, in ExoPlayer we shutdown some of the display issues. In games, we pause the entire event loop to save battery when paused events occur. In camera apps/barcode readers, we close/open the camera.

  4. onNewIntent, we use this to swtich to new data coming in, for example if someone clicks on a images in a file browser, it might send a newIntent to our image viewing app to load that image.

  5. ios Delegate -> shouldAutorotate - control rotation of screen. (Not sure if flutter has something like this call) Many iOS delegates expect a sync return back when they call the delegate function.)

I've also written about 30 cross-platform plugins for a competing framework (NativeScript) and I can tell you there is a whole lot of Synchronized callbacks on both iOS and Android. So if I can't call into Dart from the Android or iOS side and easily wait for the Dart side callback to generate and return a value that I then return to the native framework I'm calling. These plugins could NEVER be done in Flutter (one of them that I worked on last week is Google's GVRKit, it uses a Sync callback on iOS to return a created and customized control)

So I think we DO need a standard way to do Sync callbacks for at least plugin developers to be able to easily bring NATIVE stuff into Flutter properly in the places where the Framework expects and requires it...

@Hixie
Copy link
Contributor

Hixie commented Mar 8, 2018

I hear what you're saying. Sync callbacks are simply a non-starter though. They make it WAY too easy to deadlock your application, let alone worries like introducing jank and so on.

In general we can implement most of these by having the Dart side proactively tell the OEM side what to reply when a request comes in. For some others, we'll have to do more of the work on the OEM side.

@NathanaelA
Copy link

@Hixie - Ian, I'm not sure you really do get what I'm saying if you aren't willing to do anything Sync, and you just dismiss it because it is not something you like. I do realize it makes it a bit/lot more "messy" and isn't perfect, but the platforms you are supporting aren't perfect and you have NO control over them. Sometimes to make your stuff work inside the ecosystem properly you have to unfortunately get a little messy.


Let me give a bit of context, so maybe my opinion will have a bit more sway, since I'm new to this community and you don't know me from a $5 dollar developer from the north pole. 😀 I've written a couple books on NativeScript, I've produced ~30 cross platform plugins for NativeScript everything from driving portable bluetooth printers , Sqlite, ExoPlayer, SciCharts, Wikitude, & GRVKit integration. I've dug deep into the v8 and JavaScript core engines, and literally can build & debug the entire NativeScript stack. I've written both pure native and NativeScript apps on both ios and android platforms. So I am very well versed in the mobile platforms, including the requirements to make apps that work properly for the end users, which is the most important piece of the pie. (Oh, and I also do Desktop & DevOps Server work, be hard to find a field I haven't done well in. 😀 ). Hopefully this gives you a general idea of my background and knowledge base. Now onto this actual issue... :D


I'll give you a couple REAL examples. Tonight I was working on some Swift and ObjC code -- Google's own GVRKit requires that at certain points it will pass me a value and I must respond with a different type of newly instantiated memory HEAVY classes each time. They free the prior classes. I CANNOT do this AOT, these classes will conflict with each other and each have some serious resources associated with them. (i.e. they are basically the entire render stack for which ever 3d mode that the user is wanting to activate at that moment). Their is no way to pre-decide and pre-build this -- this is 100% completely dependent on what the OS side passes in from the users choices. And it will completely change what entire components tree I return. And this is 100% sync, where I have to RETURN the class (& its tree) at that moment. Without sync support, GVRKit is pretty much unsupportable in Flutter since it is NOT open source!

Also things like (android) OnLowMemory, (android) onTrimMemory, (ios) applicationDidReceiveMemoryWarning - it is CRITICAL that you handle this in a Synchronous manner, because when it returns back to the OS side; the OS side then runs GC and then re-evaluates right then if it needs to take a more drastic actions and just kill the app. So, if you handicap flutter and just send a ASYNC message to the dart side to clean memory and return right away to the OS -- bye bye flutter app. Where a normal app can easily clear some heavy resources, and then return. Which will typically leave a much nicer experience for the user... Again, the whole reason the OS is asking you about releasing resources, is because your app is hogging them. It is giving you an opportunity to be friendly before it kills you, and you just choose to handicapped Flutter from being a good citizen on BOTH platforms.

In addition to those two use cases; if you do manage to integrate Native Views into the Flutter rendering system (which would help grow the ability for us to do a lot of visual plugins); You need to realize on iOS the majority of the "native" controls have delegates and they are pretty much are all synchronous calls. Android can has a similar issue with some controls; but most of them are NOT async when dealing with controls, so it is less of an issue. Yes some of it can be pre-computed; but a lot of it cannot be pre-decided and you need to run code somewhere (hopefully in Dart) to decide which answer to give the OS.

And a final example; several months ago I worked with a client on a cross platform NativeScript app. One feature he wanted was to use the Up/Down volume key buttons to trigger different actions depending on where he was in the app. So in this case, I had to determine what field and screen I was on to decide if I needed to handle the request OR if I wanted to let the OS handle it as actual volume control event. The interface was very natural for the end users, so it made sense to them. The ONLY way to capture this information on Android (& ios) is the application wide onKeyDown SYNCHRONOUS event. So I abstracted both platforms in a fairly simple NativeScript plugin. The actual application code is the same for both platforms. It activates the global plugin handler and then subscribes to the plugins keydown handler. The plugin itself handles getting the events the and standardizing them before sending them to the app. To do this the in Flutter the way you are proposing; would require a hell of a LOT more busy work, and add a lot of additional maintenance costs. In flutter, I would literally have to send to the native plugin on every single screen and every single field if this field needed to capture the up/down events as I entered it and then signal the plugin that I no longer needed to capture the event on exit of the field. In addition to that overhead -- each time I modify a screen I would have to make sure I added this code to all the new fields somehow. (In my humble opinion, you are absolutely NUTS 😀 to think this is the best way, since it has to pre-calculated on every field enter/exit and sent to the native plugin so that the plugin can respond properly! )

In NativeScript, the plugin fires my event code -- I grab the value from the framework about which screen I'm on and then ask the framework what field I'm on. If no field is selected I can return out of my routine and just let the OS handle the event properly (i.e. return false). If I'm on a field, I can look at the field type and/or my simple array of field names that the up/down applies to and see if the names match and then return true/false to the OS. Again, a simple array of field names is all I need for the special cases. Maintenance is pretty minimal and if I add any more special fields to any screen, I just need to add it to once to the array. This solution is totally cross platform, and I only maintain one small set of code base in the app to handle everything for the entire application. Pre-calculating on each change of the field is IMHO a total nightmare for Flutter...


Finally, I also just took a few minutes to look at ARKit, ARCore, Google Maps; and ExoPlayer. Three out of the Four of those cannot ever be FULLY supported in Flutter without some sort of synchronous support. I only briefly looked at them, so this is after glancing at them for about a couple minutes each and I found issues quickly...

  • ArCore can be, as far as I can see can be fully supported.
  • Arkit not at all, several important delegates would break behavior as they need a SYNC response back.
  • Google Maps - Several nice features will be totally broken and/or unimplementable on Android, I suspect on iOS it is worse because of delegates (but didn't check).
  • ExoPlayer most parts will work, DRM'd videos probably won't.

So just grabbing a couple native plugins off the top of my head -- 3/4th's of them had issues. So I beg you to reconsider this! You are choosing to make Flutter a less "cross" platform choice and handicapping it significantly on both platforms for anything outside of a simple application. Flutter doesn't do any good if the framework itself is awesome, if we can't do a large chunk of what we need to with it because it is impossible to interact with the Native OS properly on BOTH platforms...

I understand adding Sync methods can potentially increase the chance of Deadlocks (although I believe there are a lot of already SOLVED ways to mitigate this and to eliminate that risk entirely). I also don't see why you would get any Jank; to my knowledge the rendering doesn't happen on the same thread as you use to communicate with the native side.

@Hixie
Copy link
Contributor

Hixie commented Mar 9, 2018

I really do understand what you are saying.

I think the most productive way for us to proceed is probably to consider each concrete case that you care about in its own bug. For example, if you want to use GVRKit with Flutter, file a bug about why you can't do so, and we can inspect those use cases individually. Maybe the solution to one case is that we need some specific features in Flutter, maybe the solution to another is that we need a first-party plugin to hide the boilerplate code, maybe the solution to another is that we have to work with the GVRKit team to have that product changed, maybe the solution to yet another is that we have to have Android itself be changed. It's hard to say, in the abstract.

@NathanaelA
Copy link

@Hixie - See the problem with your idea; is you still choose to totally hamstring me from the start. My tools should be trying to enable me, not cripple me by default.

I maybe the odd man out here, my focus isn't on apps most the time, it is on making things work from the app to the native side. I am hired frequently to tie the two sides together and make a easy to use cross-platform interface to so that the client can type doX() or on(eventY, doY) and it just works no matter which platform they build the source for. I handle all the hard to do stuff, they just tie the pieces together to make the app work. Plugins are a passion of mine. ;-) I can tell you with all the experience I've had; this refusal to allow proper synchronous communication with the native platforms will seriously curtail doing a large chunk of same plugins that I've written over the years.


In each of my prior examples; the issue is that without the ability to talk back to Dart so that the code running in the app by the client (ie. on the Dart side) can make choice based on the event, it can't be done properly, if at all. Already Flutter is considerably harder to do a plugins than NativeScript; and was originally in my opinion about equal to React. However now finding about about this significant limitation; this really does make Flutter the worst platform to do any integration plugins on; and I've used almost all of the cross-platform platforms.


For example, if you want to use GVRKit with Flutter, file a bug about why you can't do so, and we can inspect those use cases individually

I only used GVRKit as an example; because I was working in it at that moment and I had a real case that I can see won't work in Flutter. I honestly don't need it in Flutter, Native works way better for VR/AR apps currently than any of the cross-platform platforms; and this probably won't change any time soon. So you don't need to worry about it. 😀

Maybe the solution to one case is that we need some specific features in Flutter,

Synchronous message support would solve all of them. 😀

maybe the solution to another is that we need a first-party plugin to hide the boilerplate code

LOL, if you want the APP to respond dynamically then you still have to add a sync call back to dart; because the listener is expecting the answer when it returns out of the delegate/listener... 😀

maybe the solution to another is that we have to work with the GVRKit team

This might work in some limited cases where you have direct ability to interact with the team. And that is making a very huge presumption that the team in question is even is responsive to your changes, and finally are they responsive? -- GVRKit releases are about a three to four month wait between any changes (closed source, no ability to do my own fix/release). I'm sure my client would be happy with me saying, yeah I choose Flutter and it was awesome; but because of X; we now have to wait for ~3 more months to launch so that Y will work in your app. Yeah, I can tell you right now that this is a HORRIBLE solution, way to many uncontrollable if's that I wouldn't wish on my worst enemy...

In addition are you really serious about working with the google maps team and have them rewrite parts of their long time established api to be fully async; as they have several sync callbacks on both Android and iOS. For example this one:
- (nullable UIView *) mapView: (GMSMapView *) mapView markerInfoWindow: (GMSMarker *) marker The idea behind this callback is that you provide a custom uiview BACK to google maps to show over the marker when it is tapped. You have NO idea which marker they picked until you got this called. So you cannot pre-compute the entire window and send it from dart. This is a sync callback for a really excellent reason, it passes you the marker tapped; you decide how to generate the custom window based on it and return it to the maps api which handles opening and showing it in the proper location on the map.

Or maybe this one from a 3rd party AirBNB Lottie:
Bitmap fetchBitmap(LottieImageAsset asset);
The idea behind this api is that anytime it needs to show a bitmap image it gives you the asset info; you choose how to RETURN the image (i.e. load/download/cached/generate/etc). This is fully dynamic again. You could implement a state engine in java and then pass an array of x=y states from Dart to Java to control what gets returned; but that brings up lots of cases that you still can't handle easily. I highly doubt you can convince AirBNB to replace their Sync API (which again makes full sense here) with a more error prone ASYNC api just because only one cross platform system can't work with it properly.

Cordova/Ionic, Titanium, Xamarin, React and NativeScript can all handle all these of these issues easily and no need to wait on any "if's"...

None of these are abstract issues; this is REAL deployed code and code that is used to solve real issues in real apps. Yes, I do realize neither of these plugins currently work in Flutter yet -- but if/when Flutter supports rendering of native UI inside the Skia render tree; then BOTH these plugins could be made to work by someone like me, except for the ALL the pieces that you just tied my hands...

maybe the solution to yet another is that we have to have Android itself be changed.

Say you do have the power to get the Android team to add X feature as a ASYNC api; and they add it in Android 10 ; it still doesn't help you unless you are planning on ditching support for 99% of the Android devices. So unless you know something I don't -- I have a hell of a lot of devices that won't be ever running Android 10. 😀 (and then ~50% of the issues are on iOS and Apple won't ever change their delegate/event system, it is totally core to everything on Mac and iOS. ) 😀 So this really is a less valid option than even adding sync support.. 😀

I think the most productive way for us to proceed is probably to consider each concrete case that you care about in its own bug.

You asked, you might not like the number of issues I raise that have no good solutions now... 😀
But you realize that if they don't all get solved very quickly; you prove my point about crippling and tying my hands when I have to wait for you to solve them because of closing this PR which would allow me to solve them right now. I do plan on giving Flutter a fair shake for a couple applications I have in mind to test it out fairly well.

But when it comes to any customer apps; this limitation alone will pretty much cause me to nix Flutter where the client may need to do anything that isn't already built into Flutter. (Which unfortunately is pretty much all clients have one requirement like this.) The risk is now way too great that I will run into something that can't be done in Flutter and I won't be able to fix it. I've learned a long time ago that this is one of the worst places to be when dealing with paying clients waiting for you and you have no ability to solve the issue because your hands are tied. I would much rather choose a technology that may not be as good; but also won't block me.

@passsy
Copy link

passsy commented Mar 11, 2018

@NathanaelA onKeyDown is a great example where a synchronous API is required. Can you open a ticket in flutter/flutter? It would drive this PR because I don't see an alternative solution.

One on hand I understand the concerns about bringing this API to Flutter. Chances are high that it will be abused at some point. On the other hand I've seen too many developers losing interest in Flutter because they can't interact with Android APIs as they are used to. It's not that their current patterns are the best solution, but the inability to use those is a problem.

@Hixie
Copy link
Contributor

Hixie commented Mar 12, 2018

You asked, you might not like the number of issues I raise that have no good solutions now...

So long as each issue clearly describes the use case (in other words, don't just copy-and-paste a five paragraph boilerplate message into each bug and change the subject line...), the more the merrier.

But you realize that if they don't all get solved very quickly; you prove my point about crippling and tying my hands when I have to wait for you to solve them because of closing this PR which would allow me to solve them right now.

Nobody's crippling your hands... this project is entirely open source. You can take the source and do whatever you want with it whenever you want.

@NathanaelA
Copy link

@Hixie

But you realize that if they don't all get solved very quickly; you prove my point about crippling and tying my hands when I have to wait for you to solve them because of closing this PR which would allow me to solve them right now.

Nobody's crippling your hands... this project is entirely open source. You can take the source and do whatever you want with it whenever you want.

Ian, really? I figured with a developer of your caliper; I would NOT have to explain that statement in-depth. You are 100% correct, MY hands themselves are not tied; I'm perfectly capable of applying this patch and patching things myself, in fact I have build servers that can do a lot of this automatically -- that is not the issue. ;-)

However, my hands ARE tied if I want to be able to create plugins that OTHERS in the general eco-system will use. Without this type of support I'm CANNOT create a plugin that will work with an "official" flutter engine out of the box (i.e. I'm crippled in my ability to ship it!).

I have in the past tried to work out of a eco-system with plugins and it ended up being a losing cause. People loved the plugin; but without the official engine supporting it -- I literally had to rebase my patch (and fix changed patch points ) way to frequently. In effect it ties my hands because their is NO EASY WAY to distribute plugins that use this feature to the rest of the eco-system. Second this is a losing issue for the Flutter team, because when I fall behind in keeping my "patched" engine updated; then you (i.e the Flutter team) get all the bug reports that were already fixed in the current version and so you guys are having to tell people it is fixed in the latest version and/or link to the bug report as duplicates. The Flutter team ends up shouldering the majority of the support issues in this case. So in my humble opinion, forking in this scenario is typically the worst of all worlds, and my hands are tied and crippled because their is no GOOD choices... 😀

@NathanaelA
Copy link

NathanaelA commented Mar 13, 2018

@passsy

@NathanaelA onKeyDown is a great example where a synchronous API is required. Can you open a ticket in flutter/flutter? It would drive this PR because I don't see an alternative solution.

Agreed, and will do. 😀

One on hand I understand the concerns about bringing this API to Flutter. Chances are high that it will be abused at some point.

On the other hand I've seen too many developers losing interest in Flutter because they can't interact with Android APIs as they are used to. It's not that their current patterns are the best solution, but the inability to use those is a problem.

Oh, I fully understand Ian's point -- and there is a serious potential to abuse this. But I honestly don't see a easy way to support the vast number of API's that require Sync in iOS and Android. Just ignoring the issue; doesn't make it vanish. All it does is make the devs that need to access these API's vanish...

@eseidelGoogle
Copy link
Contributor

Note this may be revisited as part of: flutter/flutter#7053

@NathanaelA
Copy link

@eseidelGoogle

From my perspective; Flutter is a not production ready until it has sync support; so I sincerely welcome your statement. But I honestly didn't expect to see sync support in flutter while Ian had any say in things; so your statement greatly confuses me as to how you think this will now occur...

According to @Hixie:

Sync callbacks are simply a non-starter though. They make it WAY too easy to deadlock your application, let alone worries like introducing jank and so on

Why would flutter/flutter#7053 make that any different? Why wouldn't C/C++ be just forced to do everything async? I honestly am not understanding why you remotely even think Ian will approve a method of Sync support into flutter when he was pretty adamant that having sync support is a "non-starter" as of several months ago...

@eseidelGoogle
Copy link
Contributor

Sync callbacks do make it very easy to deadlock. They're a real foot-gun for developers, or so was our experience on the web. That said I could also believe exposing some synchronous ways to get in/out of Dart code will be required over time.

Regardless, we are currently looking into a FFI interface for C++, which as I understand it would be synchronous: dart-lang/sdk#34452

@NathanaelA
Copy link

Oh, I recognize the issues with Sync, and I also prefer Async calls as much as possible. But the reality is that both iOS and Android has a lot of API's that are sync that are still broke by design in flutter...

However, I honestly don't understand why Ian would let Sync calls in for FFI if he believes SYNC is a non-starter... I would hate to have whoever is working on the FFI interface to waste a lot of time on SYNC support if Ian is still believing sync support is a "non-starter". That would be huge a waste of time on that developers part... That is why I was asking what makes you think sync support would be re-evaluated?

@Hixie
Copy link
Contributor

Hixie commented Jan 6, 2019

The C/C++ FFI doesn't cross threads, so there's no deadlock potential. It's just about running code on the same thread as the Dart code, but written in C/C++.

@yongjhih
Copy link

yongjhih commented Nov 9, 2021

Do we have any plan for trapping onPictureInPictureModeChanged into ActivityPluginBinding or somewhere?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.