Update UI from an AsyncTaskLoader - android-asynctask

I've converted my AsyncTask to an AsyncTaskLoader (mostly to deal with configuration changes). I have a TextView I am using as a progress status and was using onProgressUpdate in the AsyncTask to update it. It doesn't look like AsyncTaskLoader has an equivalent, so during loadInBackground (in the AsyncTaskLoader) I'm using this:
getActivity().runOnUiThread(new Runnable() {
public void run() {
((TextView)getActivity().findViewById(R.id.status)).setText("Updating...");
}
});
I am using this in a Fragment, which is why I'm using getActivity(). This work pretty well, except when a configuration change happens, like changing the screen orientation. My AsyncTaskLoader keeps running (which is why I'm using an AsyncTaskLoader), but the runOnUiThread seems to get skipped.
Not sure why it's being skipped or if this is the best way to update the UI from an AsyncTaskLoader.
UPDATE:
I ended up reverting back to an AsyncTask as it seems better suited for UI updates. Wish they could merge what works with an AsyncTask with an AsyncTaskLoader.

It's actually possible. You essentially need to subclass the AsyncTaskloader and implement a publishMessage() method, which will use a Handler to deliver the progress message to any class that implements the ProgressListener (or whatever you want to call it) interface.
Download this for an example: http://www.2shared.com/file/VW68yhZ1/SampleTaskProgressDialogFragme.html (message me if it goes offline) - this was based of http://habrahabr.ru/post/131560/

