Thursday, July 31, 2014

#threadproblems

Ran into another interesting bug yesterday.  In retrospect, I am actually really proud of the fact that I was able to find the issue and fix it so quickly.  Thankfully, this time the bug was not due to my own stupidity.

I'm not sure if I have mentioned it yet, but I am currently working on a game.  I'll try to keep the details sparse, because they don't really matter.  In this game, you have a certain number of clicks to use, and after you run out of clicks, the game ends.  Sometimes, this would behave as expected.  But the bug was that most of the time when you used your final click, the game would immediately end instead of handling that click.

The game runs in a separate thread from that which handles touch events, so after briefly reviewing my logic it seemed pretty clear that it was some threading problem.  I will admit that multi-threading is probably my weakest area of expertise, mainly due to a simple lack of experience.  (That, and the fact that I have only had one professor briefly teach threading, and he is one of the worst teachers in our department, so I did not pay much attention.)  So maybe this was actually my fault.  Maybe I broke some sort of fundamental law of threading, I'm not sure.  But anyway, in my game thread, I have a while loop that keeps looping as long as the player has clicks left, and another while loop inside it that runs the actions caused by the player's click.  Both of those loop variables were being changed from the UI thread instead of the game thread.  So, it basically looked like this:


And the thread that was modifying those values looked something like this:


Now this wasn't too hard; if the touch event happened while the game thread was at the top of the outer loop, (between lines 1 and 2,) it would work fine.  Otherwise, it would ignore the last click because _clicksLeft == 0, so it would never even get into the outer loop.  So I quickly added another condition to the loop:


Fixed!  ...Or so I thought.  I tested it a few times, and it looked like the bug was gone, so I pushed my code and went to go work on something else.  Then, I was testing a couple days later, and I ran into the bug again.  D:

Clearly my fix had done something, because I didn't see the bug again for a few days.  So I tested it a few more times to make sure my eyes weren't playing tricks on me, and it happened again.  *sigh*  Time to go back to the code...  The first thing that came to mind was to try to syncronize the blocks that were dealing with those variables.  Unfortunately, that froze my entire UI, so that didn't seem to be an option.  I stared at it for about an hour, and then it hit me:

There is a rare edge case where both of the above loop conditions will be false, even if the game is not supposed to be over.  _isRunningTurn must be false, _clicksLeft must be 1, and the touch event must happen while the game thread is in the middle of checking the loop condition!  If I spread out the timing, it becomes a bit more obvious:


Of course, it probably wouldn't happen with exactly this timing, but it's possible.  The solution is simple though: just reverse the loop conditions:


Regardless of the execution timing, there is now no case where both conditions are false unless the game is really over.  Pretty crazy, I never would have thought the order of loop conditions could be such a problem!  Whoever first said "You learn something new every day," I think they might have been on to something.

Monday, July 21, 2014

Google Play Game Services

A few days ago I finished adding Google Play Game Services to my app, and I wanted to make a quick post about an issue I ran into.

The problem that I had was not mentioned anywhere in the Getting Started guide, and apparently it's not a very common one.  It took me a few hours before I finally found the answer online, in a StackOverflow answer that was sitting at the bottom of the page with 0 points.

Specifically, the problem was that whenever I would try to log in to the game services, I would get an error that simply said: "Unknown issue with Google Play Services."  The log output wasn't much more helpful, it just said "Internal error, see log for details."  So I had to find the actual error in the unfiltered logs (which is a challenge in itself), which said: "Access Not Configured. Please use Google Developers Console to activate the API for your project."  This didn't really help, because Google had already activated the API's that I was supposed to need.

So I went back to make sure I hadn't missed anything.  I followed the Getting Started guide to the letter.  This is important, because there are actually a lot of things that can go wrong, and they are all pretty easy to miss.  I double and triple checked all of the common problems.  Nope.

Eventually I went back to look at the error message again.  The error also included a link to a troubleshooting page, but I had already looked over all of the solutions it offered.  However, this time I realized that the link was not to a Google Play troubleshooting page, but a Google Drive troubleshooting page.  So I just decided to activate the Drive API on the off chance that it would work.  And wouldn't you know, that was the problem!  I eventually found this solution in a neglected StackOverflow answer, although I'm still not 100% sure why it was necessary.  It automatically activated the other API's, even though I don't need half of them, so why doesn't it just activate the Drive API as well?

Friday, July 11, 2014

Creating a Reusable Dialog Class

The past couple of days I have been working on a task that I thought would be a much more common problem.  However, after much Google searching I couldn't find a single page that could help.  So, as always, I'm going to dump my code here in case it can help out someone in the future.

