How to enable and disable a progress indicator when using JMS in javafx8 - jms

on action i send a message to a jms topic to process data and i have a call back method which gets called when the data is ready and loads a TableView.
public void onEnter(ActionEvent actionEvent) throws IOException, InterruptedException {
new Thread() {
public void run() {
Platform.runLater(() -> {
progressIndicator.setVisible(true);
scrollPane.setDisable(true);
});
// Construct the message and publish it to a topic
};
}.start();
}
}
public void callBackMethod(List<Object> list ) {
progressIndicator.setVisible(false);
scrollPane.setDisable(false);
//load data in the table
}
This does what i want, but what if something goes wrong at the messaging system end , the call back never gets called and the UI component will be disabled forever .
Any suggestions to improve this will be helpful.

Presumably, the messaging system is going to throw some kind of exception if it fails to send the message, so you'll need a way to catch that and recover properly. If you use the JavaFX "Task" class, then you'll get events when that happens. You'll still have to deal with a failure at the receiving end, or implement some kind of a time-out, if that's appropriate.
Also, you're starting up a thread and then immediately tossing a job onto the FXAT with RunLater. The onEnter event handler, by definition, is already running on the FXAT, so you can just do your GUI stuff before you start up the thread (or Task, as I suggest). Here's a sample that shows how to launch the Task, and clean up if it fails with an exception:
public class SampleTask extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
BorderPane root = new BorderPane();
ProgressIndicator progressIndicator = new ProgressIndicator(0);
ScrollPane scrollPane = new ScrollPane();
Button button = new Button("Start");
root.setTop(progressIndicator);
root.setCenter(scrollPane);
progressIndicator.setVisible(false);
root.setBottom(button);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
button.setOnAction(actionEvent -> {
progressIndicator.setVisible(true);
scrollPane.setDisable(true);
Task<Void> testTask = new Task<Void>() {
#Override
protected Void call() throws Exception {
// Send the message
return null;
}
};
testTask.setOnFailed(event -> {
progressIndicator.setVisible(false);
scrollPane.setDisable(false);
});
new Thread(testTask).start();
});
}
}

Related

WindowBuilder update from paho callback (on MQTT Receive) [duplicate]

I have a simple Java SWT app in Java so far but the weird thing is when I try to launch a messagebox/alert box upon listening to an event fired by one of my own classes, I get an error saying "Invalid thread access".
My class event is fired and heard by the main class but it is when it has to show the MessageBox that the "Invalid thread access" error appear. I am trying to show the MessageBox in a function that consist of all the other codes that will create the SWT GUIs. This is how the function looks like:
public void createContents() {
Shell shell = new Shell();
//.....all the SWT GUI codes....
MessageBox msg = new MessageBox(shell, SWT.OK);
myClass.addEventListener(new MyClassEventClassListener() {
#Override
public void myClassEventHandler(MyClassEvent e) {
msg.setText("Hello");
msg.setMessage("Event fired!");
int result = msg.open();
}
});
}
These are the auxiliary functions together in the class.
<!-- language: lang-java -->
protected static Shell shell;
public static void main(String[] args) {
MyClass new myClass = new MyClass();
try {
SWTApp window = new SWTApp();
window.open();
} catch (Exception e) {
}
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
The error stack trace is as follows:
Exception in thread "AWT-EventQueue-0" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:4083)
at org.eclipse.swt.SWT.error(SWT.java:3998)
at org.eclipse.swt.SWT.error(SWT.java:3969)
at org.eclipse.swt.widgets.Display.error(Display.java:1249)
at org.eclipse.swt.widgets.Display.checkDevice(Display.java:755)
at org.eclipse.swt.widgets.Display.getShells(Display.java:2171)
at org.eclipse.swt.widgets.Display.setModalDialog(Display.java:4463)
at org.eclipse.swt.widgets.MessageBox.open(MessageBox.java:200)
Any help will be great.
Thanks!
It is thrown because your listener code is called from outside the SWT Display thread. You run code on the display thread like this:
Display.getDefault().syncExec(new Runnable() {
public void run() {
// ...
}
});
or, asynchronously:
Display.getDefault().asyncExec(new Runnable() {
public void run() {
// ...
}
});
Maybe this will help you:
FAQ Why do I get an invalid thread access exception?
https://wiki.eclipse.org/FAQ_Why_do_I_get_an_invalid_thread_access_exception%3F

