I had this app that fetches some data from a remote API. So the data that I am going to receive and display in a JSON Future:
{"status":200,"out":{"summary":[{"bc":"1876","wc":"488679","pc":"731904"}],"last":[{"id":"1877","place":"7","publisher":"-1","bookid":"01877","title":"Neither Civil Nor Servant","author":"Peh","region":"\u65b0\u52a0\u5761","copyrighter":"","translated":"0","purchdate":"2017-04-18","price":"200.00","pubdate":"2016-01-01","printdate":"2016-01-01","ver":"1.1","deco":"\u666e\u901a","kword":"0","page":"220","isbn":"978-981-4642-63-7","category":"","location":"","intro":"TT\u8d60\u4e66\u3002","instock":"1","p_name":"\uff08\u672a\u6307\u5b9a\uff09"}]}}
I will extract the out field from this JSON and assing summary and last to two variables:
initState() async {
var getter = createHttpClient();
String uri='http://api.rsywx.com/book/summary';
var res=await getter.get(uri);
Map data=JSON.decode(res.body);
var out=data['out'];
setState(() {
_today=formatDate(new DateTime.now());
_lb=out['last'][0];
_bs=out['summary'][0];
_lb['purchdate']=formatDate(DateTime.parse(_lb['purchdate']));
});
}
So _bs and _lb are all compound objects.
In my widget build function, I will display the contents of these two objects:
new TextSpan(
text: numFormatter.format(int.parse(_bs['bc'])),
style: aboutTextStyle,
),
The program compiles OK but when launched, a quick splash RED screen will appear:
And soon enough, the correct screen will appear:
I know that during the initial build, the object _bs, _lb is not there yet and the async call to a remote API is still trying to populate the returned response, so in this case, _bs['bc'] will definitely be not callable. Thus the non-blocking error pops out.
Workaround
I can eliminate this error by declaring a bunch of variables and assign them in the initState function; instead of rendering _bs['bc'], I will render a new variable _bookCoount. This way, the rendering will be done without this RED screen and the value of that variable will initially be null and soon be the correct value fetched from remote API.
But this is too cumbersome, if you get what I mean: A lot of used-only-once variables.
Or, shall I make the data fetched on the parent level, so that it will be passed to this widget as props? Not tried yet.
Would appreciate your best practice input.
Update
The issue really comes from int.parse. If I took out that call, the program runs peacefully.
So the question now becomes
I would suppress int.parse prompting an error before the value it is going to parse becomes valid.
Not sure what you mean with your workaround. In your example setState() won't be called before await getter.get(uri); returns a value.
I guess this should do
new TextSpan(
text: _bs != null && _bs['bc'] != null ? [numFormatter.format(int.parse(_bs['bc'])) : 0,
style: aboutTextStyle,
),
Related
I know that it is not the best title in the world but let me explain the problem first,
I have implemented a Glance widget with some items in it and when you press them, the app should be opened and navigated to the specific screen given via deep-link in NavHost. However, sometimes the page navigated via deep-link works and sometimes not. When it is not, got an error something like this:
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4d in tid 2486 (DefaultDispatch), pid 2259
I believe this is a segmentation error as I pass the argument from widget to app by using Gson()toJson(itemData). However, I do not understand why this sometimes works as I use the same thing for each item in order to open the app and navigate to the details screen.
The other thing is when the app does not crash with that error, the app directly opens the details page rather than showing the splash screen first and navigating to the detail screen.
In the end, how can I solve that segmentation error during deep-linking and how can I prepopulate the backstack(or just first navigate to splash and then to detail screen)?
For some more information, there is a sample code:
// CODE IN GLANCE WIDGET
#Composable
private fun Item(
model: ItemData,
) {
val intent = Intent(
Intent.ACTION_VIEW,
"${URL}/${Screens.DetailScreen.passArgument(model.toDetailData())}".toUri() // pass model just converts detailData to string with Gson().toJson()
)
Text(
text = model.title ?: "",
modifier = GlanceModifier
.clickable(
actionStartActivity(
intent
)
)
)
}
// CODE IN NAVHOST
NavHost(
// navhost parameters such as route, controller, start destination
// where start destination set to Splash Screen
){
composable(/*routeOfSpashScreen*/){SplashScreen}
.
. // These dots are some sub-navGraphs
.
.
detailScreenNavGraph()
}
// DETAIL SCREEN SUB-NAVGRAPH
fun NavGraphBuilder.detailScreenNavGraph(
controller: NavHostController? = null // This is optional and does not relate to problem
) {
navigation(
startDestination = Screens.DetailScreen.route,
route = DETAIL_SCREEN_ROUTE
) {
composable(
route = Screens.DetailScreen.route + "/{model}",
arguments = listOf(
navArgument(
name = "model"
) {
type = DetailDataNavType()
},
),
deepLinks = listOf(
navDeepLink {
uriPattern = "${URL}/" + Screens.DetailScreen.route + "/{model}"
}
)
) {
val model = it.arguments?.getParcelable<DetailData>("model")
if (model != null) {
DetailScreen(
model,
controller = controller ?: LocalNavigationManager.current
)
}
}
}
}
Any help or advice is appreciated.
Since no one is answering and I have solved my problem, I believe a proper explanation is needed if someone gets stuck like me.
What are receivers and how do they work?
Receivers are like singleton classes that control each widget, not only the widget called by. If you have to update the widget, you should have its proper GlanceId; currently, there is no official way to get that id. However, there is a hacky way such as:
// Add this to your callback and to your intent before broadcasting it
// so you can access the widget's GlanceId
val id = glanceId.toString().filter { it.isDigit() }.toInt()
Since you have the id in your onReceive of your receiver, you can properly update your widget
Why I am having a "Cannot marshall a parcel that contains binder objects" error?
This is because you are using LazyRow or LazyColumn and during drawing, as bitmaps contain smart components named binders which take extra space in your memory during drawing, you exceed the available limit given to the widget for memory. I have tried to reduce the size of the image and compress it as much as possible and it loads with the most disgusting images. However, if you use a Column instead of LazyColumn' you can easily render any image, at least this is what I have experienced.
How are these questions relate to my problem?
As I was trying to click an item on the widget that lazily loaded, I believe that I tried to access a memory space that has been either collected or freed during or after the widget is drawn. The other possibility is there was no memory space for the app to be launched and during launch, the memory is cleaned and I tried to access the deleted memory part. Either way, these are just assumptions and feel free to correct me if I am wrong.
What about the backstack part?
I totally changed the deeplink handling mechanism of the app such as:
if(appIsClosed)
start splash -> navigate home -> open the desired page from here
// pass the received intent parameters throughout navigation hierarchy
else
handle as deep-link to home -> open desired page from here
I have a page that contains a login form. I am calling a separate function (which resides in a separate .js file) on the onsubmit event of the form. Below is the function
var LoginClick = function() {
// Omniture Code
s.linkTrackVars="events, eVar1";
s.linkTrackEvents="event1";
s.events="event1";
s.eVar1="Login";
s.tl(true, "o", "Login Clicks");
};
while the events data is getting passed on the s.tl call, the evar1 value turns up as "none" in the conversion report. I used a packet sniffer (omnibug) to check the values being passed. Even though s.eVar1 is assigned the value "Login" it does not pass that value.
Ofcourse, s.t() works well (evar value gets passed) but I dont want to do that.
I have tried s.tl(this, "o"...) which doesnt work either.
What am I doing wrong here?
Appreciate your time and help.
remove the space between the comma and eVar1
s.linkTrackVars="events, eVar1"
should be
s.linkTrackVars="events,eVar1"
I'm currently teaching myself the Dart language, and my first app doesn't seem to be working right. Here's the code that's causing trouble:
usrLoc = int.parse(query("#txtLoc").text);
When I try to run the app, it opens fine, but when I click the button that triggers this (and three other similar parses), the debugger stops and tells me "Source not found" for int._native_parse(), int._parse(), and int.parse().
Any help would be greatly appreciated.
The text property for the specified element #txtLoc returns an empty string.
The parse method requires that:
The source must be a non-empty sequence of base- radix digits, optionally prefixed with a minus or plus sign ('-' or '+').
You can specify an onError named argument in your call to parse, which takes a callback that handles the invalid input. E.g., if you want the parse call to return the value 42 for all invalid input, you can do this:
usrLoc = int.parse(query("#txtLoc").text, onError: (val) => 42);
If you really expect the element to have some text, you can store the result of query("#txtLoc").text into a separate variable and verify the value. It would also be interesting to check what the real element type is or which tag is marked with id #txtLoc.
If you want to get the content of an input element, you should use the value property instead of text:
query("#txtLoc").value
I have a method that displays a validation result using the syntax
Box::info(message,title);
However, the first time I run the code it displays the correct title, but the message refreshEx.
Debugging the code the message that is being used is correct, Valid Account Number, but what displays is refreshEx. If I rerun the process the correct message is displayed, this only happens the first time.
Just in case it matters the flow is
Form - DoValidation method creates Class to call...
Class - public AccountValidation method that calls...
- private displayValidation method that contains this code
Thanks...
I have seen this error (unfortunately), in an AX 2009 installation, launched from code behind a button in a form:
if(HIEItemOrderSetup.RMAvailable < HIEItemOrderSetup.RMQuantity)
{
ok = DialogButton::Ok == box::okCancel("#HIE848",DialogButton::Ok,"#HIE849");
}
As far as I can tell it only occurs when you have a breakpoint on your form, when you are updating it. Removing the breakpoint will show the original message or at least this is what I have found.
If the message contains some fields from the database, try to execute a reread() or refresh() or refreshEx() method (depending on the context) to the datasource before showing the value through the info box.
May be the cached data is not refreshed after an update or insert.
EDIT:
If you are specting a return parameter from an Event, don't forget that this is an async process. An example on MSDN:
http://msdn.microsoft.com/en-us/library/gg843664.aspx
I'm building an nsIProtocolHandler implementation in Delphi. (more here)
And it's working already. Data the module builds gets streamed over an nsIInputStream. I've got all the nsIRequest, nsIChannel and nsIHttpChannel methods and properties working.
I've started testing and I run into something strange. I have a page "a.html" with this simple HTML:
<img src="a.png">
Both "xxm://test/a.html" and "xxm://test/a.png" work in Firefox, and give above HTML or the PNG image data.
The problem is with displaying the HTML page, the image doesn't get loaded. When I debug, I see:
NewChannel gets called for a.png, (when Firefox is processing an OnDataAvailable notice on a.html),
NotificationCallbacks is set (I only need to keep a reference, right?)
RequestHeader "Accept" is set to "image/png,image/*;q=0.8,*/*;q=0.5"
but then, the channel object is released (most probably due to a zero reference count)
Looking at other requests, I would expect some other properties to get set (such as LoadFlags or OriginalURI) and AsyncOpen to get called, from where I can start getting the request responded to.
Does anybody recognise this? Am I doing something wrong? Perhaps with LoadFlags or the LoadGroup? I'm not sure when to call AddRequest and RemoveRequest on the LoadGroup, and peeping from nsHttpChannel and nsBaseChannel I'm not sure it's better to call RemoveRequest early or late (before or after OnStartRequest or OnStopRequest)?
Update: Checked on the freshly new Firefox 3.5, still the same
Update: To try to further isolate the issue, I try "file://test/a1.html" with <img src="xxm://test/a.png" /> and still only get above sequence of events happening. If I'm supposed to add this secundary request to a load-group to get AsyncOpen called on it, I have no idea where to get a reference to it.
There's more: I find only one instance of the "Accept" string that get's added to the request headers, it queries for nsIHttpChannelInternal right after creating a new channel, but I don't even get this QueryInterface call through... (I posted it here)
Me again.
I am going to quote the same stuff from nsIChannel::asyncOpen():
If asyncOpen returns successfully, the
channel is responsible for keeping
itself alive until it has called
onStopRequest on aListener or called
onChannelRedirect.
If you go back to nsViewSourceChannel.cpp, there's one place where loadGroup->AddRequest is called and two places where loadGroup->RemoveRequest is being called.
nsViewSourceChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
{
NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);
mListener = aListener;
/*
* We want to add ourselves to the loadgroup before opening
* mChannel, since we want to make sure we're in the loadgroup
* when mChannel finishes and fires OnStopRequest()
*/
nsCOMPtr<nsILoadGroup> loadGroup;
mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
if (loadGroup)
loadGroup->AddRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
this), nsnull);
nsresult rv = mChannel->AsyncOpen(this, ctxt);
if (NS_FAILED(rv) && loadGroup)
loadGroup->RemoveRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
this),
nsnull, rv);
if (NS_SUCCEEDED(rv)) {
mOpened = PR_TRUE;
}
return rv;
}
and
nsViewSourceChannel::OnStopRequest(nsIRequest *aRequest, nsISupports* aContext,
nsresult aStatus)
{
NS_ENSURE_TRUE(mListener, NS_ERROR_FAILURE);
if (mChannel)
{
nsCOMPtr<nsILoadGroup> loadGroup;
mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
if (loadGroup)
{
loadGroup->RemoveRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
this),
nsnull, aStatus);
}
}
return mListener->OnStopRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
this),
aContext, aStatus);
}
Edit:
As I have no clue about how Mozilla works, so I have to guess from reading some code. From the channel's point of view, once the original file is loaded, its job is done. If you want to load the secondary items linked in file like an image, you have to implement that in the listener. See TestPageLoad.cpp. It implements a crude parser and it retrieves child items upon OnDataAvailable:
NS_IMETHODIMP
MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctxt,
nsIInputStream *stream,
PRUint32 offset, PRUint32 count)
{
//printf(">>> OnDataAvailable [count=%u]\n", count);
nsresult rv = NS_ERROR_FAILURE;
PRUint32 bytesRead=0;
char buf[1024];
if(ctxt == nsnull) {
bytesRead=0;
rv = stream->ReadSegments(streamParse, &offset, count, &bytesRead);
} else {
while (count) {
PRUint32 amount = PR_MIN(count, sizeof(buf));
rv = stream->Read(buf, amount, &bytesRead);
count -= bytesRead;
}
}
if (NS_FAILED(rv)) {
printf(">>> stream->Read failed with rv=%x\n", rv);
return rv;
}
return NS_OK;
}
The important thing is that it calls streamParse(), which looks at src attribute of img and script element, and calls auxLoad(), which creates new channel with new listener and calls AsyncOpen().
uriList->AppendElement(uri);
rv = NS_NewChannel(getter_AddRefs(chan), uri, nsnull, nsnull, callbacks);
RETURN_IF_FAILED(rv, "NS_NewChannel");
gKeepRunning++;
rv = chan->AsyncOpen(listener, myBool);
RETURN_IF_FAILED(rv, "AsyncOpen");
Since it's passing in another instance of MyListener object in there, that can also load more child items ad infinitum like a Russian doll situation.
I think I found it (myself), take a close look at this page. Why it doesn't highlight that the UUID has been changed over versions, isn't clear to me, but it would explain why things fail when (or just prior to) calling QueryInterface on nsIHttpChannelInternal.
With the new(er) UUID, I'm getting better results. As I mentioned in an update to the question, I've posted this on bugzilla.mozilla.org, I'm curious if and which response I will get there.