Androidsx is now Perops. Visit us at perops.com

Androidsx Androidsx | android and wearable developers

 

Subscribe for market insights and new posts

 

How to use Android’s RecyclerView and CardView (example app)

April 25, 2015 at 12:30 pm | android, software quality | No comment

 

If you’re writing a new Android app, and a list of cards make sense for your layout, chances are you want to look at the new RecyclerView and CardView. This post is a quick tutorial about how to use them. We won’t go into details, for that check out this GitHub repo.

When we’re done here (in 15 minutes), we should have this simple app built:

Sample app

First, just to get familiar with cards, we can create a simple card. This will do:

<android.support.v7.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <!-- Content inside the card. -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp" >

        <!-- A photo on the left. We'll just use the launcher icon. -->
        <ImageView
            android:id="@+id/person_photo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:src="@mipmap/ic_launcher"
            tools:ignore="ContentDescription" />

        <!-- The person name, on top -->
        <TextView
            android:id="@+id/person_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toEndOf="@+id/person_photo"
            android:layout_toRightOf="@+id/person_photo"
            android:layout_alignParentTop="true"
            android:textSize="30sp"
            android:text="Emma Wilson"
            tools:ignore="HardcodedText" />

        <!-- The age, in the bottom -->
        <TextView
            android:id="@+id/person_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toEndOf="@+id/person_photo"
            android:layout_toRightOf="@+id/person_photo"
            android:layout_below="@+id/person_name"
            android:text="23 years old"
            tools:ignore="HardcodedText" />
    </RelativeLayout>
</android.support.v7.widget.CardView>

Pretty straight-forward, right? In our example GitHub repo, this is done in this changeset, check out that version for a minimalistic working example project.

Now let’s create a recycler view (that is, a modern listview, so to speak) with a bunch of cards. Follow this changeset for the details.

First, we need an adapter, to provide the data. What’s special about the adapter for a recycler view is that it provides built-in support for the view holder. Note that we’re gonna use the view holder, and provide it with a layout for every card:

class QuizListAdapter extends RecyclerView.Adapter {
    private final List quizzes;

    QuizListAdapter(List quizzes) {
        this.quizzes = quizzes;
    }

    @Override
    public QuizViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        final LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
        final View v = layoutInflater.inflate(R.layout.quiz_card, viewGroup, false); // Layout for every card
        return new QuizViewHolder(v); // View holder
    }

    @Override
    public void onBindViewHolder(QuizViewHolder quizViewHolder, int i) {
        quizViewHolder.quizQuestion.setText(quizzes.get(i).getQuestion());
    }

    @Override
    public int getItemCount() {
        return quizzes.size();
    }
}

Here’s our view holder for a very simple card:

class QuizViewHolder extends RecyclerView.ViewHolder {
    TextView quizQuestion;

    QuizViewHolder(View itemView) {
        super(itemView);
        quizQuestion = (TextView) itemView.findViewById(R.id.quiz_question);
    }
}

And the layout for every item in the recycler view, a very simple card:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/quiz_question"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</android.support.v7.widget.CardView>

Lastly, we hook up the adapter into the recycler view, with an intuitive myRecyclerView.setAdapter(new QuizListAdapter());.

And that’s pretty much it! For a fully working example, check out this GitHub repo.

 

Technical Debt In Android App Development

January 7, 2013 at 5:06 pm | android, blog, software quality | 11 comments

 

Many indie developers think they can get away with introducing hacks all over the place while developing an Android application. Those shortcuts do speed up development. Probably, they’ll even take you closer to the launch day.

However, chances are that they’ll end up doing more harm than good.

The main reason is pure technical debt. Technical debt makes reference to the work that remains to be done in a project in order to consider it complete. All those hacks contribute directly to it.

As described in a a CAST report, the technical debt is like a ticking bomb: first, it doesn’t go off with a bang, it’s more a slow burn. Change starts to take longer… and operation costs start to spiral — it will not be a single cataclysmic event; it will be death by a thousand cuts.

