On Android, Where else can a FlutterEngine be displayed except for Activity (and when?)? - flutter

In what other cases is the FlutterEngine available but the Android Activity isn't? For example, if I am running a static BroadcastReceiver when the app is not running, there is definitely no activity, but is there a FlutterEngine? I need to know the characteristics of Flutter in the Android lifecycle. I was reading an article (Modern Flutter Plugin Development) by the Flutter team, which mentions
// You cannot access an Activity here because this
// FlutterEngine is not necessarily displayed within an
// Activity. See the ActivityAware interface for more info.
This is partially answered in the ActivityAware documentation, under the onDetachedFromActivity method.
Detachment can occur for a number of reasons.
The app is no longer visible and the Activity instance has been destroyed.
The FlutterEngine that this plugin is connected to has been detached from its FlutterView.
This ActivityAware plugin has been removed from its FlutterEngine.
However, I wish it went into more depth. Here are some things I (don't) understand in depth:
When the activity is destroyed as per the Android activity lifecycle, the activity is gone. The FlutterEngine might still be around, in case a new activity is launched in the future. Perhaps there is still a component running in the Android app, e.g. a Service, and the FlutterEngine does not get destroyed yet? I wonder which cases FlutterEngine does not get destroyed when the activity gets destroyed.
A FlutterView can detach from the engine, and this is probably done if you're adding/ removing Flutter from a native Android app (aka. using add-to-app). You probably don't have to worry about FlutterView detaching from FlutterEngine. In a normal Flutter app, in what cases does FlutterView detach from FlutterEngine?:
This FlutterView will clear its UI and stop forwarding all events to the previously-attached FlutterEngine. This includes touch events, accessibility events, keyboard events, and others.
The relatively easy one: If a plugin unregisters from the FlutterEngine, Flutter cleans up the plugin by calling this method. You don't manually unregister plugins. You'd have to find out when Flutter unregisters plugins on your behalf. When?

To answer my questions after reading the Flutter Android shell code:
I wonder which cases FlutterEngine does not get destroyed when the activity gets destroyed.
When a Fragment or Activity gets detached or destroyed, the Engine might get destroyed. This is the only case where this happens. The Activity/ Fragment being destroyed is configurable by e.g. EXTRA_DESTROY_ENGINE_WITH_ACTIVITY:
#Override
public boolean shouldDestroyEngineWithHost() {
boolean explicitDestructionRequested =
getIntent().getBooleanExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, false);
if (getCachedEngineId() != null || delegate.isFlutterEngineFromHost()) {
// Only destroy a cached engine if explicitly requested by app developer.
return explicitDestructionRequested;
} else {
// If this Activity created the FlutterEngine, destroy it by default unless
// explicitly requested not to.
return getIntent().getBooleanExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, true);
}
}
or for fragments:
#Override
public boolean shouldDestroyEngineWithHost() {
boolean explicitDestructionRequested =
getArguments().getBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, false);
if (getCachedEngineId() != null || delegate.isFlutterEngineFromHost()) {
// Only destroy a cached engine if explicitly requested by app developer.
return explicitDestructionRequested;
} else {
// If this Fragment created the FlutterEngine, destroy it by default unless
// explicitly requested not to.
return getArguments().getBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true);
}
}
Therefore, the user can actually specify this.
If the flutter engine is cached, it won't get destroyed. It doesn't get put into the cache by default, developers have to do this themselves. Therefore, not relevant in a normal flutter app.
If the activity/ fragment creates this FlutterEngine, it gets destroyed. This is the default case in a normal Flutter app.
Both of the above can be disabled by the user if they explicitly set it.
The mechanism to destroy a FlutterEngine, aka. when flutterEngine.destroy(); is called, is done only in onDetach() of the FlutterActivityAndFragmentDelegate. So the question would be: when does the Activity get detached?
In a normal Flutter app, in what cases does FlutterView detach from FlutterEngine?:
When the FlutterView is attempting to attach to a different Flutter engine, it will detach from its current FlutterEngine, if it is attached.
The FlutterView will detach from the Engine when the Activity or Fragment is being detached from the Flutter Engine. Right before the FlutterEngine might get destroyed.
You'd have to find out when Flutter unregisters plugins on your behalf. When?
When the Activity or Fragment detaches from the FlutterEngine, and the FlutterEngine is destroyed, its associated PluginRegistry is destroyed, and when this happens, all the plugins are removed by calling PluginRegistry.removeAll.`

I wonder which cases FlutterEngine does not get destroyed when the activity gets destroyed.
I think it depends on which activity and how you've decided to implement it.
I imagine one scenario is, an instance of FlutterEngine is created by the MainActivity and lives for as long as that activity is around.
If you have more than one Activity then you'd have a choice. Keep a single FlutterEngine instance around in memory and attach/detach the various activities, or destroy the instance and create a new for for each Activity.
I couldn't comment on best practice.
Running multiple Flutters
Though this link raises an interesting scenario.
https://flutter.dev/docs/development/add-to-app/multiple-flutters
FlutterView
Not entirely sure what your question is here. But given https://flutter.dev/docs/development/add-to-app/android/add-flutter-view It appears that you do a lot of manual binding between the activity, view and your Flutter view (binding thats done automatically with the FlutterFragment or FlutterActivity)
As such, when the FlutterView is or is not disaplayed is very much tied to the activity Life cycle in control of that View and the bindings you setup in the first instance.
The link above also states the minimum API's to implement for FlutterView to work properly.
The absolute minimum implementation needed for Flutter to draw anything at all is to:
Call attachToFlutterEngine when the FlutterView is added to a resumed
Activity’s view hierarchy and is visible; and
Call appIsResumed on the FlutterEngine’s lifecycleChannel field when
the Activity hosting the FlutterView is visible.
The reverse detachFromFlutterEngine and other lifecycle methods on the
LifecycleChannel class must also be called to not leak resources when
the FlutterView or Activity is no longer visible.

Related

MaterialApp init function/widget with proper context

Like every other app that I created I have some boilerplate code that I need to execute before loading the application:
session check
store hydration
route change listener
user session listener
...
This code, more often than not, requires a BuildContext context to be available. I cannot use the context that sits at the same level with MaterialApp due to complaints from flutter so I need to go deeper 1+ levels in order to grab a context from a descendant. That descendant is my home route and it obviously fails when you navigate away.
What is a proper place to grab a context and initiate my watchers app wide? Where would you continuously watch for a navigation change for example?

Swift: Clearing .sharedApplication().shortcutItems on application quit

I'm having a very simple problem with my implemented 3D Touch dynamic quick action shortcuts.
I want the shortcuts to be cleared whenever the app is terminated (by double clicking the Home button and swiping up).
I am calling UIApplication.sharedApplication().shortcutItems.removeAll() as follows:
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
UIApplication .sharedApplication().shortcutItems?.removeAll()
self.saveContext()
}
However it has no effect, and the quick actions still show when 3D touch is used.
If I place UIApplication.sharedApplication().shortcutItems?.removeAll() inside
func applicationDidEnterBackground(application: UIApplication), this works exactly as intended...
I read something about applicationDidEnterBackground being the function used in most cases due to background processing or something...but there has to be a way to achieve what I want when the user terminates the app using the app monitor swipe up.
Thanks
Didn't tried this. But this tweak should work.
Start a background task on applicationWillTerminate and end it after some small delay. In the mean time, you can call 'UIApplication .sharedApplication().shortcutItems?.removeAll()'.
This will hopefully clear the shortcut items.
There are dynamic and static quick actions. The first kind you define through the shortcutItems property of the UIApplication instance (like in your example). The second kind you register in the plist file.
From the documentation:
Your code creates dynamic quick actions, and registers them with your app object, at runtime.
The system registers your static quick actions when your app is installed.
If a user installs an update for your app but has not yet launched the update, pressing your Home screen icon shows the dynamic quick actions for the previously-installed version.
This means that even when the app is closed the system remembers about both kinds of quick actions. While your app is in memory, such as when going into background, the system can still query the UIApplication for the dynamic actions but it must keep some other sort of persistence of quick actions when the app is closed.
I think there is just no guarantee about the point at which the system synchronizes with the dynamic quick actions. My guess is that the system does not necessarily synchronize when closing the app, yours might be an unsupported use case.

Finish activity onPause but still be in backstack?

I'm trying to minimize memory usage in my app, and one of the things I'm doing is calling finish() in the onPause method (which I know is not the best way to do things). For the most part, it seems to be working well, but when the user clicks the back button from the next activity, it logically skips over the finished activity and goes back further. Is it possible to have that activity in the back stack and just get recreated if the user presses back?
No. This conclusion comes from the task and backstack documentation as well as the activity documentation and a general understanding of how a stack data structure works.
A stack data strucure only has 2 possible operations push/put, which adds something to the collection, and pop, which removes it. Stacks folow a last in first out model, or LIFO, where by last thing added - in your case an activity - is the first thing removed when pop is called.
Within the android lifecycle activities are generally popped from the stack when the back button is pressed. At that point onDestroy() is called and the activity is removed (you can verify this by overriding the onDestroy() method and logging the results if you want to check). Alternativly you can force onDestroy() to be called by calling finish() as you are. Finishing an activity effectivly does the same thing as pressing back. The activity is destroyed and must be recreated before it can be added to the stack.
For what you're trying to do the stack would have to incorporate some intermediate state in which an activity does not exist but rather something akin to a reference is held that, when moved to the top, would indicate that the corresponding activity should be recreated. Since this is not how the sack works - it only holds activities - that state cannont exist and so the result you are talking about is not possible.
Your Goal is to minimize memory usage,Just make use of activity life cycle, You can do this alternative(if you need)
-Just leave onCreate() method blank.(only do setContentView(layout))
-Override onResume();
-whatever you were doing in onCreate just copy paste to onResume().
-and In onPause(), Recycle your all bitmaps and set them to null(I think you are using Bitmaps thats why you are very cautious about it ). and remove your views.
Now what will happen, when you launch your new activity, onPause() would be called. that will remove your all bitmap and views. and when you come back, onResume() will be call.(onCreate will not be called). and that will again initialize your view and bitmaps.
No, i don't think that is possible. Once you finish the Activity it's gone. You could, however, implement and handle your own stack. On back pressed, you would just start the closed Activity again.

Where to put state that's needed by different GWT activities?

I'm currently working on a GWT application and I'm still a little fuzzy on how it all fits together (the joy of having to make changes without first coming to an understanding of the whole framework, although that might be difficult anyway).
We have a few Activities that all correspond to parts of the UI (e.g. main content, a toolbar and a list of things). I'm not really sure whether that's even how Activities are intended but I guess I can't really change that easily now. My problem now is that the list of things holds state (the current selection) that the main content also needs and in a way the toolbar too (at least the toolbar currently has it – I'm beyond asking).
But what would actually be an appropriate place to store that? I guess coupling the actual view implementations together and storing the selection only in the list isn't such a bright idea.
I see two main solutions here:
keep the state within each activity and keep them synchronized through events (on the EventBus). That is: the "list of things" has a current selection, main view has one too, so does the toolbar; and each time that value changes, the activity that's making the change fires an event on the event bus so that the other activities can update their state, so that all activities have the same value in their own state.
use a singleton object (if you're using GIN and dependency injection, simply annotate the object with #Singleton and inject it in all the activities) to keep the state at a central place. The activities register event handlers on the state holder object to be notified when it changes. That is, each time an activity calls setCurrentSelection (for example), an event is fired (for example a ValueChangeEvent), and because all activities listen for it, they can update their view or whatever depending on the new value. You can choose to either dispatch the event on the event bus (similar to the PlaceController) or have the state holder implement HasValueChangeHandlers. Just make sure to unregister the handlers when the activities stop to avoid memory leaks (dispatching on the event bus makes it easier: simply register the handlers on the bus passed as argument to the start method and they'll be unregistered automatically when the activity stops, and you don't even have to think about it).
Actually, PlaceController is a good example of such shared state (the current place).

GWT button clicks getting laggy very quickly using Places/Activities in GWT2.1

i'm new to GWT2.1's Places and Activities.
I'm using the same ActivityMapper as example below and getting laggy button clicks after just a few navigations.
The MVP architecture I'm using has the Presenter create listeners and define the View interface, but View objects are singletons.
Is my problem with lag due to all the listeners building up exponentially? Should I change it so that the View creates all the listeners instead? Or should I try unbinding instead?
E.g. in the GWT2.1 example here a new Activity is created every time getActivity(Place) is called.
public Activity getActivity(Place place) {
if (place instanceof HelloPlace)
return new HelloActivity((HelloPlace) place, clientFactory);
else if (place instanceof GoodbyePlace)
return new GoodbyeActivity((GoodbyePlace) place, clientFactory);
return null;
}
The view here adds event handlers to its widgets. It later calls its presenter when appropiate. I don't think that unbindinding the events would free up much memory or speed up the browser's event queue.
I believe you should further investigate if the actual button click is slow, or if it's the creation of Activities happening upon the event, or whatever else.
In the end I changed the MVP architecture to the same as the example here with View defining Activity and ViewImpl containing the UI Handlers. Since the Activities no longer contain handlers and the ViewImpls are singletons the problem goes away.