Android – Introduction to AndroidAnnotations, Maven in Intellij IDEA

I think, as developers, all of us want to write less code but still keep the code structure clean and clear as possible. When I develop app in Android, I always miss MVVM (WPF in Visual Studio) where I can bind control to event easily. Android makes me ‘bored’ when I have to look up each control with findViewById() and then initialize them so that I can use them later. Even though that the problem with findViewById() can be solved with Roboguice, but I still have to write the code for registering event such as click, long click,… It’s really boring and I want to have less code for that task. I need something ‘clean and clear’. Therefore in this post, I would like to introduce AndroidAnnotations, a framework for helping us keeping our code thin and healthy.

One code row says more than thousand words. How amazing is our code with AndroidAnnotations is shown in sample below

@NoTitle
@Fullscreen
@EActivity(R.layout.bookmarks)
public class BookmarksToClipboardActivity extends Activity {
  
  BookmarkAdapter adapter;
  
  @ViewById
  ListView bookmarkList;
 
  @ViewById
  EditText search;
  
  @App
  BookmarkApplication application;
  
  @RestService
  BookmarkClient restClient;
 
  @AnimationRes
  Animation fadeIn;
  
  @SystemService
  ClipboardManager clipboardManager;
 
  @AfterViews
  void initBookmarkList() {
    adapter = new BookmarkAdapter(this);
    bookmarkList.setAdapter(adapter);
  }
  
  @Click({R.id.updateBookmarksButton1, R.id.updateBookmarksButton2})
  void updateBookmarksClicked() {
    searchAsync(search.getText().toString(), application.getUserId());
  }
  
  @Background
  void searchAsync(String searchString, String userId) {
    Bookmarks bookmarks = restClient.getBookmarks(searchString, userId);
    updateBookmarks(bookmarks);
  }
 
  @UiThread
  void updateBookmarks(Bookmarks bookmarks) {
    adapter.updateBookmarks(bookmarks);
    bookmarkList.startAnimation(fadeIn);
  }
  
  @ItemClick
  void bookmarkListItemClicked(Bookmark selectedBookmark) {
    clipboardManager.setText(selectedBookmark.getUrl());
  }
 
}

1. Prerequisites

I use Intellij IDEA as my Android IDE. Its current version is 13.1.1 but it seems to be that it has a bug when resolving resource R.java file therefore I have to downgrade it to version 13.0.3. If you want to install this old version, you can get it here

http://confluence.jetbrains.com/display/IntelliJIDEA/Previous+IntelliJ+IDEA+Releases

The error happens in generated file of AndroidAnnotations saying that Intellij IDEA can’t resolve “import *.R.layout;” even that the R.java was there. I already reported the bug at Jetbrains

http://youtrack.jetbrains.com/issue/IDEA-123897

but they don’t want to fix it. 🙁 So we can only hope that the author of AndroidAnnotations change his import template. If not, then we have to choose between not to upgrade Intellij IDEA or not to use AndroidAnnotations. The problem was fixed in Intellij IDEA 14.

In this post, I will focus on how to make AndroidAnnotations run with Maven in Intellij IDEA and some code examples to show how AndroidAnnotations works. I won’t bla…bla… about what AndroidAnnotations or Maven is. If you want to read more about these two frameworks then the homepage of AndroidAnnotations is at http://androidannotations.org/ and Maven is at http://maven.apache.org/.

For Visual Studio developers, Maven is such a same thing like Nuget (or Nuget is such a same thing like Maven, I don’t know which one comes first :)). With Maven, we don’t need to make reference directly to any .jar file. Just simply define in pom.xml that we want the reference to that library, Intellij IDEA will download it when when we compile the code. That means, no big code repository because of dependencies anymore and no need to care about dependencies of dependencies anymore.

2. Install AndroidAnnotations by using Maven in Intellij IDEA

2.1. Activate Maven framework support

– Create a normal project of Android Application Module
– After project was created, in Project Toolwindow, right click on project, and choose Add Framework Support…

Add Framework Support

– Select Maven then click OK

Maven

– When Maven framework was added, we’ll have a new file pom.xml in project folder

pom.xml file

– Now we have a Android project with Maven support. Remember to set Maven settings for your project in pom.xml file

<groupId>com.hintdesk.AndroidAnnotationsExamples</groupId>
<artifactId>AndroidAnnotationsExamples</artifactId>
<version>1.0-SNAPSHOT</version>

2.2. Add reference to AndroidAnnotations

