1. Po raz pierwszy odwiedzasz EDU. LEARN

    Odwiedzasz EDU.LEARN

    Najlepszym sposobem na naukę języka jest jego używanie. W EDU.LEARN znajdziesz interesujące teksty i videa, które dadzą Ci taką właśnie możliwość. Nie przejmuj się - nasze filmiki mają napisy, dzięki którym lepiej je zrozumiesz. Dodatkowo, po kliknięciu na każde słówko, otrzymasz jego tłumaczenie oraz prawidłową wymowę.

    Nie, dziękuję
  2. Mini lekcje

    Podczas nauki języka bardzo ważny jest kontekst. Zdjęcia, przykłady użycia, dialogi, nagrania dźwiękowe - wszystko to pomaga Ci zrozumieć i zapamiętać nowe słowa i wyrażenia. Dlatego stworzyliśmy Mini lekcje. Są to krótkie lekcje, zawierające kontekstowe slajdy, które zwiększą efektywność Twojej nauki. Są cztery typy Mini lekcji - Gramatyka, Dialogi, Słówka i Obrazki.

    Dalej
  3. Wideo

    Ćwicz język obcy oglądając ciekawe filmiki. Wybierz temat, który Cię interesuje oraz poziom trudności, a następnie kliknij na filmik. Nie martw się, obok każdego z nich są napisy. A może wcale nie będą Ci one potrzebne? Spróbuj!

    Dalej
  4. Teksty

    Czytaj ciekawe artykuły, z których nauczysz się nowych słówek i dowiesz więcej o rzeczach, które Cię interesują. Podobnie jak z filmikami, możesz wybrać temat oraz poziom trudności, a następnie kliknąć na wybrany artykuł. Nasz interaktywny słownik pomoże Ci zrozumieć nawet trudne teksty, a kontekst ułatwi zapamiętanie słówek. Dodatkowo, każdy artykuł może być przeczytany na głos przez wirtualnego lektora, dzięki czemu ćwiczysz słuchanie i wymowę!

    Dalej
  5. Słowa

    Tutaj możesz znaleźć swoją listę "Moje słówka", czyli funkcję wyszukiwania słówek - a wkrótce także słownik tematyczny. Do listy "Moje słówka" możesz dodawać słowa z sekcji Videa i Teksty. Każde z słówek dodanych do listy możesz powtórzyć później w jednym z naszych ćwiczeń. Dodatkowo, zawsze możesz iść do swojej listy i sprawdzić znaczenie, wymowę oraz użycie słówka w zdaniu. Użyj naszej wyszukiwarki słówek w części "Słownictwo", aby znaleźć słowa w naszej bazie.

    Dalej
  6. Lista tekstów

    Ta lista tekstów pojawia się po kliknięciu na "Teksty". Wybierz poziom trudności oraz temat, a następnie artykuł, który Cię interesuje. Kiedy już zostaniesz do niego przekierowany, kliknij na "Play", jeśli chcesz, aby został on odczytany przez wirtualnego lektora. W ten sposób ćwiczysz umiejętność słuchania. Niektóre z tekstów są szczególnie interesujące - mają one odznakę w prawym górnym rogu. Koniecznie je przeczytaj!

    Dalej
  7. Lista Video

    Ta lista filmików pojawia się po kliknięciu na "Video". Podobnie jak w przypadku Tekstów, najpierw wybierz temat, który Cię interesuje oraz poziom trudności, a następnie kliknij na wybrane video. Te z odznaką w prawym górnym rogu są szczególnie interesujące - nie przegap ich!

    Dalej
  8. Dziękujemy za skorzystanie z przewodnika!

    Teraz już znasz wszystkie funkcje EDU.LEARN! Przygotowaliśmy do Ciebie wiele artykułów, filmików oraz mini lekcji - na pewno znajdziesz coś, co Cię zainteresuje!

    Teraz zapraszamy Cię do zarejestrowania się i odkrycia wszystkich możliwości portalu.

    Dziękuję, wrócę później
  9. Lista Pomocy

    Potrzebujesz z czymś pomocy? Sprawdź naszą listę poniżej:
    Nie, dziękuję

Już 62 515 użytkowników uczy się języków obcych z Edustation.

Możesz zarejestrować się już dziś i odebrać bonus w postaci 10 monet.

Jeżeli chcesz się dowiedzieć więcej o naszym portalu - kliknij tutaj

Jeszcze nie teraz

lub

Poziom:

Wszystkie

Nie masz konta?

Google I/O 2010 - Writing zippy Android apps


Poziom:

Temat: Media

