Oddbean new post about | logout

Notes by P-Y | export

 Shouldn't call them "fries" if they weren't fried at least twice. Looking at you In N Out 👀 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq joint specific strength tra... 
 @96a56018 Isn't that just rollerblading though 😉? 
 Blue Angels, read the room? 
 The USPS just et us know that they're suspending mail delivery because a neighbors dog has been attacking letter carriers.

Obviously that's bad, you shouldn't be attacked by dogs as part of your job.

Honest question: what can I / should I do about this?

Really have no clue what to make of this. 
 Nothing sucks quite like testing file-manipulation code in Java. 
 @a8871cd2 very true.. I've found such things easier to test when I was using Okio (though, you said Java, and that's Kotlin) 
 Lvl 1: Tshirt size annual plan estimates
Lvl 2: Multiply by a factor for padding
Lvl 3: add KTLO budget
Lvl 4: increase KTLO budget until it looks like you're slightly over capacity, wow, sounds like the team will be hard at work! 
 nostr:npub1xqhvru0uh9g4vsdaeu8tjhzd2df0kdvdxy0td9a9w998g0737ecs4ykhw9 nostr:npub1slp5q3n9nyjn3p28... 
 @b0f43739 @302ec1f1 Using it does have some complexity, not necessarily easy to figure out how to set it up right for your need and what all the implicit behavior is.

Even if that wasn't the case, the internal complexity does matter, because bugs will happen more, and we need to be able to understand what could be happening.

Right now Datastore doesn't provide any hooks for telling you what it's doing. We have super slow inits and trying to figure out what it could be. 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq Having said that... I'm hon... 
 @ccfb7e45 I wish SharedPrefs didn't block the main thread to flush on component lifecycle, and it's a bit wasteful to store as XML especially if you have a single value. Other than that, it's pretty robust & simple (though people forget to eagerly call Ctx.getSharedPreferences to trigger async loading of the file) 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq All the mentioned libraries... 
 @ccfb7e45 Datastore had data corruption problems at Google when it was internal, they didn't take the time to figure out why / how to prevent that afaik, the same issues are in the oss version.

Never had corruption issues with shared prefs or SQLite (but Tape yes) 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq I'm glad we rolled out our ... 
 @f6beb887 Honestly if you'd use SQLite for a key-value storage I wouldn't even blink. 
 Just checking, is "Right sizing" corporate bullshit lingo? Feels like a word that's popped up a lot recently, trying to spin something that's never great?  It's not like when downsizing in the past people would get the new size wrong..

Just checking if anyone thinks "no actually rightsizing is a better word" 
 🎤 If you block it then you shoulda put withLock{} on it. 🎶 
 Someone found a way to spam Mastodon and escape Spam reports: they pinged me on a post then immediately deleted it so I can't report the post.

Account is: @oyPhFrxPx0@mastodon.social

Anyone knows what to do to get it kicked out of that instance?

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/133/426/701/082/050/original/cd5067137ac4039f.png 
 Wow they also deleted the account. 
 Has anyone published a maturity model for mobile software performance, i.e. where orgs are in terms of monitoring perf (production, CI) culture, setting goals, etc? If not mobile then backend software perf maturity model? 
 E.g. assess where your org is on things like:

- Reporting perf in prod
- Progressive rollout tracking + Triage process 
- Tools for investigation (remote, local)
- Eng skills
- Shift left, detecting in CI
- Setting goals as part of new feature work
- Surfacing top metrics to org reviews
... 
 This showed up in my Dock after updating to Ventura. Did they really not see it??

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/127/298/179/169/010/original/869100c40b6db5df.png 
 This! https://furry.engineer/@soatok/111115849471897182

In 10 years, my experience working at Square has been they've generally been fair with their employees. In March 2020 Jack said "alright, we knew remote was the future but we weren't sure how to get there. Now we're forced to do it so let's learn how to do it right, there is no going back.

Our execs have been clear that RTO is BS, and that's a huge hiring advantage for us. This often comes up with strong candidates I interview who wouldn't otherwise leave. 
 @535ec9d7 @d82adc62 Interesting! If I understand right, there's a receiver that triggers on package install, scans the manifest for this key (and an associated signature) and then applies an updated config globally, for all apps? https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java;l=98;drc=e2b2432bc64fbbca667f11c79d0dc5e4f627284b

Looks like this is a way to dynamically relax more APIs at the device level, maybe? 
 This is a common app bug when using views btw: replace the view hierarchy at just the wrong time (in between down & up) leads to ignored taps. Well written UI never replaces a set of views with the exact same views on any kind of sync or timer. 

I wonder if this type of problem is gone with Compose. 
 Setting touch delegates wasn't working great because the app sometimes uses though as well.

But then I had a better idea: after ACTION_DOWN, find the touch target and set a detach listener on it. If it gets detached before ACTION_UP, crash immediately. 
 Fun evening hack: trying to early detect Espresso flakes caused by tapped views being replaced in between ACTION_DOWN & ACTION_UP, which turns taps no ops without Espresso noticing.