In this list, we’ll attempt to classify the different types of technical debt, sorted by how much damage they inflict:

  • Choosing the wrong technology for your platform. Migrating to a different technology usually implies changes at many different levels, plus learning the new platform. A very costly process. This doesn’t usually apply for an Android app, tough.
  • Designing the wrong architecture. If the architecture does not fit the problem it intends to solve, extensions become more and more cumbersome, until a major rework may remain as the only alternative to scale. This is usually a huge task to tackle. This applies for big applications, where several layers communicate to each other.
  • Error-prone code. Code that is hard to understand, and thus expensive and dangerous to modify. Also, difficult to extend. It was perhaps originally done badly, or it worsened with subsequent modifications. Many of those hacks fall within this category.
  • Code that is difficult to test. If it is difficult to test, chances are that it falls in both the previous (Error-prone code) and the following (Poorly tested system) categories. Testing costs tend to go up with future changes, which just makes things worse. Remember that even if you don’t create an Android Test project, or you don’t do GUI testing, you can still write traditional unit tests for the critical modules of your application. You’ll be glad you did.
  • Poorly tested system. This leads to the classic I fixed X, but broke Y by mistake. No tests warned you about it. As a consequence, one tends to become lazy when it comes to improving the design: attempting to refactor a poorly tested system is scary. On the other hand, a well-tested code base provides you with confidence to improve things with a higher expectation of succeeding. After many releases, the time spent doing manual testing (aka click around before the release and hope everything works as it used to) will surpass the time it would have taken to write proper tests in the first place.
  • Duplicated business knowledge. if the DRY principle is applied successfully, a modification of any single element of a system does not require a change anywhere else. Often, the “rename” operation is expensive and extremely error-prone. In practical terms, your constants are defined in a single, central location, right? How about your assumptions about what extras your activities accept, or the data format you use for storing data into persistent storage?
  • Missing or poor error handling. Does your application let Force Closes happen? Or even worse, does it catch exceptions and try to go on as if nothing had happened?

  • Inefficient implementation. Isolating the bottleneck, profiling and fixing it is not an easy task. If your application is a service, do make sure you won’t drain the phone battery. You really want your applications to feel responsive in all supported devices.
  • Code that works, but nobody quite understands why. Usually somebody that left wrote it, and it’s been running untouched for a long time. Sooner or later, somebody is going to have to fix it or adapt it to whatever external change. That may be expensive in terms of time and frustration.
  • Outstanding bugs in code and unresolved static analysis warnings. It depends on the severity of them. However, many of them may have been there already for a long time, and didn’t cause any real problems yet. A cheap fix.
  • Duplicated code. This is one of the first things that comes to mind when discussing technical debt. One of the star features of static code analysis tools. But, how bad is it? In a given codebase, there may be several versions of some assertContains method. Sure, we’d be better off if they were in some commons library. But how much harm do they do, in terms of maintenance costs? I’d argue it’s not much.
  • Outdated documentation. Out-of-date documentation can lead you in the wrong direction, when it claims things that don’t hold anymore. Write little documentation, but make sure it is close to the code, and up to date.
  • Bad style: magic numbers, missing comments, wrong indentation, … Untidy code is more tedious to read. But this is trivial to fix. (By the way, fixing the formatting is fine as long as it is not done in the same changeset as a change in the actual logic.)

The point is, use your limited time in the most effective way. Fixing all warnings feels good, but, does it help that much? Did you make sure your design is scalable?

Talking about scalable designs, we pretty much killed our suite of News Reader applications because of a few bad design decisions. Long, long time ago we launched AnyRSS, a simple, widget-based RSS reader. It is the parent application for several other single-RSS readers, such as FML. They were great, the CTR in both Admob as well as Mobclix for them was really high, compared to our other applications. As you can imagine, all these applications share almost all functionality. But the architecture was just not right.

We are definitely not doing the same mistakes with Chat Toolkit and Smileys For Chat. The setup is similar: several free applications perform upselling to a paid one. Not a single line of code, not a single string, is duplicated (well, almost). Several module libraries collaborate to create these applications, and they have well-defined responsibilities and APIs. Coding new features is just so easy as doing it on top of a Hello World app, and most modules have a way of running in stand-alone mode.

There are other reasons why you want to keep a well-designed, clean, clearly documented codebase. What if you want to bring in a contractor to help you out? Will it be easy for them to get up to speed? Or is it just impossible? Can you give them one single module on which to work, or is the whole code a piece that can’t be easily decomposed in subpieces (i.e., different JARs)?

Also, are you proud of your code? Would you publish it into GitHub? Maybe at some point you want to have some of your code published while considering a job change? But, is it good enough to be out there in the open?

Some interesting articles we have checked out in order to write this post:

 

Stop the spread of nulls

October 4, 2010 at 7:38 pm | blog, software quality | 3 comments

 

Say we want to write an Android application that listens to everything you say, and tries to find the information that you might need before you even ask for it. For instance, you ask your friend over the phone Is there any good pizzeria near your place?, and this application will perform a query against Google Maps using your friend’s address retrieved from your phone contacts and provide you with a couple of recommendations.

To parse the user commands, after converting the speech to text, and provide the appropriate action handlers, we have these high level interfaces:

public interface Parser {
    Action findAction(String userInput);
}

