My name’s Ryan Cooke. I’m on the Pinterest Core Experience team. My talk today here is talking about various Android Libraries: Pros, Cons and Gotchas. The vision is to give a high-level overview of as many libraries as we can, help you decide when you have the problem, is this an already solved problem, and what is a good solution, while also trying to help you avoid pitfalls that you might run into.
Picking the right library means that, instead of spending three months building a solution, you can have an already built better solution. Knowing about these libraries is the first step.
I hear many people wanting to do the first library, can’t we do it with Async task or something native? Google realizes that if there’s already a solution built, they don’t want to build a competitor that’s going to do the same thing. They’ve expressed that sentiment many times, in multiple places. But you have to be aware of some of the options that you have out there.
One of the highest valuable things a library can have is being reversible: you add the library into your app, then you remove it (there’s no cost). You can take it out at any time. Not everything can do that, but if it can, that is a much easier decision to make than a library that’s going to be stuck with your app for the duration of your lifetime.
One way to do this better is to write a wrapper around your library. It has many benefits in the sense that if you have your own class that calls into the library methods, you can make it to have your own API. For example, if it returns unknown in some situations, you want to mess with the API to get it. It’s only touching one file in your code. Having a wrapper can often be easy to make the library work.
If you are doing a library, you’re going to have somebody in your team who’s already used it, knows how to use it and is going to want to not use your wrapper. This check style throws an error on build time, or in the app if they use the import statement for connection class, tells them “don’t use connection class, use the wrapper instead”. Then you say don’t do it on this specific file. If you don’t have that, the wrapper tends to not be very useful because people are going to break it eventually.
In addition, it’s a good idea to unit test third-party libraries. You can’t always get away with that, but if you’re looking at something like Joda-Time, you can unit test to understand the weird edge cases of it. You might even learn how it works better by unit testing it. You might discover that if you add one to today, you can end up in a February 30th situation, instead of using the add day approach.
In the Android world, we have to think about method counts (64,000). (I talk about third party in library things; they’ll have the script up later). The website method counts, allows you to put the library, the Gradle import statement, and it tells you how many method the library has (it’s a great thing to look at when you’re thinking of a library). With ProGuard, many libraries will get much smaller. The rogram makes it so you’re only using the parts of the library that are used, but it’s hard to predict what you’re going to use before you use it. You can sometimes do the “I know I’m only using this method from the library”, and see how small it is.
Dex-method-counts goes into your gradle and you can do on your build system, keep track of your total method counts in your app. Apk-method-counts is a great tool. IYou can drag your APK over and look at the method counts there (63,905 methods, I like to live on the edge of 64,000). Even when you’re past the MultiDex limits, every method you add is still going to slow down your startup time for older devices. Even though your ship has sailed and not MultiDexing, there’s a difference between 100,000 methods, 120,000 methods. There’s always benefit of having less methods. Instagram is famous for pulling out what they need from a library instead of using the full library. It’s a good approach if you have the manpower to do it.
Beyond that, I’d recommend focusing on the popular mature libraries. Every month (especially if you’re on a Reddit Android dev), you’re going to hear the newest, coolest library. But the more developed, more popular, the ones that have been around, they’re going to work with the other existing things. People aren’t going to find the compatibility issues with them. Whenever you have a problem, the stack overflow result’s going to be there.
Also, be aware of unusual approaches. This is something that does not do something in standard Java way. If you’re building your layouts completely different, you can expect that there might be compatibility problems. That’s a reason to be more hesitant on a library.
Facebook is the gold standard for social logins (if you don’t like the Facebook implementation, it only gets worse from there). They provide test users, so you’re allowed to get the test. Instead of having to make your fake accounts, you should be using their test user accounts - it works much better. One thing I’ve seen people miss before is that you do not get email guaranteed from Facebook signups. About 10 to 15% of Facebook users don’t have email. If you’re expecting email, that could be 10 to 15% of signups that are failing mysteriously.
They require key hashes (which is a random thing that is always knowing to get that code snippet on the side prints out the key hash), so you don’t have to deal with it and ever think about it again. If you’re going for crazy permissions, they’ve locked down on the permissions these days. If you have this crazy idea and you don’t have the app built, but you want a bunch of permissions, they’re going to want to see how you’re using the permission app, so be warned about that ahead of time.
In logins and signups, log the errors that you’re getting. That is how we found out that were failing 10 to 15% of signups because of not getting the email. Sometimes logging errors there can be the biggest conversion gain you get by finding something you didn’t know about.
There’s a lot of other login choices: Twitter, LinkedIn, Google. My mentality is that (especially Twitter, LinkedIn) while they’re up there in the most popular, tend not to be worth the effort. You’re going to see about less than 2% of your signups from there, unless you’re a very good use case for them. LinkedIn is especially challenging API (that was not my original word choice, but PR told me I should change it). Twitter, they don’t provide very much information, but you can go go to this link, and, say I need email and basic information, they grant permission to everyone. But overall, it’s adding more complexity to your app, unless you have a great situation.
Google is mixed. On Android, many people are going to have Google accounts. It’s weirder if they don’t have a Google account. But a lot of the functionality you can get from having Google login - you can get from having email hints and other things. Also, the UI is hideous on it. Beyond that, it’s a bit slow, so you have to think about having loading dialogues.
There’s two big names in networking these days if you’re using the native: Retrofit, Volley. There’s also the native HTML URL Connect (but that’s generally not recommended). For Retrofit and Volley, you often see these side-by-side comparisons, and you see Volley doing more than Retrofit does (e.g. image downloading and neat things). That can be deceptive.
Get more development news like this
Retrofit’s built by Square, and they have a philosophy with their libraries where it does as little as possible. They try to make it so that it strictly solves the specific problem, and when solving that problem, it tries to enforce good user practice. Retrofit’s probably their best example of that. This code base (see slides) is an example of how I used Retrofit in a random app I have to create a very clean interface for my API calls.
Volley gives you enough rope to hang yourself with. You can use Volley, but it’s very easy to use Volley badly, too. Retrofit forces good practices better, and I tend to prefer it for that reason.
Many times you’re going to hear OkHttp: that’s doing the actual behind-the-scenes heavy lifting of the networking calls. It’s in Android 4.4 natively.
Thinking about network debugging, you have your network calls going, you want to understand what’s going on.
Stetho is a great option. It’s from Facebook. It provides the Chrome developer tools view for your networking calls. It also gives many nice things: they can look into your database, see what’s going on there; you can see your layouts, and if you don’t know what’s going on on a screen, you can figure out. The caveats on it are that you have to pull it up each run, or it closes itself. While those times are accurate, it makes your network calls take longer.
I tend to prefer a simple HttpLoggingInterceptor. You add the interceptor to your network request, and it prints out on your log file what’s going on. You can see the JSON files, as well. There is a bunch of similar things all named logging interceptor. Using the standard HTTP logging from Square gives you a very easy view and it’s very lightweight to do.
As somebody who works at Pinterest, images are important. If you’re doing images, you probably want to use a third-party library to handle the caching, the downloading and getting everything right with resizing.
The two biggest names in images are Picasso and Glide. They have very similar interfaces; it’s the exact same code for the most standard use case.
Picasso is smaller. The newest version, it’s 849 lines versus 2,879 lines. But Glide tends to catch every use case that you want: it can load GIFs, it can do preview images on videos.
Picasso tends to focus its use on specifically if you’re loading images. It tends to be a bit more popular; there’s a bit more documentation, a bit more support. Both libraries are good. If you think you have some weird use cases, might as well go ahead and take the plunge and use Glide. If you have very standard image use cases, Picasso’s a good choice.
I’ve mentioned I work at Pinterest. Images are important. I have to throw out a bunch of tips on images.
The easiest thing that people don’t do, because these libraries don’t do it, is prefetching on a list. Whenever you’re scrolling a list, you’re seeing every image load while you’re there. If you prefetch the next one or two images, that’s going to give you less frame drops when you’re scrolling. That’s going to give you better engagement because people aren’t waiting there. If you prefetch too many images, you’re going to be competing for resources that are going to be thrown out, but prefetching the next one or two items makes a better experience.
Beyond this, these do not solve your image memory problems. The easiest thing to do is disable, make the images not work to profile it, and see how much images are costing in terms of memory on your app. The easiest thing is to break your images and see how much memory that saves. Also, save images that don’t have the larger image than you’re displaying and resize the image to the size that you’re displaying it at. Cloudinary is a cool hosting service where you can request images in a specific resolution you want. They do work to make it like that. But when you’re doing too much resizing of your requests, you have to make sure that you’re also not breaking your caching by requesting similar images (so there’s a trade-off).
I am a fan of enabling largeHeap. An app we enabled it on, we saw about 1/4 as many out-of-memory crashes as we had previously. Google discouraged it because it causes you to have other apps in the background shut down, so switching between apps is not as good. But at some point, that’s not your problem. There’s also the garbage collect will take longer and you can use it to mask memory issues (which you shouldn’t do). In general, largeHeap is good. If you go to the Play Store and you search largeHeap, you can download a cool app that will show you all the apps on your phone that have largeHeap enabled. You’ll notice almost everybody has taken the plunge and is using largeHeap.
When you’re thinking about memory and images, it’s a simple formula. Some people are looking like, “this is a two kilobyte JPEG. Why is it so big on my actual memory?” The memory it’s going to take is its pixel width times its pixel height times four. That’s setting your expectations. The standard format they use is ARGB_8888 - it has four bytes it uses to store the color space. You can reduce it to times two if you use RGB_565. Colors very similar to each other, you will see less color spaceand it also can’t do alpha, but it will halve the amount of usage that your memory does. You can think about doing that on something like is low ram device or a low API. That can be a good way to save on out-of-memory issues.
Facebook has released a magical library that solves all of the out-of-memory issues on APIs before 21. There was a bug in the Android system before API 21 where you could use memory outside of the app’s memory. They used that so it creates the weird situation where you have no out of memories on your older devices, but then your newer devices perform worse. Because they’re loading it much different than other libraries, they can do things like progressive JPEGs, where your images load in layers. But it’s much deeper implementation into your app. With Glide and Picasso, you can keep it at a very high level away from your code. Instead of using imageViews, you have to use their type of Drawees, and there’s much more insular code you get.
I’d recommend Fresco if you’re targeting lower devices, you’re expecting to have many need for out of memory on older devices. Beyond that, I’d stick with Picasso and Glide.
Memory - Leaks
LeakCanary is the magical tool these days that helps you figure out when you’re leaking memory. Leaks are not all of your memory problems, but it’s worth keeping an eye out for them. When you add LeakCanary into your app, it’s going to automatically start watching for memory leaks with your activities. That’s a mistake I’ve seen people do before, that they add LeakCanary to their app. They fix one or two leaks, and then they think that they don’t have any more memory leaks. It only starts out watching activities. If you use this code here (see slides), you can make it watch fragments. It still doesn’t mean you’re not leaking code elsewhere. Activities and fragments are a good place to watch, but if you don’t set a watcher to an object, you don’t know if it will be leaking or not.
Briefly, how it works is cool. As a weak reference to whatever object you’re referencing: you attach it to something you expect to no longer be referenced. You put it in the on destroy section of a code. Then it does a garbage collect. If the object is still there, it knows that there’s a memory leak, so it sees what’s holding on. It gives you this reference of what’s going on over there with the home fragments. I find this difficult to read (some people think it’s super easy. Some people think it’s impossible.).
A brief view of this example here (see slides). You start at the bottom (is generally the best thing; that’s the thing that’s being leaked). I have home fragment. That’s referenced inside pin grid fragment - pin grid fragment is the parent of that object. Then we have this weird $0 - it explains there is that’s a runnable. There’s a handler inside the object that has a runnable. The code (that’s not mine), the Android OS, is the handler working. The handler has the runnable going. I need to make sure that the handler no longer holds onto the runnable. In this case, I could do on destroy view, I clear out all my runnables and the handlers, and that would fix this memory leak.
You could also use this random tool called WeakHandler, which makes handler references to be weak references. It can be garbage collected if you go to another one. The downside of that is they can be garbage collected - if you’re not expecting them to be garbage collected, that could come as a surprise. It’s worth noting that most memory leaks I run into is generally a non-missed class is holding the outside class while it’s still going (that’s the most common place to look). It’s worth also having a bug triage approach for your memory leaks. There’s nothing worse than having 70 memory leaks on your phone, and not having any next step to what are we going to do about this. As a team, figuring out what you’re going to do when we see a memory leak is always a good idea.
We all know the blood wars that are going on these days. We got activities was the old standard. Then you had fragments because we wanted something reusable. But then we made this great graphic showing how complicated fragments are. Trying to find another approach, view-based architecture, it tends to be the more popular one. View-based architecture you have a frame layout, and instead of dealing with any of the native Android navigation stuff, you replace whatever’s inside the frame layout with the view you wanted to be there. The hope is that it solves the complexity of fragments.
Fragments have been simplified, and newer versions, they’re much more stable these days, but there’s still an argument to be made for view-based architecture. If we’re making that argument, we should look at some of the libraries (the bottom 1/2 of that graphic, see slides).
Where it all started was Mortar and Flow. I believe they were the first ones to popularize view-based architecture. Flow was the actual one that does the view-based architecture, but they often go together, where Mortar’s doing a model view presenter-type approach. When you do view-based architecture, earlier beware of weird solutions.
You could find out some weirder, but nothing is built around your app being view based, and you’re going to run into random issues as you go, that this one library was built, it works well for activities, but views, you’re going to have to solve it. There are already standard issues you’re going to have view based, things like on-activity results, on permission. Even the back button and figuring out the navigation, you’re going to have to figure that out on your own with view-based architectures.
Mortar and Flow do the basic navigation step, but they don’t solve all of the common issues (e.g. saving state on the screen).
Conductor came with the issues we see every view-based architecture has, and they created a solution: save states, transitions, etc. But you’re still going to have compatibility issues with them. Overall, they are very good, and they’re architecture agnostic, so you can do it MV.
Scoop is from Lyft. They use it in production. It is the most production tested of the view-based architecture approaches. They don’t save state, so Lyft can’t save state. That’s the one big thing that’s lacking, out of the main common problems you’re going to have with view-based architecture. The transitions are easy to use, but limited, which is somewhat disappointing with view-based architecture, which you can somewhat do good transitions.
Overall, if you don’t need to save state, Scoop is probably your go-to, most production-safe one. Conductor is also very good. I’d recommend thinking of those two.
Model View Presenter (MVP)
There are a few great model view presenter libraries. Model view presenter is the basic idea that we want to separate our code out so that the view logic, the core Android stuff, lives together, and then the presenter is separated, and we can unit test this presenter. Hypothetically, we can change out the views without having to completely rework the business logic that’s going on.
There’s many ways to do MVP. The first place to look I’d recommend is Mosby. Even if you don’t use Mosby, they have great tutorials and Android-specific contexts (here is what MVP should look like, here is how it goes). The main thing it provides is having view states for your presenter.
Nucleus is very similar to it. Mortar, part of Mortar and Flow, was one of the early ones, but it often has more problems (upgrading) than it’s worth. The most common that I see companies do is build MVP themselves: On create, you start your presenter; on destroy, you stop your presenter. Each activity or whatever your view’s based on has a presenter that sticks around with it.
Trade-off for all of these is that while it sounds simple, when you’re saying one use case, there’s many edge cases to think about with MVP. You have your adapters, your view items. Making sure everybody is on the same page about what lives where and finding ways to enforce it is the hardest part about MVP. But the end result is that you get much more unit testable code. You can get much more stable code.
Testing - Performance
I had a bunch about testing here, but I didn’t want to keep you guys for three hours, so we skipped to use JUnit 4 for unit test. You have a build system on Gradle, have Espresso. Those are all fairly standard. One thing that’s not very standard is a performance testing tool.
There’s two main approaches for doing performance testing. Google tends to advocate looking at frame drops using a Systrace-type approach where they have a code lab that will give you almost working code (but not quite working code). That allows you to do a automated performance test to see if you increase from rate drops.
NimbleDroid has a free trial option where you can upload your APK, and it will automatically test cold startup, a few other key flows in your app you can do. They provide a timing and a breakdown of the methods they go into and happen. It’s free to do a trial (e.g. upload your APK and get your cold startup time and see if there’s any obvious problems). If you want to use it for regression testing tool, it is expensive, but it will help you see the, like somebody made a change and suddenly our startup time is slower, or our key flows are slower. If performance is a top-level goal, it’s well built for that.
One thing people see a lot when they’re thinking about performance is JSON. There’s many JSON libraries. My first caveat is people like to look at JSON libraries purely on performance. I think that’s a mistake. I took our largest request I could find (150 kilobytes, 30,000 lines, huge JSON file). I ran it thought GSON and Jackson. It came out around 20 milliseconds, which is not crazy for something that’s that huge.
My first recommendation tends to focus more around something that’s easy to use, something that’s well documented. You want to have the support. You don’t want to spend weeks of developer time trying to figure out an issue because there’s no stack overflow results. Unless performance is super high level for you, GSON and Jackson are the biggest, most popular ones, GSON especially on Android.
Moshi is Square’s approach on it. It follows very similar to GSON. Tries to give some opinions on how you should do GSON well. You can agree or disagree with those, but it’s not going to lead you too wrong.
If you do care about the JSON parsing performance and you want it to be faster, LoganSquare is a good option. They do a lot of the work that is done at run time, and the other libraries, they do it a compile time. That will get you about 1/4 of the time, or four times performance gains, compared to the other libraries. If you really care about JSON performance, you should not be using JSON.
Instead, you can use Flatbuffer. JSON’s human readable, Flatbuffer is not, but because of that, it loses a lot of the extra junk. But Flatbuffer compared to JSON will be almost like non-existent time in parsing. Facebook did this conversion. They saw about 10 to 15% increase in startup time, or improvement in startup time. They also saw better use of memory. But it is much harder to work with than JSON, so use with caution.
Database - SQL
For databases, we know the common SQLite - our standard Android library.
An easy thing to throw on top of SQLite is SQLBrite; this gives you a reactive interface with the databases. You can look at your user. If your user has any changes, you get a notification, and you can update your UI appropriately. It’s a very small library. It’s neat for working with SQL.
SQLDelight is also from Square. They make many libraries. It tries to avoid being a full ORM, taking the argument that ORM tends to leak too much into your code and tends to force you to do too many things you want to do, but it still tries to make working with the database much easier. It makes the APIs type safe, it makes organizing the SQL statements good (tries to make it easier without being too heavy).
If you are willing to go for a full ORM, there’s many good ones: GreenDAO, OrmLite, DBFlow are some of the more popular ones (there’s a handful more). They’re all going to say that they’re the greatest thing ever, in terms of performance. They each have graphs of them being like 100 times faster than all the other ones. My recommendation is to find one that’s easy to work with as a first priority, and second priority that they’re going to be pretty good in performance.
Database - NoSQL
Realm is the NoSQL approach. It is super fast. Unlike comparable database options, Realm is that is well documented and supported. This can end up saving you a whole lot of development time, regardless of the database type.
Another NoSQL approach that Google would recommend is LevelDB. Key-value pair, but it comes into the “you can figure it out, but you’re going to have to figure out each step on your own”. Realm db helps you avoid that.
The biggest caveat for Realm database is that it is huge in your app (I think it doubled our app size when we initially added it). The reason for this is that it’s a native library. For each chip architecture, they include a complete copy of the Realm database in your app. To avoid that, you can put this code into your Gradle (see slides for code), and this generates a separate APK for each architecture. That will reduce its size to however many architectures you split it out into. It’ll make a pretty huge save in your memory size. This is true for any native running C++ codes, so you’ll see gains in Crashlytics and a few other popular libraries.
There is another cool Google Play Store app, Native Lib. - it should show up in the Play Store it’ll show you what native libraries they have so you can see if they’re making you download the APKs libraries for code that’s not for your phone. But having this reduces the memory size. If you’re doing a hard solution to save data on your phone, you’re probably expecting to have data on your phone. If you’re building this for instant apps, don’t. If you’re building this for Realm for using people that have small storage on their phone (why are you storing a lot on their phone in that case?). But overall, it’s a very great database solution.
Database - Mobile Platform
On the other side of it is a mobile platform: the mobile developers dream, that we don’t need any of those server guides anymore. We can build the apps ourselves. It used to be Parse was the best example of it (may it rest in peace).
Now the biggest names in mobile platforms are Firebase and Realm. they’re both NoSQL models, which have some querying challenges to them, but are very, very fast. They’re very good for synchronous updates to get to the. Chat is the most common solution, where you send a chat message, and suddenly all the phones update. What these do is instead of having to build your database and instead of having to have your server guys, you can have one of these solutions. It’s great for building something very fast, in terms of building an app and getting it out the door right away. It’s also great for having it work very fast.
Realm’s distinction from Firebase, is that it has a offline-first focus. Both of them can do offline, but Firebase does it more as a afterthought, while Realm does the primary use case, in many ways, is supporting offline. Many apps almost don’t need online behavior. If you’re thinking about a run tracking app that, we want to sync our runs, but primarily we want everything to function perfectly offline, and then to have the online work fine. Realm stands out for that.
I like analytics. This lean startup type of saying is. If you can measure it, you can optimize it. If you don’t have numbers on things, going randomly, you’re guessing at how to make it better. I like to have detailed analytics to understand is this change making things better? Are people using this feature? How can I improve it?
Analytics are the perfect case for wrapping some third-party library. If you wrap your analytics, ideally, then you should be able to completely replace your analytics service without touching any of your real code. You should be able to add a new analytics service so both of them are going at the same time. It’s a very good case to have a wrapper around your analytic code.
If you’re adding analytics, the easiest thing to do is log your screens, log what screens people go to, put it in your abstract activity. That’ll get you many value quickly.
The common mistakes I see people making with analytics are they get lost in adding too many analytics. They’ll add a thousand events everywhere, and then nobody knows what this analytic means. Then they don’t get too tested, so you’re really, registrations only counts for 2/3 of the registrations. be very cautious when you’re adding analytics. Try to make the naming simple and standard. Don’t use analytics to delay easy decisions. If there’s an easy decision, do it. If your analytics are telling you “people don’t like free money”, assume your analytics are wrong first. It’s easy to get a wrong conclusion. If you have a big conclusion, first thing to do is double check that there’s nothing crazy on your analytics side.
Firebase used to be a realtime database, and then Google (to confuse us) started calling a bunch of tools Firebase. Firebase Analytics underlies all of the Firebase tools. what Google saw is that iOS and Android developers were solving these same problems. They were using these third-party services and paying money to solve all these problems. What they figured is we can make it easier to build apps, and it’s easier to build apps, more money we make. They made competitors for all these tools. Trying to make them better, what they did is they tried to make their analytic tool power all the tools. The vision of this is if you get the crash reporting, knowing your analytics, it can sort your crashes by how much revenue you cost them. Then because your notifications are also looped into that, you can send notifications to the most expensive crashes, and telling people here’s some free credit. We know that you’re about to buy because you’re a high-value crash.
Getting them all together is a cool vision. They’re not there yet, to be perfectly honest. Some of those things work, but they’re still trying to build out, and it isn’t a top-line focus for Google. But many the tools are still better (not as good as competitor tools). The analytics, though, it’s free. You have unlimited events. There’s not too much reason not to use it.
Another popular one is Google Analytics. It gets you many basic information about your user very easily. It’s the only one I’ve seen that has this cool behavior flow graph, where you can see how people are moving between your events. It still is a bit old. Its style of logging events is older, different than all the other analytic tools. It’s also free, and Firebase is meant to replace it.
I personally think that A/B testing is over hyped. It’s often used to slow learning so that you can figure out, instead of deciding this easy naming decision, you can put it out into your users, and you can find out that 55% of your sample size of 100 users prefer this one. That’s often how you see A/B testing used.
It’s also excellent for making bugs. Essentially, the formula for how many versions of the app you have by your A/B test is two raised to the amount of A/B tests you have in your app. If you have three A/B tests in your app, there’s eight different versions of your app that all have to work together. You’re probably not testing all the different versions of your app that you’ve generated. But you sometimes can find the learnings that are not otherwise for these big decisions that aren’t clear.
My go-to solution is user analytics service. Mixpanel, specifically, has a very good A/B testing tool, and it build in with your analytics. There are many tools built for A/B testing. The biggest names, I’d say, are Optimizely and Apptomize. Optimizely is a bit more web focused (they’re all a little mysterious on their pricing). They help you serve different versions and they help you tell is this a statistically significant variation on this one thing being better than the other.
Firebase Remote Config, technically, you can do A/B testing with it. It’s not built for A/B testing. It’s built for being able to send key value things from the server without needing developer updates, but they then were like, we can do A/B testing. You can send two different versions of a key-value pair, and then you can have codes to decide what to do. Then you can pass it into your own analytics surface and figure out how to parse it. But at some point, you’re doing all the work if you’re using Firebase Remote Config.
You can also A/B test store listings. That’s worth doing, especially if you’re looking at different countries. It’s relatively easy to do, and you can get some gains there.
Like analytics, there’s a thousand solutions. I’m only going to cover a handful of them.
The first thing people look at, especially if they’re new to Android, is the Play Store. Whenever you get a crash, you have the option to press report. Everybody ignores that. About 1% of your crashes show up in the Play Store. Occasionally, though, if you have a very high volume, very mysterious crash, it might be worth pulling that up because people also give comments on what happened. That said, you’re getting 1% of the crashes there; “99% of what people say is it sucks, it crashed”. But every once in a while, they’ll be like, “I was rotating my phone on this screen and it crashed” (You’re like, ah, you’re the developer. I appreciate it.)
Crashlytics tends to be the most popular. That’s part of Twitter’s Fabric Suite, or now Google’s Fabric Suite. It’s free to implement and gives you a very good, high-level, organized view of your crashes. You’re still going to have to sort and figure out how to prioritize them on your own. It has some issues in that it’s hard to search and query through. I’ve seen people use Bugsnag in addition to that if they want to query and search through their searches a bit better. Bugsnag tends to be pretty good for that, but it’s not free.
Instabug and Telescope. Telescope’s a library. You put it into the bugs, a third-party service. What’s nice about these is focusing not on crashes, but they give you the ability to shake your phone and to report if there’s something wrong. This is especially nice to have designers on your team who see something wrong, but they’re not going to file a JIRA ticket or go through all the work of making sure somebody does it, but you still want to fix the bugs that aren’t crashes. Having a tool in there so beta users, alpha users, people in your company can see something wrong and tell you to fix it.
As a developer, the instinct might be to use GCM or now Firebase Cloud Messaging, which are a rebranding. The problem is that those aren’t great for marketing people. One of the easiest things, if you already have a good analytics service, Firebase and Mixpanel (and many other ones) also send notifications. You can send them based on your event. You can say somebody who’s never made a purchase, but has made three searches, send them a notification to suggest that they buy something. Those are the easiest to go with.
Sometimes you want something a bit deeper. Urban Airship tends to be entirely focused around push notifications, at least that was their primary focus. They do things like notes that, “they’re only going to see these first 60 characters” because I know we all get these push notifications where we can only see the first few words of it. It’s like, “you’re not going to believe this…”, and it cuts out. Then you push and it brings you to a random screen, and you never even knew what the push notification was about. Urban Airship is nice in that they help you think about things like that and give you a bit more nuance and thought outs push notification service.
Kahuna is a up and coming popular one that I’m going to recommend strongly against, but I see it a lot. The pitch is cool. They use AI, so they figure out that you can send email or text message or push notification for this person. They figure out what time of day is going to work best and if they want flowers to go with their push notification. The last part they don’t do. But they also do text variations. They try to make it like, to give the best push notification possible, that idea is cool, and hopefully it’ll come together soon. But right now, on the base implementation level, they end up with many issues, and the users’ notifications get sent to the wrong users. Users get merged together and are treated the same. Be cautious when using them. Some people have had good experience. I’ve been burned by them.
There’s many great tools for making your development easier.
The go-to, easier tool is Butter Knife (@indVView). This makes your code prettier. Instead of having to find all your views, you can have, with find view by ID, you have @BindView. With Zelezny, it’s a Android Studio plugin. You take to the layout, and it automatically generates the BindViews and the OnClicks. It save you a little bit of time if you’ve been typing that out. The biggest qualifier here is that annotation processing will break your incremental builds. If you have Butter Knife, you’re probably going to not have as fast of incremental builds as if you don’t have Butter Knife. For me personally, I love all the annotation processing tools, where that ship has sailed. I’ve accepted that I’m going to have annotation processors break mine. I’ve heard people recommending trying to keep them all in the same module. That is the only downside, but many annotation processing is going to have that problem. Maybe one day it will be fixed. JRebel claims to fix it for incremental builds for specific libraries. Beyond that, Butter Knife is great.
Hugo is a Jake Wharton library (1/2 of these when I say Square are Jake Wharton. We’ll use them the same). It’s a lightweight library where you do @Debug.Log on top of a method, and then it prints out how long the method took and what the parameters that came in. It’s especially great for on-the-spot performance logging. “Do I need to cache this variable? Is this getting called too much and slowing down?” You can see, “no, this took 10 milliseconds. I should put this on a background thread; or, it took less than a millisecond. We’re not going to worry about this”. It’s very easy to get that into your code and get that number quickly.
Dart &Henson, you have intents; instead of having key value maps, they give you what the values should be. They help do a little annotation mapping magic to show what’s the value should be between the intents. The other approach that I tend to like better for this is having static intents in your activity that you’re calling. That way, you can pass in what you want as parameters, but both of these solve the problem of having mysterious key-value pairs. It makes it more tight, which can prevent errors.
If you love lambdas, you should be using Retrolambda. The biggest caveat is that it generates four methods versus normal anonymous classes. I think that you can also use Retrolambda to make your code less readable, but that’s something that you’re going to have to make the wise decision on.
Harder (at First)
Let’s talk about how to make it harder, or at least harder to work with at first. Overall, these are good tools.
RxJava, for those who haven’t heard about it, the idea is that code used to be you start at point A, you get to point B. But in the Android world, the mobile world. You start at point A. Then the person clicks on something, so you have to do something else. Then a notification comes in, so you have to do something else. Then a database request comes up. Reactive is trying to make it so that you can react to things coming into your app, and it helps you organize those and move it in the right way.
It’s harder at first because it’s a learning curve. If you’re eating the code, it makes it less clear, especially for somebody who hasn’t done RxJava. Easiest thing is networking. But if you have a few standard cases, you can use those and make sure the best practices are. When you start having all of it, it has a very powerful and you can use it for almost anything in your app you want to do, but when you do that, so whenever you hire a new developer, you’re going to have to teach them what you’re doing in every single screen, so there is some caveat to that.
Also, it’s easy to use wrong. Even many talks on RxJava will miss some best practices that you should be doing. For example, if you don’t unsubscribe from a network call, then you can get the call back and you can have a crash happen because the screen’s no longer there. Use it with caution but overall is good.
When I said beware of unusual approaches using a language that is not built or a new language, is about as unusual an approach as you can get. Kotlin does produce much cleaner code. It’s much more conduced. It makes it easier to read your code, and since there’s going to be less nonsense there, it’s the core stuff that you need. It also does help with no pointers.
The biggest downsides are that it will break things randomly, and if anything ever breaks, you’re going to suspect Kotlin. It might be Kotlin’s fault, and it might not be, but your going to lose many developer time trying to figure out if it’s Kotlin’s fault. You’re going to update the Gradle build, and that’s not going to work. It might be Kotlin’s fault, it might not be Kotlin’s fault, but you’re going to have to look into Kotlin.
The same thing, your Android Studio is crashing randomly, or ProGuard’s not working because it’s getting rid of some Kotlin class. These happen pretty often and it’s very hard to reverse. There’s many trade-offs to think about with Kotlin. We use it in our code. We do have some issues from it, but at the same time, it does make code reviews faster for the people who do know Kotlin. There’s a learning curve for the people that don’t. There’s a trade-off. This is one you have to make your own decision on.
If you ever worked with Camera in Android, and you’ve done it natively, which is not a crazy way to do it, but you’ve discovered that there’s a camera one API and a camera two API, and the camera two API is not much better than the camera one API, and you have to use the camera one API if you’re supporting reasonably new, unless you’re only on very new devices. My first recommendation, if you have a camera use case, do a camera intent. Let somebody else’s camera app do it if it’s not a very core use case for you.
If that’s not in the cards, there’s a library called Material Camera, which I’ve forked before. You can add your own look to it. It creates this camera to the side. It solves issues. But when you first set it up following the tutorial, things are going to be displayed sideways, the picture’s going to be upside down and you’re going to have to test on every single phone because they all mount their cameras differently. There’s many edge cases to think about. Material Camera is great for having figured all those out. It also does video. That’s video mode. You can change the UI on it, and it works pretty well. Again, if Camera is your very core use case, you probably should build it out again, but this gives you a happy medium ground.
With Camera, every app I’ve been on that has cropping does it terrible. I don’t know what the deal is. You try to scroll out, and suddenly your crop is all the way to the middle. There’s a nice library called Android Crop. It provides a very simple new activity that does the crop.
As Android developers, we’re thinking about the developers all around the world. A few things you have to think about if you’re developing for developers everywhere is network emulation: you want to be able to test on lower quality networks. This is a bit harder to do on Android than iOS. The emulator technically provides the native emulator, AVD, but normally, my experience is if you go to anything that’s 3G on the emulator, none of the apps work. I feel like some of my apps sometimes work on 3G.
What I’ve had the most success with is Charles proxy. This is something that you can tell it to go through this proxy on my computer. It’s fairly easy to set up. You can tell it, here’s how fast the download should be. It still has a challenge that you need to know how fast the network shaping could be. Even saying something like Brazil 3G is almost a meaningless statement - 3G is very different all the way across Brazil, it’s all very different everywhere. At some point, you have to try to find some numbers, pick some numbers.
There’s a handful of resources out there, but it is one of the hardest part for picking network emulation. There’s a tool called Augmented Traffic Control, which is from Facebook. This allows you to connect to a server, or set up a router, and then you can set the profile - when you connect to the router, how fast your internet should be. If you have non-developers testing, they can still connect to this wifi network and set what level of connection they have. Product managers and testers in general should be also looking at lower quality networks. ATC server does a very good job of helping out with that.
I believe the new Android O is hinting they’re going to have something similar to AutoFitTextView, but for now, this is a library that whenever you have text that’s too big for the text box, it shrinks it. If you’re translating German, which is much larger than English or many letters for the same things, it will shrink it into the box. It’s not your first solution for translation, but it’s better to have them shrink into the text size, rather than fill up an entire screen and cover everything. It will resize your text (change your text size so it fits), and tends to be a nice safety measure for translations.
YearClass is another library from Facebook. It classifies phones by which one would be the newest for that year. We were saying 2015 is the highest number for it, but you don’t care about doing something special for phones that are made in 2016. It is nicest for an analytic point of view - understanding that this crash is, even though it’s happening on Android OS Marshmallow, all the phones on it are very low-power phones, and sometimes that happens. You can get a sense of the, here’s the year class of that phone to understand the errors by that rate.
Connection Class is a very cool tool for understanding the connection quality of the user (t’s the sample class I recommended wrapping at the beginning). It classifies their network. Instead of looking at 4G, 3G, because that varies so much, it looks at their bandwidth, and it says excellent, good, moderate or poor, or unknown. It does that by sampling how much the download speed is. Then it moves the average. The value of this is, for example, you’re a image site that shows many images, you might want to reduce the image quality for lower quality networks or do something else, e.g. prefretch clever. There’s many things you can do by knowing the bandwidth.
The biggest caveat with this is that its sampling is wonky. What they do is they give you this start sampling and stop sampling. You’re like, I’ll start when I open the app, and stop when I don’t, when I close the app. What they’re doing is they’re looking at the amount of data you’ve downloaded, and they’re refreshing every second. If the user has your app open and they’re not doing anything, they’re going to think you’re on terrible network. What we’ve done with that is that you can have it going while you, in a interceptor for either your network calls or specifically if you have many image calls, that works well. But beyond that, it is very good to have a strong understanding of how good your person’s network is.