Emm... you shouldn't be doing this.
because how an anonymous class access parent class Method or Field is by storing an invisible reference to the parent class.
for example you have a Activity:
public class MyActivity
extends Activity
{
public void someFunction() { /* do some work over here */ }
public void someOtherFunction() {
Runnable r = new Runnable() {
#Override
public void run() {
while (true)
someFunction();
}
};
new Thread(r).start(); // use it, for example here just make a thread to run it.
}
}
the compiler will actually generate something like this:
private static class AnonymousRunnable {
private MyActivity parent;
public AnonymousRunnable(MyActivity parent) {
this.parent = parent;
}
#Override
public void run() {
while (true)
parent.someFunction();
}
}
So, when your parent Activity destroys (due to configuration change, for example), and your anonymous class still exists, the whole activity cannot be gc-ed. (because someone still hold a reference.)
THAT BECOMES A MEMORY LEAK AND MAKE YOUR APP GO LIMBO!!!
If it was me, I would implement the "onProgressUpdate()" for loaders like this:
public class MyLoader extends AsyncTaskLoader<Something> {
private Observable mObservable = new Observable();
synchronized void addObserver(Observer observer) {
mObservable.addObserver(observer);
}
synchronized void deleteObserver(Observer observer) {
mObservable.deleteObserver(observer);
}
#Override
public void loadInBackground(CancellationSignal signal)
{
for (int i = 0;i < 100;++i)
mObservable.notifyObservers(new Integer(i));
}
}
And in your Activity class
public class MyActivity extends Activity {
private Observer mObserver = new Observer() {
#Override
public void update(Observable observable, Object data) {
final Integer progress = (Integer) data;
mTextView.post(new Runnable() {
mTextView.setText(data.toString()); // update your progress....
});
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreated(savedInstanceState);
MyLoader loader = (MyLoader) getLoaderManager().initLoader(0, null, this);
loader.addObserver(mObserver);
}
#Override
public void onDestroy() {
MyLoader loader = (MyLoader) getLoaderManager().getLoader(0);
if (loader != null)
loader.deleteObserver(mObserver);
super.onDestroy();
}
}
remember to deleteObserver() during onDestroy() is important, this way the loader don't hold a reference to your activity forever. (the loader will probably be held alive during your Application lifecycle...)

Answering my own question, but from what I can tell, AsyncTaskLoader isn't the best to use if you need to update the UI.

In the class in which you implement LoaderManager.LoaderCallback (presumably your Activity), there is an onLoadFinished() method which you must override. This is what is returned when the AsyncTaskLoader has finished loading.

The best method is to use LiveData, 100% Working
Step 1: Add lifecycle dependency or use androidx artifacts as yes during project creation
implementation "androidx.lifecycle:lifecycle-livedata:2.1.0"
Step 2: Create the loader class as follow, in loader create in public method to set the livedata that can be observed from activity or fragment. see the setLiveCount method in my loader class.
package com.androidcodeshop.asynctaskloaderdemo;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import androidx.loader.content.AsyncTaskLoader;
import java.util.ArrayList;
public class ContactLoader extends AsyncTaskLoader<ArrayList<String>> {
private MutableLiveData<Integer> countLive = new MutableLiveData<>();
synchronized public void setLiveCount(MutableLiveData<Integer> observer) {
countLive = (observer);
}
public ContactLoader(#NonNull Context context) {
super(context);
}
#Nullable
#Override
public ArrayList<String> loadInBackground() {
return loadNamesFromDB();
}
private ArrayList<String> loadNamesFromDB() {
ArrayList<String> names = new ArrayList<>();
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
names.add("Name" + i);
countLive.postValue(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return names;
}
#Override
protected void onStartLoading() {
super.onStartLoading();
forceLoad(); // forcing the loading operation everytime it starts loading
}
}
Step 3: Set the live data from activity and observe the change as follows
package com.androidcodeshop.asynctaskloaderdemo;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.MutableLiveData;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity implements
LoaderManager.LoaderCallbacks<ArrayList> {
private ContactAdapter mAdapter;
private ArrayList<String> mNames;
private MutableLiveData<Integer> countLiveData;
private static final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNames = new ArrayList<>();
mAdapter = new ContactAdapter(this, mNames);
RecyclerView mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter);
countLiveData = new MutableLiveData<>();
countLiveData.observe(this, new androidx.lifecycle.Observer<Integer>() {
#Override
public void onChanged(Integer integer) {
Log.d(TAG, "onChanged: " + integer);
Toast.makeText(MainActivity.this, "" +
integer,Toast.LENGTH_SHORT).show();
}
});
// initialize the loader in onCreate of activity
getSupportLoaderManager().initLoader(0, null, this);
// it's deprecated the best way is to use viewmodel and livedata while loading data
}
#NonNull
#Override
public Loader onCreateLoader(int id, #Nullable Bundle args) {
ContactLoader loader = new ContactLoader(this);
loader.setLiveCount(countLiveData);
return loader;
}
#Override
public void onLoadFinished(#NonNull Loader<ArrayList> load, ArrayList data) {
mNames.clear();
mNames.addAll(data);
mAdapter.notifyDataSetChanged();
}
#Override
public void onLoaderReset(#NonNull Loader loader) {
mNames.clear();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
Hope this will help you :) happy coding

Related

Issues running/debugging mapbox android code examples

I am trying to get started with mapbox android and can't get any of the example projects to work.
My problem is with the imports
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxandroiddemo.R;
I get I "cannot resolve symbol annotation", "cannot resolve symbol v7" and "cannot resolve symbol mapboxandroiddemo".
I feel like this is some android problem that I am just not understanding correctly so if anyone has some insight that would be amazing. I have tried taking out some code and using the recommended bug fixes but all that has done is break my project.
Here is the entire MainActivity.java file
package com.example.mapboxtut;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOffset;
/**
* Use the {#link MapView#addOnStyleImageMissingListener(MapView.OnStyleImageMissingListener)}
* to handle the situation where a SymbolLayer tries using a missing image as an icon. If an icon-image
* cannot be found in a map style, a custom image can be provided to the map via
* the listener.
*/
public class MissingIconActivity extends AppCompatActivity {
private static final String ICON_SOURCE_ID = "ICON_SOURCE_ID";
private static final String ICON_LAYER_ID = "ICON_LAYER_ID";
private static final String PROFILE_NAME = "PROFILE_NAME";
private static final String CARLOS = "Carlos";
private static final String ANTONY = "Antony";
private static final String MARIA = "Maria";
private static final String LUCIANA = "Luciana";
private MapView mapView;
private MapboxMap mapboxMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Mapbox access token is configured here. This needs to be called either in your application
// object or in the same activity which contains the mapview.
Mapbox.getInstance(this, getString(R.string.access_token));
// This contains the MapView in XML and needs to be called after the access token is configured.
setContentView(R.layout.activity_styles_missing_icon);
mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(#NonNull final MapboxMap mapboxMap) {
// Add Features which represent the location of each profile photo SymbolLayer icon
Feature carlosFeature = Feature.fromGeometry(Point.fromLngLat(-7.9760742,
41.2778064));
carlosFeature.addStringProperty(PROFILE_NAME, CARLOS);
Feature antonyFeature = Feature.fromGeometry(Point.fromLngLat(-8.0639648,
37.5445773));
antonyFeature.addStringProperty(PROFILE_NAME, ANTONY);
Feature mariaFeature = Feature.fromGeometry(Point.fromLngLat(-9.1845703,
38.9764924));
mariaFeature.addStringProperty(PROFILE_NAME, MARIA);
Feature lucianaFeature = Feature.fromGeometry(Point.fromLngLat(-7.5146484,
40.2459915));
lucianaFeature.addStringProperty(PROFILE_NAME, LUCIANA);
// Use a URL to build and add a Style object to the map. Then add a source to the Style.
mapboxMap.setStyle(
new Style.Builder().fromUrl(Style.LIGHT)
.withSource(new GeoJsonSource(ICON_SOURCE_ID,
FeatureCollection.fromFeatures(new Feature[] {
carlosFeature,
antonyFeature,
mariaFeature,
lucianaFeature}))),
new Style.OnStyleLoaded() {
#Override
public void onStyleLoaded(#NonNull Style style) {
MissingIconActivity.this.mapboxMap = mapboxMap;
// Add a SymbolLayer to the style. iconImage is set to a value that will
// be used later in the addOnStyleImageMissingListener below
style.addLayer(new SymbolLayer(ICON_LAYER_ID, ICON_SOURCE_ID).withProperties(
iconImage(get(PROFILE_NAME)),
iconIgnorePlacement(true),
iconAllowOverlap(true),
textField(get(PROFILE_NAME)),
textIgnorePlacement(true),
textAllowOverlap(true),
textOffset(new Float[] {0f, 2f})
));
}
});
}
});
// Use the listener to match the id with the appropriate person. The correct profile photo is
// given to the map during "runtime".
mapView.addOnStyleImageMissingListener(new MapView.OnStyleImageMissingListener() {
#Override
public void onStyleImageMissing(#NonNull String id) {
switch (id) {
case CARLOS:
addImage(id, R.drawable.carlos);
break;
case ANTONY:
addImage(id, R.drawable.antony);
break;
case MARIA:
addImage(id, R.drawable.maria);
break;
case LUCIANA:
addImage(id, R.drawable.luciana);
break;
default:
addImage(id, R.drawable.carlos);
break;
}
}
});
}
private void addImage(String id, int drawableImage) {
Style style = mapboxMap.getStyle();
if (style != null) {
style.addImageAsync(id, BitmapUtils.getBitmapFromDrawable(
getResources().getDrawable(drawableImage)));
}
}
// Add the mapView lifecycle to the activity's lifecycle methods
#Override
public void onResume() {
super.onResume();
mapView.onResume();
}
#Override
protected void onStart() {
super.onStart();
mapView.onStart();
}
#Override
protected void onStop() {
super.onStop();
mapView.onStop();
}
#Override
public void onPause() {
super.onPause();
mapView.onPause();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

JavaFX. Register eventHandler in custom class

I try register eventHandler in my custom class. I don't know what interface or methods I have to implement for having addEventHandler method in my custom class. For this reason my Model class extends Rectangle (Rectangle class has addEventHandler mechanism).
Also I don't know why assigned source object not working (please see comment in Controller class).
Creating custom events I make by this tutorial: https://stackoverflow.com/a/27423430/3102393.
Project Structure
Controller
package sample;
import javafx.event.Event;
public class Controller {
private Model model;
public Controller() {
model = new Model();
model.addEventHandler(MyEvent.ROOT_EVENT, this::handler);
}
private void handler(MyEvent event) {
if(event.getEventType().equals(MyEvent.INSTANCE_CREATED)) {
// Why is event.getSource() instence of Rectangle and not instance of assigned MyObject?
Object obj = event.getSource();
System.out.println(event.getMyObject().getText());
}
}
public void clickedCreate(Event event) {
model.makeEvent();
}
}
Model
package sample;
import javafx.scene.shape.Rectangle;
import java.util.ArrayList;
public class Model extends Rectangle {
private ArrayList<MyObject> objects = new ArrayList<>();
private Integer counter = 0;
public void makeEvent() {
MyObject object = new MyObject((++counter).toString() + "!");
objects.add(object);
fireEvent(new MyEvent(object, null, MyEvent.INSTANCE_CREATED));
}
}
Custom event MyEvent
package sample;
import javafx.event.Event;
import javafx.event.EventTarget;
import javafx.event.EventType;
public class MyEvent extends Event {
public static final EventType<MyEvent> ROOT_EVENT = new EventType<>(Event.ANY, "ROOT_EVENT");
public static final EventType<MyEvent> INSTANCE_CREATED = new EventType<>(ROOT_EVENT, "INSTANCE_CREATED ");
public static final EventType<MyEvent> INSTANCE_DELETED = new EventType<>(ROOT_EVENT, "INSTANCE_DELETED");
private MyObject object;
public MyEvent(MyObject source, EventTarget target, EventType<MyEvent> eventType) {
super(source, target, eventType);
object = source;
}
public MyObject getMyObject() {
return object;
}
}
And finally MyObject
package sample;
public class MyObject {
private String text;
MyObject(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
Note (and question): I also tried using a ObservableList of instances of MyObjects, but I think that there is no notify for updating instance attribute.
Basics of Events
Events are fired using Event.fireEvent which works in 2 steps:
Build the EventDispatchChain using EventTarget.buildEventDispatchChain.
Pass the Event to the first EventDispatcher in the resulting EventDispatchChain.
This code snippet demonstrates the behaviour:
EventTarget target = new EventTarget() {
#Override
public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) {
return tail.append(new EventDispatcher() {
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
System.out.println("Dispatch 1");
tail.dispatchEvent(event);
return event;
}
}).append(new EventDispatcher() {
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
System.out.println("Dispatch 2");
tail.dispatchEvent(event);
return event;
}
});
}
};
Event.fireEvent(target, new Event(EventType.ROOT));
It prints
Dispatch 1
Dispatch 2
As you can see, the way the EventTarget constructs the EventDispatchChain is totally up to the EventTarget.
This explains why you have to implement addEventHandler ect. yourself.
How it's done for Nodes
This is described in detail in the article JavaFX: Handling Events - 1 Processing Events on the Oracle website.
The important details are:
Different source objects are used during the event handling.
EventHandlers / EventFilters are used during the event dispatching (2.).
This explains why the source value is unexpected.
How to implement addEventHandler
It's not that hard to do this, if you leave out the event capturing and bubbling. You just need to store the EventHandlers by type in a Map<EventType, Collection>> and call the EventHandlers for each type in the EventType hierarchy:
public class EventHandlerTarget implements EventTarget {
private final Map<EventType, Collection<EventHandler>> handlers = new HashMap<>();
public final <T extends Event> void addEventHandler(EventType<T> eventType, EventHandler<? super T> eventHandler) {
handlers.computeIfAbsent(eventType, (k) -> new ArrayList<>())
.add(eventHandler);
}
public final <T extends Event> void removeEventHandler(EventType<T> eventType, EventHandler<? super T> eventHandler) {
handlers.computeIfPresent(eventType, (k, v) -> {
v.remove(eventHandler);
return v.isEmpty() ? null : v;
});
}
#Override
public final EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) {
return tail.prepend(this::dispatchEvent);
}
private void handleEvent(Event event, Collection<EventHandler> handlers) {
if (handlers != null) {
handlers.forEach(handler -> handler.handle(event));
}
}
private Event dispatchEvent(Event event, EventDispatchChain tail) {
// go through type hierarchy and trigger all handlers
EventType type = event.getEventType();
while (type != Event.ANY) {
handleEvent(event, handlers.get(type));
type = type.getSuperType();
}
handleEvent(event, handlers.get(Event.ANY));
return event;
}
public void fireEvent(Event event) {
Event.fireEvent(this, event);
}
}