How do I display two javafx GUI screens in the same program

My question is how to display more than one user interface screen per program. I'm sure this question has been asked before, but I haven't found a solution that works for me (or should I say that I understand). There isn't anything exotic about the scenarios I'm talking about. The first is simply validating inputs from a screen and re-displaying the same screen in the case of errors.
I'll pose the question in terms of the second more complicated scenario: displaying an input data screen, processing the inputs; and then displaying the outputs. This complicated somewhat by the fact that the first, a simple screen with 5 text boxes and a command button, uses an FXML file, whereas the second, a multi-select list box does not. The flow is:
1. Main program calls
2. A loader program which loads the FXML and somehow or another calls
3. A controller which receives the inputs and processes them to produce output.
The final step is to display the output in the form of a multi-select list box. Note that the first GUI employs a controller, which is a separate file, to process the inputs, whereas the second uses an event handler, which is in the same file as the screen definition, to make the selection(s) when the user clicks a command button.
Various SO posts have said that the way to go is to not shut down the application once the first GUI has completed via but Keep the JavaFX run time going in the background with
Platform.setImplicitExit(false);
and to define each GUI and simply switch scenes to the one you want to display. But where, given the scenario I described do you put the code? The second GUI has three pieces: screen definition, event handler(s), and scene switching code. Where do you put each? #2 or #3. If you put some in #2 and some in #3, how does #3 know what you did in #2?
The code for #2 the FMXL loader:
public class inputData extends Application {
public static void load() {
launch();
}
#Override
public void start(Stage stage) throws Exception {
GridPane inpRoot = FXMLLoader.load(getClass().getResource("inputData.fxml"));
Scene inpScene = new Scene(inpRoot, 300, 275);
stage.setTitle("Amsnag 2.1 - Query Input");
stage.setScene(inpScene);
stage.show();
}
}
Code for #3, the list box definition and handlers, which worked fine running separately. It's only when I tried to incorporate it with the rest of the program that it failed.
public class multiList extends Application {
public static void load() {
launch();
}
public static final ObservableList options = FXCollections.observableArrayList();
#Override
public void start(final Stage stage) {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// lcreate temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
} ); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
final VBox layout = new VBox(10);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
layout.getChildren().setAll(listView, controls);
layout.setPrefWidth(320);``enter code here
Scene scene = new Scene(layout);
// stage.setScene(new Scene(layout));
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
public static void main(String[] args) { launch(args); }
}
You can't reuse an Application subclass in a different application.
The Application class represents an entire application, or perhaps more specifically its lifecycle. So it has methods such as init(), start(), and stop() which are invoked by the FX Application Toolkit at the appropriate moments in the lifecycle of the application.
The layout for your multiList (aside: please use proper naming conventions) class is performed in the start() method, so it can only happen at the start of the application. By putting the layout code here, you make it impossible to reuse so that it is performed at a later point in a different application.
So move the layout for MultiList to a separate class:
public class MultiList {
public static final ObservableList options = FXCollections.observableArrayList();
private final VBox view ;
public MultiList() {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// create temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
} catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
}); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
view = new VBox(10);
view.setAlignment(Pos.CENTER);
view.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
view.getChildren().setAll(listView, controls);
view.setPrefWidth(320);
}
public Parent getView() {
return view ;
}
}
Now if you want to test this out on its own, you can write an application for it:
public class MultiListApp extends Application {
#Override
public void start(Stage primaryStage) {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
primaryStage.setScene(scene);
primarStage.setTitle("Select one or more options");
primaryStage.show();
}
}
Or in the controller class for InputData.fxml, you can do the same thing:
public class InputDataController {
#FXML
private void someEventHandler() {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
}

SWT Dialog does not display correctly

When opening a new dialog, while its loading, you click couple of times on parent shell, apparently the new dialog does not display correctly.
Please see the example below:
Examples
https://i.stack.imgur.com/ZovxE.png (eclipse IDE example)
https://i.stack.imgur.com/5zVar.png
https://i.stack.imgur.com/u86b9.png
https://i.stack.imgur.com/FGaAr.png
Initially I encountered the problem in december 2014, and back then also reported by vaious in house devlopers which were using different development systems and then same problem has been reported by our several customers.
This behavior can be reproduced using following environment:
Windows Version: 7 Pro 64 Bit - 6.1.7601
Java Version: RE 1.8.0_121_b13
SWT Versions
3.8.2
4.6.2
4.7M6
I20170319-2000
I could only reproduce the problem on Windows 7 with the windows basic theme/design/style (not with classic or aero).
On windows 10 its not reproducible.
reproduce
code to reproduce
package test;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class Main {
public static void main(String[] args) {
Display display = new Display();
final Shell shell = createShell(display);
createButton(shell);
shell.open();
eventLoop(display, shell);
display.dispose();
}
private static Shell createShell(Display display) {
final Shell shell = new Shell(display);
shell.setLayout(new RowLayout());
shell.setSize(500, 200);
return shell;
}
private static void createButton(final Shell shell) {
final Button openDialog = new Button(shell, SWT.PUSH);
openDialog.setText("Click here to open Dialog ...");
openDialog.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
TestDialog inputDialog = new TestDialog(shell);
inputDialog.open();
}
});
}
private static void eventLoop(Display display, final Shell shell) {
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
class TestDialog extends Dialog {
public TestDialog(Shell parent) {
super(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MIN | SWT.MAX | SWT.RESIZE);
setText("Dialog");
}
public void open() {
Shell shell = new Shell(getParent(), getStyle());
shell.setText(getText());
createContents(shell);
shell.pack();
initializeBounds(shell);
shell.open();
eventLoop(shell);
}
private void createContents(final Shell shell) {
shell.setLayout(new GridLayout(2, true));
Label label = new Label(shell, SWT.NONE);
label.setText("Some Label text ...");
final Text text = new Text(shell, SWT.BORDER);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
text.setLayoutData(data);
createCloseButton(shell);
/* time for the user to create the misbehavior */
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void createCloseButton(final Shell shell) {
Button closeButton = new Button(shell, SWT.PUSH);
closeButton.setText("Close");
GridData data = new GridData(GridData.FILL_HORIZONTAL);
closeButton.setLayoutData(data);
closeButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
shell.close();
}
});
shell.setDefaultButton(closeButton);
}
private void initializeBounds(Shell shell) {
Rectangle bounds = shell.getBounds();
Rectangle parentBounds = getParent().getBounds();
bounds.x = parentBounds.x;
bounds.y = parentBounds.y;
shell.setBounds(bounds);
}
private void eventLoop(Shell shell) {
Display display = getParent().getDisplay();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
}
steps to reproduce
Start the application
it should look like: https://i.stack.imgur.com/dMJ9e.png
Click on the button.
Keep continuously clicking on right bottom corner of the parent shell (avoid hitting the new opening dialog), till mouse cursor changes to wait icon and parent shell changes its color.
it should look as following: https://i.stack.imgur.com/c1Ikp.png
Wait until the new dialog appears.
it looks likes as following: https://i.stack.imgur.com/kTDgQ.png (incorrectly displayed)
instead: https://i.stack.imgur.com/cHVjn.png (correctly displayed)
steps to reproduce done in video
https://youtu.be/7ukhloCPf0k
When you mouse hover some of the UI elements (the originally not correctly drawn), you can notice some of them to be get painted (e.g. table rows).
https://i.stack.imgur.com/kkMKn.png (before opening the dialog)
https://i.stack.imgur.com/ZXIKc.png (after opening the dialog)
https://i.stack.imgur.com/25M7S.jpg (after mouse over)
Even calling Shell.update() or Shell.redraw() after the Dialog opened does not fix it.
In Windows Performance Options -> Visual Effects -> disable "Use visual styles on windows and buttons" is the only option I found which provides a workaround,
which seems to be the same as changing the design/theme/style to classic.
https://www.sevenforums.com/tutorials/1908-visual-effects-settings-change.html (How to Change Windows Visual Effects)
In the end, I have following questions:
Is it a SWT or Windows problem?
Is there any related topic in bug entries for Windows or in Eclipse Bugzilla?
Is there someone else who experienced the same problem? please share the experience.
Is there any settings in SWT or Windows which could affect its look n feel and fix the problem?
In the end, I have following questions: Is it a SWT or Windows problem?
Neither. As others have mentioned, you certainly should not tie up the UI thread with any long-running task. That work belongs in a background thread.
In regards to using a background thread, there are several ways you could go about this depending on how you want your Dialog to behave.
One option would be to kick off the background thread and then open the dialog when the task is done. I personally don't care for this because while the task is running, a user may think that nothing is happening.
Another option would be to open the dialog but display a "Loading" message, or something to that effect to give meaningful feedback and let a user know that the application isn't frozen (like how it looks/responds in your example).
The strategy would be to:
Create the dialog
Start the long task on a background thread and register a callback
Open the dialog with a "Loading" message
When the task is complete, the dialog will be updated from the callback
If you search around a bit on using Executors, you should find some far better examples and detail on how to use them.
Here's a brief example to illustrate what that might look like:
(Note: There are definitely a few issues with this code, but for the sake of brevity and illustrating the point I opted for a slightly naive solution. Also there are Java 8-esque ways that would be a bit shorter, but again, this illustrates the idea behind using a background thread; the same concepts apply)
Given a Callable (or Runnable if you don't need a return value),
public class LongTask implements Callable<String> {
#Override
public String call() throws Exception {
Thread.sleep(15000);
return "Hello, World!";
}
}
You can use the Executors class to create a thread pool, and then an ExecutorService to submit the Callable for execution. Then, using Futures.addCallback(), you can register a callback which will execute one of two methods depending on whether the task was successful or failed.
final ExecutorService threadPool = Executors.newFixedThreadPool(1);
final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(threadPool);
final ListenableFuture<String> future = executorService.submit(new LongTask());
Futures.addCallback(future, new FutureCallback(){...});
In this case I used the Google Guava implementation ListeningExecutorService which makes things a bit cleaner and simpler, in my opinion. But again, you may not even need this if you opt for a more "Java 8" approach.
As for the callback, when the task is successful, we update the Dialog with the results. If it fails, we can update it with something to indicate failure:
public static class DialogCallback implements FutureCallback<String> {
private final MyDialog dialog;
public DialogCallback(final MyDialog dialog) {
this.dialog = dialog;
}
#Override
public void onSuccess(final String result) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus(result);
}
});
}
#Override
public void onFailure(final Throwable t) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus("Failure");
}
});
}
}
In this case I opted for the Callable to return a String, thus the FutureCallback should be parameterized with String. You may want to use some other class that you created, which will work just as well.
Notice that we use the Display.asyncExec() method to ensure that the code which updates the UI runs on the UI thread, because the callback may execute on the background thread.
Like I said, there are still a few issues here, including what happens when you click the cancel button before the task completes, etc. But hopefully this helps illustrate an approach for handling long-running background tasks without blocking the UI thread.
Full example code:
public class DialogTaskExample {
private final Display display;
private final Shell shell;
private final ListeningExecutorService executorService;
public DialogTaskExample() {
display = new Display();
shell = new Shell(display);
shell.setLayout(new GridLayout());
executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
final Button button = new Button(shell, SWT.PUSH);
button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
button.setText("Start");
button.addSelectionListener(new SelectionAdapter() {
#SuppressWarnings("synthetic-access")
#Override
public void widgetSelected(final SelectionEvent e) {
final MyDialog dialog = new MyDialog(shell);
dialog.setBlockOnOpen(false);
dialog.open();
dialog.setStatus("Doing stuff...");
final ListenableFuture<String> future = executorService.submit(new LongTask());
Futures.addCallback(future, new DialogCallback(dialog));
}
});
}
public void run() {
shell.setSize(200, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
executorService.shutdown();
display.dispose();
}
public static void main(final String... args) {
new DialogTaskExample().run();
}
public static class DialogCallback implements FutureCallback<String> {
private final MyDialog dialog;
public DialogCallback(final MyDialog dialog) {
this.dialog = dialog;
}
#Override
public void onSuccess(final String result) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus(result);
}
});
}
#Override
public void onFailure(final Throwable t) {
dialog.getShell().getDisplay().asyncExec(new Runnable() {
#SuppressWarnings("synthetic-access")
#Override
public void run() {
dialog.setStatus("Failure");
}
});
}
}
public static class LongTask implements Callable<String> {
/**
* {#inheritDoc}
*/
#Override
public String call() throws Exception {
Thread.sleep(15000);
return "Hello, World!";
}
}
public static class MyDialog extends Dialog {
private Composite baseComposite;
private Label label;
/**
* #param parentShell
*/
protected MyDialog(final Shell parentShell) {
super(parentShell);
}
/**
* {#inheritDoc}
*/
#Override
protected Control createDialogArea(final Composite parent) {
baseComposite = (Composite) super.createDialogArea(parent);
label = new Label(baseComposite, SWT.NONE);
return baseComposite;
}
public void setStatus(final String text) {
label.setText(text);
baseComposite.layout();
}
}
}
The code seems to be straight forward, only that you are making the main Thread sleep for 15secs hence the delay. If not required remove the sleep or reduce the time for sleep to 5secs or so.