– As usual we have to go to homepage of library, download the .jar files and then add references to them. But with Maven, we don’t have to.
– Go to http://search.maven.org/ and search for androidannotations and androidannotations-api
– Click on link of version you want

androidannotations maven

– Copy the XML code in Dependency Information on the left. The Project Object Model on the right is the dependencies of AndroidAnnotations

Dependency Information

– Paste that code to your pom.xml file

<groupId>com.hintdesk.AndroidAnnotationsExamples</groupId>
<artifactId>AndroidAnnotationsExamples</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
	<dependency>
		<groupId>org.androidannotations</groupId>
		<artifactId>androidannotations</artifactId>
		<version>3.0.1</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>org.androidannotations</groupId>
		<artifactId>androidannotations-api</artifactId>
		<version>3.0.1</version>
	</dependency> 
</dependencies>

– Make your project and you’re done with reference to AndroidAnnotations. To check if everything works, go to File –> Project Structure –> Libraries, be sure that your libraries are there.

Maven libraries

– Go to File –> Project Structure –> Modules be sure that your libraries are correctly referenced.

Maven Modules

2.3 Annotation Processors

– AndroidAnnotations uses standard Java Annotation Processing Tool to add automatically an extra compilation step when generating compiled class. The generated code will enhance our activities/classes through annotation like introduction at the beginning. So we have to check if Maven has already set Annotation Processors correctly.
– Go to File –> Settings –> Compiler –> Annotation Processors

Annotation Processors

Check if all settings are same as in image below

Annotation Processors

2.4 Add generated source code to source root

– The generated files are still not in our source root right now. Therefore if we use the generated files, it won’t work.
– Go to File –> Project Structure –> Modules, be sure that annotations folder under generated folder is included in our Sources

Add to Source root

– The configuration is now finished, AndroidAnnotations is ready to use.

3. Examples with AndroidAnnotations

In this section we will make some basic examples with AndroidAnnotations to see how wonderful it is

3.1 Enhanced Activity

To activate AndroidAnnotations for our activities, we need to make 2 steps. First, we have to decorate our activities with @EActivity attribute. Second, we have to declare the generated activity in AndroidManifest.xml instead of the original one. For example, I enhance my MainActivity like code below

@EActivity(R.layout.main)
public class MainActivity extends ListActivity {

    @StringArrayRes
    String[] examples;

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @ItemClick
    void listItemClicked(String example) {
        int index= Arrays.asList(examples).indexOf(example);
        if (index>=0)
        {
            switch (index)
            {
                case 0:
                    ControlAndEvent_.intent(this).start();
                    break;
                case 1:
                    RestClientAndAsyncTask_.intent(this).start();
                    break;
                case 2:
                    AdapterAndView_.intent(this).start();
                    break;
                default:
                    break;
            }
        }
    }
}

and I need to declare generated class in AndroidManifest.xml

<activity android:name="MainActivity_"
		  android:label="@string/app_name">
	<intent-filter>
		<action android:name="android.intent.action.MAIN"/>
		<category android:name="android.intent.category.LAUNCHER"/>
	</intent-filter>
</activity>

The MainActivity is a ListActivity with one ListView. The entries of this ListView will be filled with a constant string array. When the activity get loaded, each string item in array will represent an item in ListView.

MainActivity

When user clicks on an item, @ItemClick function will be triggered and we’ll show the appropriate activity to user. In enhanced MainActivity, I have used 2 types of annotations and one static method of AndroidAnnotations. @StringArrayRes and @ItemClick are used to access resources as well as declare a handler for item click event. Please note that @StringArrayRes works by name convention. The decorated variable must have same name with variable in string.xml file.

To start activity, instead of calling Android function, I use the static function of AndroidAnnotations

ControlAndEvent_.intent(this).start();

As you can see, in comparison to normal code, the enhanced code is much more cleaner, easier to read and maintain.

3.2 REST client and background task

If you follow my blogs, you see that we have already many examples relevant to consuming a REST web service. In other old posts, we have implemented ourselves our JSONHTTPClient for negotiating data content with server.

AndroidAnnotations provides us also a wrapper around the Spring Android RestTemplate for working with REST web service. To activate this component, we just need to go to pom.xml file and declare dependencies to Spring Android RestTemplate and use it

<dependency>
	<groupId>org.springframework.android</groupId>
	<artifactId>spring-android-rest-template</artifactId>
	<version>1.0.1.RELEASE</version>
</dependency>

<dependency>
	<groupId>com.google.code.gson</groupId>
	<artifactId>gson</artifactId>
	<version>2.2.2</version>