Parse Local Datastore e Message "no results found for query"

I am trying to finish this program and i am stuck. This is my first program and now it wont work. I keep getting this error when i add query.fromLocalDatastore(); The code runs fine until i try to get it from the local storage. This is telling me there is nothing there for it to retrieve and i don't know why. When i added my test data it worked fine but when i try to pull data from another table i get the error above. Apparently when i added the test data the server synced with the local datastore. Now it is not. Can someone tell me what I did wrong?
public class DataHolder extends Application {
int age;
#Override
public void onCreate() {
super.onCreate();
Parse.enableLocalDatastore(getApplicationContext());
Parse.initialize(this,key, key);
ParseUser.enableAutomaticUser();
ParseACL defaultACL = new ParseACL();
ParseACL.setDefaultACL(defaultACL, true);
}
public class MainActivity extends ActionBarActivity implements Disclaimer.DisclaimerListener {
protected void continueToRun() {
spinner1.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> adapter, View v, int x, long lng) {
final ParseQuery<ParseObject> query = ParseQuery.getQuery("Phone_Numbers");
query.fromLocalDatastore();
if (x == 1) {
final Intent intent = new Intent(getBaseContext(), Protocol_Template.class);
query.fromLocalDatastore();
query.whereEqualTo("objectId", "uGANULyrdL");
startActivity(intent);
}
}
public class Protocol_Template extends Activity {
DataHolder global;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_protocol__template);
final TextView protocol = (TextView) findViewById(R.id.txt02);
findViewById(R.id.btn2timesUpperLeft);
final ParseQuery<ParseObject> query = ParseQuery.getQuery("Phone_Numbers");
query.fromLocalDatastore();
query.getFirstInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
final String protocols = object.get("PhoneNumber").toString();
protocol.setText(protocols);
} else {
protocol.setText(e.getMessage());
}
}
});
}