Slow loading of layout

I have a super class which is in a library. This library take care of initializing some basic layout components and other stuff. My problem is that it takes 1.x seconds to load the layout, and shows the default layout for a while, before setting the child-specified layout.
This is the method of my super class:
public void InitializeWindow(Activity act, int layoutResourceId, String windowTitle,
Object menuAdapter, int slideMenuMode) {
super.setContentView(layoutResourceId);
super.setBehindContentView(R.layout.menu_frame);
this.menuAdapter = menuAdapter;
this.slideMenuMode = slideMenuMode;
setWindowTitle(windowTitle);
initializeSlidingMenu();
}
This is called this way:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.InitializeWindow(this, R.layout.activity_home, "\t\tHome",
new MenuAdapter(this, R.menu.slide_menu), SlidingMenu.TOUCHMODE_FULLSCREEN);
}
The application works like a charm, but it takes, as I said around 1.x seconds to load the layout passed from the child-class. Why does this happen?
By request, this is my initializeSlideMenu() method:
public void initializeSlidingMenu() {
this.setSlidingActionBarEnabled(true);
getSlidingMenu().setBehindOffsetRes(R.dimen.actionbar_home_width);
getSlidingMenu().setShadowWidthRes(R.dimen.shadow_width);
getSlidingMenu().setShadowDrawable(R.drawable.shadow);
getSlidingMenu().setTouchModeAbove(slideMenuMode);
getSlidingMenu().setBehindScrollScale(0.25f);
ListView v = new ListView(this);
v.setBackgroundColor(Color.parseColor("#000000"));
v.setAdapter((ListAdapter) menuAdapter);
getSlidingMenu().setMenu(v);
}
To avoid such problems there are three ways in general.
Let your onCreate() finish after setContentView() call as early as possible. You can use postDelayed runnable to delay few initialization which may not be needed at early stages.
Do some task when the view is ready, it causes the Runnable to be added to the message queue of that view.
Snippet
view.post(new Runnable() {
#Override
public void run() {
}
});
If none of the above helps consider "Optimize with stubs" link : http://android-developers.blogspot.in/2009/03/android-layout-tricks-3-optimize-with.html
Hope it helps.
I suspect that the trouble spot for you is with:
v.setAdapter((ListAdapter) menuAdapter);
You should do this as part of an AsyncTask. It will often be very slow to execute the loading by the adapter.
Here is a snippet from a sample AsyncTask implementation:
//before starting the load, I pop up some indicators that I'm doing some loading
progressBar.setVisibility(View.VISIBLE);
loadingText.setVisibility(View.INVISIBLE);
AsyncTask<Void, Void, Void> loadingTask = new AsyncTask<Void, Void, Void>() {
private ArrayList<Thing> thingArray;
#Override
protected Void doInBackground(Void... params) {
//this is a slow sql fetch and calculate for me
thingArray = MyUtility.fetchThings(inputValue);
return null;
}
#Override
public void onPostExecute(Void arg0) {
EfficientAdapter myAdapter = new EfficientAdapter(MyActivity.this, thingArray);
listView.setAdapter(myAdapter);
//after setting up my adapter, I turn off my loading indicators
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
RelativeLayout layout = (RelativeLayout)MyActivity.this.findViewById(R.id.spacey);
if (layout != null) {
LayoutInflater inflater = LayoutInflater.from(MyActivity.this);
View view = inflater.inflate(R.layout.name_tabled_sub, layout);
NamedTableView tableView = new NamedTableView(MyActivity.this, view);
}
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
};
loadingTask.execute();
You can also do "PreExecute" items with the Async task, as well as update.

Android basics: running code in the UI thread

In the viewpoint of running code in the UI thread, is there any difference between:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
or
MainActivity.this.myView.post(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
and
private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
protected void onPostExecute(Bitmap result) {
Log.d("UI thread", "I am the UI thread");
}
}
None of those are precisely the same, though they will all have the same net effect.
The difference between the first and the second is that if you happen to be on the main application thread when executing the code, the first one (runOnUiThread()) will execute the Runnable immediately. The second one (post()) always puts the Runnable at the end of the event queue, even if you are already on the main application thread.
The third one, assuming you create and execute an instance of BackgroundTask, will waste a lot of time grabbing a thread out of the thread pool, to execute a default no-op doInBackground(), before eventually doing what amounts to a post(). This is by far the least efficient of the three. Use AsyncTask if you actually have work to do in a background thread, not just for the use of onPostExecute().
I like the one from HPP comment, it can be used anywhere without any parameter:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
There is a fourth way using Handler
new Handler().post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
The answer by Pomber is acceptable, however I'm not a big fan of creating new objects repeatedly. The best solutions are always the ones that try to mitigate memory hog. Yes, there is auto garbage collection but memory conservation in a mobile device falls within the confines of best practice.
The code below updates a TextView in a service.
TextViewUpdater textViewUpdater = new TextViewUpdater();
Handler textViewUpdaterHandler = new Handler(Looper.getMainLooper());
private class TextViewUpdater implements Runnable{
private String txt;
#Override
public void run() {
searchResultTextView.setText(txt);
}
public void setText(String txt){
this.txt = txt;
}
}
It can be used from anywhere like this:
textViewUpdater.setText("Hello");
textViewUpdaterHandler.post(textViewUpdater);
As of Android P you can use getMainExecutor():
getMainExecutor().execute(new Runnable() {
#Override public void run() {
// Code will run on the main thread
}
});
From the Android developer docs:
Return an Executor that will run enqueued tasks on the main thread associated with this context. This is the thread used to dispatch calls to application components (activities, services, etc).
From the CommonsBlog:
You can call getMainExecutor() on Context to get an Executor that will execute its jobs on the main application thread. There are other ways of accomplishing this, using Looper and a custom Executor implementation, but this is simpler.
If you need to use in Fragment you should use
private Context context;
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
((MainActivity)context).runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
instead of
getActivity().runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
Because There will be null pointer exception in some situation like pager fragment
use Handler
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
Kotlin version:
Handler(Looper.getMainLooper()).post {
Toast.makeText(context, "Running on UI(Main) thread.", Toast.LENGTH_LONG).show()
}
Or if you are using Kotlin coroutines:
inside coroutine scope add this:
withContext(Dispatchers.Main) {
Toast.makeText(context, "Running on UI(Main) thread.", Toast.LENGTH_LONG).show()
}

Resources