>> Ben Lower: Okay. So my name is... ZapFest. And if this is your first time joining...

advertisement
>> Ben Lower: Okay. So my name is Ben Lower, and I am the sponsor for
ZapFest. And if this is your first time joining us, welcome, and if it's not your first
time, welcome back. And ZapFest is a series of events where we're trying to
connect developers or people interested in developing apps for Windows Phone,
connect them with internal experts who work on the product here at Microsoft.
And before I introduce our speakers tonight, I wanted to mention that tomorrow
I'm actually going to do a blog post about a contest that we're working with
Core77 on. And Core77 focuses on design. So they have a huge audience of
designers, and they've been running the contest since last month to get great
design ideas from designers in the area of productivity apps for Windows Phone.
And we're expecting that a lot of these designers are going to have a need for
development help. So I hear from developers all the time, hey, I don't know what
my next idea is. If your coding is like mine, I can build an interface that only a
developer could love. So I always love to work with a great designer.
And so I've posted the link here at the top, which is just the overall contest that's
going right now if you're interested, but the link at the bottom for developers is -basically it's a self-referral page, and so if you're interested to put your hat in the
ring and say, hey, I'm interested to look into helping some of these designers
bring their apps to market, please do that, because we've gotten some designs in
already. I think we've had about 67 submissions, and some of these things are
really amazing. You get great designers and their ideas. Things look really
good. But we need developers to help. So please, if you're interested, check
that out. You can email me if you have more questions.
I also want to make one more plug for something that we started doing recently
with loaner devices. So we have devices here tonight if you want to see the new
Samsung Focus S, the HTC Radar or the Samsung Focus Flash. We've got
those if you want to check them out. But we also make devices available on a
loaner basis if you need to test your app with some new hardware that you don't
normally have access to. So if you want a loaner device, email ZapFest-Devices
and we'll get you squared away.
So without further ado, it's my pleasure to introduce Stefan Wick, who is a
principal test lead working on Windows Phone, and I'm really looking forward to
his presentation.
So, Stefan?
>> Stefan Wick: Thank you very much, Ben. I appreciate the invitation.
Hello, everybody. My name is Stefan Wick. I'm a test manager on the developer
platform team in the Windows Phone group, and I'm responsible for the
Silverlight platform in Windows Phone.
I want to talk a little bit about testing and optimizing your Windows Phone manual
applications today and hopefully I can give you and handful of useful tips that you
can use to improve and optimize your apps that you have today or that you might
write in the future.
A little bit of motivation up front here. I'll give you a second to read the slide.
So I don't think actually, you know, buying a lottery ticket increases your odds of
success, but instead I think really understanding the platform and optimizing your
app to take best possible advantage of all the capabilities and testing your app to
find your bugs and to make your app more robust and more performant is
actually the key to success here.
I'm not going to talk about money, but if you want your app to be in the top 100 or
whatever of the marketplace, I think the key differentiator between your RSS
reading and the next person's RSS reader is probably how performant is it, how
well it is designed, how responsive is it to your input and so on and so forth. And
those are all things that you can control, and with a few little things you can easily
do -- you can make your app look better than the next person's and maybe that
gets you from a three-star rating to a four-star or five-star rating. And those are
the apps that will bubble to the top.
So I also have a little bit of a hidden agenda here today. So I guess it's not that
hidden now that I talked about it. But I want also to motivate you guys to actually
upgrade your apps to 7.1. So how many of you already have written some 7.0
apps and published them in the marketplace? Quite a few.
How many of you have upgraded them to 7.1 yet? Not too many.
So I think it's a good goal for me to maybe motivate the rest of you as well to do
that step. And along those lines there was actually an interesting thread just
today on the Windows Phone developer discussion AS, if some of you follow
that. There were a few people who have added some instrumentation to their
app to find out how many of their users are already running Mango versus 7.0,
and they actually found that the vast majority already runs on Mango. So that is
another good data point that hopefully motivates people to actually put your effort
into building 7.1 apps, because that's where the majority of the customers are
and that's also where you get the best performance and best appearance for
your apps.
So let's take a quick look at the buckets of manage improvements, and I'm not
actually going to talk about all the features that we added in Mango, just taking a
look here real quick at the improvements in the existing features from 7.0.
We can put the improvements in three big buckets. There was one, the general
improvements that are baked in the platform. Every app will take advantage of
those. If you have a 7.0 app, you run it on Mango, you will get better scrolling,
you will get a lower memory for print and you will get better video playback, just
to name a few.
But then there is a second bucket where we improve the default behavior for 7.1
apps, but we didn't change it for 7.0 apps. That is because of our high
compatibility bar that we have for 7.0 apps, and some of the changes we
introduced would have broken existing apps, so we couldn't just make a default
for everyone.
Examples for that are fast-app switching and also our new XAML parser.
Everyone know what fast-and switching is? Do we need a demo or ->> Ben Lower: I think an example would be good.
>> Stefan Wick: Okay. Just to -- let me just launch a few apps here real quick
and then I navigate out of them forward.
So now I have launched a few apps, and when I go into my app-switching mode
by holding down the back button, I get into this screen where I can see all my
apps that are running on the back stack and I can really quickly switch between
those different apps.
So this is a very seamless experience for the user. You don't have to weight for
the app to relaunch or deserialize the data. Everything is right there where you
left it off and you can continue. So this is something that your app gets for free
just by recompiling it as 7.1. If your app is still a 7.0 app, you don't get that
feature even when you run on Mango.
So then there's the third bucket which I call here like new optional behaviors in
7.1. Those are behaviors that, you know, your app may or may not want, and so
you have the option to use an API or property to opt into the new behavior and
then take advantage of it.
And this is what I want to focus on for the most part of the talk to give a couple of
those examples of where you can do a little bit of work in your app and get some
really good improvements on 7.1.
So first thing I want to talk about is a very simple feature, actually, and very easy
to enable in your application. The background here is in 7.0 we only supported
16-bit per pixel rendering in third-party applications. Even though most devices
actually were able to display 32 bits per pixel, you couldn't take advantage of it.
And Mango we now enable that, but we didn't make it the default, again, to
maintain compatibility and consistency with existing experience. But in your app
you can easily opt into it. And let me just pick a random Visual Studio project
here.
The way to do this is very easy. You just go to your WM app manifest dot xml in
your project and it's just one property that you set here. And it's conveniently
named bits per pixel, and so by default you don't have this in your manifest. So
the default is empty, which is 16, but if you set this property, you opt your entire
app into this new rendering mode.
And that gives you some pretty significant advantages or improved rendering,
specifically for scenarios where you render photos or you have designs that take
advantage of gradients. So if any one of you ever tried gradients in your 7.0
application, you probably noticed some pretty ugly banding and then you
probably stopped using that.
>> Ben Lower: Is it okay to raise questions as we go?
>> Stefan Wick: Yes. Please.
>> Ben Lower: So a question from the folks online. What's the performance hit,
if any, for opting in?
>> Stefan Wick: Yeah, the performance is really low on this one. So I would still
recommend if you opt in that you retest your application in both scenarios and
make sure there's no noticeable difference. But there should be only very
minimal difference in terms memory usage and no difference in interactivity.
Yes, please?
>>: [inaudible]
>> Stefan Wick: Really good question.
>> Ben Lower: Can you repeat that question?
>> Stefan Wick: The question was does it apply to X and A applications as well.
The answer is yes.
So -- oh, one important thing to point out here is for video applications, we
actually automatically opt you in on Mango. So this was a decision we made
because we thought most video applications are kind of full-screen anyway, and
there is no other UI that would be affected, and are the video playback in 32 bits
per pixel looks so much better and so much more natural that we thought that it's
the right call for video in general.
Yes, please?
>>: So the splash screen, sometimes you run -- like if there are gradients, there
are shifts. Will this help with that as well?
>> Stefan Wick: No, the splash screen not affected by this setting.
>>: Because in my experience when I designed and planned and put it in
Windows Phone, sometimes there is a banding effect.
>> Stefan Wick: Yeah. So the question was about the splash screen. Splash
screen is not affected by this. So you want to make sure you design your splash
screen that it still looks okay in the 16-bit mode.
Okay. So I do have a quick demo for this as well, but in case the projector
doesn't show the difference, I also put it on the slide here. And I hope you can
actually see this.
The banding on the left and no banding on the right -- can you see these stripes
over here? So this is a typical effect that you get in 16 bits per pixel. So on the
left is the 7.0 case and on the right is Mango when you opt into the 32 bit per
pixel mode.
It actually is much more visible if you look at it on the device itself, and, of course,
it depends on you're seeing the more gradients you have and, you know, if you
have a photo with like a sky, you see it very noticeably.
Maybe if we can switch to the projector, we can show it here. So I have the
same app in 16 bits per pixel and there bits per pixel. So does it show? Yeah.
So you can tell which one is which, I think, and when you animate it actually is
even more noticeable.
And if you look at the device directly, you can actually see it much more visibly.
So it's something you might want to try on your app. If you have lots of colorful
things, it might help you get a significant better rendering experience.
Yes?
>>: Does that apply to the live tile at all or is that all handled differently?
>> Stefan Wick: The question is does it apply to the live tile? No, it does not.
So live tile is not affected by this setting.
>>: Does that mean the live tile is, by default, 32 or is it ->> Stefan Wick: No, I think live tile is, by default, 16.
So the next thing that is kind of new in -- let's go back to PowerPoint.
The next thing that is new in 7.1 that you can opt in very easily is the background
image decoding. So we did a lot of work in 7.1 to improve the list scrolling
scenarios, and specifically when you have lots of images in your list, you may
have seen problems in terms of responsiveness and stuttering in your list. So
one of the things that we have enabled in Mango is to move your image decoding
on a background thread, because in 7.0 the image decoding always happened
on the UI thread, which then interferes with your input and your interactivity on
your list or whatever piece of UI you have.
With a simple option by setting creating options to background creation you can
move that to a background thread and achieve a much natural experience for the
user.
Of course, in the end the time it takes to decode an image doesn't change, but by
moving it to a background thread, the perceived user experience is much better.
Let me try to demonstrate this here on the projector.
So I have two identical apps. One has the background creation flags set
[inaudible], the other didn't, and so what I want to do first is just kind of scroll as
fast as I can down here. So first I can see -- so both devices are running Mango,
and so the responsiveness is kind of nice. The scrolling seems okay and natural.
But now when I try to scroll as fast as I can, you notice on the left one there's a
lot of these completely blank items where the UI just completely disappears for a
few seconds even. And I don't have this in the right application because here my
image decoding happens on the background thread.
So the images, you know, they still take a while and then they pop in, but my text
and the rest of my list items is still there as the user will expect.
And another thing to demo here this improvement nicely is if I change the images
in my list by clicking this button here and then interact, you see the one on the
right responded immediately while the one on the left was actually stuck for a
good number of seconds before it even started to scroll. So this is a big thing
that you can enable in your app with just one property setting.
Yes, please?
>>: This property is available for creating the programatically, right?
>> Stefan Wick: You can do it programatically as well, yeah. If you create a bit
map image instance, you can set the create options property at that point.
>>: Are you sharing this PowerPoint?
>> Stefan Wick: Yeah, I will be sharing the PowerPoint and also the demo
applications. Yeah.
And this one here I think is actually up on our [inaudible] block already for a
while, so you can also get it from there.
So that was the second one. Really, really simple thing that can give you big
improvements in your app if you're dealing with images a lot. If you just have one
image, then you may not care. But especially for lists, it's a really good
improvement.
So we've seen the demo. The next one -- I actually want to talk about that quite
a bit -- is memory testing and debugging and optimizing. If you're new to
Windows Phone development and you come kind of from a desktop background,
you may not think about 10 megabytes here, 100 megabytes there, you know,
who cares, right? But on the phone, memory is one of the resources that are
very, very limited and precious. So generally we want apps to stay under 90
megabytes in the certification process.
So you do want to be careful how much you allocate in your app. And, most
importantly, you want to be careful that you're not leaking any memory, because
we've seen apps that pass the certification because -- yeah, they don't exceed 90
megabytes at that point, but when an actual user uses them in the real world for
more than 10 minutes and the app has a leak, then you easily go up to 200 or
more megabytes, and at that point you're likely to crash on the device. And we're
actually see a lot of what's in reports where third-party applications are crashing
with out of memory exceptions. So that is a real problem. And a lot of it has to
do with people's apps are leaking.
There was a question?
>>: You mentioned certification. So how is certification done? Is it, like,
manually people would run the app and measure ->> Stefan Wick: Yeah. So it's a mix of some automated and static analysis and
actual manual testing. So there are humans who are exploring the app, doing
exploratory testing as much as they can. Of course, they don't fully understand
each app so the mileage may vary. But, yeah, humans are testing that.
But then the real world is always different, right? You can consider kind of a
Facebook application and you test it with five friends, everybody may be fine, but
then someone has 50 friends. Now does that mean your memory usage is 10
times as much? Then you might have a problem. So that's something you also
want to consider in your testing. Put it to it the real world test with real world data
in a real world environment.
Also consider monitoring your heavy objects. And it's something I will
demonstrate in a second. Just understanding, you know, the number of
instances of your pages or your user controls on the heap is a very useful metric
during the development and testing of your app to actually identify any leaks your
application.
And it is really a fairly common problem that people have leaks in their app.
Initially people think, oh, it's managed code so how can I leak, right? There's
garbage collection. What can go wrong? But almost all apps that we have
analyzed that have performance problems, we've found that they have some
amounts of leaks in their managed code. And that is something that you can
prevent up front.
Also I wanted to call out, even if you have just a tiny little thing in your UI that has
a problem and leaks like, you know, a single element that is maybe not well
written like a user control and it leaks, this can actually cause or typically does
cause your entire visual tree to leak because in the tree braced framework that
we have, each element has a reference to all the children as well as to all their
parents. So if one guy leaks, it keeps everything else alive, the entire subtree.
And so this can be your page. When you navigate back, your entire page may
be leaked, and if that has a bunch of images and our heavy of data objects, one
navigation is leak 5 megabytes right there.
And last but not least, I want to encourage people to take a look at the new 7.1
profiling tools which include a memory profiler, which I will also show in a second,
and that is a very useful tool to actually track down your bugs. Once you have a
suspicion there is a leak in your app, you can use the profiling tools to actually
get to the bottom of it.
So for demoing memory testing, I'll switch you over here to my emulator. And so
I have a little app here that has a small problem.
So this is just a dummy app. It just has two hyperlinks here. On the top I just
have some diagnostics so you can see what's going on here. And so all it does
is it navigates to one page which shows a photo, and I can navigate to a second
page which shows a different photo.
These are pretty large photos. I picked large ones so that you can easily see the
memory problem.
So let's monitor what kind of -- what our usage is. So we're already up to 16
megabytes here, so let me repeat this cycle here a few types. So I'm going to
these pages repeatedly as a user, and so now my memory usage is already up
to 29 megabytes, so this is pretty disturbing because typically, or the way it's
designed, when you navigate back from a page, that page becomes eligible for
garbage collection and it should just disappear at some point.
So it could just be that garbage collection hasn't kicked in yet. That's a
possibility. And actually it looks like that's what happened here. So I can force
garbage collection and my memory goes down, but it's still not where it started,
where it should be, and my little heap tracking indicator here shows that there
seems to be a problem with the second page class because all those three
instances are still there while the first page instances have been collected.
Let me kind of try to validate this a little more. When I repeat this with first page,
it always goes nicely back to zero, and when I do this with second page, it just
keeps going. And now I'm at 5 and my memory usage is at 22 megabytes and
I'm really on a bad trajectory here and I will soon crash if I keep doing that.
So let's first take a quick look how I'm tracking the data here in my testing and
then we'll take a look how we can use the memory profiler to get to the bottom of
what the bug might be and how we can fix it.
So what I'm doing here to display my text in my little diagnostics helper, there are
properties that we expose as APIs that you can just query to get the current and
the peak memory usage. So that's pretty straightforward.
To get the number of instances for my pages, I have exposed a little static on
these classes, a public static that just counts the instances. So whenever I hit
my constructer, I increment them, and whenever my page gets finalized and
collected by garbage collection, I decrement the counter. So this way I can just
keep track of what's currently active on the heap.
So let's start the performance profiler. Why is this not -- oh, I'm on the -- okay.
Interesting.
And we want the memory profiling here at the moment. There's also execution
profiling, but we'll focus on memory profiling, obviously, for this problem.
So this will now launch my application with the profiler attached so we can collect
detailed data about the allocations and who's holding onto what.
>>: [inaudible]
>> Stefan Wick: Oh, yeah. That's -- good catch. It was actually running over
here.
Okay. I have to wait a second until it's done analyzing this. So let's close this
out, and we want to run on emulator.
So this should start in a second. Okay. Here we go.
So let me just go through this iteration real quick -- oops. I think that was not
what I intended. Let me try this again. I don't know why it just crashed.
Okay. Let's try again.
Okay. So we go to second page. Let's do it -- okay. I don't know why it exits on
me. But maybe that's actually enough data to look at this.
So I have all my memory data here. It tells me excessive allocations. Okay.
That's good. I can probably figure that myself.
And I get a heap summary. So there's lots of data here. I will not be able to get
into all the details here, so that's probably one talk of its own. But I want to take
a look at the retained allocations at the end here because that's kind of what's left
over.
And so from our own initial testing, we already had the suspicion that something
with the second page is not getting collected. So I'm going to sort by type name
here and find my second page type somewhere down here. Phone app, second
page. So here it is.
And I can scroll over here and I see, okay, there are two instances. That's not
what I expect. So I can look at these two instances. And so let's take a look at
the first one that was created. So that's definitely one that should not exist
anymore, and so now I'm missing of the data here. So give me one more try.
Actually, let me restart the emulator. Sorry about that. Give me just one second
restarting the project and then do the profiling from scratch.
So what I want to get to is in the data that the profiler spits out for my instances
for second page, it can tell me what are all the objects that hold references to
that page, and by looking at that data, I can get a really, really good hint of where
is the problem, what is the culprit in my code that is causing my page to not be
eligible for garbage collection, because obviously there's something in the app
that still has a reference on it, so the garbage collector thinks it cannot collect
that instance yet.
Okay. So hopefully we're back in business now.
Okay. So this looks much better. Now I can stop my profiler and hopefully I
have the full data set now.
Okay. This looks pretty good. So let's analyze this and look at our heap again.
Allocations at end and we're scrolling down to [inaudible] and we're scrolling
down to the second page instances. Here we go.
Okay. So now I can go ahead, I can either look at the object graph of what it is
referencing or the GC roots. Those are the roots that prevent my object from
being garbage collected.
So if I follow this down here, I get a really good indicator here. So there appears
to be a context menu that is holding reference to my page at the end of the day.
Actually, it's holding reference to the image, and since the image has a pointer to
its parent and the parent at some point is the page, everything in that page
including the image and the context menu and everything else is just leaked.
So some of you may be familiar with that bug because that's actually a bug that
was present in the tool kit. So for those of you using the Silverlight tool kit, the
context menu is a control that ships on code packs with the tool kit, and it had
this bug.
So the fix for me now would be very easy here. I'm just using a very old version
of the tool kit, and if I just upgrade to the current August version, the bug will be
gone. So that's the fix here.
But another possible fix would be -- if you didn't have that fix available, another
possible fix here would be on the navigate event before the page goes away you
can basically just unhook the context menu from its element and this way it
doesn't cause any harm. The context menu may still leak, but everything else
will be collected. So that is kind of a work-around you can do. If you consume a
third-party control that has a leak on your page, then you can just throw it out of
the tree before the tree goes away. This way everything else becomes eligible
for garbage collection.
Yes, please?
>>: So question: Would it be a bad idea to call the garbage collector explicitly in
your app?
>> Stefan Wick: The question was is it a bad idea to call garbage collection in
your app. Not necessarily. So there are different camps on this one. It depends
on your app and how do you it. You don't want to call it all the time because that
also gives you a sense performance hit, right? But if you kind of know what
you're doing, then it's okay. But generally you should not have to do it. So the
garbage collector should be smart enough to do the right thing.
>>: There some general guidelines around when you might want to consider ->> Stefan Wick: Well, if you have a situation where you know the user isn't
interacting with the app right now and there are cycles available, you might as
well kick it off because it's not causing any harm at that point and it might lower
your memory pressure. So that would be okay. But if the user is interacting with
the app and scrolling and sliding and flicking, at that point you don't want to call it
because then the user will see stuttering and things will just stop for a moment.
Yeah, generally you shouldn't have to do it. And obviously calling GC explicitly
doesn't help with situations like this where something holds a reference to your
objects because they are just not eligible for garbage collection.
Okay. So, actually, let me go back to my little app here real quick.
So obviously this is kind of a contrived sample and demo, and you don't want all
this kind of UI a top of your application to always monitor, you know, all the
different pages that you have in your app or other objects and memory usage
and have a GC button and whatnot.
So I want to just use this opportunity to kind of highlight another Mango feature
that we have added, and that comes in handy here because that enables me
actually to move this piece of UI out of my phone and I can just throw it into my -into an application running on my Windows desktop and monitor things that way.
Let me just show that real quick.
I'm going back to my project here. So I have a little controller app here. This is
just a standard WPF application that I put, and I just put the same UI in there
pretty much. And so now when I launch my phone application -- now it's not
working. It should actually connect to this one. Maybe it's because I'm running
also the device here.
Okay. Let me try from the device.
Okay. It looks like the demo gods are not with me today. It worked before in my
office.
Anyway, the general idea is that I just put my monitoring UI up here in a Windows
application that I'm running on my desktop and I'm using sockets to connect to
my phone or to my emulator, and this way I can actually operate the app and
monitor anything that's going on there. So ->>: Does the feature have a name? You said it was a new feature. Are you
referring to sockets being the new feature?
>> Stefan Wick: Yeah, sockets being the new feature, right? So I'm just using
that to enable the connection between my desktop app and my phone application
here.
Okay. I'm not going to debug this here. So maybe I'll get it working after the
presentation, but hopefully you kind of got the idea.
So that is something that can come in pretty handy for debugging purposes, or,
you know, if you just want to do some logging and things like that, this can be a
useful feature you can add just during the development process.
So next topic, very important, kind of end-to-end testing for your application. I
think most of you are engineers, so I don't need to explain the importance of
testing and the value of testing. But, still, I want to highlight maybe some of the
obvious benefits here and then show a little bit of a demo of how this can look
like in an application on a phone.
So if you're just developing kind of a 99-cent iFart clone application, you know,
your end-to-end test is probably not that complex and you can probably get away
with just kind of ad hoc testing it and then you're done. But if you have anything
more complex than that, you do want to have some structured way of actually
going through your app, hitting all your different code paths, all your different
pages and controls and your business launching and verify along the way that
your state of your application is correct and the visuals look right and that you
don't hit any crashes or memory problems.
So in simple cases, probably some sort of manual script will suffice there, but if
you have a slightly more professional app or you make more long-term
investments in your app, you might want to consider having some sort of
automated script for that. So that's something where we don't have really any
tooling support today to help you with that, but, still, I think it's something that I
would encourage you to consider because using the public API servers that we
have and using some standard techniques around mocking and inversion of
control, you can actually very easily build these kind of scripts that will exercise
your application and verify a lot of things along the way.
One important thing for that to work is actually that you design and develop your
application with testing in mind from the beginning. If you kind of treat it as an
afterthought, then it might already be too late and you will not have an easy time
to actually do it.
Yes, please?
>>: [inaudible]
>> Stefan Wick: Yeah. So there aren't -- as I said, there aren't many -- there is
no real tooling support available for this, and also guidance is very limited today.
The parents and practices guys actually put out some guidance along those
lines. I think last month they put something up on CodePlex with some guidance.
The only other sort of framework that is available is for unit testing. So this is
[inaudible] unit test framework, but that won't really help you much with the
end-to-end testing because it's fairly limited in terms what you can actually
execute.
But kind of to show more conceptually how this can look like and what you can
do, I have a little demo here in my emulator. So let me deploy that. So I've
picked the fuel tracker application, which is -- some of you might have come
across this one on MSDN. It's an application -- or it's a sample on MSDN. It
comes with a fairly nice article about building an application from start to finish,
and that is basically the application that they're building in that article.
And so what I've done is -- so I took that application and modified it a little bit to
make it more testable and then I wrote some tests against it.
So I have here the fuel tracker application. Let me just demonstrate it real quick,
how it works. So you can enter your car name, choose a picture for it. I don't
have a car picture here, so whatever. And so now that you've set up your initial
automater and now that you've saved your car, you can track your fuel
consumption.
So there's some things to review. And so whenever you fill up your car, you can
go ahead and enter your new automater and enter how many gallons you filled
up with and then the price you paid for your gallon. And then you can get your
average miles per gallon, you see your history, and so on and so forth.
So a very simple application, and nothing exciting, but it's still more than Hello
World and it's pretty close to something that you could consider a real
application.
So now the application is here in this project. So what I've done here in my
second project, this produces a second zap which basically is a super set of my
fuel tracker zap with my test code added. So this way I keep it nice and separate
so my shipping zap doesn't have any of my test code and I have a secondary zap
that uses this exact same code that I'm shipping plus my tests. And then these
are just kind of [inaudible] libraries.
So now when I launch my test application, I get to a test manual or test page first,
and from here I can launch different tests against my application. I could just do
a manual test which will just launch my application and I can go through it
manually. This actually is pretty useful -- can be useful because I also have
some additional sort of features here that I can enable for my testing. I can
enable frame rate counter, memory counter, and some other debug flags that
help me identify problems or issues in my application.
I can also turn leak detection on, which is something similar to what I've shown in
the previous demo where I just kind of count the instances of my interesting
objects, and whenever I see something didn't go away after page navigation, I
basically fail my test.
So with these flags the manual test can actually be fairly interesting as well.
So I also have unit tests here, and this is using the [inaudible] unit test framework
that you may already be familiar with. So this is just standard .NET stuff for unit
testing. This is nothing different from what you probably are already familiar with.
But then the end-to-end test, that's probably the more interesting piece here. So
in the end-to-end test, that's basically where I have my automated script that go
through the entire procedure of my -- of using my application and actually kind of
hits every page and every control and does something useful with it.
Let me actually show the test finding a bug. So if I run my end-to-end test now
with leak detection on -- let me run it again -- so now at the end of my test it
actually flags it that, hey, there's a leak, and so I can see, oh, my fill up page
didn't get collected at the end so there's probably a bug. So my test failed.
Let me go fix this bug real quick.
So I go to my views here, fill up page, and of course I already know the bug so
it's easy to fix. Otherwise, I would use the profiler to tell me where the bug is.
But the bug here is kind of a standard .NET bug that people easily do. I
subscribe to a static event here for touch input, but I forgot to unsubscribe in the
end. So that one keeps my page alive. So the fix -- luckily someone already
coded it up here -- the fix is before I navigate away from my page, I go and
unsubscribed from my event so this way I don't leak my page.
So let me rebuild the app and deploy.
And so now I can run it again. Let's not forget to turn on the leak detection, run
my end-to-end test.
And now the test passed.
So the system works -Yes, please?
>>: [inaudible]
>> Stefan Wick: Yeah, sure. I'll definitely share it. And I can also -- I don't know
how much time -- we have eight minutes left, so I can also go a little bit into the
code right here.
And so then my last option that I have here is just a stress test. It's very simple.
What I'm doing here is just I run my end-to-end test just in a loop. That's
basically all I have. But you can certainly get more creative here and more crazy
and do more stressful things.
So maybe we can pick a small piece of the code here and look at that.
So one thing that's kind of traditionally challenging if you're trying to test your
phone application is some of the launches and choosers that you interact with.
For example, in this application I have the photo chooser, so I can pick my -- the
photo for my car from the library.
So what happens when you run your app, you launch the photo chooser and that
actually puts your app in the background and the chooser is in the foreground
and then you're no longer in control, right? And so that's kind of challenging for
tests.
What I've done here is I've built a little extension for tasks, and so in this case I
only implemented it for the photo chooser task, but the same would apply to
others as well.
So the way the photo chooser task works in the platform is it just has a show
method which launches it and then it has a completed event that fires when it's
done, and when you get control back then it gives you the pointer to the picture
that the user has picked.
So what I'm doing here, I'm adding basically an overload to the show method that
also takes a delegate to completed method. And so now I just have two different
execution modes. In case the user uses the application, I just do the a lot of
same thing I would normally do. I'd just call show on my task and then when the
completed event fires, I erase the other completed event that my application is
subscribed to and everything just works exactly like it does in the normal world.
But if I'm not in the user execution mode, meaning I'm in the test automation
mode, then I go down here, and what I'm doing here is I just skip launching the
task and instead create the result myself and then launch the completed event
with that result. So this way I have kind of mocked the photo chooser task and
my automation can just continue. Because the goal for me is not to test that the
chooser works, the goal for me is to test that the application works, and I just
assume the chooser works correctly because that's actually my job and my day
job.
So that is one example and similar things you can do with, you know, the other
parts of the platform as well using mocking or inversion of control type of
techniques.
Okay. I think actually that's -- yeah, that's all I have. So, yeah, thank you very
much.
[applause]
>> Stefan Wick: And, yeah, I think we still have a bunch of time for questions.
>>: Is there any way to see what it happening on the [inaudible]? I have some
problems in my app where just something seems to be [inaudible] and I don't
know what, and I'm not sure how to find out what exactly is happening
[inaudible].
>> Stefan Wick: Okay. The question was is there any way to find out what's
happening on the UI thread.
And so the 7.1 profiling tools actually give you some good indication of what's
going on on the different threads. So if you run your app in the sort of execution
profiler, you get a breakdown of what's happening on what thread.
So I would recommend trying that, see if you can see anything obvious. There's
a lot of data that you get, so it might take a while to kind of actually wrap your
mind around that and trace it back to, like, what you're doing in your code that
triggers it, but it should show you what's happening on the UI thread.
>> Ben Lower: So some of the folks online are asking if you would do some
more general coverage of the profiler. Since we have some time, they were
wondering if you can just show more, because they're really intrigued by what
they saw so far.
>> Stefan Wick: Okay. For the memory profiler or the execution profiler?
>> Ben Lower: They're replying right now. Memory.
>> Stefan Wick: Okay. Actually, yeah, why don't we try to debug the other
problem here that I just fixed in the fuel tracker application using the memory
profiler. So let me put the bug back in here and let's see what the profiler has to
say about this one.
Okay. So now I have to get all the way to the fill-up page to see the bug. And so
the fill-up -- why is it invalid?
>>: [inaudible]
>> Stefan Wick: All right. There you go. Wow [laughter].
So now -- yeah, now that I got back from the fill-up page, I should have leaked it.
So let me stop the profiler and see if the profiler can confirm that and then also
hopefully it will point me to the problem. So there's a lot of data now since it took
a while to get there.
Let's still look at everything here. So my heap summary, allocations at
[inaudible]. Oh, yeah. There's actually probably something useful to call out
here. I should have actually said that before.
So in this view, intuitively, when you look at the types here, you would probably
intuitively go by -- sort by size and just look for what are the biggest things that
are not collected yet. But in most cases actually that gives you kind of a false -puts you in the false direction because if you look at the sizes here, they're not
actually that large. This is in bytes, so we're just -- these are just like 160K or
something like that, so that's not really that important. It is important, but not that
significant.
It's important to understand that many objects in the framework have both a
managed memory footprint as well as a native memory footprint. Like when you
have an image controller with a large image, the manage image control, that's
just a couple of bytes. That is nothing really significant. But the decoded image
which we keep in native memory on our side of the framework, that's where all
the megabytes are.
So in that case -- with that in mind, the actual managed bytes that you see in the
profiler don't necessarily tell you where the big chunks are that are being leaked.
So it is more important to look for the instances of objects that should not be
there, like the several-page instances that we saw in the other example.
But here let's look for our fill-up page -- actually, I need to take a look at the name
space here so I can actually find it. It's fuel tracker reviews.
Fuel tracker reviews, fill-up. I think that's probably it.
No, fill-up page. This one.
So this is the one that should not be there anymore because I've already
navigated back from it, so that is all leak. So let's see what the GC root says
here.
So there's a lot of data. It's a slightly more complex page. So this part obviously
was not rehearsed at all, so ->> Ben Lower: There's a question as you're clicking through. They're running
the profiler on their machine and they don't see the bottom window so they're
wondering if you did something special to get the bottom half of the analysis
page to have ->> Stefan Wick: No. That should just show up. Maybe they need to rearrange
their windows a little bit. That should be up by default. So I didn't do anything
here.
>>: I thought you had to select like a section of the ->> Stefan Wick: Oh, that's right, yeah. You first want to select a section of ->>: You need to select something on the timeline?
>> Stefan Wick: Yeah. Exactly.
>>: Okay.
>> Stefan Wick: Yeah. So there's -- since I'm looking at the root of the page and
the page contains a bunch of stuff, that's why we're seeing tons of roots here. So
that is a little bit hard to [inaudible] here right now and pinpoint the exact issue
right there. So that takes a little more, I think, time and also practice to kind of
know how to get through the data here and actually pinpoint the thing in code.
But the good thing -- oh, here we are [laughter]. Sorry. At the bottom.
So, anyway, it is something that, yeah, you do want to practice a little bit. I think
it's not the most, you know, intuitive thing. At the end of the day, the data is there
and can help you pinpoint your leaks, but, yeah, I would recommend practicing
that a little bit maybe with some sort of contrived leaks that you sort of introduce
yourself and then make sure you can see them and then with that knowledge you
can expand later to find other leaks as well.
>>: So I missed -- as you were scrolling through the list, I missed two thing.
What were you looking for when you went through the list, and then when you
got to the bottom, how did you know that was the thing you were looking for?
>> Stefan Wick: Yeah. So actually there's more data than this -- actually, on my
laptop, in this resolution, it's hard to kind of see everything together. There's also
the root kind that you may want to take into account, and so we're looking at the
end of these -- of these various roots that go all the way down. And, again, it's
kind of hard to see everything together here.
It also is a little bit kind of knowing some of the normal patterns that you see on,
like, every page that is just alive by default versus things that shouldn't be there if
they are collected. So I think that goes back to what I said, that you might want
to practice that a little bit. And looking at a page is probably kind of the more
complex scenario here because there is a lot of things that have references to
the page.
So you might actually want to pick a simpler example where you just have one
control on your page or one panel on your page that contains a leaking control
and then inspect what's going on on that panel. And so look at the panel, you
know, without that control and with that control and see what the differences are.
Okay. Other questions?
Yes, please?
>>: Would you say that the majority of leaks happen because of unsubscribed
events? Is that something ->> Stefan Wick: That is definitely a big class of problems, yeah. So that could
easily be the largest class, yeah.
>>: Is there a way to kind of separate the event handlers in that place or ->> Stefan Wick: No, issues that not. Yeah, so that's definitely one, but also just
another common pattern is where you have maybe some static collections, and
so a control that adds itself to a static collection and then later, when the UI goes
away, the control is still referenced by that static collection. That's another
pattern that's quite -- that you see quite often.
And I think in practice, in many cases you will actually find it's not necessarily
your leak that you're hitting in your application. If you're using a lot of, like,
third-party MVVM frameworks or, you know, control libraries, you actually inherit
all of their bugs as well. And so that's another thing you will likely encounter.
Especially in the MVVM world we've seen a lot of these problems as well.
>> Ben Lower: Another question from online. Do you have any tips around
using a dispatcher versus a background worker?
>> Stefan Wick: Well, both are fundamentally different, right? The background
worker spins up a new thread so you're executing on a whole different thread
versus the dispatcher just basically puts the work into a queue on the UI thread
and then executes it when the queue is ready to process that. So that's the main
difference.
So if you want stuff to be off the UI thread, then, yeah, background worker or any
other thread is the right choice. If you want or need your work to be executed on
the UI thread but you don't want to run it right this moment but only later when
the UI is ready, then this picture would be the right choice.
Okay. Any more questions online?
>> Ben Lower: That's it.
>> Stefan Wick: Okay. Cool. All right. Thanks again.
[applause]
Download