JavaFX2 - very poor performance when adding custom made (fxml)panels to gridpane dynamically

Problem
I want to add custom made panels, built via javafx scene builder, to a gridpane at runtime. My custom made panel exsits of buttons, labels and so on.
My Attempt
I tried to extend from pane...
public class Celli extends Pane{
public Celli() throws IOException{
Parent root = FXMLLoader.load(getClass().getResource("Cell.fxml"));
this.getChildren().add(root);
}
}
... and then use this panel in the adding method of the conroller
#FXML
private void textChange(KeyEvent event) {
GridPane g = new GridPane();
for (int i=0 : i<100; i++){
g.getChildren().add(new Celli());
}
}
}
It works, but it performs very very poor.
What I am looking for
Is there a way to design panels via javafx scene builder (and as a result having this panels in fxml) and then add it to a gridpane at runtime without make use of this fxmlloader for each instance. I think it performs poor because of the fxml loader. When I add a standard button e.g. whitout fxml it is very much faster.
Short answer: No, it is not (as of JavaFX 2.x and 8.0). It may be in a future version (JFX >8)
Long answer:
The FXMLLoader is currently not designed to perform as a template provider that instantiates the same item over and over again. Rather it is meant to be a one-time-loader for large GUIs (or to serialize them).
The performance is poor because depending on the FXML file, on each call to load(), the FXMLLoader has to look up the classes and its properties via reflection. That means:
For each import statement, try to load each class until the class could successfully be loaded.
For each class, create a BeanAdapter that looks up all properties this class has and tries to apply the given parameters to the property.
The application of the parameters to the properties is done via reflection again.
There is also currently no improvement for subsequent calls to load() to the same FXML file done in the code. This means: no caching of found classes, no caching of BeanAdapters and so on.
There is a workaround for the performance of step 1, though, by setting a custom classloader to the FXMLLoader instance:
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class MyClassLoader extends ClassLoader{
private final Map<String, Class> classes = new HashMap<String, Class>();
private final ClassLoader parent;
public MyClassLoader(ClassLoader parent) {
this.parent = parent;
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> c = findClass(name);
if ( c == null ) {
throw new ClassNotFoundException( name );
}
return c;
}
#Override
protected Class<?> findClass( String className ) throws ClassNotFoundException {
// System.out.print("try to load " + className);
if (classes.containsKey(className)) {
Class<?> result = classes.get(className);
return result;
} else {
try {
Class<?> result = parent.loadClass(className);
// System.out.println(" -> success!");
classes.put(className, result);
return result;
} catch (ClassNotFoundException ignore) {
// System.out.println();
classes.put(className, null);
return null;
}
}
}
// ========= delegating methods =============
#Override
public URL getResource( String name ) {
return parent.getResource(name);
}
#Override
public Enumeration<URL> getResources( String name ) throws IOException {
return parent.getResources(name);
}
#Override
public String toString() {
return parent.toString();
}
#Override
public void setDefaultAssertionStatus(boolean enabled) {
parent.setDefaultAssertionStatus(enabled);
}
#Override
public void setPackageAssertionStatus(String packageName, boolean enabled) {
parent.setPackageAssertionStatus(packageName, enabled);
}
#Override
public void setClassAssertionStatus(String className, boolean enabled) {
parent.setClassAssertionStatus(className, enabled);
}
#Override
public void clearAssertionStatus() {
parent.clearAssertionStatus();
}
}
Usage:
public static ClassLoader cachingClassLoader = new MyClassLoader(FXMLLoader.getDefaultClassLoader());
FXMLLoader loader = new FXMLLoader(resource);
loader.setClassLoader(cachingClassLoader);
This significantly speeds up the performance. However, there is no workaround for step 2, so this might still be a problem.
However, there are already feature requests in the official JavaFX jira for this. It would be nice of you to support this requests.
Links:
FXMLLoader should be able to cache imports and properties between to load() calls:
https://bugs.openjdk.java.net/browse/JDK-8090848
add setAdapterFactory() to the FXMLLoader:
https://bugs.openjdk.java.net/browse/JDK-8102624
I have had a similar issue. I also had to load a custom fxml-based component several times, dynamically, and it was taking too long. The FXMLLoader.load method call was expensive, in my case.
My approach was to parallelize the component instantiation and it solved the problem.
Considering the example posted on the question, the controller method with multithread approach would be:
private void textChange(KeyEvent event) {
GridPane g = new GridPane();
// creates a thread pool with 10 threads
ExecutorService threadPool = Executors.newFixedThreadPool(10);
final List<Celli> listOfComponents = Collections.synchronizedList(new ArrayList<Celli>(100));
for (int i = 0; i < 100; i++) {
// parallelizes component loading
threadPool.execute(new Runnable() {
#Override
public void run() {
listOfComponents.add(new Celli());
}
});
}
// waits until all threads completion
try {
threadPool.shutdown();
threadPool.awaitTermination(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// seems to be a improbable exception, but we have to deal with it
e.printStackTrace();
}
g.getChildren().addAll(listOfComponents);
}
Just adding code for "caching of already loaded classes" in #Sebastian sir given code. It is working for me. Please suggest changes in it for better performance.
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("In Class loader");
Class result;
System.out.println(" >>>>>> Load class : "+name);
result = (Class)classes.get(name);
if(result != null){
System.out.println(" >>>>>> returning cached class.");
return result;
}else{
Class<?> c = findClass(name);
if ( c == null ) {
throw new ClassNotFoundException( name );
}
System.out.println(" >>>>>> loading new class for first time only");
return c;
}
}