</dependency>

I have declared here 2 dependencies. One for Spring Android RestTemplate and one for Gson because Spring Android RestTemplate doesn’t provide us any concrete implementation for converting JSON to object, so we need Gson for converting JSON format to our contract classes.

After adding dependencies, we just need to declare an interface decorated with @Rest attribute

@Rest(rootUrl = "http://restwebserviceforandroid.apphb.com/api",converters = {GsonHttpMessageConverter.class})
public interface IHDRestClient {
    @Get("/products")
    Product[] getProducts();
}

then use it in our activity

@EActivity(R.layout.restclientandasynctask)
public class RestClientAndAsyncTask extends ListActivity {

   @RestService
    IHDRestClient hdRestClient;
    private ArrayList<HashMap<String, String>> productList;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @AfterViews
    void afterViews() {
        new LoadAllProducts().execute();

    }

    class LoadAllProducts extends AsyncTask<String, String, String> {
        private ProgressDialog progressDialog;

        @Override
        protected String doInBackground(String... params) {
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
            Product[] products = hdRestClient.getProducts();
            productList = new ArrayList<HashMap<String, String>>();
            if (products.length > 0) {

                for (Product product : products) {
                    HashMap<String, String> mapProduct = new HashMap<String, String>();
                    mapProduct.put(Product.PRODUCT_ID, String.valueOf(product.getId()));
                    mapProduct.put(Product.PRODUCT_NAME, product.getName());
                    productList.add(mapProduct);
                }

            }
            return null;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();    //To change body of overridden methods use File | Settings | File Templates.
            progressDialog = new ProgressDialog(RestClientAndAsyncTask.this);
            progressDialog.setMessage("Loading products. Please wait...");
            progressDialog.show();
        }

        @Override
        protected void onPostExecute(String s) {
            progressDialog.dismiss();
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ListAdapter adapter = new SimpleAdapter(RestClientAndAsyncTask.this, productList, R.layout.list_item, new String[]{"Id", "Name"}, new int[]{R.id.textViewId, R.id.textViewName});
                    setListAdapter(adapter);
                }
            });
        }
    }
}

Once again, AndroidAnnotations helps us to keep our code structure thin, small but very efficient.

3.3 Adapter and view

In previous section, we have learnt how to consume a REST web service with AndroidAnnotations. The code is thin but it looks little messy because of AsyncTask. We want that our activity contains at least code as possible and shouldn’t contain logic code. So we will improve the way of loading and pushing data using Adapter. We’ll make a simple Adapter basing on BaseAdapter, make a call to REST web service, load data and push data to activity

@EBean
public class ProductAdapter extends BaseAdapter {
    Product[] products = new Product[0];

    @RestService
    IHDRestClient hdRestClient;

    @RootContext
    Context context;

    @AfterInject
    void afterInject()
    {
        new LoadAllProducts().execute();
    }

    @Override
    public int getCount()
    {
        return products.length;
    }

    @Override
    public Product getItem(int position)
    {
        return products[position];
    }

    @Override
    public long getItemId(int position)
    {
        return products[position].getId();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        ProductItemView productItemView;
        if (convertView == null) {
            productItemView = ProductItemView_.build(context);
        } else {
            productItemView = (ProductItemView) convertView;
        }

        productItemView.bind(getItem(position));

        return productItemView;
    }

    class LoadAllProducts extends AsyncTask<String, String, String> {
        @Override
        protected String doInBackground(String... params) {
           products = hdRestClient.getProducts();
            return  null;
        }

        @Override
        protected void onPostExecute(String s) {
            notifyDataSetChanged();
            super.onPostExecute(s);
        }
    }
}

In activity, all we have to do is just binding this adapter to our list view and done.

@EActivity (R.layout.adapterandview)
public class AdapterAndView extends ListActivity {
   @Bean
    ProductAdapter productAdapter;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @AfterViews
    void afterViews()
    {
        getListView().setAdapter(productAdapter);
    }
}

4. Conclusion

AndroidAnnotations is a powerful framework which helps us to create thin, reliable, efficient and maintainable source code. It’s easy to use, not complicated to understand. It speeds up Android development and takes care of the plumbing so that we can concentrate on what’s really important.
Maven helps for resolving dependencies, reduces complexity for installation/management/updates of dependencies. Both of frameworks are really useful and I suggest that we should integrate them in our own apps.

Source code: https://bitbucket.org/hintdesk/android-introduction-to-androidannotations-maven-in-intellij

Leave a Reply

Your email address will not be published. Required fields are marked *