>>Brad Fitzpatrick: All right. Hello, hello, hello.
All right. This is a talk on writing zippy Android apps.
I am Brad Fitzpatrick, and I will talk about writing zippy Android apps.
There's the obligatory live Wave that we can get to at the end for questions, or if people
have questions at any time, if mics are set up, just blurt something out and I will try
to repeat. This is basically the outline of the talk.
I will basically say who I am and why I care about writing zippy Android apps. We can talk
about ANRs and what people call Jank. What is a Janky app which is the original title
of my talk which they changed to zippy. Talk about some new and some old Android SDK
features to use to avoid Jankiness. And some general numbers to know.
And new things that are in Froyo to help you measure the stuff and actually some war stories
from optimizing Froyo. We'll just do Q&A. But notably this is not a talk about the JIT
and this is not a talk about V8 or anything like that. So if you are CPU bound, probably
not the right talk. This is about making responsive apps.
So, me. I am Brad. Prior to Google, I did a bunch of stuff around like social and back-end
infrastructure stuff, around mem cache and file systems and also social stuff. And then
at Google I got sucked into social stuff for a while. So I did this social graph API and
profiles and hubbub. But I really like open source, so now I am back to hacking on Android
now and back to hacking on open source. I joined the Android team because I wanted
it to be faster. I believed in it philosophically, and I wanted Android to succeed because it's
open source, but I didn't want it to fail in the marketplace because it was kind of
janky and nonresponsive. So that's me. So jank. I keep saying "jank." Jank is what
the Chrome team calls, like, stalling the event loop or being, like, not responsive.
So the Chrome team is awesome about this. They only do, like, asynchronous messaging
between processes all the time and they never really do any, like, blocking function call.
Android, historically, not so good about jank. So there's a lot of places, Android kind of
lets you get away with stalling your event loop, your UI thread for way too long, and
the documentation, in my opinion, isn't as strict about telling you what thread things
happen on and what you should defer till later and all of this.
So basically this whole talk is about not being janky, being fast, doing work in the
background, and minimizing the amount of work you do.
A lot of it is about I/O and not doing too much I/O when the Flash is slow and stuff
like that. So this is the ultimate worst case. I'm sure
you have seen this from the docs or seen it in your own app. If you ever seen this, this
is saying something is wrong. This is not a bug in the framework. This is saying some
event is happening in the system, like probably a key press or something and your application
is not responding to it. So this means, like, there's one thread on
-- your main thread is your UI thread that is sitting there waiting to process events.
And if you are doing something else like a network request or a really slow database
query this is comes up. So this is like the ultimate worst case of
jank, this is after five seconds, but even under that users will still be like this app
is kind of weird and unresponsive. So what I just said. So, yeah, it's five seconds
or an activity, and if it's a broadcast receiver, which also by default will run in the same
process as your activity. If you have like a broadcast receiver and a service and activity
they will all share the same main thread. So responding to a broadcast receiver in what
you think is a whole, like, maybe separate isolated environment, or I used to think that,
is actually stalling the same event loop that your activity is on.
So it's really important to either use separate processes or don't do that janky thing on
your main thread instead. So the worst cases are typically network requests
and slow disk operations. So some numbers to put things in perspective.
I think these -- yeah, these numbers were on the Nexus One. Switching back and forth
between two processes, so basically a contact switch speed. This is Dalvik to Dalvik, just
writing a byte along a Unix pipe. It's about .04 milliseconds.
So contact switch speed is about that. Doing a Binder call, which is like the RPC
mechanism on Android, between two processes, about .12 or less.
We had a regression at the end of Eclair and most of Froyo was like twice this slow, and
then we found the bug right at the end of Froyo, or the performance regression, and
got it back down to I think even less than that.
Reading a byte from flash varies. If it's uncached, it's not in any of the kernel caches,
between 5 and 25 milliseconds. Writing, on the other hand, writing is all
over the place Writing is extremely slow. And the next slides will go into how slow
writes are. And as far as what is perceptible to a user,
there are some numbers about like frames of video and what the human perception of a slow
action. Like at Google, we try to make all of our Web properties be under 500 milliseconds
or under 250 milliseconds because that's like what a user really notices.
If it's over that, users drop off and users complain that things are too slow.
So network, on the other hand, network is like ridiculously slow, or it's all over the
board, at least. So never, never do any network requests in your UI thread.
We did an experiment through Froyo somewhere in the middle of the development cycle to
see how slow file systems got, or how slow the nand got as your file system got more
and more full. Because I was always complaining that my phone was incredibly slow, and it
turned out all my disk space was used up. I only had a little bit left, and it turns
out the file system gets incredibly slow as it gets full because it has to garbage collect
and move stuff around on the nand. Because unlike typical hard drives where you just
read and write, those are the two verbs you can do to 512 byte sectors, flash is -- or
nand parts where you run a flash file system on it, there is erase which is really slow,
write which is kind of slow, read which is incredibly fast with no seek time.
So those writes and erases are terrible, and it has to do that more as it shuffles things
around and things get full. If you have a phone and you think it's kind
of slow, the first thing I would recommend is moving a whole bunch of apps to the SD
card, now that you can in Froyo. Your phone will get a whole hell of a lot faster.
Sometimes people say that factory reset makes it faster. It's the same thing; you are deleting
a lot of stuff from your data partition. So, yeah, those are two different phones.
The point is not blaming phones. They all have different speeds butt curve is about
the same. So we will probably be more aggressive past
Froyo, and right now I think we warn at 15% that your data partition is filling up.
We are probably going to go shift that or maybe auto migrate some apps to SD card or
something or warn more aggressively. Anyway, the nutshell is performance varies
a lot. So let's talk about SQLite performance. You
probably should avoid writing in your UI thread because writes can take a long time. Even
a single byte in SQLite likes to write a lot. So if you don't know what SQLite is doing,
don't assume it's magic. It does oftentimes quite literally what you tell it to do.
So there's things like explain and explain query plan that you can see what stuff is
doing. We are going to bounce in and out of demos
here. I wrote ally little app here for this talk.
The code is at code dot -- here code.google.com/zippy-Android. So basically this code does a lot of bad things
that we can introspect it and we can see what tools are available.
Let's start with another one. Here is a button that does nothing. It's literally hooked up
to nothing. Here is a button, that's like 600 milliseconds. So that was noticeable.
I just made the screen go red so you can kind of feel it, but this button actually stalls
the event loop for 600 milliseconds. This one, on the other hand, does it for six
seconds, and I can click around, these buttons aren't doing anything. And it's kind of like
kind of working but kind of not. And after about six seconds here or five seconds, there
it is. So basically, the framework was like, hey,
we have all these events piling up and you are not responding in more than five seconds
so do you want to force quit this? So that was just a sleep in there in that
six-second one. But let's play with databases. Let's make a little database here and meanwhile
we will switch over to a shell while that's still going.
All right. More than one device. Let me unplug this other phone.
All right. So in data/data com.Google/io2010.zippy, which is my little app. Here is my demo database.
So that just made a little -- must have meant this one. Demo dot DB. So I made a little
5 meg database here. So we can pull this. You should always, like, look what's in your
databases and kind of understand their structure. So databases.
So the SDK actually comes with SQLite, the same version that's on the device. So I can
run it host side. You can see that it's in -- I don't know, is that readable? Let me
make it a little bit bigger. All right. So we can go in here and look at
the database that I put on there. Look at the schema. It just made a little
table called foo with the primary key and two properties and a big junk column. And
I just put a junk column in there to put a big thing just to make rows big and to make
it 5 meg. And I put an index on property one but not on property two.
So now we can do fun stuff like -- well, maybe select count from foo where prop 1 equals
-- I just populated prop one and prop two with random numbers between one and 100. So
you can see six rows there have 57 of 500 total.
And here is another column that has, you know, five. About the same.
So if you are ever curious like what those queries are actually doing, you can say explain.
And so property one is the one that's indexed. And you can see this is SQLite's internal
byte code that it steps through. So that's a little hard to parse. But explain
query plan is slightly different. Where you see table foo with index, prop one
index. So that's a little more readable. You can see what's going.
This is a really simple table. Sometimes people have views and views and
views of joins and selects. You probably shouldn't do that. Sqlite isn't
as magical, like I said, as you think it can be. So if you are thinking it's Oracle or
something, don't. Just treat this as simply as you can, if you want good performance,
in my experience. Let's do this. There is no read line here.
Do this on a different index. 57. You will see something different.
Oh. Explain query plan. So here is the one that
just had table foo. So sometimes it's kind of hard to see what's
actually going on, so one tool I found useful and I put on zippy Android on the code project,
is I have this SQLite wrapper where I give it a database. What was this called? Demo.DB,
and I can do a query, let's say this one, and it can tell you -- it basically runs it
under S trace and then parses the S trace output. This is like -- I mean, you can see
the script. It's just like a page of ugly Perl.
You can see that it did 65 reads, and it ended up reading 64K of data. Change this to property
one and you can see it read 3K of data and five reads.
I kind of like when I am testing queries and I'm not quite sure what the SQLite optimizer
is going to do and I want to convince myself it is actually doing something more efficient,
you can do explain query and explain query plan to kind of see that it's different, but
if you really want to be sure how many bytes it's sucking in from flash, I find this to
be helpful. So it's a little tool I have used. You can
grab it. The other fun thing to look at is proc/YAFFS
on the device. The file system that these things are on isn't
like X3 or FATS or something. It's actually called YAFFS, yet another flash file system,
that runs directly on top of the MTD, the memory technology device, the flash part that
actually does raw reads, writes and erases rather than work in terms of sectors.
So on the device you can show cat proc yes. You can see all the steps here, like here
is the system partitions. It's the read-only one that OTAs come to. And here is slash data,
the user data device. And you can see, for instance, the page writes, reads and erasures.
Erases are the really slow operations. Page reads are pretty fast. Writes are not as fast.
So it's also kind of interesting to watch this as your app runs and see how often it's
reading and writing. This is global on the whole system so try to turn off your sync
and turn off other parts of the system you don't need while you watch this. But that
can be a useful tool as well. So maybe we will jump back to SQLite towards
the end, but so far what I wanted to emphasize is that writing to disk is slow. You shouldn't
do it on the main thread. And writing to the network is slow.
And even if your app seems to work, especially in the emulator, the emulator has very different
properties sometimes than the actual device. For instance, the CPU can be a little bit
slower on the emulator depending on your machine, but I/O is incredibly fast on the emulator
compared to real parts. In this app here, one of the other apps in
the Google.code project is this thing I wrote a while ago called I/O Droid. These just read
and write a single byte. These are, like, immediate. Like, writes are one millisecond
in the emulator, which is not true in reality. I will put this on the other device later
and we can see that writes jump all over the place. And SQLite in turn does tons and tons
of writes. So don't trust a millisecond or even 20 milliseconds
because it's going to add up to quite a bit. So, yeah, things will be slow. Some users
will have weaker devices or data partitions that are more and more full.
So pretty much doing any writing or network on your main thread, even though it works
most of the time, is pretty much a recipe for an ANR at some point.
Or, worst case, you work at Google, and I'm just filing lots of bugs on you, which is
basically what I've been doing the last several months.
So tools. Things that you can use to avoid running into
ANRs. The first one is AsyncTask, this has been
in SDK for a while. I forget the exact version, but at least,
like, Cupcake. So the docs say that "enables proper and easy
use of the UI thread." So, basically, you don't have to deal with
handlers and sending messages to handlers. It's a little less efficient than doing it
yourself but it makes it dead easy to react to some event on the UI thread, kick it off
to a background thread that you don't have to create and get the result back on the UI
thread later. So the example that's in the Docs is downloading
some files. So you call this -- you call -- you create
the async tact like this. You instantiate the task once and send it
some things to do which is parameterized by the URL.
So it basically has three template parameters, the type of work to do, which is often void;
the progress update as the thing is proceeding, which is often void, but in this case it's
an integer; and then the final result of the computation, which can also be void, but in
this example, it's long. So the framework when you make an AsyncTask
and instantiate it and call execute on it will run your -- doing background on some
background thread. Then you return some result that you can occasionally
say publish progress as you go. That will also come to the UI thread.
Unprogress update runs on your UI thread when it gets a message from your background thread.
That's optional. You don't have to override that.
And on post execute is your final one. This is also called on the UI thread.
Then you can, like, do other things like mess with the viewer hierarchy, you know, and change
widgets states and stuff like that. So that's pretty much the best practice way
to do something in the background. Sometimes, people -- for instance, this is
code in Froyo's browser. There were a whole bunch of ANRs in the browser
that we fixed. Sometimes it's as simple as fire and forget,
like writing to something that SQLite in the background.
This was handle Websearch request. And the code was pretty much -- it got passed
into content resolver. And then in the background, we updated the
visit history with the content resolver out of the searcher well, and basically, it was
void, void, void. We just, like, in the background kicked off
writing to -- this to SQLite and returned immediately.
So a whole bunch of stuff like this went into Froyo.
Some caveats. It is -- You have to have a handler around.
You have to have a main thread. So don't just go stick this in libraries just
to be safe, like, if you have some library that needs to write to the database.
Oh, like I might be called from the library sometime so I use AsyncTask and if you do
and there's not a looper around and you're not on the main thread, it'll kind of blow
up. I mean, it will blow up.
So don't do that. Also, if you have to do something important
that's, like, the user would expect to be persistent, if you call this from an activity
and the user leaves your activity and your AsyncTask is still running, your system is
like, oh, that's just an activity. That's not important.
I can kill that to reclaim the memory and reinstantiate it later.
So only do this if it's not really important. If you need to do critical stuff, use instead
intent service. The Docs in Eclair were actually really terse,
and no one actually used this. We grew up through our own code base and it
turned out despite this income the SDK, we never really used it ourselves, because one
guy knew what it meant. So Jason filled us in, and now the Docs, you
know, have, like, three paragraphs instead of one, and it says -- so I'm not going to
read all this. But, basically, it's kind of like AsyncTask,
but it starts up this ephemeral service and does the work in the service, and then the
service when it's out of work do shuts down. Because you're now running a service, your
process is, like, special, because you have a service running and the system isn't as
apt to kill you to reclaim memory. So yeah, you have a service running.
You won't be killed. Anyway, it's a real easy way to use the service.
Everyone thinks, like, oh, Android background stuff, it's so hard because it's client/server
and you have to write servers. And by default, like, when you add a service,
it's just some other object that's in your process.
And by default, it's in the same process. They're actually really lightweight.
If you want to make it a separate process, you can.
But.... So here is one use I found of intent service.
Calendar at some point has some broadcast receiver, and it gets something, I don't know
-- somehow it has to dismiss all alarms for some reason.
Maybe you hit the dismiss. I don't really know this code.
But for some reason, they need to dismiss all alarms.
And they have to update this content resolver. And this is probably some SQLite query in
the background behind this content resolver on the content provider side.
So they on handle this intent, and they don't even use the intent.
Basically, they use the class as, like, we do one thing.
We just delete star or whatever. So that's, like, the simplest use of intent
service. So it kind of feels like AsyncTask.
It doesn't really have return values. Anyway.
Other miscellaneous tasks. Disable UI elements, like, immediately when
a user does something, even if you have a AsyncTask, just so the user knows you're working
on it, you felt their touch. Do some little animation or something, or
progress dialogues. If you don't know how long it will take, flashing
up a modal dialogue for 10, 20 milliseconds and going away would be jarring.
You probably only want to disable it and spin some animation and then only bring up a dialogue
if it's actually going to take a while and you want to convey progress.
So going back to this little demo app, it shows some of these techniques.
This button, we showed the janky one and the ANR one.
Here is a slow one that use AsyncTask. Notice immediately goes gray.
And up at the top, there's this little thing spinning.
So that's, like -- the user is like, oh, okay, I see something moving.
Something must be working really hard for me.
So -- but notice this. Like, I'm going to click AsyncTask, and you'll
see the little spinny thing, because that animation is happening on the UI thread.
But then I will go hit Janky button, which will still be responding, because the UI thread
is free. But then watch that the animation stops when
I hit the Janky button. Because I'm killing all animations on the
phone because I'm installing the UI thread. So one Janky button can make your whole app
kind of be Janky. Speaking of Janky stuff, this is how you kind
of want your ListViews to feel. They're kind of like fast.
The problem is, lots of people do database lookups or Binder calls or something in their
populate view or list -- whatever it's called. And then their ListViews ended up looking
like this, where it's kind of look chop, chop, chop, chop, chop, chop.
That's another Janky thing, whenever it's not smooth, it's not really responsive.
Anyway, so what you should really do -- and apparently -- I went to the ListView talk
yesterday, and apparently, I'm doing it wrong. So don't copy my code.
But, basically, the point is, you should do it async.
You should populate -- this simulates that we're still smooth, but there's some, like,
loading that needs to be done. One thing I did in the Cupcake release when
I was 20 percenting, was, the MMS app was doing database lookups on the UI thread.
So scrolling your SMSs was, like, chunk, chunk, chunk, chunk, chunk, and sometimes would ANR.
So I did all of that async. And that's, like, when you scroll up and actually
see, like, dot dot dot while it's loading and then, like, the text comes in later.
It's kind of hard to do that with list adapter. Well, it was until I saw the talk yesterday
about the proper way to do it on notify -- on data notify change or whatever.
I'm not really a UI guy at all. So this might have been one of my bigger apps
I've ever written. Okay.
So those are the three ListView examples. Okay.
So other nonjank-related tools. This stuff you probably know about.
It's pretty well documented in the SDK Traceview. If you are CPU-bound or not quite sure what's
going on, you used to have to use Traceview, you used to have to say start and stop method
profiling in your code. You had to explicitly, like, rebuild your
app to add these start and stop points. But now there's a way -- or it's not that
new. But as of a release or two ago, there's a
way to do it at run time with the AM command that's on the phone.
So we will demonstrate profiling the browser here.
So let's bring the browser up. Bring a shell up.
All right. There you see the browser running.
So we'll run ADP shell AM, which is activity manager, profile, and then the process name,
com.android.browser, start, and the file name to write the trace to.
So we'll write it to the SD card. I'm going to start that profiling.
So that sends a message to the process being, like, yo, turn on your profiling.
And we'll go to something here. I'll scroll around a little, zoom in a little.
It's using a lot of CPU. Oh, I even have Ethernet up here I think.
I didn't even use that. Okay.
So assume it did some stuff. Then you can say stop.
And now it'll flush out the trace from that process.
So this actually will make your program run a little bit slower.
So now, if we look at SD card, you see, there's the trace we just did.
So we can pull
that. Now there's Traceview, which comes with the
SDK, and you can look at foo2.trace and see what was happening during the time that we
were profiling it. So you can see all the different threads that
were running on the phone. There's your UI thread, some binder threads.
There's your AsyncTask thread. And the cool thing is, you can see all the
function names down here, and you can see how many times they were called.
Sometimes it's interesting to sort by number of calls, being like, why was string.length
being called 5,000 times? But that's probably a common one.
Sometimes you see surprising things, and you're like, what?
I thought that was cached. Why is that thing being loaded a thousand
times? Sometimes it's useful to sort by exclusive
time and you can drill down into all the children. So this is good if you're CPU-bound.
But the JIT and stuff like that should help with this anyway.
Sometimes, though, this can give really confusing numbers, because it kind of skews everything.
It doesn't really adjust for the tracing overhead. So sometimes you have a small method that
is actually fast, but when you look at it in Traceview, it seems to suggest that it's
slower than it is. So I'll talk about some stuff that's coming
up later to kind of fix that. All right.
So, yep, Traceview's good. Other thing that, you know, never hurts is
just some print here, here, here with Android u2 log.
A lot of people make, like, a little struct that just has a wall time and a CPU, like,
current thread. There's Android system debug VM thread CPU
time nanos. So you can see, like, wall times versus CPU
time and put it at various points of your program just to see, oh, that little function
that looked so -- looked so nice and clean turned out calls, you know, abstraction hell
and ends up doing tons of CPU behind your back.
So we found some surprising things just dropping these around.
I've seen a whole bunch of one-off, like, structs that do this.
There's actually a class, performance collector in the SDK, but it's hidden, because it wasn't
quite polished. At some point, I'm sure we'll make that public.
Turns out no one actually used it anyway. So you should watch your own device.
What I've done over the last five, six months is, we added a bunch of instrumentation all
over in Froyo. I went in and added instrumentation to all
database queries, so all of SQLite, all Binder queries, all RPCs between processors, content
resolvers, content providers, and also had the Dalvik team add logging on Mutexes that
were contended. And, indeed, each one of these found tons
of surprising things. We logged for each of these whether or not
we were on the main thread, what was the thread or process name, what version of the build,
because we have, like, new nightly builds every night.
So -- and then we have weekend builds for non-Android team members, like, more Google
employees. So we log a small percentage of these events
as a function of how slow it was. And every day, like, every Google employee
was uploading a couple meg a day. And then it would go up to Google log analysis
stuff, and I would generate a bunch of reports, and just found tons of fun stuff, where fun
is totally broken. But that's -- that's, hopefully, most fixed.
I mean, they're -- now the problem is making sure we don't regress and trying to automate
the -- automate the process so, like, things are detected quickly when we regress, and
so that doesn't happen. So let me show you some of these.
For instance, here is Android util binder. Literally, all it kind of does is look at
the uptime millis of the phone, do the Binder call, and then afterwards, conditionally log
it. And conditionally logging it is just, if it
took over 500 milliseconds, this one is hard-coded to be 500.
Some of them we made tunable, but this was, like, at a low level enough that making it
tunable was a pain. If it was super fast, we wouldn't log it.
Then we would log all the stuff, the process name and blah, blah, blah.
So we did that to content resolver in here, like, whenever you're querying something,
you'll see stuff in the source code later about, like, you know, look at the time before
and after, and we've done the databases, logging stuff there.
You can see, like, system properties. For instance, if you want to -- this is in
the SDK and it's part of the Froyo SDK now, so if you want to go see your own, I'll show
you how you can set these properties DB, DB operation threshold millisecond.
By default, it's on at 500 milliseconds. But you can turn that way down while you're
debugging your application to see everything. So let's see some of that.
So here's my app again that's supposed to do slow stuff.
We populated the database already with five meg of stuff.
So let's logcat. All right.
That's actually maybe -- let's actually maybe bring up another logcat.
This one will be the event logcat. So I actually have another -- there's actually
two logcat buffers that are notable. There's also, like, the radio logcat.
Basically, they're all separate radio buffers that are in the kernel just in memory.
The normal logcat that you get from Android u2 log is just big strings.
You pretty much just, like, say things that are human-readable.
The other one, the event log, is more for, like, the platform itself to use, and they're
all structured stuff. So they have names and numbers, and these
are all, like, some packed binary structure. So this button starts two threads that are
fighting over the same Mutex. And because Dalvik is logging slow Mutexes,
please wait, Mutexes are fighting. You can see DVM lock sample, Dalvik virtual
machine lock sample events are going by. You can see that in process com.google.IO
zippy. It was not the main thread.
In fact, it was called thread 14 or 15. So these are the two I started up and down.
It took 184 milliseconds. And it was at -- it was at this file and this
line number. And because of how I have the logging set,
it logged 100% of these. So this is how we extrapolate when we subsample.
So you can actually see, I set this low by setting on the phone data.local.prop.
So there is something called system properties on the phone which is different from settings.
System properties are things like global things that are in memory. So if you say ABD shell
get prop, you can see all these things that are set by the phone manufacturer. It's kind
of like part of the build, but they are tunable and if you are root you can change these.
So this file, data local.prop, you can override them. So in the emulator, if you set this
and then reboot your phone, when this comes up -- ADB shell getprop, grep Dalvik -- you
can see, indeed, the lock profiling threshold is set to 10 milliseconds. By default it's
500, but that would make for a much slow demo if I had to have mutexes fighting for 500
milliseconds instead of 10. So that is the DVM lock sample one.
Let's also so initializing a database here. So it's going to drop it. Now you can see
some database sample things. So the event there is database sample. There is the database
name. If it has an e-mail address in it, the e-mail address is cleaned up so it doesn't
leak any stuff. There's like the first N characters of the
SQL query. You don't get the placeholder values. And then you see how many milliseconds. This
was the name of the processor thread it was blocking. And if it wasn't, as this one isn't,
it was on a background thread, and this is the subsampling percentage.
So you can turn this one way down, too. That's in the code as -- let's show that.
So there's this DB, DB operation threshold milliseconds and right now it's set to 500.
But we can go on the phone and say ADB shell setprop, and we will set this to -- I don't
know, ten. Make it really Spammy. Make sure it is actually there.
Oh, helps to actually run the command. Anyway, now you can see I have Dalvik and database
samples set at 10 milliseconds. So this was cached in a static variable, so I will exit
my app and restart it. And now we have the -- here's the event log
again. I will do some selects. So this one is not using an index so you can
see them going by occasionally. DB sample. You can see the select, primary key, it took
like five milliseconds. This one uses index, and this one I would
have to bang on a lot for it to show up statistically. There is one.
If you set this low enough, and like I said I/O on this thing is really fast. If you are
using read and writes, it's a lot slower. For instance, here is a write where I update
everything. I think this will take three or four seconds so now you see this update go
by. Update foo and this one took, yeah, 3.8 seconds. I was just appending a character
to all the junk rows. These are fun things to do in your app to
see where you are doing slow things that you are not expecting.
So we did that a lot in Froyo. We talked about log versus event log.
And those are all the events that were added. The database samples, binder samples, content
providers were broken into queries and updates depending how we wanted to structure that.
And Dalvik lock contention when you are acquiring the mutex.
We don't currently have instrumentation for who is holding the mutex the longest to kind
of like find the person who was to blame for it, but generally it's pretty obvious from
your code. But we probably will be adding maybe DVM unlock sample or something like
that. Coming later.
This is all speculative. This is all kind of things I want to do. I haven't totally
prioritized all this stuff. Need a lot more instrumentation. Every time
we have added something, I've found more and more things that I want to instrument.
I have always found offensive things whenever we added instrumentation that was surprising,
so I just want to keep adding more and make more dashboards.
I also want to make all this sort of stuff be easier to use, be you'll SDK platform features
for end developers rather than this hackie kind of thing you have to do with set prop
and changing these things and rebooting the emulator. It's not quite easy to use now.
I also want a strict mode. Right now the documentation doesn't really tell what you thread you should
do stuff on often, or sometimes it's ambiguous how you should do stuff. And sometimes the
-- like the view system crashes if you invalidate a view on the wrong thread.
But other things of the view system, you can do off the wrong thread. And sometimes you
get away with it and sometimes it happens to cause a repaint, and then you explode.
I basically want a strict mode where you blow up early, so you can maybe add in your manifest,
like, strict mode or call Dalvik OS debug.set strict mode, and maybe blow up if you do writes
on your main thread or blow up if you do networking. Maybe give you granular control over when
you blow up. This is all stuff you can do yourself now if you just drop in little sanity
check functions at key points, but it's kind of a pain.
You saw during the keynote the thing where show Android feedback, or I'll go -- whatever.
In the market, you can see all the stack traces from things that crashed in your users' apps.
I kind of want to do that for performance data. Figure out a way to maybe let users
opt in to performance collection and upload that to the Android Market. So you can go
in and be like, oh, these are like the files and lines and mutexes that are making my apps
slow for my end users. I think that would be kind of cool.
This is what we have now internally, but trying to figure out how to surface that in a way
that doesn't kill people's networks and batteries and privacy, that stuff is harder.
The other thing I am excited about and after Froyo is SQLite is getting another feature
called write ahead logging where it basically makes SQLite have multi-version concurrency
control, kind of like real databases where reads don't block writes and writes don't
block reads. So at the time you go to read it, you get
the version of the database that existed at the time you were reading and then writes
continue to be appended to the end. And then when that write is done, there can still only
be one writer at the time, but when that write is done it says this is the new version number.
And then the readers go and apply all the blocks -- all the pages that were (inaudible)
retroactively on top of it. So this will let you do things like write
to a database from your provider without block, like, say, Gmail scrolling your conversation
list where that's faulting it in from your the SQLite database. And sometimes you can
kill your animations or your activity was noticeably janky because it was blocked on
the SQLite F lock. So I think the concurrency out of read-ahead
logging will be interesting post-Froyo. So the summary, I can do some more demos.
Yeah, get off the main thread. Things are actually slow that sometimes you are fast
-- I'm sure your home network connection and your emulator or your phone on your home
Wi-Fi is really fast, and emulators, if you have an empty device you are developing on,
their disk is going to be a lot faster than users in the field with full disks.
Yeah, basically assume things are slow. Know what SQLite is doing. Add your own instrumentation.
Just because it's, you know, not automatic -- auto magically exposed to you right like
through the market, you can totally do it yourself for your own apps or put a little
server on app engine or something and upload performance data from your own users if they
agree to that. So all the code is up that you could play
with in these demos. I think I will also show kind of how slow
I/O is on a real device here. Android.
Kill the emulator. All right. Switch to this guy.
This is another fun tool we wrote during Froyo that lets us measure various speeds of things.
Little micro benchmarks on reads and writes and settings and binder calls along things.
You can see like a binder call between two processes was like .18 milliseconds. Or here
is a binder call returning a string or content provider calls, or writing settings. We just
did a thousand of those. Reading files.
So we were using tools like this to kind of measure improvements over time.
You should have told me it was way up there. So that is in the open source tree, whenever
it is released in Froyo. So where is my I/O Droid here?
Alphabetical order is hard. Here is a random one byte read. You can see
it's, like, sometimes fast. Even a read jumps up to 139 milliseconds there sometimes. This
isn't very scientific. This is just kind of fun. Writes, on the other hand, like 391,
200. That's like writing a single byte. Maybe my SD card -- or my internal flash got full
again, but it kind of jumps all over the place. A single byte writing was like 390 milliseconds.
So switch it back to this guy. One thing that's fun here is if you think
like your internal caches are making your thing fast, you can always go to your device
and say -- oh, ADB root. Echo 3 to PROC SYS VM, drop caches. Spelled correctly. So basically
three is the bit mask of one and two, which is one is like inodes and two is like slab
or something like that. I/O is just echo 3 to this and basically drops a whole bunch
of the kernel caches. So then switching back my app here, reads
are, like, slow again. So I can just sit up here and hit up enter, or paste, paste, paste,
and I can force it to be slow. Slow, slow, slow, slow, slow.
That's a fun trick, too, to like simulate worst case.
You should also put like a big file on your data partition that makes your app -- like
training in Denver. Make your data partition really, really slow to simulate the worst
case for your end users, and get your file system, like, 80, 90% full, and then you will
see like SQLite at its worst. Anyway, do some questions and maybe we could
jump around code if there aren't any questions. I think I saw a bunch on the Wave.
If there are live questions, too, we can do those.
All right. Just go in order.
If there's any plan for nexus two or does it already exist?
I have no clue. I guess the web store is shutting down, and I guess Nexus One is kind of marketing
name anyway, so maybe someone will name a phone nexus two. I don't know.
What is the most common performance problem you see? I think pretty much just people doing
stuff in the wrong thread, I/O being slow. SQL queries that look innocent but if you
look at the table it's selecting from it's really a view of a view of a subselect view.
And you say what? It was just a view. Why is it slow? So, yeah, would I say I/O and
doing stuff in the wrong thread. I have no idea about Google TV although I
have one and it's fun. I can run my Android apps on it.
Oh, this as good one. Any rules of thumb to follow to improve the speed of UI redraws
when the orientation of the phone changes? So I actually bookmarked this one to point
out there is something here. There is something called on retain nonconfiguration instance,
which doesn't sound like on prepare to rotate the screen.
[ Laughter ] >>Brad Fitzpatrick: But that is effectively
what it is. So rotating the screen is a configuration
change. Basically, you have a whole new type of screen, rather than a screen that's tall
you now have a screen that's wide. And because you have a different type of schools you can
have different layout files and different drawables and all this stuff.
So the way the Android system works is if you don't declare in your manifest that you
don't handle a certain type of configuration change, then by default, like your activity
is just killed. Not your process, just your activity is shut down and reinstantiated with
the new configuration. So if you have a bunch of expensive state
in your activity, like network resources you loaded or data or JPEG or the user was drawing
something and you didn't write it out somewhere, as you should, then when your application
is restarted or the activity is destroyed and reinstantiated with the new configuration,
you have to load all that stuff again. So you have two options. You can either declare
in your manifest that I handle rotating the screen, I handle that sort of configuration
change, or you can do this. Generally, you don't want to do that first
one because then you have to handle on -- I think it was on configuration changed.
And if the configuration is like now I am wider versus tall and those are different
layout files, then you would have to handle like destroying all your views and changing
everything yourself. Basically what you want to do is destroy the whole activity and start
it up again with the correct views that the system will inflate for you.
What you don't want to do is redo all those heavy objects. So that's when would you use
on retain nonconfiguration instance. Basically, this is called right before on
stop and on destroy, and it says you can return any arbitrary object. This is like you are
expensive state. And then when the next activity is started right after you, which is the exact
same one, you call get last nonconfiguration instance, and you restore your state that
was expensive. So that is the answer to that question.
How is Android 2.2? I love it. I really wanted to throw my phone
against the wall half the time in Eclair. And I mean, this is why I joined Android.
I was just really frustrated by performance. Like certain parts that just weren't responsive.
One of my pet peeves that I got into Android 2.2 which is kind of a side thing is that
power control widget. Like just T just wouldn't respond. You would turn Wi-Fi on or off or
Bluetooth on or off and sometimes it would hang and the UI wouldn't change or sometimes
there was a little toast because it was doing all really expensive things that went all
the way down to the kernel and drivers and Bluetooth and the PPP daemon.
That was all happening synchronously before any UI was ever updated. So I tried to make
that responsive. There are still a couple of buttons that aren't as responsive as I
would like in there, but the first three or four are pretty good.
There's a whole bunch of other stuff. Basically -- Let me see if I have any reports here
that we did. No.
Basically, we just made tons of reports about all the slow parts, and we had meetings once
a week after we did the weekend builds for all the Googlers.
On Mondays, we'd go and look at these reports and be, like, whoa, what is that thing?
So, yeah, I like it. There's still a lot more to do.
But -- is that a question? >>> Yeah.
We have an application that's downloading a bunch of thumbnails in a ListView, kind
of two parts to this question. Do you have a good piece of code that kind
of illustrates asynchronously downloading that stuff into ListView or is there a good
pattern to follow? >>Brad Fitzpatrick: So I do.
It's -- I open sourced it this morning, this little sample app.
Apparently, I did one part of it wrong, so I'm going to go update the code.
So yeah. Basically, -- we can go and look at the code.
>>> So the second part to that question is that we have an implementation that's working.
It's -- I think it's pretty fair. But it's downloading these thumbnails using
the context get cachedir and just basically throwing everything into the cachedir.
And based on everything you are saying about file reads and writes, is there anything special
we should do? I mean, the documentation on get cachedir
says, basically, Android is going to manage this for you and delete stuff whenever it
feels like it needs to. Is there anything we should do to kind of
-- >>Brad Fitzpatrick: Android is going to delete
what part? >>> When you use the context.get cachedir
and just throw files into it, the documentation basically says Android's going to manage this
and delete files when it needs space. So is there anything from a performance perspective
we should do to help it manage that? >>Brad Fitzpatrick: I think that's actually
on a separate partition that's probably not very contended.
So I think you're probably fine there. And also, raw files are actually pretty good.
One thing I didn't mention was, if you're just, like -- sometimes all you need is raw
files and you don't need an SQLite database. Especially, like, log data.
If you have something like my tracks, where you're recording points over time, just inserting
a row into a database over and over again, if you actually go look at (inaudible) while
you're doing it, you can see it does a lot of operations, like reads, writes, erases
to Flash that just appending to a file doesn't. So it's not really totally relevant to your
question, but I -- I think cache is probably fine.
Raw files are generally pretty fast. >>> Okay.
>>Brad Fitzpatrick: In terms of asynchronously doing something, in here, I made this Jankable
list adapter where, when I create it on the constructor, I can select my Jankable segments,
I do the proper convert view thing if one's being recycled, I use it, otherwise, I inflate
a new one. And I didn't do the pattern that they talked
about yesterday during the ListView talk to have that little struct.
So I'm doing the correct way and not the fast way, where I look up the views every time.
Like I said, I don't really do any UI work. But what I did here is -- okay, so ignore
the Jank one. For the async stuff, what I do is set off
AsyncTasks that just sleeps, just to kill some type.
Post execute, which happens back on the UI thread, the problem is after 500 milliseconds
or whenever I'm actually scheduled, because all AsyncTasks run serially in one background
thread, the view that I was modifying may be gone, it may be recycled because I was,
like, actively scrolling. I basically set a tag on the thing when --
when I finally -- when I first show the view on the screen, I set a tag, being, like, what
position I'm at. And this gets -- I think that's, like, an
object or something, it's auto boxed to an integer.
Later, I check to make sure that it's still the same number, like, that actual view instance
is the same one. Apparently, you're not supposed to do this.
So don't do that. But, basically, the pattern is, you do some
AsyncTask or other thread from your get view and then you go update your data set afterwards.
So I'll go update this code to be correct. So....
Yep. >>> Hey, you recommended for long-running
background I/O, I think you called it intend service.
How far back does that go for OS versions that we're targeting?
>>Brad Fitzpatrick: I think that's pretty old.
>>> Is it? >>Brad Fitzpatrick: Yeah.
We can check here. Intent service.
Well, we'll find out whenever the Wi-Fi is cooperating.
Since API 3, so -- yeah, old, Donut or something, Cupcake.
>>> Great. Thank you.
>>Brad Fitzpatrick: Yeah. >>> Hey, the last app I worked on, we had
to (inaudible) much later, and I tried out SQL and, like, serialization, and XML, and
so on, and it turns out serialization is awfully slow in Dalvik.
Do you know if this is better in the Froyo? >>Brad Fitzpatrick: Probably.
If it was CPU-bound, it will be a lot better. >>> Okay.
>>Brad Fitzpatrick: So. And if it's still really bad, maybe that should
be done in native code. I mean, maybe we're already using libXML.
The question was, XML or some data structure -- Was it XML?
>>> Yeah, I tried XML, SQL, ZSV, all of them. But it turns out SQL was the fastest because
of the native code. And I tested it, like, on the normal PC, but
the serialization of the 10,000 objects, it was, like, 70 milliseconds.
And on the Android device, then it was, like, five seconds, which was painfully slow.
And, like, on the Dalvik, there was all triggering the --
>>Brad Fitzpatrick: So even with JIT, sometimes things will actually be CPU-bound and they
won't be I/O-bound. But that's still something that you can do
on a background thread and have a little progress indicator in your UI and actually set that
background thread to a lower priority. There's somewhere in the API to reduce your
priority of a thread. And that way, the user can still use your
app while you're maybe exporting the SD card or something like that.
>>> How expensive are random access rights into a flat file?
>>Brad Fitzpatrick: It's about what I was showing here.
They jump around. Switch to the ELMO.
You know, like, writing a single byte, which is about the same, up to, you know, 2K or
something on -- it jumps around. Sometimes it's, like, 200 milliseconds.
Sometimes it's, like, very few. It really depends how -- how full the device
is. >>> But it would be significantly faster than
the equivalent SQL, SQLite update? >>Brad Fitzpatrick: Yeah.
Generally, SQLite will -- when you append -- like, when you do a transaction, even
when you're not doing. Anything, will create a roll-back journal,
create a header to it and delete it at the end.
So what this one slide was doing where I showed the graph, this was actually the operation
-- this was creating a file, writing 512 bytes to and to have it and deleting it.
This is what SQLite does on every transaction, whether or not you write at all.
So this bug is now fixed in SQLite, but not in time for Froyo, or it was just too risky.
So post Gingerbread, all SQLite transactions will be faster.
If you're going to conditionally write and you're, like, oh, I'll use a transaction and
then do my read and then bail out, out of my transaction, without doing a write, that's
actually more expensive than -- you should do your read outside of your transaction,
'cause reads outside of a transaction are a lot faster.
Then go into your transaction, do the read again, double-check that you -- that the condition
still applies, and then do your write. Because if -- just entering a transaction
is -- involves disk I/O and it's pretty slow. And it also blocks out all other readers on
the phone. So -- all right.
I think we'll wrap up, then. Thank you.
[ Applause ]
Mobile Analytics