Demultiplex / delegate for GWT event system, using enums

I have an enum, say Fruits { Apple, Banana, Cherry }. I want to write a event subsystem for my application so that I can have the following pattern :
class AppleListener implements HasFruitPriceChangeListener<Apple> {
onFruitPriceChange(int newPrice) {
// do stuff
}
}
and a single listener, that can delegate tasks in the following format:
class FruitPriceListener {
public void onPriceChange(EnumMap<Fruits, Integer> pricePoints) {
// for each fruit, throw the event as
// FruitPriceChange.fire(Apple, newPrice);
}
}
Is there a way to do it in the above manner ? I would probably like to use ValueChangeEvent, but creating another 1 event and handler is also fine too. What I do not want to do is have event/class definitions for each item, like AppleFruitPriceChangeEvent, and so on.
You can use the EventBus for this things, which google suggested ( http://www.youtube.com/watch?v=PDuhR18-EdM ) Here how to use it.
Your globl Eventbus
public static SimpleEventBus bus = new SimpleEventBus();
Your change event:
import com.google.gwt.event.shared.GwtEvent;
import eyeweb.client.gwtMessages.JSPollingEntry;
public class EventModified extends GwtEvent<EventModifiedHandler> {
public final static Type<EventModifiedHandler> TYPE = new Type<EventModifiedHandler>();
private final Fruits fruits;
public final JSPollingEntry getPollingMessage(){
return fruits;
}
public EventModified(Fruits fruits) {
this.fruits = fruits;
}
#Override
public com.google.gwt.event.shared.GwtEvent.Type<EventModifiedHandler> getAssociatedType() {
return TYPE;
}
#Override
protected void dispatch(EventModifiedHandler handler) {
handler.onUpdateRecivde(this);
}
}
the handler for the event
package eyeweb.client.eventbus;
import com.google.gwt.event.shared.EventHandler;
public interface EventModifiedHandler extends EventHandler {
public void onUpdateRecivde(EventModified handler);
}
The event when something changes
EventBus.bus.fireEvent(new EventModified(fruite));
and the handler which gets the event
EventBus.bus.addHandler(EventModified .TYPE, new EventModifiedHandler() {
#Override
public void onMessageSend(EventSendData e) {
//... do stuff }
});
Well that sould be all ;)
Regards,
Stefan
So the solution I came up with was:
Create the enum, and associate a GwtEvent.Type with them:
enum Fruits {
Apple, Banana, Cherry;
public GwtEvent.Type getGwtEventType() {
return new GwtEvent.Type();
}
}
Create a new event.
class FruitPriceChangeEvent extends GwtEvent<?> {
private final Fruit fruit;
FruitPriceChangeEvent(Fruit fruitEnum) {
this.fruit = fruitEnum;
}
#Override
public GwtEvent.Type<?> getAssociatedType() {
return fruit.getGwtEventType();
}
// ... other stuff...
}
And then pass it through the whole event handler loop as #Stefan has mentioned. The beauty/hack of this approach is that the SimpleEventBus maintains a HashMap<GwtEvent.Type, List<HasHandlers>> from which to get the events, and everytime you create a new GwtEvent.Type it generates a unique hashcode (check the implementation for more details).
References:
http://grepcode.com/file/repo1.maven.org/maven2/com.google.gwt/gwt-servlet/2.1.1-rc1/com/google/gwt/event/shared/GwtEvent.java?av=f
http://grepcode.com/file/repo1.maven.org/maven2/com.google.gwt/gwt-servlet/2.1.1-rc1/com/google/gwt/event/shared/SimpleEventBus.java?av=f

Resources