Fork me on GitHub

Sunday, February 17, 2013

Android multiple layout directory considered harmful

In mobile application, displaying different layout for different orientation or screen size is important. For example, An app can be shown in different layout according to the screen orientation as shown below:




Android SDK provides a way to specify a different layout for the screen orientation or the size. This specification is based on directory naming. If you place a layout file in "layout-land", it is for the landscape orientation, and a file in "layout-port" for the portrait orientation. This is intuitive and simple. However, it has a major drawback: maintainability.

Suppose you added a button in /layout/main.xml, you tested your new button works well. Is it OK? No. You may have another layout directory that has main.xml such as /layout-land/main.xml, /layout-large-land/main.xml, /layout-small-land/main.xml  or even /layout-normal-long-hdpi/main.xml. You have to modify and test all of these layouts or some layout does not have the button you added, and omits runtime exception. Writing and testing for every permutations of screen size prevent you to focus on the main idea of your program.

The function of your app might not be changed regardless the size of the screen. Changed thing is the position and the size of widgets. Maintaining multiple versions of basically the same thing is not a hacker way.

Responsive layout on Android

It is much better to write a new layout class that acts responsively. For the example screenshot above, the layout is consisted with two blocks, and the position of each block is changed according to the orientation. This can be achieved with plain-old LinearLayout with the orientation attribute. I wrote a new class that takes charge of the behavior:

class ResponsiveLayout(implicit context: Context) extends SLinearLayout {
  private val portrait = context.getResources.getConfiguration
                        .orientation == Configuration.ORIENTATION_PORTRAIT
  if (portrait) orientation = VERTICAL
  private var first = true

  override def +=(v: View) = {
    if (first) {
      v.<<(if (portrait) FILL_PARENT else WRAP_CONTENT, WRAP_CONTENT)
      first = false
    }
    super.+=(v)
  }
}

It checks current screen orientation, and assigns different properties according to it. It may feels naive for you, but it works fine in practice. Android layout has much room to improve in current status, especially when it is compared with HTML+CSS. However, HTML+CSS undergone numerous refinements for nearly two decades. Android could catch up with it in short time if intensive researches are done based on a solid foundation.

Wednesday, February 13, 2013

Enriched methods of TelephonyManager

TelephonyManager has several listeners that can be invoked for a certain phone state changes.

val callStateListener = new PhoneStateListener() {
  override def onCallStateChanged(state: Int, incomingNumber:String) {
    if(state == TelephonyManager.CALL_STATE_RINGING) doSomething()
  }
}

override def onCreate(b: Bundle) {
  super.onCreate(b)
  // ...
  telephonyManager.listen(callStateListener, 
                          PhoneStateListener.LISTEN_CALL_STATE)
}

override def onDestroy() {
  telephonyManager.listen(callStateListener, 
                          PhoneStateListener.LISTEN_NONE)
  // ...
  super.onDestroy()
}

The boilerplates that keeps track the listener object obscures the main idea from your code. Using the trait Destroyable, listening phone states becomes much simpler:
onCallStateChanged((state, number) => {
  if(state == TelephonyManager.CALL_STATE_RINGING) doSomething()
})

This enhancement is available on the current snapshot, and will be released in version 1.1.

Tuesday, February 12, 2013

Better resource releasing on Android

In Android programming, suppose you have some resources to release, then you probably override onDestroy() method. For example, unregistering BroadcastReceiver can be done as the following Java code:
class MyActivity extends ListActivity {
    protected List<BroadcastReceiver> receivers = 
                         new ArrayList<BroadcastReceiver>()

    public IntentFilter registerReceiver(BroadcastReceiver receiver,
                                         IntentFilter filter) {
        receivers.add(receiver);
        super.registerReceiver();
    }

    public Void onDestroy() {
        for(receiver : receivers) {
            unregisterReceiver(receiver);
        }
        super.onDestroy();
    }
}
Looks not bad, although the code is wordy. However, a really bad thing is revealed when you have to release various resources in multiple classes. You might have a TCP connection, opened file to be closed, phone state listener to be unregistered, or asynchronous job to be properly cancelled. Therefore, the compact representation of resource releasing is important. Moreover, the resource-releasing code should be reused between different classes when it is needed in both Services and Activities.

However, current Android resource management has two reusability issues:

Problem 1: No common parent of Activity and Service

For the example above, there is no way to reuse the code between Activities and Services, because Activity and Service has no common parent interface sharing onDestroy() method!


Problem 2: Maintaining list of resources

The code shown above is consisted with three parts:
  1. Allocates a collection that will contain opened resources (in line 2)
  2. Stores a resource that is acquired (in line 6)
  3. Release the resources (in lines 11-13)
If you manage different kinds of resources (e.g. TCP connection and files), the code is not reusable between the other kinds of resources. That is, you have to allocate two collections for two different resources, and release it individually in onDestroy() method. For example, suppose your service opens files and starts threads that should be closed onDestroy():

class MyService extends Serivce {
    protected List<WorkerThread> threads = new ArrayList<WorkerThread>()
    protected List<File> files = new ArrayList<File>()

    public void openFile(...) {
        //...
        receivers.add(openedFile);
    }
    public void startNewJob(...) {
        //...
        threads.add(workerThread);
    }

    public Void onDestroy() {
        for(file : files) {
            file.close();
        }
        for(thread: threads) {
            thread.sendCancelMessage();
        }
        super.onDestroy();
    }
}