I was trying to make my own Dialog class that I could reuse throughout my app.  Since I had to use it in many different ways, I needed to be able to put a custom layout in the middle of the dialog, similar to how the AlertDialog class works.  This was the biggest issue; I wanted to completely change the way my dialogs looked, more than you can change by simply creating a style.  (Although after looking through the code, I realize that a style could have changed a lot more than I originally thought.)  Finally, I wanted to be able to change the colors used in the dialog on the fly.  This last one is not really related to the rest, but is just something I am trying to do everywhere in my app.  (I'm mentioning it now because my code would be pretty confusing otherwise.)  You can see my original concept over to the right, to get an idea of what I was aiming for.

So, here is all of my code:

two_color_dialog.xml
TwoColorDialog.java
dialog_background.xml
dialog_title_box.xml
dialog_close_button.xml
I'm not going to talk about the color-changing aspect, because that's not really the point of the post.  There are a couple things I would like to talk about though.

Notice in the dialog's layout, I have a LinearLayout (id/dialog_background) within a FrameLayout, which I use to show the background drawable.  Why not just put the background on the FrameLayout and get rid of that LinearLayout?  Well, the background drawable automatically surrounds all of its children.  Since I wanted the title and close button to float a bit outside of the main dialog, I had to put those two elements outside of the layout that contains the rest of the "inner" dialog elements, so that they wouldn't be surrounded by the background.

The big thing here is the FrameLayout that holds the custom view.  This is actually the same method that is used for the AlertDialog class; I ended up looking through the source code for some inspiration.  And although I have a lot of code up there, it all really boils down to this:

This is how you can stick different views inside of the dialog, allowing you to use it in different places.   It seems pretty simple, but I bring it up because it took me much longer to figure out than I would care to admit.  In fact, that is the first of the big issues I ran in to:

Problems and bugs

1.) It took me way too long to figure out how to embed a custom layout within the dialog's layout.

 I tried a bunch of different ideas.  It seemed like a pretty simple task, but no matter what I tried I couldn't get it to work.  I couldn't find any examples online, except for one StackOverflow question.  I tried all of the different answers that were suggested, but no dice.  I even eventually went to the AlertDialog source code, but even after using the same method, I just couldn't get the inner view to show up!

Of course, the whole time I assumed that my code to display the view was at fault.  I took for granted some things that I assumed could never be the problem.  (I tend to do that a lot.)  In the end, the problem was NOT with my code, but with the layout I was trying to display.  When I was trying to test the dialog, I just grabbed a random layout that I was already using elsewhere in my app.  The layout that I chose contained only a single ListView.  Of course, if you don't set up an adapter for a ListView, it just shows up empty.  So my code was working the whole time, but I thought it wasn't because I was adding an empty layout.

2.) The backgrounds of my button drawables were turning black for no reason.

The link above explains the issue pretty well: My buttons were working fine, but for no reason whatsoever the backgrounds suddenly all turned black.  I still have absolutely no idea why this happened.  I was able to fix the problem by explicitly adding a transparent background to the drawable.

3.) The shadow of my "close" button would disappear after the dialog was shown the first time.

This is even stranger than the last one:  My dialog would look perfect, but only the first time it was displayed.  After that, the shadow of the close button would disappear.  (See the link above for a picture.)  I tried debugging to see what was different between the two states.  I tried Googling, but my search turned up empty.  I tried asking StackOverflow, but all I got was a single comment that didn't even address the issue.  As a last resort, I just tried changing things in the drawable to try to find the problem.  I knew that the title box didn't have any issues, and that was almost identical.  After much trial and error, I narrowed down the problem to the "padding" tag.  For whatever reason, the problem only happens when I am using that tag.  So I took it out and added position attributes to all of the other layers instead.  Again, I have no idea what caused this; I can only assume that it is actually an Android bug. 

Finally, here is an example of how to use the dialog:
And here is what the final result looks like:


Not too bad!  Obviously not perfect; it only has the functionality that I needed at the moment, so it's not nearly as robust as the original AlertDialog class.  But it's definitely a start!

Wednesday, July 2, 2014

Colors Are Hard, Man

I ran into a pretty funny bug last night.  In this app, I am allowing users to change the background color.  The choice of background color is stored in a SharedPreference.  As you may know, every time you read from SharedPreferences, you have to provide a default value for the preference you are reading.  I was reading an int, so I used a default value of -1 to check if the preference was initialized or not.  My code looked like this:
However, the above exception was being thrown every single time, and I couldn't figure out why.  I double- and triple-checked the names for typos, just in case I was assigning to "backgronud" or something like that.  Nope, no typos.  So here's where I initialize the preference, in my main activity's onCreate:
I threw that log message in there to confirm that, yes, the preference was definitely getting initialized.  But then why was it going with the default -1 and throwing that exception?  I had to break out the debugger before I figured it out.

So, in Android, colors are stored as integers, (which are 32 bits,) with 8 bits for each channel: Alpha, Red, Green, and Blue.  So if you write out the value for a color in hex, it takes this format: 0xAARRGGBB.  So, for example, the color Green has a value of 0xFF00FF00, and White would be 0xFFFFFFFF.  Do you see the issue here?

If not, read up a bit on Two's Compliment and then come back here.

Done?  Good.

0xFFFFFFFF in 32-bit Two's Compliment is -1.  It was initializing to the same value that I was using to see if it was not initialized.  So it would start out at -1, and then throw the error because I thought that a color would never have a value of -1.  How wrong I was.

Of course, this is easily fixed by choosing a color value that actually will never be used.  I went with 0 instead of -1.  I could also just delete the error checking and make it default to white everywhere, but I prefer having the default value assigned in only one place, making it easier to change later.