public interface Action {
    void performAction();
}

There are two proposals for the implementation of the parser:

// Altenative A
class DbBasedParserA implements Parser {
    @Override
    public Action findAction(String userInput) {
        // Find the appropriate action in the DB
        return actionFound ? theAction : null;
    }
}

// Altenative B
class DbBasedParserB implements Parser {
    private static final Action DO_NOTHING = new Action() {
        @Override
        public void performAction() {
            // Do nothing
        }
    };

    @Override
    public Action findAction(String userInput) {
        // Find the appropriate action in the DB
        return actionFound ? theAction : DO_NOTHING;
    }
}

What are the consequences of this choice? Let’s see the code of the clients [1]:

// Client for alternative A
Parser parser = ParserFactory.getParser();
if (parser == null) {
    throw new IllegalStateException();
}

Action action = parser.findAction(someInput);
if (action == null) {
    // Do nothing
} else {
    action.performAction();
}
// Client for alternative B
ParserFactory
    .getParser()
    .findAction(someInput)
    .performAction();

And this is contagious. Those if (action == null) are going to keep on spreading over the different layers of the system unless some API stops it. Help save lines of code and NPEs by ensuring no nulls are passed around!

[1] There is also a ParserFactory, with two implementation alternatives that react differently when no parser is found: one returns null, while the other throws unchecked exceptions. Back to post.

 

How to write untestable code

January 14, 2010 at 11:08 am | blog, software quality | 2 comments

 

Say that we write code for a machine that it so powerful that rumors say it could generate a black hole. We need to be serious about security: we cannot let those evil international terrorists find out our black-hole-making secrets and blow half the Milky Way Galaxy.

If they managed to access our source code, they could extend it and adapt it to their cruel use case, which would represent a huge threat for the humankind. In order to reduce risks, we should write code that is difficult to understand, to maintain and of course, to test. Here’s how:

  • Depend on concrete classes – Tie things down to concrete classes – avoid interfaces wherever possible: they let people substitute the concrete classes you’re using for their own classes which would implement the same contract in the interface. By depending on concrete implementation, we make sure that they’ll have a hard time testing whether their evil plans will release enough energy to toast their breakfast bread.
  • Make your own dependencies – Instantiate objects using new in the middle of methods, don’t pass the object in. Anyone that wants to test that code is forced to use that concrete object you new’ed up: they can’t inject a dummy, fake, or other mock in to simplify the behavior or make assertions about what you do to it.
  • Conditional slalom – Feel good when writing lengthy if branches and switch statements. These increase the number of possible execution paths that tests will need to cover when exercising the code under test. The higher the cyclomatic complexity, the harder it is to test and understand! Why use polymorphism instead of conditionals? Don’t make it so easy! Make the branching both deep and wide: if you’re not consistently going at least 5 conditionals deep, the patient terrorists might even find out what the code intends to do under all possible combinations.

    It is also known as the Arrow Antipattern:

    if a
        if b
            if c
                foo();
            else
                bar();
            endif
        endif
    endif
    

    Refactoring into something more suitable is usually possible:

    if a && b
        if c
            foo();
        else
            bar();
        endif
    endif
    
  • Use global flags – Why call a method explicitly? Set a flag in one part of your code, in order to cause an effect in a totally different part of your application. The testers will go crazy trying to figure out why all of a sudden a conditional that was evaluating true one minute is all of a sudden evaluating to false. Not to mention the mess that using that system becomes: if we manage to keep the wikis safe from their hands, they’ll have a very hard time figuring out what system properties to set in order to get the deadly weapon working.
  • Loop-switch sequence – By means of which a clear set of steps is implemented as a byzantine switch-within-a-loop. Fulfilling our mission to obfuscate the code , is much more difficult to decipher the intent and actual function of the code than the more straightforward refactored solution.

    This is a terrorist-safe code snippet:

    // parse a key, a value, then three parameters
    String key = null;
    String value = null;
    final List params = new LinkedList();
    
    for (int i = 0; i < 5; i++) {
      switch (i) {
        case 0: key = stream.parse(); break;
        case 1: value = stream.parse(); break;
        default: params.add(stream.parse()); break;
      }
    }
    

    While this is too easy to understand:

    // parse a key and value
    final String key = stream.parse();
    final String value = stream.parse();
    
    // parse 3 parameters
    final List params = new LinkedList();
    for (int i = 0; i < 3; i++) {
      params.add(stream.parse());
    }
    

References: This post on the Google Testing Blog, the Arrow AntiPattern in Cunningham & Cunningham webpage.

 

Don’t look for things. Ask for them!

