2 Modules Android project with Robolectric, Gradle, Android Studio

Start a new Android project this week, so I started to setup the development environment. To be honest, it is not an easy experience to me.

Features

Here are the features I got now:

  • Unit test with Robolectric. (Very fundamental requirement)
  • Unit tests are separated into an independent module. (Provide flexibility when project growing larger. And provide a more clear view of code organization)
  • Running unit tests recognized by Android Studio/IntelliJ unit test plugin and debug UTs in IDE. (This is very important when diagnose failure UT)
  • Running unit tests via CLI with gradle wrapper. (This is necessary requirement for CI servers)
  • Using resources in Robolectric test. (Avoid Resource Not Found exception in unit tests)
  • Test Android Annotation powered async implementation. (AA introduces new Async implementation, which is not supported by Robolectric by default)
  • AssertJ core support. (Fest has been deprecated since no one is maintain it now.)

Versions

The major difficulties that I met are version compatibility, I almost tried all available version combinations to make them all works. Here are the versions that I uses

  • Gradle: 2.1
  • Groovy: 2.3.6
  • Ant: 1.9.3
  • JVM: 1.6.0_65 (Apple Inc. 20.65-b04-462)
  • OS: Mac OS X 10.9.5 x86_64
  • Android Studio: 0.8.11 (build AI-135.1446794)
  • IntelliJ: IDEA 14 CE EAP, build IC-138.2458.8
  • Android gradle plugin: com.android.tools.build:gradle:0.13.0
  • Android ADT gradle plugin: com.neenbedankt.gradle.plugins:android-apt:1.4+
  • Compile SDK version: 20
  • Build tool version: 20.0.0
  • Robolectric: 2.3

Known Issues

My current solution isn’t perfect. But for now, I haven’t working solution for them. Hope it could be fixed in the future

  • AAR is not supported in Unit Test. (A tricky issue, I’ll explain more in detail later)
  • AssertJ-Android is not supported. (Cause by AAR support issue, alternative available.)

Project Structure and configurations

Here are the project structure:

1
2
3
4
5
6
7
8
RootProject
|- settings.gradle
|- build.gradle
|- Launcher
\- builde.gradle
\- UnitTest
|- build.gradle
\- UnitTest.iml

Here are the contents

