I am trying to display some objects in SwiftUI that I created using an NSManagedObjectContext set to a private queue (so that in case the user presses cancel, the objects aren't committed anywhere, basically the 'scratchpad' MOC). I initially create new a background context:
let privateMOC = appDelegate.persistentContainer.newBackgroundContext()
Then I create new objects using this background context, and set the environment variable for managedObjectContext to this MOC:
let rootView = ImportCSVMappingFieldsPage().environment(\.managedObjectContext, privateMOC)
I then want to display the created objects in the SwiftUI view. However, when I do this, the app crashes and I get this error:
SwiftUI/FetchCommon.swift:47: Fatal error: Can only use main queue
contexts to drive SwiftUI
Is there something I'm missing here? I should be able to display objects created with a background managedObjectContext inside a SwiftUI view, right?
A background context is for performing work in the background. If you want a scratchpad context for managing user edits, you need to make a main queue context:
let editingContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
editingContext.parent = appDelegate.persistentContainer.viewContext
let rootView = ImportCSVMappingFieldsPage().environment(\.managedObjectContext, editingContext)
When you save this context, the changes will be merged back to the view context. If you cancel and the context is destroyed, the changes are discarded.
If you want to do work on a background queue, then make user edits, and have the entirety of that work be disposable, then create a background queue context with the parent of the editing context above. When the background work is finished, save the background context. The editing context will now have that data available to work with or discard as you please.
Related
I'm doing some kind of social, it's when i open a post a new scene is created which initializes the elements with the data taken from the database. the problem is that the big photos take time to load, and until they load the program does not respond, I would like a way to be able to open the scene first so as to wait until the image loads without the program crashing
public void init(int idpost) throws SQLException {
this.post = new PostDAOImpl().getPost(idpost);
photo.fitWidthProperty().bind(imgContainer.widthProperty());
photo.fitHeightProperty().bind(imgContainer.heightProperty());
photo.setImage(new Image(post.getPhoto()));
name.setText(post.getProfile().getName());
username.setText("#" + post.getProfile().getUsername());
if (post.getProfile().getAvatar() != null)
avatar.setImage(new Image(post.getProfile().getAvatar()));
description.setText(post.getDescription());
}
here is the code, which is executed as soon as the scene loads. I was thinking of doing another DAO to first fetch all the data except the photo, load the scene and only then fetch the image or something like that, but I don't know how to do it
I think it is always a good idea to create and show a GUI in the empty state quickly first and then launch some background task to collect the data and once this data is available update the GUI. Just think of a word-processor GUI. When you launch it it's in the empty state without any document. Then the user selects a document which gets loaded and as soon as the data is available the GUI changes and displays the document. The only difference in your case is that you already know which data you want to load.
I have a requirement to generate a bitmap out of an EditText and then perform some manipulations on it.
My main concern is not to call View.buildDrawingCache() method on the UI thread and possibly block it, especially when talking about large screens (i.e. Nexus 10) since the EditText will occupy about 80% of the available screen size.
I execute Runnables inside a ThreadPoolExecutor, those will inflate dummy views on a worker thread and set all the required attributes to them, then simply call buildDrawingCache() & getDrawingCache() to generate a bitmap.
This works perfect on some devices yet recently I have encountered a few devices that crash with the following message:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I understand why this happens, as some phones must have modified implementation for EditText that creates a Handler and thus requires Looper.prepare() to be called first.
From what I've read online there is no issue with calling Looper.prepare() inside a worker thread though some stated it is highly unrecommended yet I could not find a reason for that.
Other than that, most posts related to this issue state you are not supposed to inflate views inside a background thread, probably due to the following from Android's official documentation (Processes and Threads):
"Do not access the Android UI toolkit from outside the UI thread"
What is the recommended approach to dealing with this problem?
Is there any harm in calling build/get drawingcache from the main thread? (performance-wise)
Will calling Looper.prepare() inside my worker thread solve this problem?
EDIT
Just to elaborate on my specific requirement, I have a user-interface consisting of an ImageView and a custom EditText on top of it, the EditText can change it's font and color according to the user selection, it can be zoomed in/out using "pinch to zoom" gesture and can also be dragged around to allow the user to reposition it on top of the image.
Eventually what I do is create a dummy view inside my worker thread using the exact same values (width, height, position) it currently has on the UI and then generate it's drawingcache, the original image's bitmap is decoded again from a local file.
Once the two bitmaps are ready I merge them into a single bitmap for future use.
So to put it simple, is there anything wrong with executing the following code (from within a background thread):
Call Looper.prepare()
Create a new view with application context, call measure() & layout() manually and then build+get drawingcache from it, i.e.:
Looper.prepare();
EditText view = new EditText(appContext);
view.setText("some text");
view.setLayoutParams(layoutParams);
view.measure(
View.MeasureSpec.makeMeasureSpec(targetWidth, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(targetHeight, View.MeasureSpec.EXACTLY));
view.layout(0, 0, targetWidth, targetHeight);
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
How does this apply to the restriction with not accessing the Android UI toolkit from outside the UI thread, what could possibly go wrong?
In your case, you can do it of course, but be carefull only reading values from UI data, to avoid synchronizations bug.
Also you should not recreate the EditText from the background thread, it will be more efficient to directly access the already existant one instead:
Looper.prepare();
myEditText.setDrawingCacheEnabled(true);
Bitmap bitmap = myEditText.getDrawingCache();
If your question is : why it is not recommanded by android guidelines, here is a good SO answer to your question.
Calling View.buildDrawingCache() calls Bitmap.nativeCreate which can be a large allocation, so yes, it can be potentially harmful to run on main thread. I don't see a problem with calling Looper.prepare() in your background thread. However, it's unclear what you are trying to achieve and there may be a better solution to your problem.
The reason you are not supposed to the UI toolkit from other threads is that it is not written to be thread safe it is written under the assumption that only one thread runs it. This means it's really hard to tell what can go wrong, the bad effects, if any, will mostly happen in an un-repeatable due to specific timing of threads.
Your description of what you are trying to do it not too clear. In your case, I would just allocate a large bitmap, and draw text into it. Why are you using the EditText in the first place ? It seems like a kind of a hack, and hacks tend to break eventually.
Why View.buildDrawingCache()? What about using View.draw(Canvas canvas) to manually render to a Canvas backed by a Bitmap? Method seems simple enough to not cause problems on background threads.
EditText edit = (EditText)findViewById(R.id.edit);
edit.buildDrawingCache();
ImageView img = (ImageView)findViewById(R.id.test);
img.setImageBitmap(edit.getDrawingCache());
Lalit when you try to build the cache in the onCreate method, the drawing hasn't happened yet so the drawingCache should have nothing. Either put the buildDrawingChache method in the onClick method. Or use the following code in onCreate.
ViewTreeObserver vto = editText.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
editText.buildDrawingCache();
}
});
I also encountered this error a few times already:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
my solution:
new Thread(new Runnable(){
#Override
public void run(){
//add implementations that DOES NOT AFFECT the UI here
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run(){
//manage your edittext and Other UIs here
}
});
}
}).start();
just create a handler inside your worker thread to apply data changes to your UI
In Swing you have paint or update(Graphics g) method for each UI component which executes every frame. You can put your drawing logic in the overrided method and draw whatever you want. What is the paint method for JavaFX2 UI?
What I want to do is my UI receives control information from a socket server (another thread). Whenever a control information is received, I should update the UI.
There are two problems:
1. The control information is received from a different thread, it can not access the rendering thread directly.
2. How to update the UI constantly?
For Q1, I have a solution if I know where the update function is (Q2).
I can declare a List object, and insert the new control command received from the socket into the list. In the rendering loop, I can just observe the List object, retrieve the unprocessed command, and delete the already processed command.
However, where can I find such a rendering loop function? I guess maybe I can also do it with javax.concurrent.Task, but I don't find a way to do it.
I think i find the method, I use the following method and it can work. I don't know if there is a better solution
final Duration oneFrameAmt = Duration.millis(1000/10);
final KeyFrame oneFrame = new KeyFrame(oneFrameAmt,new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
//put UI update here
}
});
TimelineBuilder.create().cycleCount(Animation.INDEFINITE)
.keyFrames(oneFrame)
.build()
.play();
I am dynamically creating content in a qooxdoo desktop application based on ajax calls. Some of the content I am creating has a lot of widgets in it (on the order of 200+).
I did some profiling with google chrome and it looks like most of the processing time is taken up by the layout manager since I am calling add() on the container for every widget I create.
Is there a way to pause the layout manager while I add all the widgets and then have it run once at the very end?
Is there a better approach altogether for dynamically adding lots of widgets to containers?
Here is an example of what I am doing:
var container = new qx.ui.container.Composite(new qx.ui.layout.Flow());
var groupbox = new qx.ui.groupbox.GroupBox();
groupbox.setLayout(new qx.ui.layout.Grid(10, 10));
container.add(groupbox);
// loop through data received from AJAX call and add it to the group box
var row = 0;
data.each(function(pair) {
var label = new qx.ui.basic.Label(pair.key);
var field = new qx.ui.form.TextField(pair.value);
groupbox.add(label, {row: row, column: 0});
groupbox.add(field, {row: row, column: 1});
++row;
});
You can probably
first add all the widgets into a "unattached" container, i.e. a container which has not been added anywhere yet or its ascendants are not part of the layout
then add the "unattached" container to the layout, triggering the widget layouting
When you add a widget, it is added to a queue to be executed after you thread ends, if you add many widgets on the same thread, the layout reflow will be executed once.
So if there is no asynchrony between one .add() and the next one they are on the same thread and they do only one reflow.
The time you see on the profile is the normal time the layout manager takes to render, it has to access to the dom to know the size of some elements and this operation takes a lot, the last profile I did the more expensive operation was "el.innerWidth".
I think there is nothing to do there if you want to use Qooxdoo.
I have a UITableViewController fed by an NSFetchedResultsController. From it, the user can call up a modal ViewController in which he or she can enter new data. As this begins, I create a temporary object as follows:
newPtr = [[Entry alloc] initWithEntity:[NSEntityDescription
entityForName:#"Entry" inManagedObjectContext:self.nmocontext]
insertIntoManagedObjectContext:self.nmocontext];
As the user makes choices, attributes of this 'provisional' object, newPtr, are set.
The problem is that the base UITableViewController remains active while the modal ViewController is visible. It seems to be freaking out (causing crashes) in some cases when it realizes a mandatory attribute of newPtr has not been set yet.
What can I do to stop the NSFetchedResultsController from looking at my managed object context until the modal ViewController is dismissed?
Core Data supports "nested" managed object contexts which allow for a flexible architecture that make it easy to support independent, cancellable, change sets. With a child context, you can allow the user to make a set of changes to managed objects that can then either be committed wholesale to the parent (and ultimately saved to the store) as a single transaction, or discarded. If all parts of the application simply retrieve the same context from, say, an application delegate, it makes this behavior difficult or impossible to support.
I haven't tested this myself but a possible approach would be to implement viewWillAppear and viewWillDisappear, and set the fetchedResultsController delegate to self on will appear and nil on will disappear.
OR
You could create an NSObject that mirrors the attributes of your NSManagedObject in your editing window. Once the user has finished editing the attributes (and you have run the appropriate validation rules) you can pass them back to your NSManagedObject instance and let the fetchedResultsController do its job.