What if other kinds of resources to be managed? What if the same thing is needed in your Activity? This is a specimen of reusability hell.

A solution

In Scala, the example above could be reduced as follows:
class MyService extends SSerivce {
  def openFile(...) {
    //...
    onDestroy(openedFile.close())
  }
  def startNewJob(...) {
    //... brace can also be used
    onDestroy {
      workerThread.sendCancelMessage()
    }
  }
}

Because Scala supports anonymous function as a parameter, the code passed through onDestroy(...) method will be executed when the service is destroyed.

If you have to use the same code in your activity, you can extract it as a trait:
trait OpenFile extends Destroyable {
  def openFile(...) {
    //...
    onDestroy(openedFile.close())
  }
}
Then, just inherit it in your activities or services:
class MyActivity extends SActivity with OpenFile {
  // Done! just use openFile() method and forget about releasing it.
}

This solved the problems by no explicit access of collection object, and introducing common triats that inherits both SActivity and SService.
The trait Destroyable is defined for onDestroy(), and Creatable is defined for onCreate(), and other lifecycle related methods including onResume(), onStart(), onPause(), and onStop() are implemented as well.


Destroyable in Action: UnregisterReceiver

For the case of BroadcaseReceiver, it is done by just inherit a trait in your class.
class MyService extends SService with UnregisterReceiver {
  // Done. Every registered receivers unregistered automatically.
}
This is the full source code of UnregisterReceiver implimentation:
trait UnregisterReceiver extends ContextWrapper with Destroyable {
  override def registerReceiver(receiver: BroadcastReceiver, 
                                filter  : IntentFilter) = {
    onDestroy {
      unregisterReceiver(receiver)
    }
    super.registerReceiver(receiver, filter)
  }
}

Monday, February 11, 2013

Scaloid 1.0 is released

Today, I released Scaloid 1.0. Some of major improvements are including:
  • Source code is subdivided into several groups
  • Traits Destroyable and Creatable is introduced
  • style() is safe for non-exhaustive PartialFunction pattern matching

For a short comment about the last thing, let's see an example as follows:
new SLinearLayout {
  style {
    case b: SButton => b.text(b.text.toString.toUpperCase)
  }
  STextView("Hello")
}
The style is defined only for the class SButton. However, a view widget STextView is defined in the layout. Previous version of Scaloid emits an exception because the pattern matching is not exhaustive. In Scaloid 1.0, it simply ignores it when the widget is not defined in the styles.

Wednesday, February 6, 2013

Scala is better than injection framework

Android programming often drives me crazy, because I have to write too much to do a simple work. Hard to write is just a little problem when it is compared to poor maintainability. For example, let us see a typical Activity implementation:

class AndroidWay extends Activity { 
    TextView name; 
    ImageView thumbnail; 
    LocationManager loc; 
    Drawable icon; 
    String myName; 

    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main);
        name      = (TextView) findViewById(R.id.name); 
        thumbnail = (ImageView) findViewById(R.id.thumbnail); 
        loc       = (LocationManager) 
                        getSystemService(Activity.LOCATION_SERVICE); 
        icon      = getResources().getDrawable(R.drawable.icon); 
        myName    = getString(R.string.app_name); 
        name.setText(myName); 
    } 
}

The method onCreate is full of boilerplate code that makes maintaining painful. To reduce them, peoples thought about injection frameworks. Roboguice is one of them. It can improve the original code to the following way:

class RoboWay extends RoboActivity { 
    @InjectView(R.id.name)             TextView name; 
    @InjectView(R.id.thumbnail)        ImageView thumbnail; 
    @InjectResource(R.drawable.icon)   Drawable icon; 
    @InjectResource(R.string.app_name) String myName; 
    @Inject                            LocationManager loc; 

    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main);
        name.setText(myName);
    } 
} 

Although it seems to be much improved, there are some drawbacks in this approach:
  • Runtime performance
  • Wiring verbose XML layout
  • Not type-safe
If you make a mistake on the type of the view widget, your app will revenge you with a horrible runtime exception. Fortunately, Scala provides a much elegant solution. Here I show a direct translation of the example above to Scala language:

class ScaloidWay extends SActivity { 
  lazy val name      = findView(TR.id.name) 
  lazy val thumbnail = findView(TR.id.thumbnail)
  lazy val icon      = R.drawable.icon.r2Drawable
  lazy val myName    = R.string.app_name.r2String
  lazy val loc       = locationManager

  onCreate { 
    contentView(R.layout.main)
    name.setText(myName)
  } 
} 

Lazy values replace injections, with no runtime performance degradation. And the code becomes much cleaner. However, this is not a final improvement. Another clutter that did not shown in this example is XML layout. It is too wordy to write a simple idea, and careful wiring with your code is mandatory. We can eliminate the source of the problem, by not writing layout in XML. The following example builds UI layout within an ordinary Scala code:

class ScaloidWay extends SActivity { 
  lazy val name      = new STextView(R.string.app_name)
  lazy val thumbnail = new SImageView()

  onCreate { 
    contentView(new SVerticalLayout += name += thumbnail)
  } 
} 

Less significant `lazy val loc` is omitted because we can use `locationManager` whenever it needed, and implicit conversion replaces the position of `myName` and `icon`.


Tuesday, February 5, 2013

Scaloid 0.9 is released

Scaloid 0.9 is out. Major improvements made on this version are:


Scaloid is continuously getting mature. Feel free to try out, experience zen of Android programming, and give me a feedback.