\Settings.gradle
1
include ':Launcher', ':UnitTest'
\build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.13.0'
}
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
\Launcher\build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.13.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4+'
}
}
repositories {
mavenLocal()
mavenCentral()
}
apply plugin: 'com.android.application'
apply plugin: 'android-apt'
android {
compileSdkVersion 20
buildToolsVersion '20.0.0'
defaultConfig {
minSdkVersion 9
targetSdkVersion 19
versionCode 1
versionName "1.0"
}
buildTypes {
debug {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
apt {
arguments {
androidManifestFile variant.processResources.manifestFile
resourcePackageName 'me.timnew.game.launcher'
}
}
def AAVersion = '3.1'
dependencies {
apt "org.androidannotations:androidannotations:$AAVersion" // Process AA annotations
/*
* Android Studio will remove this line if you try to edit project configuration with GUI.
* It seems it is a bug of Android Studio since it does not understand DSL `apt`
*/
compile "org.androidannotations:androidannotations-api:$AAVersion" // AA Runtime API. Becareful
compile 'de.greenrobot:eventbus:2.2.1'
compile 'org.easytesting:fest-reflect:1.4.1'
compile 'com.google.guava:guava:18.0'
compile 'com.koushikdutta.ion:ion:1.3.8'
compile fileTree(dir: 'libs', include: ['*.jar', '*.aar']) // Well although I mentioned aar here, but it doesn't load correctly.
compile 'com.android.support:support-v4:20.0.0'
compile 'com.android.support:support-annotations:20.0.0'
compile 'com.android.support:appcompat-v7:20.0.0'
}
\UnitTest\build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.13.0'
}
}
apply plugin: 'java'
repositories {
mavenLocal()
maven { url "$System.env.ANDROID_HOME/extras/android/m2repository" } // Fix 'com.android.support:*' package not found issue
mavenCentral()
}
dependencies {
def appModule = project(':Launcher')
compile appModule
testCompile appModule.android.applicationVariants.toList().first().javaCompile.classpath // Include classes from main project
testCompile appModule.android.applicationVariants.toList().first().javaCompile.outputs.files
testCompile files(appModule.plugins.findPlugin("com.android.application").getBootClasspath())
testCompile('junit:junit:4.+') {
exclude module: 'hamcrest-core' // Exclude problematic 'hamcrest'
}
testCompile 'org.robolectric:robolectric:2.3'
testCompile 'org.mockito:mockito-core:1.9.5'
testCompile 'org.assertj:assertj-core:1.6.1'
}
tasks.withType(Test) {
scanForTestClasses = false
include "**/*Should.class"
include "**/*Test.class"
include "**/*Tests.class"
}

Why uses java plug-in instead of android unit test plug-ins

Well, Gradle DSL provides a lot of flexibility to developer. But it also brought a lot complexity to IDE implementation. To figure out project configuration, IDE need to parse and understand the gradle scripts. Not only DSLs provided by gradle but all stuff come with plug-ins. From IDE, this is almost an impossible mission. So IDE need to figure out a way to simplify the process, such as support a subset of DSL.

For Android project, IntelliJ has difficulties to understand the all variations of android unit test plug-ins. So it is not easy to make unit test runnable from IDE. To solve the issue, you either try to teach IDE about the DSL by providing plug-in to IDE, or uses some languages that IDE understood.

I tried some plug-in available today, but none of them works for me. So I decide to use java DSL, which IntelliJ understood natively. As a trade off, since java gradle plugin doesn’t understand Android Library, so it cannot import .aar libries.

Besides I tried all android unit test gradle plugins, I found all of them depends on android gradle plugin. And android plugin depends on AndroidManifest.xml and some other stuff. It is wield to provide an manifest for your unit test.

So as the final solution, I uses java plug-in, avoid using aar in the test.

Why so complicate

Configurate an working Android project isn’t as easy as it sounds. Differ from iOS community, Google isn’t strong-minded as Apple. As a consequence that Android community is fragmented and lack of unified solution. There are tons of solutions available, but you might need to try them one by one to figure out which fits your requirement.

To make your tool chain, dependencies, IDE work together, compatibility is always your enemy. Even Google has to publish Version Compatibility Table to mitigate the pain.

What a mess!!!!!

References posts, plugins or template projects

Here is a list of things that I tried but failed to fit my requirement. List here since it might be helpful to other people.

Trello Confetti Effect

Trello just created a page to celebrate their user number reaches 5 million. In their celebration page, they introduce an interesting effect, a number of blue squares rotating and falling from the sky.

By inspecting their source code, I extracted the effect implementation from the page.

As you can see, the effect is implemented with Canvas animation. Trello guys created a light weight particle system.

  • ConfettiMachine is the particle system controller, which takes the responsibility to create new particles and call draw method on them one by one.
  • Particle is the representation of the blue square, it takes the rotation, fading out and rendering.

The source code is extracted for study purpose, all code and credits belongs to Trello guys.

Otto and Android Annotations Compatibility Issue Analysis

Introduction

Otto is a great Android event bus solution developed by SquareUp guys. These guys extract the event bus related classes from Google’s Guava, and optimized it for Android. Event Bus is a great solution to keep your code away from messy anonymous or internal classes only used for event handling! And Otto’s simplicity and performance makes it used to be the most popular event bus solution in Android development.

Android Annotations, as known as AA, is another great library that makes Android developers’ lives a lot easier. Just annotating the code, AA helps developer handles the most boring or error-proning tasks for you, including binding widgets binding, asynchronous tasks, life time management, etc…

Issue

Otto and AA are the libraries focusing on different aspects. Theoretically speaking, there shouldn’t be any compatibility issue between them. But the reality Otto event is never delivered to AA annotated classes. By inspecting the code with step by step debugging, I found the root cause of the issue is Otto failed to located @Produce and @Subscribe annotated methods in AA generated code.

Analysis

After a few study work, I finally understood what is the reason behind:

For performance consideration, Android Annotations actually does it work during the compilation time instead of Runtime. AA will derive the annotated classes, and generate some code according to the annotations applied. During the runtime, the classes got instantiated is actually the derived classes instead of the original one, although in the code is accessed via the original class as interface.

Here is a simple example:

I have the class ServerListAdapter, which is used to provide data for a grid view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@EBean
public class ServerListAdapter extends AdvBaseAdapter<InetAddress, ServerView> {
@RootContext
protected Context context;
@SystemService
protected LayoutInflater inflater;
@Bean
protected Bus bus;
public ServerListAdapter() {
super(new ArrayList<InetAddress>());
}
@AfterInject
protected void afterInject() {
bus.register(this);
}
@Override
protected ServerView createView(ViewGroup parent) {
return ServerView_.build(context);
}
@Override
protected ServerView updateView(ServerView itemView, InetAddress item) {
itemView.update(item);
return itemView;
}
@Subscribe
public void fetchServerStatus(DiscoveryStatusEvent event) {
setItems(event.addresses);
}
@Subscribe
public void onServerStatusUpdated(DiscoveryStatusChangedEvent event) {
switch (event.type) {
case SERVER_ONLINE:
getItems().add(event.address);
break;
case SERVER_OFFLINE:
getItems().remove(event.address);
break;
}
notifyDataSetChanged();
}
}

And this is the derived class generated by AA during the compiling-time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public final class ServerListAdapter_
extends ServerListAdapter
{
private Context context_;
private ServerListAdapter_(Context context) {
context_ = context;
init_();
}
public static ServerListAdapter_ getInstance_(Context context) {
return new ServerListAdapter_(context);
}
private void init_() {
inflater = ((LayoutInflater) context_.getSystemService(Context.LAYOUT_INFLATER_SERVICE));
context = context_;
bus = Bus_.getInstance_(context_);
afterInject();
}
public void rebind(Context context) {
context_ = context;
init_();
}
}

And here is how it is consumed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@EFragment(R.layout.fragment_server_list)
public class ServerListFragment extends Fragment {
@Bean
protected DiscoveryService discoveryService;
@Bean
protected ServerListAdapter adapter;
@ViewById(R.id.server_list)
protected GridView serverGridView;
@AfterViews
protected void afterViews() {
serverGridView.setAdapter(adapter);
discoveryService.start();
}
@Override
public void onDetach() {
super.onDetach();
discoveryService.stop();
}
}

As you can see, in the ServerListFragment, the ServerListAdapter instance injected into bean is actually the instance of ServerListAdapter_. And due to polymorphic, the instance just behaves like a ServerListAdapter instance.

On the other hand, according to the description on Otto’s Home Page:

In order to receive events, a class instance needs to register with the bus.

Registering will only find methods on the immediate class type. Unlike the Guava event bus, Otto will not traverse the class hierarchy and add methods from base classes or interfaces that are annotated. This is an explicit design decision to improve performance of the library as well as keep your code simple and unambiguous.

Otto only search annotations in direct class, which is ServerListAdapter_ in instance, and there isn’t any annotation included. As a consequence, all the @Subscribe annotated methods are ignored by com.squareup.otto.AnnotatedHandlerFinder. So the posted events become dead event due to no subscriber found.

Comments

There is rumor that this issue will be fixed in Otto 2.0. But according to the comment from Jake Wharton, Otto’s developer, Otto 2.0 will take forever to release.

In fact Otto 2.0 Repo has been not touched for 2 years already. Although we could check out the code and build Otto 2.0 by ourselves, but it takes efforts. Especially when some bug is found.

Conclusion

Luckily, although Otto turns into “maintenance mode”, the compatibility issue takes forever to resolve. I found a great Otto alternative, EventBus from GreenRobot. A boring name, but great library.

According to EventBus‘s document, it provides richer feature and better performance than Otto.
The most important EventBus is friendly to AndroidAnnoations. They two works well together.

EventBus and Otto Feature Comparison

EventBus Otto
Declare event handling methods Name conventions Annotations
Event inheritance Yes Yes
Subscriber inheritance Yes No
Cache most recent events Yes, sticky events No
Event producers (e.g. for coding cached events) No Yes
Event delivery in posting thread Yes (Default) Yes
Event delivery in main thread Yes No
Event delivery in background thread Yes No
Asynchronous event delivery Yes No

EventBus and Otto Performance Comparison

EventBus over Otto
Posting 1000 events, Android 2.3 emulator ~70% faster
Posting 1000 events, S3 Android 4.0 ~110% faster
Register 1000 subscribers, Android 2.3 emulator ~10% faster
Register 1000 subscribers, S3 Android 4.0 ~70% faster
Register subscribers cold start, Android 2.3 emulator ~350% faster
Register subscribers cold start, S3 Android 4.0 About the same

An interesting share box hover effect

Today I spent some time on customizing the share-box used in this blog.

Here is the effect

The cover effect for the social network icons are simulate the icon is floating from the background by animating the shadow. In the implementation, I used some shadow described in Material Design Shadow in HTML. For the texts, I use the text-shadow effect that used on the same text shadow used on tht title of the article.

Hexo file name escape issue and solution

I’m working on reorganize posts today, and found some filename issue with Hexo.

Speical symbols are not escaped properly

Just found this issue today.

To ensure filename generate from title is legal as either file name or url path, Hexo uses hexo.util.escape.filename to escape the illegal symbols. filename does escape some symbols, but it doesn’t cover all illegal symbols, such as +, =, &, etc. If these symbols are not escaped properly, which generates illegal url link. As a result, your post will never be acessible.

I found this issue is because, I have a post with +3 trainer in its title, since + is not escaped, and + will be treated as space by http. As a consequence, my post will never be able to open, unless I escape + as %2B. To avoid this kind of problem, I wish all the symbols in post name are escaped as -.

Besides this issue, Hexo has another issue with name escaping that the escaped file name might contains continues -. For example, your post title is “A great introduction - part 1”, you will get escaped name a-great-introduction---part-1.md. I wish the continues - in the file name should be replaced with single -, name a-great-introduction-part-1.md is more readable than previous one.

To fix the 2 issues, I just created a pull request, hope it will be merged soon.

I cared about this issue so much is because I uses hexo-consle-rename plug-in, which also uses util.escape to handle file name. To keep naming consistency when between different version of Hexo, I addd a kind of evil monkey patch in the v0.1.2. So make sure the plug in can work properly even with old Hexo.

File name case issue

Besides the Hexo naming issue, I also met the case of the filename today. Although it depends on the blog hosting, but it might cause 404 if the file name case changed.

To avoid this kind of issue, I strongly recommend to set filename_case: 1 in _config.yml, which will make sure all file name are in lower case.

There is common pitfall here for Windows or Mac with case-insensitive file system. If you have deploy the website once with wrong filename casing. Regenerate after updated filename_case won’t help, because file system won’t treat case change in filename as “change”. So you cannot really commit the “change” to fix the 404 issue.

To fix this issue, there is easy and efficient trick. Go to .deploy folder, execute following commands:

1
2
3
4
$ git rm -rf *
$ git ci -m "Clean all file"
$ hexo clean
$ hexo d -g

To force clean the repo once enforce git to treat 2 files with same name but different casing as different ones. So the name casing issue can be fixed.

Use Hexo Asset Folder to manage resource used by post

Hexo asset folder is a folder that with the same name as you post file, the content in which will be copied to the folder where the rendered post html file located. It is a great way to keep the relationship between the post and its referenced resources. Personally, I prefers to put all the images or other downloadable files that referenced by the post into its asset folder.

Although asset folder is a great idea, but in practice, there are some common pitfall might disappoint you badly.

Relative Path Pitfall

The idea of asset folder is actually based on an assumption, that the asset resources will be placed under the same folder with the html. The html can reference these files with relative path.

Suppose we have following files as post file:

1
2
3
4
/2014-08-19-awesome-post.md
/2014-08-19-awesome-post/
screenshot.png
document.pdf

The compiled file structure will be like this:

1
2
3
4
/2014/08/19/awesome-post/
index.html
screenshot.png
document.pdf

The asset files are located in the same folder as the html file. So in the post file, you can reference the resource files as

1
2
3
![ScreenShot](screenshot.png)
[Document](document.pdf)

Then this will be compiled as following Html:

1
2
3
4
5
6
<p>
<img src="screenshot.png">
</p>
<p>
<a href="document.pdf">Document</a>
</p>

So far it looks great. But if you open your home page of you site, you find the image isn’t displayed, and the link to document.pdf is also broken.

The problem here is that the the relative path assumption only works in /2014/08/19/awesome-post/index.html. But the content compiled from 2014-08-19-awesome-post.md might also be used by HomePage(/index.html), Archive Page(/archive/index.html), and tag pages and category pages. For these html pages, the relative path relationship doesn’t exist. So the relative link causes 404 error.

To solve the issue, someone use absolute url in the post. So they write markdown in this way:

1
2
3
![ScreenShot](/2014/08/19/awesome-post/screenshot.png)
[Document](/2014/08/19/awesome-post/document.pdf)

This approach fix the link issue, but makes you lost the benefits of using relative path.

So you need hexo-tag-asset-res, which allow you to reference asset resources with relative path. It will convert them into absolute path during compilation. Easy and efficient.

Empty asset folders

For convenience, I turned on the post_asset_folder to true in my _config.yml. So the asset folder will be created along with post when I execute hexo new. It is great because create asset folder manually is boring and error-proning. If you introduced a typo carelessly, the link will be broken immediately.

But by ask Hexo to create asset folder automatically causes another problem. I don’t really have asset resources for each post, then there must be a number of empty asset folders. So I wish these folders can be removed if it is empty.

For this purpose, you might need hexo-console-clean-asset-folder. This plugin helps you to remove all the empty asset folders automatically.

Rename the post

Well, renaming a post already published is that common. But it is very likely to rename a draft. When renaming the post file, you have to remember also rename the asset folder too, or the link will broken.

To help you on this issue, you might need hexo-console-rename. The plugin helps you to rename the asset folder along with post. And it also helps you on migration once you updated your new_post_name pattern.

So this is the common pitfalls in using asset folder in Hexo, and the plug-ins that help you to mitigate the pain.

Hexo plug-in to rename the post according to title automatically

When writing blog with [Hexo], I uses hexo new command to create new post file. If the title of the post is provided, then the file is named according to the tile. This is super convinient, and I’m really loving it.

But there is problem! If I changed my mind when during the writing, so I changed the title of the post. As a consequence, the post file name doesn’t match to the post title any longer.

In the past, I have to rename the post file manually. If there is asset folder, I also have to remember rename it accordingly. And I have to becareful to avoid introduce typo, or it either break the reference or cause other problems.

Besides, if you have special name pattern for your post, such as have time-stamp in your post name. The problem is more complicated. You have to reserve the time-stamp carefully, and replace all the space or any other improper characters with -.

At least for me, it is a complicated, error-proning and unpleasant work to do.

I’m a lazy guy, I don’t want to repeat this pain time and time again. To save myself from such pain, I create the plug-in hexo-console-rename.

The plug-in reads the front-matter of the post, then figure out the proper name. It is smart enough to know what is the proper name for the post, when you changed your configuration, it changes its behavior also.

To use the plug-in is super easy. I usually use it in this way:

1
$ hexo r source/**/*.md

Then it scans all the posts for me, and fix the filename when necessary. Easy and efficient.

Advanced Usages

Actually after I created the plugin, I figured out sevearl advance usages of this plug-in. Sometimes, it could become your life savor!

new_post_name updated

If you change the new_post_name in your _config.yml. You new post will follow a different name pattern than old ones. At this time, you might really want to rename all the old posts to keep consistency! But do it manually is a painful job to do!

Then hexo-console-rename is your live-savor! You just run

1
$ hexo r -p ':title.md' source/**/*.md

Then all the old files will be renamed under your new naming rule! Aesome!

date in your post changed

It isn’t a common case, but if you have changed the date field in the front-matter of your post. And you have time-stamp in your file name. You can also use hexo-console-rename to rename the file for you.

For more detail, check out the hexo-console-rename home page.

Trick to use CoffeeScript in Hexo

Hexo has a scripts folder, and the files under which are loaded by Hexo when start. I usually uses this folder as the development folder for my plug-in scripts. And extract them into independent package after polished it into package-ready quality.

Usually, the files under scripts should be javascripts. But as I’m a fan of Coffee Script, so I wish to use coffee-script to write the plug-ins. For the formal package, I compile the coffee scripts into javascripts before release. But for development, I wish to use coffee script directly.

In node.js, it is possible to require coffee-script directly, if you registered the coffee-script runtime compiler:

1
require('coffee-script/register');

And as how node.js require function is implemented, you cannot register coffee-script runtime compiler in .coffee file. Or the compiler will complain:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[error] HexoError: Script load failed: plugin.coffee
SyntaxError: Unexpected string
at Module._compile (module.js:439:25)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at /usr/local/lib/node_modules/hexo/lib/loaders/scripts.js:17:11
at Array.forEach (native)
at /usr/local/lib/node_modules/hexo/lib/loaders/scripts.js:15:13
at /usr/local/lib/node_modules/hexo/lib/util/file2.js:339:7
at done (/usr/local/lib/node_modules/hexo/node_modules/async/lib/async.js:135:19)
at /usr/local/lib/node_modules/hexo/node_modules/async/lib/async.js:32:16
at /usr/local/lib/node_modules/hexo/lib/util/file2.js:335:11
at Object.oncomplete (evalmachine.<anonymous>:107:15)

Theoretically, it is possible to put the coffee-script registration code in a javascript file and also put it under /scripts folder. SO Hexo will load it when start-up.

Well, this approach doesn’t really work. If you try it, it is very likely to get the exactly same error as before. The reason is related to Hexo implementation. Hexo uses Scripts Loader to require files under /scripts. The loader doesn’t really provide an explicit way to specify which file is loaded before another. So the registration file is guaranteed to be loaded before any .coffee.

So far, it seems that there is no cure to this problem! But actually it does. There is undocumented feature will help to solve this issue.

Hexo uses [Script Loader] to load the scripts. In Scripts Loader use hexo.util.file2 to populate the source files under /scripts. And hexo.util.file2 use fs.readdir to actully populate the file system entries. For fs.readdir, there is a undocumented feature, that the populated entries are actually sorted alphabetically, which means a.js is loaded before b.coffee.

With this feature, we can put our coffee-script registration in a file with lower alphabetic-order name. Personally, I’d like called ___register_coffeescript.js, since _ is smaller than any letter or number.

WARNING: fs.readdir yielding sorted files is an undocumented behavior, which means it is not guaranteed either to work across platforms or not get changed in the future. So for, it works on Mac, and I expect it behaves similar on Linux. But not sure about Windows, since fs uses a different native binding on Windows.

New Favicon design for my blog

To my wife passed driver license theory course, I created a new log for my blog

Here is what I got:

Logo for ThoughtWorkshop

Gear

Gear is the hint of workshop. Because this blog is named as TimNew's ThoughtWorkshop

In the beginning, I was intend to create the gear in Steampunk style. I like such old-fashioned style, but later I found such style is not distinguishable in small icon, so I simplified it to the dark-metel gear.

D

Letter D is from the pronunciation of my Chinese name, which might not be well-known. It is a personal logo, so I wish to have something personal in it.

The font is Gear Box from Gaut Fonts.

It is an awesome font to create some mechanical feel design.

Gear Box Logo

The rainbow color is the hint of unrestrained idea and inspiration. In Chinese, it reads 天 谱.

Well, this is it.

Favicon is not as simple as you think

Favicon is the little icon that displayed on the title bar or tab bar when you browsing a web site. At very beginning, it is used to be the little icon displayed in favoriate bar when user add the website to favorites. Then later, it becomes the the standard way to specify custom icon for a website.

Most web site provides favico, developer add one line description in the <head>:

1
<link rel="shortcut icon" href="/favico.ico" type="image/x-icon">

This simple piece of code works, but there are a lot of issues with this declaration. Actually specify the favico isn’t as simple as you might expect!

For this piece of code, according to W3C document how to favicon, there are 2 issues with it:

First, shortcut isn’t a standard value for rel, it is only for IE.

Second, ico format is a Microsoft oriented file type, not all platform likes it. Linux, Mac, iOS, Android, do not really appreciate this format.

Beides the 2 issues described above, the size of the favicon also matters.

Someone says it should be 16x16. Yes, 16x16 icon is used in tab or tree view. Some other says it should be 32x32. Well, this is also true. 32x32 icon is displayed in toolbar.

16x16 and 32x32 are the most used sizes for favico, but that’s not all. The reality is a lot complicated, I’ll explain this issue later. Let focus on this 2 size first.

To provide the image with 2 different sizes. For ico it is not an issue, since ico is a image container file format, which can encapsulate several images with different sizes and color in a single file. It is convenient for developer, but not for users. Because it means the users need to download a big file, most of the data in which is not used at all.

For the recommended png, it is no way to provide multiple images in a single file. So we have to provide 2 different separate files, and specify them with 2 different <link> tags with sizes attribute. This is a more efficient way, but unfortunately, you’re living in the world has something called Internet Explorer. The favico in png is not supported by IE until IE 11. What a hell!

Actually, there is a lot of issue with IE in this case. There is an great article by Mathias Bynen that discussed this issue in detail, which provides a lot of interesting information related to favico.

Microsoft Windows 8

Besides typical browser usage, Favicon is also used to create Metro Tile by IE 10 and IE 11 on Windows 8. It requires something quite different. Here is a MSDN document that described how to create custom tiles for IE11 websites. For these tile icon, Windows 8 also asks for a background color other than the icon itself.

Android

In the age of mobile internet, favicon is not just used by the desktop browser, but also mobile devices. On mobile devices, there are some more specific requirements.

On android, the screen size and resolution varies between devices. So the visual elements on Android are measured in dp. According to the screen resolution, there are different conversion ratios between dp and px. And to have pixel perfect image on Android, developer should provide several images for different dp-px-ratios.

Google have a well written document that described how to create icon for the web app that added to homescreen.

iOS

For iOS, it is simliar to Android, but seems to be much more complicated. On iOS, it is also possible to create a shortcut for the web apps. Apple named such icon as apple touch icon, which is used by Safari and other browsers on iOS.

For the iOS devices, iPhone and iPad have different screen sizes, so they have different size requirements for touch icon.

Furthermore, there are device with retina display and with normal one. To have pixel perfect image on retina display, it requires the resolution of the image to be doubled.

And since iOS 7, iOS changed its UI style, the icon size used by iOS 7 is also slightly changed. So you should provide new icons for iOS 7 devices!

To make the icon fit iOS visual style best, Apple recommend web application to provide precomposed icon, which is a icon that added rounded corner and background by itself.

To have the pixel perfect icon on iOS, you might need to provide around 10 different images files as apple touch icon.

What a hell!!!! The touch icon on iOS is totally a mess!!!!

Here is a document from Apple that describes how configure web application icon.

Other

Besides all cases that describe above, favicon is also used in some special cases, such as Google TV, Opera Speed Dial, Chrome Web App. They all requires different size of favico.

Conclusion

In a short description, favico isn’t as simple as it looks. And it actually used wrongly by most websites.
To provide proper favico for all platforms and devices is not a simple work to do.
Here is the favico declaration that used this blog:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
<link rel="icon" type="image/png" href="/favicon-196x196.png" sizes="196x196">
<link rel="icon" type="image/png" href="/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
<meta name="msapplication-TileColor" content="#00a300">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">

And these files are served on this blog as favico:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
favicon.ico
favicon-16x16.png
favicon-32x32.png
favicon-96x96.png
favicon-160x160.png
favicon-196x196.png
apple-touch-icon.png
apple-touch-icon-precomposed.png
apple-touch-icon-57x57.png
apple-touch-icon-60x60.png
apple-touch-icon-72x72.png
apple-touch-icon-76x76.png
apple-touch-icon-114x114.png
apple-touch-icon-120x120.png
apple-touch-icon-144x144.png
apple-touch-icon-152x152.png
browserconfig.xml
mstile-70x70.png
mstile-144x144.png
mstile-150x150.png
mstile-310x150.png
mstile-310x310.png

As you can see, to prepare all these configurations and files is not an easy job to do. It really consumes you a lot effort.

Fortunately, we have the awesome Real Favicon Generator brought by Philippe Bernard, which will save your tons of time to have the proper fav icon configuration.

Real Favicon Generator also comes with a favicon checker, which check the favorite configuration for your website, and generates beautiful report.

At last the FAQ of the site also provides a good explanation of the issue described above in details. Hope it helps.