November 26, 2009 at 12:04 pm | blog, software quality | 5 comments

 

Before our previous article, our code to model a mechanic used to look like this:

class Mechanic {
    private final Engine engine = ServiceLocator.getCar().getEngine();
    public void fixEngine() { /* ... */ }
}

Luckily, we applied some dependency injection, resulting in nicer code: testable, and with an explicit dependency on the ServiceLocator:

class Mechanic {
    private final Engine engine;
    public Mechanic(final ServiceLocator serviceLocator) {
        engine = serviceLocator.getCar().getEngine();
    }
    public void fixEngine() { /* ... */ }
}

However, does our mechanic care about the ServiceLocator at all? Not really, it doesn’t even store a reference to it! The mechanic just wants an engine.

There are some problems with this kind of code:

  • The ServiceLocator probably has references to a lot of other classes in the system. By the transitive property, our Mechanic class is too coupled with the rest of the system: if you want to reuse it in a different project, you’d need to reference not only Mechanic and Engine, but also the ServiceLocator with all its dependencies.
  • The API is not clear. An API client knows that it’d need to provide the ServiceLocator (already an advantage respect to the Singleton-based case). What it doesn’t know is what the mechanic really needs (an engine). This fact is hidden in the source code.
  • The tests contain a lot of setup junk that masks its real purpose. For every test of the mechanic, you’d need to mock the ServiceLocator, and then make sure the appropriate reference can be retrieved from it (the Engine, in this case). When we test the class Garage, that needs a Mechanic, we’ll have to replicate this tedious setup there too.

This is how the test looks like now:

@Test
public void testCantFixBrokenDownEngine() {
    final Engine engine = EngineFactory.buildBrokenEngine();
    final Car mockCark = Mockito.mock(Car.class);
    Mockito.when(mockCar.getEngine()).thenReturn(engine);
    final ServiceLocator mockServiceLocator = Mockito.mock(ServiceLocator.class);
    Mockito.when(mockServiceLocator.getCar()).thenReturn(car);
    final Mechanic mechanic = new Mechanic(mockServiceLocator);
    mechanic.fixEngine();
    assertFalse(engine.works());
}

Why not just ask for what you need?

class Mechanic {
    private final Engine engine;
    public Mechanic(final Engine engine) {
        this.engine = engine;
    }
    public void fixEngine() { /* ... */ }
}
@Test
public void testCantFixBrokenDownEngine() {
  Engine engine = EngineFactory.buildBrokenEngine();
  Mechanic mechanic = new Mechanic(engine);
  mechanic.fixEngine();
  assertFalse(engine.works());
}

Everyone wins:

API writers:

  • Unaffected by ServiceLocator changes
  • In-code documentation is easier to write (what was your comment on the Mechanic‘s constructor for the ServiceLocator)?

Test writers:

  • Tests are easy to read
  • Very little setup code
  • Unaffected by ServiceLocator changes

API users:

  • The API is clear: the mechanic needs an engine
 

Can’t test that Singleton? Try Dependency Injection!

November 11, 2009 at 9:40 pm | blog, software quality | 4 comments

 

So you want to test this method:

public class Client {

    public int process(Params params) {
        final Server server = Server.getInstance();
        final Data data = server.retrieveDate(params);
        // do stuff
    }
}

We don’t want to retrieve an instance of a real server for our little unit-test, so how can we test this method?

It is hard to test code that uses singletons.

We don’t control the creation of the singleton object, as it is performed inside a static method. There is no way to mock the object in order to test the behavior of our method in isolation.

Refactor it to use Dependency Injection.

You can refactor Client to avoid using the singleton pattern. Instead of obtaining the Server instance from the static getInstance() method, allow Client to accept it through its constructor.

public class Client {
    private final Server server;  

    public Client(Server server) {
        this.server = server;
    }  

    public int process(Params params) {
        final Data data = server.retrieveData(params);
        // do stuff
    }
}

Let’s write that test now:

@Test
public void testConnectionUpTime() {
    final Server mockServer = Mockito.mock(Server.class);
    final Params params = // ...
    Mockito.when(mockServer.process(params)).thenReturn(5);
    final Client client = new Client(mockServer);
    assertEquals(5, client.process(params));
}

The code is now both clearer and testable.

The dependency between the client and the server is now explicit: Client client = new Client(server);. There is no way a developer creates a client instance without noticing that a server instance must be configured: it is a parameter in the constructor.

The singleton allowed to create a client instance without configuring the server in advance. The object would be successfully created and the application would execute, until one of the methods runs into a non-configured/non-reachable/null server and fail at runtime :’-(

 

Categories:

Recent posts:

Search:

Subscribe