How? After ACTION_DOWN, find the touch target (reflection, hidden API) and set a fake no-op touch delegate on it. After ACTION_UP, see if it received the event. If not, fail the test. 
 This is a common app bug when using views btw: replace the view hierarchy at just the wrong time (in between down & up) leads to ignored taps. Well written UI never replaces a set of views with the exact same views on any kind of sync or timer. 

I wonder if this type of problem is gone with Compose. 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq No special treatment. They ... 
 @d82adc62 I was mostly thinking of callstack package checks when making reflection calls. E.g. if ("androidx." in callstackPackages) 
 Do AndroidX packages get special treatment for accessing otherwise blocked internal AOSP APIs?

If yes, anyone knows how that's done? 
 I'm starting a deep dive series on ANR internals.. we'll start off easy with looking at how clicks get dispatched through the view hierarchy / compose UI.

https://blog.p-y.wtf/anr-internals-touch-dispatching-through-the-view-hierarchy

#Android #AndroidDev 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq nostr:npub135vvp0upl4k0kjwl... 
 @be552d12 @8d18c0bf @f6beb887 thanks, I see that now, Iam pointed me to this: https://androiddev.social/@ianlake/111060569784580571 
 - I pre bake the tartlets for 20mm at 325°F/165°C for 20 min on a perforated Silpain mat (no bubbles + nice patterns)
- once cold, I remove the rings and paint the tartlets edges and inside with an egg wash (20g yolk & 5g cream). 
- Then I bake for 10-15 minutes more.

This creates waterproof isolation + a nice shine.

The tartlet shells are ready and can be stored at room temperature in a dry environment for a week or two.

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/061/534/798/136/232/original/2ef5f722da09dc9f.jpg

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/061/539/784/952/561/original/8d16cf7cefb0210b.jpg 
 Next up: praliné!

This is fairly easy to do and so delicious! Adds nutty flavors, crunch & sweetness.

I used 100g toasted hazelnuts, 50g sugar, 1g salt. You can swap hazelnuts for almonds, and swap the salt with a half vanilla bean (mixed up with the rest).

When the hazelnuts come out of the over, I drop them on a silpat mat in a tight bunch, I start a caramel (just heat sugar on medium, you can add a little water to help with burning) then pour the caramel on the nuts.

Once cold, mix.

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/064/337/384/854/046/original/33e563ce655ecbd6.jpg

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/064/337/614/881/302/original/594b42d1b062ac28.jpg 
 @4f9fba93 @be552d12 thank you Ian (and Craig) this is exactly what I was looking for. Love these kind of technical and well backed up articles! I never looked into the details of the paging library, so I don't have a good mental model for how it works yet. 
 nostr:npub176lt3pm7dxyrsrxnpt0u2vquy6pwue20lcefjl32q5j9rgg27xrqh5s4md nostr:npub1slp5q3n9nyjn3p28... 
 @8d18c0bf @f6beb887 with a db cursor plugged straight into the UI IO only happens when the cursor window needs to move. As you render rows you're just turning bytes into objects.

Preloading with pages, on the other hand, requires reading an entire page of items and creating all the associated objects, which implies a higher latency, and then you need to make sure you keep preloading pages faster than you scroll. 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq I see what you mean, but is... 
 @f6beb887 You can create a denormalized table with just the data needed for presentation per row. The query will be super fast, the CursorWindow takes care of reading data in batches, so the all main thread does is turning in memory bytes into list items at rendering time, and occasionally moving the cursor window. 
 @f6beb887 I was thinking about infinite scrolling list that are backed by an SQL query, rather than UI components that actually have pages. 
 @f6beb887 It looks like it can be used for infinite scrolling of course, but it does imply that you're preloading pages in sizable chunks, which will be slower (initial delay vs a live cursor) and will use more memory. 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq androidx.paging supports dr... 
 @f6beb887 I was thinking about infinite scrolling list that are backed by an SQL query, rather than UI components that actually have pages. 
 - I pre bake the tartlets for 20mm at 325°F/165°C for 20 min on a perforated Silpain mat (no bubbles + nice patterns)
- once cold, I remove the rings and paint the tartlets edges and inside with an egg wash (20g yolk & 5g cream). 
- Then I bake for 10-15 minutes more.

This creates waterproof isolation + a nice shine.

The tartlet shells are ready and can be stored at room temperature in a dry environment for a week or two.

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/061/534/798/136/232/original/2ef5f722da09dc9f.jpg

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/061/539/784/952/561/original/8d16cf7cefb0210b.jpg 
 The strips go in first. I place them in the rings, trying not to break them off, cut off excess strip at the joint and pressing the joint to make it seemless. Then I look from the under side, gently pressing the strip against the ring to remove gaps.

It's 24.5°C / 76°F in my house so I have to work quickly and put it all back into the freezer ASAP.

#baking

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/058/741/153/230/569/original/702169accbd24936.jpg 
 I like neat tartlet shells with a right angle, so we need to make the bottoms and the edge strips separately.

I use Silikomart perforated rings: the edge makes pressing & cutting dough easy. The holes let air escape (less bubbles) and create a pretty pattern. Sometimes they stick a bit after cooking (not always, not sure why exactly) so I apply a bit of melted butter & freeze them ahead of time.

