Kotlin Platform Types, Nullable Annotations and AOSP: A Cautionary Tale
I think it is fair to say that most of us are super happy with how nullability is handled in Kotlin. The overall code that we are writing with nullability in mind is mostly cleaner looking and helps us avoid the pitfalls we saw in Java. Having said that *Platform Types* are a place in particular that I ran into where I feel like the developer experience and the impact to the user is worse. If you are not aware, *[Platform Types][1]* are types that were created when Kotlin cannot infer the nullability of a type from Java.
I think it is fair to say that most of us are super happy with how nullability is handled in Kotlin. The overall code that we are writing with nullability in mind is mostly cleaner looking and helps us avoid the pitfalls we saw in Java. Having said that Platform Types are a place in particular that I ran into where I feel like the developer experience and the impact to the user is worse. If you are not aware, Platform Types are types that were created when Kotlin cannot infer the nullability of a type from Java. Let’s look at the URI
class in Android as an example, and specifically let’s look at the getQueryParameter
method:
public String getQueryParameter(String key) {
return null;
}
If you will notice the return type in Java is String
which can inherently return null
as a valid response but Kotlin will not attempt to infer that and instead will mark this type as a Platform Type or String!
. Because String!
is able to be null
we need to immediately treat it accordingly, which means using the safe call operator, ?.
.
Now there is another way for us to solve the problem if you have access to source Java code that is returning here. You can add one of six approved annotations that denote nullability, which will tell the compiler how other code should expect to handle the nullability of the return types. The six types used here are the following:
• JetBrains (@Nullable and @NotNull from the org.jetbrains.annotations package)
• Android (com.android.annotations and android.support.annotations)
• JSR-305 (javax.annotation, more details below)
• FindBugs (edu.umd.cs.findbugs.annotations)
• Eclipse (org.eclipse.jdt.annotation)
• Lombok (lombok.NonNull).
My assumption is that this list is just widely popular and/or used annotations in Java. So to fix our problem we could do the following:
@Nullable//If the return type can be null
public String getQueryParameter(String key)
Or
@NonNull//If the return type can NEVER be null
public String getQueryParameter(String key)
Cool... so we’re done here, right? Nope, not at all. We don’t have access to the source code because this is an Android class, so what have they done to tell us the return type's nullability? It turns out that Google has its own internal Annotation to handle nullability which is not on the supported list. And the example class URI that we are using actually uses that Annotation as of Pie.
So, what’s the problem with that? Well, let’s expand the scope of our example. Still using uri.getQueryParameter(...)
let’s create our own method that takes the result of that method and consumes it:
fun handleParam(param: String)
As you can see, in this case I am saying that the String type of param should not be null. And let’s now pass it the result of our URI param:
val param = uri.getQueryParameter(“id”) // Get the ID query Param
handleParam(param)
If the result of parsing the query param is null
, when we call handleParam(...)
with the null
value an IllegalStateException
is thrown and your app crashes. So basically we have swapped out an NPE for this new exception. What’s worse is that none of this fails at compile time. As far as the compiler is concerned, even though Platform Types are technically nullable, it will not proactively tell us that a possible error exists.
The result is that we need to be very careful about the return types of the Java libraries that we consume. The first and best solution is to find Kotlin based equivalent libraries where Platform Types do not exist. Though this is not a problem I suspect will be around for a long time it is something to keep in mind now and be wary of when interacting with Java code.
Related Links
• https://imnotyourson.com/which-nullness-annotation-you-should-use-on-android/
Gradle 4.1 Task Dependencies
While migrating to Gradle 4.1 an Android Gradle Plugin we discovered and issue that wasn’t documented. I would love to share this issue with you and explain how we fixed the problem.
While migrating to Gradle 4.1 and Android Gradle Plugin 3.0 we discovered an issue that wasn’t documented. I would love to share this issue with you and explain how we fixed the problem.
The Problem
My team develops libraries that get integrated into the apps we have at Under Armour and, because of that, most of the time we are working on AARs that are consumed by those apps. In our deploy script we run a series of tasks which, up until Gradle 4.1, all ran sequentially (even though you could enable parallel tasks prior to 4). So here is a rough idea of what our CI server runs when we merge code in:
./gradlew clean assemble artifactoryPublish
The problem we found after upgrading to 4.1 is that our tasks were now being run in parallel and our artifactoryPublish
task would run before the end of the assemble
task. This as you can imagine adds some complication, unless you can get the finished AARs from an alternative reality where the build is already done.
This is now parallelized due to the Worker API which was introduced in Gradle 4.1. I won't delve too deeply, but this new API was made with Android in mind and allows work from a task and build level to be parallelized with minimal effort (from the app developers side). The effort is mostly on the plugin developer, which I am not so I am going to refrain from saying more. Although I will note there is a great talk given by Paul Merlin and Gary Hale on this topic I would suggest watching.
After looking a bit more into the Worker API, I found that not only will this allow in-task parallel work, but it will also allow tasks with no relationship or dependencies to run in parallel as well:
(From the Writing Custom Tasks Guide): Of course, any tasks that are dependent on this task (and any subsequent task actions of this task) will not begin executing until all of the asynchronous work completes. However, other independent tasks that have no relationship to this task can begin executing immediately.
I was unaware of this initially, so after about two hours of experimentation we found that even though there is no way to turn the feature off there are two options for getting around the issue.
The Solution
Solution 1: Run tasks one at a time
Instead of running:
./gradlew clean assemble artifactoryPublish
We could do the following:
./gradlew clean
./gradlew assemble
./gradlew artifactoryPublish
Though I think this looks ugly, it gets the job done. Many people will scoff at this, there are times when the quick-and-dirty helps us get through a gauntlet unscathed. Most of the time I disregard these types of solutions, but in a time of need it is still valid.
Now, the reason that I say Solution 1 is viable is that I suspect many people in the Android community are not Gradle experts. I have spent 10-15% of my work time over the last year learning more about Gradle and I feel confident in my knowledge.. I suspect however, that I have still probably only touched 10 to 20% of Gradle.
Solution 2: Create task dependencies in your build gradle files
If you get into a situation where tasks are running out of order due to the parallelization of tasks in 4.1., think about adding task specific dependencies. In order to enforce the dependency between the assemble
task and the artifactoryPublish
task you can put the following line in your build.grade:
artifactoryPublish.dependsOn assemble
Note: If you have multiple modules in your gradle project you can cascade this to each of them using the subprojects
configuration block in your project build.gradle
.
The dependsOn
method is part of the Task API and allows us to tell our artifactoryPublish
that we depend on the assemble task to finish before it can run. Creating this dependency will now stop the assemble task from running before it should and causing build issues.
There have been many things that have changed with the Android plugin and the team at Google have done a great job documenting almost all of these changes. However, there are always things that are overlooked or may be under documented. I hope if you ran into task parallelization issues that this helped you get through the issue and I look forward to hearing other people's experiences.
Re: Chathead Basics
In Pierre-Yves Ricau's blog from 2013 he walks you through how to make a very basic version of Facebook's Chatheads, which if you haven't read is a great read and is yet another reason why I love Android. At the end of his blog he asked the question:
"Does this imply that Facebook Chatheads (or any application with SYSTEM_ALERT_WINDOW permission) is able to conduct keylogging and take screenshots at arbitrary time?"
It was a great question but after trying to explore more with adding views directly to the window I think I found another concern.
In Pierre-Yves Ricau's blog from 2013 he walks you through how to make a very basic version of Facebook's Chatheads, which if you haven't read is a great place to start and highlights yet another reason why I love Android. At the end of his blog he asked the question:
"Does this imply that Facebook Chatheads (or any application with SYSTEM_ALERT_WINDOW permission) is able to conduct keylogging and take screenshots at arbitrary time?"
It was a great question but after trying to explore more with adding views directly to the window I think I found another concern.
What was I trying to do:
In the advent of mobile phones I would have never thought that phones larger than 5 inches would be a market leading trend. However, here it is 2016 and I have a 6 inch Nexus 6p, which has been an amazing device despite it's size. So I set out to see if I could record any touch on the screen to see if I could start to build a heat map from my device. In doing so I made my phone un-usable. My solution was just a dirty proof of concept but I think what I proved is to be very careful adding things directly to the window.
My implementation:
As I said before right now my approach to this problem has been very basic. I took just a plain old View, made it the size of the screen and attached a touch listener to it. I let the touch listener return false, which by definition should have allowed other views to receive the touch. Most of the code is covered in Pierre-Yves' post so I won't go into much detail. What I found in practice was that I was now no longer able to interact with my screen at all.
touchView = new View(this);
WindowManager.LayoutParams params = new
WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
WindowManager windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
windowManager.addView(touchView,params);
touchView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
So it is apparent that any app with the SYSTEM_ALERT_WINDOW could render a user's phone useless or potentially could hijack the user's screen in a ransomware style attack. I am curious if anyone has found a way around this or has taken a different approach than me to do something similar. I don't want my idea to die off because though I love the Nexus 6P the one hand UX is absolutely horrible and I am trying to experiment with some different paradigms to see how they could make the experience better.
Note:
Since his post there is one slight change in how you ask for permssion to draw on the window, which I have covered in the following gist.
Android Studio Tip #001: Bookmarks and Favorites
Chances are if you are an Android developer there are a bunch of really amazing features hiding in Android Studio that you have probably yet to discover. I am definitely not the exception to that and yesterday I stumbled upon (/took the time to understand) a few features that have so far made my life much easier. These features are Bookmarks and Favorites.
Chances are if you are an Android developer there are a bunch of really amazing features hiding in Android Studio that you have probably yet to discover. I am definitely not the exception to that and yesterday I stumbled upon (/took the time to understand) a few features that have so far made my life much easier. These features are Bookmarks and Favorites.
Conceptually these are about the most basic features that we could possibly ask for and are pretty self-explanatory. Besides the fact that it is painfully obvious what the features do, I was also frustratingly upset with myself because of where these features are located, which is in the Tool Buttons bar on the left side of Android Studio. These features weren't even hiding from me but staring me right in the face.
Favorites
So as I said before the Bookmarks and Favorites features are embarrassingly easy to understand. Favorites can be made of any file, from Java classes to XML resources and everything in between. To add the current file you are on to Favorites you can simply press (⌥⇧f) or you can right click on any tab and then click on Add to Favorites
. You will probably need to create a new Favorite list or if you have any other lists already created you can easily add to them. I have found that it is helpful to name Favorite lists after tickets I have worked on or keywords so that I can easily find them.
Speaking of finding Favorites there are numerous ways you can get to the Favorites menu. The easiest way by pressing (⌘2) otherwise you can look on the Tool buttons bar (on the left hand side of the screen) and find the vertical Favorites tab. Note: If you are in Distraction Free mode you can easily show the side bars by pressing (⌘) two times. Also this view is searchable so if you start typing while you have something selected in that view it will start searching all of your Favorites and Bookmarks.
Bookmarks
Bookmarks are a bit more specific than Favorites. Bookmarks are done per line and can be created by pressing (fn F3) which will visually put a check next to the line, similarly to debug break points. You can as well have mnemonic bookmarks, by pressing (fn ⌥ F3) which in essence allows you to swap the checkmark out for number 0-9 or character A-Z. It's important to note that you can't reuse mnemonics. As well another difference from Favorites is the ability to give Bookmarks descriptions. Simply navigate to the Favorites tab on the Tool Buttons bar and right click on any Bookmark and select Edit Description
.
There you have it a great way to create and organize Favorites and Bookmarks without cluttering up your IDE with tabs.
TL;DR
Favorites and Bookmarks are possible in AS and amazing!
Favorites
- Add (⌥⇧f) or right click on tab
- View/Edit/Remove Favorites (⌘2)
Bookmarks
- Add/Remove Bookmark (fnF3)
- Add/Remove mnemonic Bookmark (fn⌥F3)
- View/Edit/Remove Bookmarks (⌘2)
Mockito Verify Inconsistency
When I initially set out to blog about Mockito I wanted to write a post where I explained the Verify API. Though there wasn't much in the realm of posts around this topic, I think that the javadocs have ample examples. Maybe the only topic that would be worth covering for a second post would be good places to use Verify. I recently have been working a lot with the Verify API and found some inconsistencies that I wanted to share so that if you are using Mockito you can avoid the pain I had. So here it is two weeks later and here is my post about some caveats while using the Verify API.
When I initially set out to blog about Mockito I wanted to write a post where I explained the Verify API. Though there wasn't much in the realm of posts around this topic, I think that the javadocs have ample examples. Maybe the only topic that would be worth covering for a second post would be good places to use Verify. I recently have been working a lot with the Verify API and found some inconsistencies that I wanted to share so that if you are using Mockito you can avoid the pain I had. So here it is two weeks later and here is my post about some caveats while using the Verify API.
The Setup
I can't exactly share the original problem classes I had, yet, so instead I have recreated my problem in a set of examples. First, let's assume we have an interface called CallbackB
(original name, I know) and it takes SomeModel
as a parameter that we will say is only made up of a String.
public interface CallbackB{
public void someMethod(SomeModel model);
}
public class SomeModel{
private String text;
public SomeModel(String text){
this.text = text;
}
}
This interface defines the signatures for any class implementing it with the method someMethod
. Now let's say we have the following class that uses CallbackB
and we will call it ClassA
(again, super original):
public class ClassA {
private CallbackB mCallbacks;
public ClassA(CallbackB callbacks){
mCallbacks = callbacks;
}
public void doSomething(String text){
if(text.contains("Artoo")) {
mCallbacks.someMethod(new SomeModel(text));
}
}
}
You can see that ClassA
expects CallbackB
to be passed to it from the constructor, but I wouldn't be too focused on that, the important part is the usage of the method doSomething
.
You will notice that doSomething
takes a string and if the string contains the text Artoo (my dogs name... sorry, only thing that came to mind as an example string) then we will call someMethod
on our callbacks which is CallbackB
.
Ok, so that’s the setup. We have a class which will act as callbacks for us, as well as a class doing some business logic which triggers the callback.
Testing
So now let's say we want to test that our callback is called at the correct time and for the correct reasons. In our case, because we are writing an interface that we expect someone else to utilize to listen for things happening in our code we don't care as much about testing what happens when the concrete versions of the interface are called, but the fact that they are called. To do this we can utilize Mockito and a tool through it called verify
. If you have never used it I would highly suggest it. I will link to the docs for it but I don't want to spend time talking too much about how to use it. I may save that for another post if people actually want that.
So to test our interface our test could look something like this:
@Test
public void doSomethingIsCalledOnlyOnceAny(){
CallbackB mockedCallbackB = mock(CallbackB.class);
ClassA classA = new ClassA(mockedCallbackB);
String testStringHappy = "Artoo rocks!";
String testStringSad = "Rocks!";
classA.doSomething(testStringHappy);
classA.doSomething(testStringSad);
verify(mockedCallbackB).someMethod(any(SomeModel.class));
}
Hopefully, this test is straightforward, basically we want to verify that someMethod
is called only once and we don't care what the parameter passed to it is. This test will pass because of the way we built ClassA
, however if we take out:
if(text.contains("Artoo")) {
}
From the mix the test will fail because Mockito expects that the number of calls for that specific method should only be one. Now comes the oddity. let's say we use a custom argument matcher and also let's assume SomeModel has a getter to get the string. Here is what our ArgumentMatcher
could look like:
class IsValidSomeModel extends ArgumentMatcher<SomeModel> {
private String mExpected;
public IsValidSomeModel(String expected){
mExpected = expected;
}
public boolean matches(Object object) {
String actual = ((SomeModel) object).getText();
return actual.contains(mExpected);
}
}
Now that we have created the ArgumentMatcher
the new test should look like this:
@Test
public void doSomethingIsCalledOnlyOnceSpecific(){
CallbackB mockedCallbackB = mock(CallbackB.class);
ClassA classA = new ClassA(mockedCallbackB);
String testStringHappy = "Artoo rocks!";
String testStringSad = "Rocks!";
classA.doSomething(testStringHappy);
classA.doSomething(testStringSad);
verify(mockedCallbackB).someMethod(argThat(new IsValidSomeModel("Artoo"));
}
False Positive
So now we re-run the test suite. The first test will fail with the following error:
org.mockito.exceptions.verification.TooManyActualInvocations:callbackB.someMethod(<any>);
Wanted 1 time:-> at com.omitneedlesscode.mockitoverifyexample.VerifyTests.doSomethingIsCalledOnlyOnceAny(VerifyTests.java:26)
But was 2 times. Undesired invocation:-> at com.omitneedlesscode.mockitoverifyexample.ClassA.doSomething(ClassA.java:14)
However, the new test, mysteriously passes. Queue Twilight Zone music. I am still unsure why it is passing. However, I have come up with a temporary solution to the problem.
First and foremost, I would suggest not using the ArgumentMatcher
while using verify
. Instead I would suggest using the ArgumentCaptor it allows you to do assertions directly on the parameters themselves and seems to not be ailed by the mysterious passing test issue.
So let's say we want to go down this route. Here is a test with the ArgumentCaptor that will now properly fail due to too many invocations:
@Test
public void doSomethingIsCalledOnlyOnceSpecificCaptor() {
ArgumentCaptor<SomeModel> argument = ArgumentCaptor.forClass(SomeModel.class);
CallbackB mockedCallbackB = mock(CallbackB.class);
ClassA classA = new ClassA(mockedCallbackB);
String testStringHappy = "Artoo rocks!";
String testStringSad = "Rocks!";
String expected = "Artoo";
classA.doSomething(testStringHappy);
classA.doSomething(testStringSad);
verify(mockedCallbackB).someMethod(argument.capture());
assertTrue(argument.getValue().getText().contains(expected));
}
So hopefully if you are using Mockito, or thinking about using it, this helps make things clear. The Verify API is a great asset in your testing toolbox, but be weary of some inconsistencies. Use ArgumentCaptors instead of ArgumentMatchers and don’t give up on tests because you can’t get them to run. The work you put in will save you regressions to your code, new bugs and will always make your code better.
What developers should care about at I/O this year
Every year for the past 7 years the tech industry, financial analysts and tech aficionados alike post numerous blogs with rumors about the years I/O. This year is no exception. To give a different perspective, and maybe an altogether different approach, I have decided to come up with a few major things that Android developers should care about this I/O.
Every year for the past 7 years the tech industry, financial analysts and tech aficionados alike post numerous blogs with rumors about the years I/O. This year is no exception. To give a different perspective, and maybe an altogether different approach, I have decided to come up with a few major things that Android developers should care about this I/O. It's not to take away from the others who have spent time on an "I/O Prediction" article in the slightest, as I still love seeing what the rumor mill has to say. Hopefully, if you are coming to I/O to get something more out of your experience than just the keynote this will help you guide your course selection.
Code Labs
If I remember correctly, last year’s I/O did not have these. In 2013, I attended my first I/O which actually had the code labs and I was amazed how helpful it was. It makes a huge difference having someone there to help walk you through new APIs, to ask questions directly to a human being who has spent a good deal of their time working to understand how to use these APIs. In short, the code labs add great value to your I/O experience because it gives you scheduled time to work directly with all the cool things you just heard about in a subsequent talk.
This could be helpful not only with announcements related to this I/O, but could provide an opportunity to gain knowledge about other parts of Android you may be struggling with. Last year the lack of Code Labs meant that when I got home from I/O is when I really got deep into trying out new APIs and at that point it was much harder to solicit feedback. For some it was months until they were able to work with any of the Lollipop offerings and even worse for Material whose general support in pre-lollipop was decent, but still left us spending time exploring and asking questions on StackOverflow.
Battery, Rendering, Memory and Network Performance Tooling
This is a no brainer for many Android developers, but I feel it needs to be mentioned because making performance changes can be hard once you have written the code and even more complicated if you are reacting to bad press due to lots of jank. Colt McAnlis is a Developer Advocate for Android and has a series of youtube videos called Perf Matters. If you haven't watched this series yet, you have the rest of the day to catch up on these and if not please check it out after I/O. These are really great resources and worth your time to take a look.
Whether you are trying to get your app off the battery hog list, trying to make your UI snappy, helping to be a better user of system resource or trying to shave off data usage these talks will be a great jumping off point to making your app more performant and hopefully result in happy users.
It's important to note that this is in fact 4 different sandbox talks and thankfully they do not overlap at all, so be sure to attend them all.
Testing
Last, but not least is testing. I am unsure if this topic has even been covered at Google I/O in years previous, but I feel that in the 2 years that I have been in attendance I have yet to see anything major on this front. Hopefully this will be a really great series as well. Google will focus on both Unit (White box) and System testing (Black box) which is monumental and well timed with the recent announcment of the Android Testing Support Library which helps unifiy all aspects of testing in Android. Testing like many things in computer science is not a silver bullet, but makes a considerable difference in reducing your tech debt and in turn giving you and your team more time to work on adding all the new features we get tomorrow. It's also important to note that Google has done a lot to make testing a first class citizen of Android Studio. There is no doubt that these talks will be heavy in talking about how to structure, write, integrate and run tests. Not to mention a few years ago Google created a framework for system testing called Espresso, and this will be the first I/O talk that they will give about it so expect to learn a lot!
I feel like after seeing this years I/O schedule we are going to have a lot of tough decisions ahead of us. Not to say that any of the other talks are not going to be informative or worth attending. These are all extremely important topics and while the new stuff may entice you away from these just remember, the new stuff is recorded and when you are deep in a heap trace or debugging visual jank in your UI, was missing these worth the extra hours you will spend now going to look this info up?
What I want from Google Maps SDK for Android
For the past two months Google Maps and mapping has consumed a large portion of my life. During this time I have come to love and hate Google Maps V2. Besides just writing up a couple of issue trackers, I figured I would share my findings in hopes that it saves another developer the stress.
Touch Listeners
User input is super critical to be able to build robust mobile applications. The maps SDK falls short at providing developers the ability to differentiate between user input and programmatic camera changes. Currently the only way to tell that the user has interacted with the map is through the onCameraChangeListnener
which should be considered a hack more than anything else. The API docs even go as far to say:
During an animation, this listener may not be notified of intermediate camera positions.
The simple fact that it may or may not be notified is a bit irritating to me. I would rather the API draw a line in the sand and either give me all of the changes or just the beginning and the end. I would say for simplicity's sake it would make sense to just give us the update at the end of the animation, then create a new listener for map touch events. Even just the basic touch up and down events would be a huge win.
Markers
A large part of the reason for having a map in your application is to visualize data or information. One tool Google gives us to do this is the marker, in essence a bitmap that represents a location on the map. Though it is probably the most utilized component of the maps SDK it is one of the most underwhelming. Lets take for instance marker animations. If you have done Android development before you would assume that the marker could be animated the same way as any other View
. Unfortunately Marker
is not a subclass of View
so all of the base property animations are off limits. Instead the only field that is worth animating, position, relegates you to simple translations of xy, through the LatLng
.
There is also some weirdness when trying to change a markers bitmap after it has been instantiated. The weirdness manifests itself as an IllegalManifestArgumentException
when using the setIcon()
method. Now Google hasn't officially responded to the issue and the status is marked as needs more info. I am hoping that they will have an answer soon, but until they do I am including this in my wish list. As of the writing of this post, the best way to side step this issue is to remove the marker and re-create the marker with the new bitmap you would like. Not the best of solutions, but I have not seen anything more staright forward.
Overall, a large portion of professional Android development right now is working with 3rd party SDKs and when doing so there is an inherent risk that we as developers assume. For me, the risk of having these issues or deficiencies in Google Maps was not enough to warrant me to look elsewhere for mapping SDKs, but the feature set put before me didn't create that restriction. Though Google Maps is a completely adequate tool, I would love to see more granular control for developers as well as better methods of communicating changes from the user to the developer.