How to restore weex instance ui and state after activity re-created? - weex

I put a weex instance in DialogFragment and define a field to store the WXSDKInstance and use it to render weex UI. Sometimes th activity will be destroyed and be re-created if user leave the activity and come back later or configuration changes. Then the mWXSDKInstance will be initialized and render weex again. We lost the weex state and users' operation state. How can I store the previous weex instance and weex javascript state after activity re-created?
public WXSDKInstance mWXSDKInstance;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mWXSDKInstance = new WXSDKInstance(getContext());
mWXSDKInstance.registerRenderListener(this);
...
mWXSDKInstance.render(packageName,
template,
options,
jsonInitData,
WXRenderStrategy.APPEND_ASYNC);
}

Related

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

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.

How to get/inject EPartService outside part or in LifeCycle Manager or How to control life cycle of e4 RCP app in true sense?

My application consists of many parts and they are defined in application's e4xmi file. I want to hide and show them dynamically. I am using EpartService to do so in handlers, where I can inject it.
But I also want to control the show/hide of parts with something like life cycle manager, where I can not inject EPartService. Is there any way to achieve and fully control RCP application's life cycle?
There seems the exact same question here and void of solution:
https://www.eclipse.org/forums/index.php/t/595958/
I want to implement 'remember me like feature' where part having sign in screen is shown instead of other parts. Also after log out same sign-in part is to be shown. So I need to control life cycle of RCP app. But I cant inject EPartService before anything in Application's e4xmi is initiated.
If you are creating a class from something which is injected (such as the LifeCycle class) you can create your class with injection using ContextInjectionFactory:
#Inject
IEclipseContext context;
MyClass myClass = ContextInjectionFactory.make(MyClass.class, context);
Or if you just pass an IEclipseContext to the class you can get the part service using:
EPartService partService = context.get(EPartService.class);
Note: There is a separate instance of the part service for each part. Depending on what you are doing you may need to make sure you have the service for the active part.
If you are not locked in to using SWT, you could use the e(fx)clipse e4 renderer for JavaFX instead.
e(fx)clipse has more possibilities to control the lifecycle of the application. For example you can return a Boolean from #PostContextCreate to signal whether you want to continue the startup or not. You will not be able to use EPartService here though, but you can roll your own login dialog using dependency injection as greg-449 has described it in his answer.
public class StartupHook {
#PostContextCreate
public Boolean startUp(IEclipseContext context) {
// show your login dialog
LoginManager loginManager = ContextInjectionFactory.make(LoginManager.class, context);
if(!loginManager.askUserToLogin()) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
}
(You can also restart the application. Form
more details see http://tomsondev.bestsolution.at/2014/11/03/efxclipse-1-1-new-features-api-to-restart-your-e4-app-on-startup/).

Creating a "live" view in the background

I'd like to define an Eclipse view that is self-contained, but able to respond to selection events as soon as the Workbench app is launched, even if the view is not visible, as long as it appears as one of the "background views" in a folder. In other words, I need a "hook" which is invoked when placeholder for my view is created.
The problem is that when a Workbench app launches, the background views are normally not instantiated. You can of course implement an IPerspectiveFactory to do an initial perspective layout in Eclipse, but the factory is only used when the perspective is initially chosen; after that, on subsequent launches, Eclipse persists mementos that don't involve creating the view.
Is there any way to accomplish this?
I think it would be a good idea to separate the data model that's driving your view from the view UI. This will allow the data model to be continuously updated and the view to be added or removed without having to maintain any kind of state.
You can add the data model as a SelectionService listener in your plugin Activator:
private ISelectionListener listener;
public void start(BundleContext context) throws Exception {
super.start(context);
listener = new ISelectionListener() {
#Override
public void selectionChanged(IWorkbenchPart part,
ISelection selection) {
// Update model
}
};
PlatformUI.getWorkbench().getActiveWorkbenchWindow()
.getSelectionService()
.addSelectionListener(listener);
}
public void stop(BundleContext context) throws Exception {
PlatformUI.getWorkbench().getActiveWorkbenchWindow()
.getSelectionService().removeSelectionListener(listener);
super.stop(context);
}
When the view is created it can use the data model to populate the UI components and listen to the data model for any live updates.
You'll need to make sure that your plugin is activated when the application is launched. The following question can help with that.
auto-start OSGi service
Edit
The following code can be used to activate your view (calling createPartControl) even if the view is currently hidden. By placing this code in your activator it can start listening to model events as soon as your plugin is activated.
PlatformUI.getWorkbench().addWindowListener(new IWindowListener() {
...
#Override
public void windowActivated(IWorkbenchWindow window) {
IViewReference view = window.getActivePage().findViewReference(
VIEW_ID);
if (view != null) {
view.getPart(true);
}
}
});

Resource Change Plugin for eclipse

I created an RCP application which detects resource change in its own view by extending CommonNavigator.
public abstract class NavigatorView extends CommonNavigator implements
IResourceChangeListener {
public void createPartControl(Composite parent) {
super.createPartControl(parent);
hookResourceChangeCommand(); // my resource tracking function.
}
}
But now I need to create a plugin for this which detects resource change in project explorer in eclipse itself. I cannot create a view now and I need to detect already existing view. How should I do it ?
Please completely remove the view that you created. You should not do anything in the UI, if you want to track resource changes, as resources are part of the workspace concept, and the workspace is generally headless (that is without UI).
Instead use the code below (taken from the resource change listener tutorial):
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResourceChangeListener listener = new IResourceChangeListener() {
public void resourceChanged(IResourceChangeEvent event) {
System.out.println("Something changed!");
}
};
workspace.addResourceChangeListener(listener);
//... some time later one ...
workspace.removeResourceChangeListener(listener);

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.