Rings are 7cm diameter, I need π×7 long strips (~22cm). Unfortunately sheet broke off though 😅

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/058/666/745/295/604/original/0ceca90a5429b261.jpg 
 The strips go in first. I place them in the rings, trying not to break them off, cut off excess strip at the joint and pressing the joint to make it seemless. Then I look from the under side, gently pressing the strip against the ring to remove gaps.

It's 24.5°C / 76°F in my house so I have to work quickly and put it all back into the freezer ASAP.

#baking

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/058/741/153/230/569/original/702169accbd24936.jpg 
 Today I'm baking lime praliné tartlets. There are many quick steps with time in between, so I do this over the course of several days, especially with kids & work.

Let's make tartlet shells first!

- Mix 150g softened butter with 100g sugar, 2g salt & ½ tsp vanilla extract
- Add 50g egg (~1 egg), mix
- Add 250g flour, mix 2mn, do not overmix (don't stretch that gluten!)
- Roll in between 3x 2 sheets of parchment paper, at 2mm thickness
- Freeze for at least 10mm

#baking

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/058/637/577/438/466/original/d67f5b34933fbb4b.jpg 
 I like neat tartlet shells with a right angle, so we need to make the bottoms and the edge strips separately.

I use Silikomart perforated rings: the edge makes pressing & cutting dough easy. The holes let air escape (less bubbles) and create a pretty pattern. Sometimes they stick a bit after cooking (not always, not sure why exactly) so I apply a bit of melted butter & freeze them ahead of time.

Rings are 7cm diameter, I need π×7 long strips (~22cm). Unfortunately sheet broke off though 😅

https://cdn.masto.host/androiddevsocial/media_attachments/files/111/058/666/745/295/604/original/0ceca90a5429b261.jpg 
 nostr:npub1slp5q3n9nyjn3p28hnxmvj5x82w6yw03cvplvkjdrnnuqqw4et3ste4xfq wow but was I right though? 
 @1ba48b1b ok you're going to hate this but it was a trap: I didn't know the name of any Drake song. You could have said "Wizard bingo" I wouldn't bat an eye. But I guess I'll remember "Hotline Bling" now so you win with a self realizing prophecy 
 @e06c1f25 thank you! I'm writing a blog post on how touch events gets delivered to Android apps. It turns out that's done through a socketpair of type SOCK_SEQPACKET (which is apparently a stream with demarcation): https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/input/InputTransport.cpp;l=338;drc=0174738c28994b2c710c52cf0ebcfc51013ed833

The whole thing is nonblocking it's quite cool.

So anyway, Android devs might not be familiar with sockets. I'll link to your comic it def helps! 
 @e06c1f25  The idea I originally had was to draw something with 2 processes that send bi directional data through a buffer in the middle and rely on ready states (from epoll) to wake up and read or write. I think I can visualize it because I've seen other comics, probs from you, that show step by step communication between various actors. 
 @e06c1f25 thank you! I'm writing a blog post on how touch events gets delivered to Android apps. It turns out that's done through a socketpair of type SOCK_SEQPACKET (which is apparently a stream with demarcation): https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/input/InputTransport.cpp;l=338;drc=0174738c28994b2c710c52cf0ebcfc51013ed833

The whole thing is nonblocking it's quite cool.

So anyway, Android devs might not be familiar with sockets. I'll link to your comic it def helps! 
 My new hobby is finding out people's ages and guessing what the first Drake song they likely reme... 
 @1ba48b1b ooh you definitely don't want to try that with me 
 Anyone knows a good comic or visual description for how bindirectional communication between processes using local sockets work?

Kind of like this from @e06c1f25 but for local sockets: https://wizardzines.com/comics/sockets/ 
 TIL Android touch event dispatching involves at least 2 queues on the system_server side, and then a socketpair to send the touch event to the target app / window, which read the touch event socket while polling for the next main thread event (another queue!), dispatches the touch event then send an ack on the other socket of the pair. If the ACK isn't received in time by system_server: ANR 
 New article: A script to compare two Macrobenchmarks runs

https://blog.p-y.wtf/a-script-to-compare-two-macrobenchmarks-runs

#AndroidDev #performance #benchmark #macrobenchmark #Android 
 I'm fascinated by the phenomenon where someone is pushing to add a formal process for something b... 
 @dc243d62 I've seen this happen so many times! 
 What debugging tools do you use to surface all the disk writes an Android app is doing, to which files, etc? 
Event not found
 @269a2baa when going through a migration, your code produces a new instance of the data. Say you have a datastore of Account, the ds will read an Account instance from disk then the migration produces a new Account instance. Datastore will write the new instance to disk only if "!readAccount.equals(migratedAccount)" 

Makes sense but not explicit that you must implement equals() on Account correctly. 
Event not found
 I'm reading the internals in the context of us having read timeouts or deadlocks on the initial read & trying to figure out what could be happening (no, "slow disk" isn't a good enough reason) 
Event not found
 @3fa03142 "you're holding it wrong" 😉