Widget become no response after updating the RemoteView

Finally i have sometimes to update my blog since i have left from my old company.

Recently my colleague and i have developed an android widget. We found that the widget become “no response” upon clicking on the widget after the android device configuration has been changed.

When you placed the widget on the home-screen,  everything will be fine when clicking on it, i can see the i am able to receive the log from the source code which handling the expected intent for clicking. But now you change the orientation from portrait to landscape, no intent action is able to recieve.

I have then written an example for testing why it would happen. The example is a very simple widget which registered the pendingIntent for the button during the widget onUpdate, and then spawn a thread which update the text of widget to random integer for each 2 seconds using RemoteViews object. (shown in the below screen capture)

1. So let see the source codes to find out the root cause of failure (no response). First we need to android widget class which extends AppWidgetProvider as follwing.

public class TestWidget extends AppWidgetProvider {

 /* The tag for logging */
 public static final String LOG_TAG = "TWIKY-WIDGET";

 /* The intent action */
 public static final String ACTION_TEST_CLICK = "org.twiky.test.click";
 ..
}

2.Then during method onUpdate, we register out pendingIntent to the widget button so that we can recieve the clicking intent from that button. This is done by making a RemoteViews object with the target widget layout, applies #setOnClickPendingIntent and then use appWidgetManager#updateAppWidget to populate the updated view to the device screen.

Additionally a thread is spawned for each widget to update the text of the button. It create another RemoteViews pointing to the widget layout, setting the button text by using RemoteViews#setTextViewText, and then updating the view again using appWidgetManager#updateAppWidget.

@Override
public void onUpdate(
    final Context ctx,
    final AppWidgetManager appWidgetManager,
    final int[] aiAppWidgetIds)
{
    Log.d(LOG_TAG, "#onUpdate: " + Arrays.toString(aiAppWidgetIds));
    super.onUpdate(ctx, appWidgetManager, aiAppWidgetIds);
    final String sPackageName = ctx.getPackageName();
    // Connect remote view
    RemoteViews oRemoteViews = new RemoteViews(sPackageName, R.layout.widget);
    // Pending intent on-click
    oRemoteViews.setOnClickPendingIntent(R.id.btn_test,
       this.createOnClickPendingIntent(ctx));
    for (int iAppWidgetId: aiAppWidgetIds){
	// Update the remote views
	appWidgetManager.updateAppWidget(iAppWidgetId, oRemoteViews);
	// Spawn a thread for updating the widget views.
	// Note that [We don't create any pending intent again to the target view].
	//
	final int iAppWidgetIdF = iAppWidgetId;
	Thread oThread = new Thread(
	   new Runnable(){
	        private int m_iCount;
		private final Random m_oSeed = new Random();
		   public void run(){
		     try {
			while(true){
			   RemoteViews oRemoteViews = new RemoteViews(ctx.getPackageName(), R.layout.widget);
			   // Once un-comment the following code, the widget
			   // will always responds correctly when you click
			   // on the button.
    			   //
			   //oRemoteViews.setOnClickPendingIntent(R.id.btn_test, createOnClickPendingIntent(ctx));
			   oRemoteViews.setTextViewText(
			   R.id.btn_test, String.valueOf(this.m_oSeed.nextInt(100)));
			   Log.d(LOG_TAG, "Updating the remote views: " + iAppWidgetIdF + " count:" + this.m_iCount);
			   Thread.sleep(2000);
			   appWidgetManager.updateAppWidget(iAppWidgetIdF, oRemoteViews);
			   this.m_iCount++;
		           }
			} catch (InterruptedException iex){
		            iex.printStackTrace();
			}
		}
	 }
     );
     oThread.start();
     g_hThreadMap.put(iAppWidgetId, oThread);
   }
}

So when the application is deployed to my mobile device, everything works fine and the widget is able to receive from appWidgetProvider#onReceive when i click on the button. (It shows a Toast and a log from logcat when clicking on it)

public void onReceive(
	final Context ctx,
	final Intent intent)
{
	Log.d(LOG_TAG, "#onReceive: " + intent);

	super.onReceive(ctx, intent);
	String sAction = intent.getAction();
	if (sAction.equals(ACTION_TEST_CLICK)){
		// This indicate the pending intent works fine when we are able
		// to receive the expected intent.
		//
		Log.d(LOG_TAG, "Button clicked [logcat] ");
		Toast.makeText(ctx, "Button clicked [pendeing intent]", 100).show();
	}
}

It seemed everything works fine and i can continue my work. But when the device orientation changes, the widget can’t receive any intent firing from the button. I.e. no more toast on my home screen.

After dicussing and investigating with my colleague, the issue seemed to be mentioned in latest Android API

Set the RemoteViews to use for the specified appWidgetIds. Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should contain a complete representation of the widget.

The RemoteViews passed into appWidgetManager must be the complete representation of the widget you want to present. I.e. In case you want to update the view, you must either “reference to the RemoteView that used before” Or “creating A new remote views which included all changes from beginning”. In this case, the RemoteViews object used when updating the display text of button in the spawned thread “”MUST include the pendingIntent registration again for each consecutive update”". Otherwise when the device orientation changes, the app widget manager re-loads the latest RemoteViews from the views cache (those cache are views without setting up any pendingIntent). So the result is obvious, the widget become no response on when clicking on it.

Solution

The solution has been mentioned already, when you need update RemoteViews, make sure the views included all pendingIntent registration. In the above code,  just uncomment the following and the widget will always click-able.

oRemoteViews.setOnClickPendingIntent(R.id.btn_test, createOnClickPendingIntent(ctx));

The complete source code
You can access the complete eclipse project for this issue under Google project hosting:
http://twiky.googlecode.com/svn/branches/widgetUpdatingTraps

生命, 想你望多一眼

樂樂, 不記得何時開始, 你肥肥的個子, 肚皮 “一節一節”, 手腳短短的舉起, 像仙人般坐著的睡姿, 老豆給你改了花名, 叫 “Hamsi 仙” (Hamsi : Hamster 的意思)

小路寶, 係一種有活力, 帶神經質, 好容易被嚇怕, 周圍躲避嘅倉鼠。 起初養你們嘅時侯, 你們常常打架, 又咬媽咪, 真係激死媽咪啦。 之後有一晚, 有一隻倉鼠女身邊吱吱聲的, 「怎麼了? 生了四隻?」正當倉鼠媽媽保護BB之際, 樂樂你仍然可以進出產房, 我便認定了你係BB嘅爸爸了。因為怕倉鼠媽媽產後太神經質, 又或者之後生產太多, 所以將你同老婆仔女分開養。後來又因為同其他倉鼠打架, 再分開你獨立一個住, 本來好惡好惡嘅你, 因為獨立養開, 慢慢開始靜了許多, 又或者係, 大個了, 生性了。

做左人 Daddy 嘅你, 乖了許多, 肥了許多, 樣子都傻了許多, 日子一天一天的過, 你已經認得, 每日給你美味食物嘅媽咪。每晚媽咪放工返到屋企, 你總會在籠嘅旁邊, 跳上跳下, 咯咯聲的用牙仔咬鐵籠, 期待著大大粒嘅乳酪粒。 ( 你最喜歡食乳酪粒, 每晚都要食一粒才安心, 所以你名字叫 樂樂 ) 沖涼嘅時候, 捉住你, 你都唔會咬媽咪, 乖乖的俾媽咪刷頭頭, 刷背背。 自己一個時, 又會在沙池裡碌黎碌去清潔身體, 碌碌下會在沙上睡著。

可愛嘅樂樂, 最喜歡休息嘅地方, 竟然係…… 轉轉和鐵籠之間嘅隙?! 像蜘蛛俠打橫的夾住, 出奇的 enjoy …… 可能係遺傳, 你仔女都好喜歡這麼睡!

平日跳跳札札的你, 昨晚回家發現你對食物冇晒胃口, 鼻子紅紅的, 很疲倦的睡,媽咪用手指摸了你頭頭幾下, 很乖, 沒有太大反應, 但似乎不沉常的乖 ……

今天早上, 媽咪又趕著返工, 出門前, 看見昨天的食物還未食, 身體熱熱的, 輕輕用手弄醒你, 你又很疲倦的, 只走到另外一角繼續睡, 心知大慨是病倒了…… 下午, 老豆 book 了醫生 (醫生兩日嘅booking 都很 full), 媽咪向公司請假, 要盡快帶你去睇醫生。

趕快的回到家裡, 從遠處望見你… 仍然睡在那個角落, 心裡一寒, 再仔細一看, 你很乖, 沒有動, 沒有再像平時一樣跳上跳下…… 媽咪又摸摸你, 還有微暖的身體, 睡著了……

「傻豬樂樂, 為什麼不等媽咪回來 ……」

「老豆, 請你打電話去 cancel 個 booking 吧 ……」

Installing Apache Tomcat 6.0 [Window XP/Vista]

This is a simple tutorial for installing Apache Tomcat J2EE server and verifying the installation.

Installing Apache Tomcat

  1. You can download Apache Tomcat 6.X from http://tomcat.apache.org/download-60.cgi. The installer name is apache-tomcat-6.X.XX.exe
  2. Execute the installer. Click next.
  3. Setup the tomcat installation path and click next. The default is 
      C:\Program Files\Apache Software Foundation\Tomcat 6.0

  4. Setup the component, usually you don’t to configurate any thing here. click next.
  5. Setup the admin from the Apache Tomcat web servers. By default the name of admin is admin and password is empty. It is HIGHLY recommended to enter the admin password for security threat.
  6. Setup the JRE path. Tomcat 6.0 requires JDK/JRE 5.0+ for installing properly.
  7. Install and Bingo !

Verifying the installation 

  1. Start Apache Tomcat web server through command line mode.
    1. Click Start – > Run -> cmd.exe 
    2. Change the current directory to your Tomcat installation path. By default you should execute
      cd C:\Program Files\Apache Software Foundation\Tomcat 6.0\bin
      

    3. Execute tomcat6
  2. Okay the tomcat6 is successfully started, lanuch a web browser and go to :
      http://localhost:8080
  3. If you able to see the web page shown before, then Apache Tomcat has started successfully without error ! Bingo :p
Have a milktea break :D
Posted in JAVA. Tags: , . 7 意見»

Writing Http Servlet Unit test using JMock2

Writing self-contained test-case related to JAVA Servlet API is something harder to the stuff you want to test. Image you have a servlet class and want to test it, the main problem is the servlet class couples with the servlet API in which you are not able to create them directly.

One of the traditional approach for this may need to bundle the J2EE server (usually tomcat) in your test-case, start the server programmatically, and stimulate the request/response lifecycle. This is obviously not a good approach and you may take several day in order to work properly ONLY in your machine.

To erase the complexity of test-case development, we can use a popular mocking framework called JMock2. It is simple yet robust framework that applies declarative mocking statement so that you can understanding everything in just few minutes.

Let start ! I will demonstrate a short example of mocking the input stream and output stream of servlet request and response.

package org.twiky.sample;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;

/**
 * This is simple JUnit4 test-case for demonstrating the mocking capabilities.
 *
 * @author twinsen.tsang
 */
public class JMockHttpServlet {

	/* JMock2 context */
	private Mockery mockContext = new Mockery(); 

	@Test
	public void testMock() throws Throwable {
		final HttpServletRequest  mkRequest  = mockContext.mock(HttpServletRequest.class);
		final HttpServletResponse mkResponse = mockContext.mock(HttpServletResponse.class);
		final ServletOutputStream sos = new ServletOutputStream(){
			@Override
			public void write(int c) throws IOException {
				System.out.print((char)c);
			}
		};

		mockContext.checking(new Expectations(){
			{
				allowing (mkRequest).getInputStream();   will(returnValue(new ByteArrayInputStream("Test Mock".getBytes())));
				allowing (mkResponse).getOutputStream(); will(returnValue(sos));
			}
		});

		// Try do something with the mocking context
		this.process(mkRequest, mkResponse);

		// Check whether all expectations are satisfied.
		mockContext.assertIsSatisfied();
	}

	/**
	 * Do something with the HTTP mocking request and response.
	 *
	 * @param req The HTTP servlet request.
	 * @param resp The HTTP servlet response.
	 * @throws IOException
	 */
	public void process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		resp.getOutputStream().write("Test Mock output".getBytes());
	}
}

The core element of JMock2 is called Mockery, which serves as the context object for storing the mocking object and expectation you want to make with the mocking objects. Expectation is the sentence you expect from the input to the stuff you want to test.

In the above example, servlet request/response object are mocked through the below code:

 final HttpServletRequest  mkRequest  = mockContext.mock(HttpServletRequest.class);
 final HttpServletResponse mkResponse = mockContext.mock(HttpServletResponse.class);

Invoking the method Mockery.mock(class) to mock the interface/abstract class. The mockery will return a mocking object conforming the interface/abstract class passing in the argument. THAT IT ! You have created fake servlet request and servlert response for your test-case. Now let add some expectation that we want our request/response to do with us.

 mockContext.checking(new Expectations(){
  {
   allowing (mkRequest).getInputStream();   will(returnValue(new ByteArrayInputStream("Test Mock".getBytes())));
   allowing (mkResponse).getOutputStream(); will(returnValue(sos));
  }
 });

Each expectation is just like a english statement. In the above example, i expected the servlet request should able to return a input stream with the content “Test Mock” and the response should return a servlet output stream which write any content to the system.out (see full source in the above).

To be continued…

Reference:

  1. JMock2 cheat sheet http://www.jmock.org/cheat-sheet.html
  2. JMock2 cookbook http://www.jmock.org/cookbook.html
 
Posted in JAVA, Test Driven. Tags: . 1 Comment »

勾起溫馨回憶的好歌

最近一拿起 iPod touch,便會點播這首歌,set 了 repeat,聽一句鐘亦唔會膩,除了旋律很動聽,它吸引我的地方是,一邊聽,一邊憶起共同渡過的佳節,甜蜜溫馨的片段在腦海裡重播,真摰動人的一刻浮現在眼前。

李克勤 – 佳節

曲:Edmond Tsang 詞:陳少琪 編:張人傑

城中燈飾優美別緻
當年開始共賞玩過幾次
年宵公園跟你冒雨找尋傘子
像很久的故事

* 同度萬聖節的境況
同抱著去等千禧曙光
從復活節離開
腳步曾踏遍在世上各方 *

# 如果一生可以尚有百萬個的佳節
我亦憑著同樣熱情解禮物結
凡塵俗世中 若某一個先告別
誰繼續留下 聖誕夜最苦澀
如果一生跟你渡過 節日氣氛還求甚麼
來日你白髮漸變多 但我跟前仍是那一個
用年青一張面容親我 #

燃點煙花喜氣盛況
當年相識或者是更好看
颱風訪港的五月節仿如昨天
在深水灣跳浪
Repeat * #

如果一生可以尚有百萬個的佳節
我亦憑著同樣熱情解禮物結
凡塵俗世中 若有一個先告別
誰繼續留下 聖誕夜最苦澀
如果一生跟你渡過 節日氣氛還求甚麼
能在每日每夜也像最初 同享過各種慶賀
人生怎麼算是枉過

Posted in Uncategorized. Tags: , . 2 意見»

永遠的佩亞達兄弟 – 沙治奧拉莫斯

西維爾球員佩亞達心臟衰竭不幸去世雖過去快一年,但事件沒有淡忘了他兄弟– 沙治奧拉莫斯 之情。

西班牙鬥牛在 2008 歐洲國家盃決賽以1球技術性擊到強勢的德國,拉莫斯脫去了西班牙隊的戰袍身披一件印有佩亞達頭像的16號T恤出現在球場上。將歐洲第一的名譽送給他的好兄弟! 感動 !

原文:

http://china.goal.com/hk/Articolo.aspx?ContenutoId=757143

Posted in Soccer. Tags: . Leave a Comment »

Use object Default values carefully

Recently i have faced an implementation decision whether using default values in a Object when input parameters are not validin the consturctor. Let see an example for visualize first


public class DefaultValues {

public static final String DEFAULT_OBJ_A = "_default_obj_A";

public static final String DEFAULT_OBJ_B = "_default_obj_B";

private String objA;

private String objB;

public DefaultValues (){
If (objA == null){
objA = DEFAULT_OBJ_A;
}
if (objB == null){
objB = DEFAULT_OBJ_B;
}

public void setObjA(String objA) {
if (some constraint fulfilled){
this.objA = objA;
}
}

public void setObjB(String objB){
if (some constraint fulfilled){
this.objB = objB;
}
}

public void invoke(){
// Do operation on objA, objB.
}

}

In the above example, when objA or objB is NULL, they use default values in the constructor for keeping the object valid for the rest. Thus calling method invoke will not throw null point exception. Originally my idea is wanted to simplify client/devleoper code as they don’t need to invoke all setter method before using the object safety.

Failed in the developer perspective

This pattern fail because the object execute under unexpected behavior in the client/developer perspective. Developer may not know the object created are using default values because of constraint violation during setting method (in the above example). They may believe setup of object is correct and thus puzzled at why the internal value of object does not expect.

Resolution

In simple, you can add comment on each method about the value constraint for client/developer. Or a better approach, using Default and Complete constructor with appropriate guard clause.

The default constructor takes no argument/parameter. It initializes it’s internal state to default value. Usually it delegate to the complete constructor.

The complete constructor takes number of parameter WHICH made the object valid and consistent, client/developer SHOULD expect no more setter are required to invoke after calling this constructor in normal case.

Let review our example after applying this pattern:


public class DefaultValues {

public static final String DEFAULT_OBJ_A = "_default_obj_A";

public static final String DEFAULT_OBJ_B = "_default_obj_B";

private final String objA;

private final String objB;

/*
* Commont about the default values.
*/
public DefaultValues (){
this(DEFAULT_OBJ_A, DEFAULT_OBJ_B);
}

public DefaultValues(String objA, String objB){
if (objA NOT fulfilled some constraint){
throw new IllegalArgumentException("some thing about objA");
}
if (objB NOT fulfilled some constraint){
throw new IllegalArgumentException("some thing about objB");
}

this.objA = objA;
this.objB = objB;
}

public void invoke(){
// Do operation on objA, objB.
}

}

Now the classes DefaultValues has none setter. Initialized parameters are passed in the second constructor. This constructor validates all parameter, throwing IllegalArgumentException when any one does not satisfy the constraint. I liked this pattern because it blocks developer in the object creation and send out a message “You are making a invalid object, please fix your code)” to them prior to throwing other exception during execution of object behavior with others.

Default values are applied only when using default constructor. It delegate the complete constructor so that you don’t need to maintain two code set of object initailization.

Conclusion

The default values pattern still offer benefits for developer to simplify but have to use it carefully to provide communicate and readable for your audience by introducting default and complete consturctor with guard clause.

Eclipse 3.4 – Ganymede has released

I heard a great news Eclipse 3.4 (a.k.a Ganymede) has released and now can download from offical site

http://www.eclipse.org/

One of major improvement in this release is able to leveage multi-core for compiling classes file, it speed up the compiling time for my working about at least double. :p

For more information about this release,

Java Development Tool improvement in Ganymede

Posted in IDE. Tags: . Leave a Comment »

Understanding Serializable UID

We know Java has provided super easy way to flatten object (not database) to filesystem using build-in serialization mechanisms.

Do you notice you are always given a warning during developing serializable class, asking for generate a serializable UID. what is it stand for ?

Introduction

serializable version UID is the unique identifiers of the object in serialization word. I.e There are not exist TWO Serialization object with same serialization version UID. Thus a serializable ID is used to validate whether a serialized object from the file is compatible to class definition.

Hey ? You may ask, serialization work out of the box, why do i need to understand what is serializable version UID?

Let understand how serial version UID is generated. Quoted from Java doc API,

The default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization.

This default UID is generated when a serializable class does not explicitly declare a serialVersionUID like below:

private static final long serialVersionUID = 42L;

For example, when you serialize something using Microsoft JVM / IBM JVM, and would like to perform deserialization on other side using Sun JVM. you will likely to suffer from InvalidClassExceptions since the default serial version UID does not match on either side.

Even you are developing serialization stuff under single version of JVM, you still need to pay attention to the serial version UID as it is highly sensible to every field and method in the serializable classes.

Example

Let see the following example to illustrate the puzzle:


public static class SerializationTarget implements Serializable {

private int field1 = 0;

private int field2 = 1;

private int field3 = 2;
}

The above is the class you want to serialize to file-system, you can achieve by


ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("example.ser")));
oos.writeObject(new SerializationTarget());
oos.close();

And after a period of time, the class SerializationTarget has modified due to any reason but you want to de-serialize the object from example.ser, You execute the following code


ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("example.ser")));
SerializationTarget serialTarget = (SerializationTarget) ois.readObject();
ois.close()

You are probably received a InvalidClassExceptions again for deserialization because the default serializeation version UID has been changed when you change the class no matter which JVM you are using.

The solution is to ALWAYS generate or create the serializable version UID when start to develop your serializable classes. Thus keeping their signatures identifiers to be equal at any time for maximizing the compatibility issue.

Making your class in-compatible to serialized

on the contrary, you can make your class in-compatible to the serialized object permanently by modifying the serial version UID on your own. It is common case when refactoring projects and you don’t want any legacy serialized object to be able transform back to the refactored object which their state and behavior and subtle. By careful, changing the serial version UID equvialent to make any previous serialized stream (not legacy, including any build before you made this changes) fail to transform back to object.

Reference

Versioning compatibility of Serializable Object

Posted in JAVA. Tags: , . Leave a Comment »

Configurate your Java Logger to File

In real/production environment, application system should log their message for troubleshooting to persistence media like File rather dumping to console.

Java Logging Framework has offered FileHandler which supports simple

  1. Message Formatting (plain or XML)
  2. Message Encoding (default=platform encoding)
  3. Log Rotation and Log file format

The simple configuration files is shown below :

# Specify the handlers to create in the root logger
# (all loggers are children of the root logger)
# The following creates two handlers
handlers = java.util.logging.FileHandler

# Set the default logging level for the root logger
.level = ALL

# Set the default logging level for new FileHandler instances
java.util.logging.FileHandler.level = ALL

# Specifies the name of a Filter class to use
# (defaults to no Filter).
# java.util.logging.FileHandler.filter 

# Specifies the name of a Formatter class to use
# (defaults to java.util.logging.XMLFormatter)
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter

java.util.logging.FileHandler.encoding = UTF-8

# specifies a pattern for generating the output file name.
java.util.logging.FileHandler.pattern= %h/java%u.log

# Specifies an approximate maximum amount to write (in bytes) to any one file.
# If this is zero, then there is no limit. (Defaults to no limit).
java.util.logging.FileHandler.limit = 1048576 

# Specifies how many output files to cycle through (defaults to 1).
java.util.logging.FileHandler.count = 5 

Message Formatter

The log message can be formatted by the property

<logging-name>.formatter=java.util.logging.SimpleFormatter | java.util.logging.XMLFormatter

Message Encoding

The log message encoding is set thru from the property

<logging-name>.encoding=UTF-8

File Log Pattern

When using FileHandler, you can specify the pattern of the log file using file pattern property through some special symbols like %h/java%u.log which represents the path <user.home>/java<unique-id>.log

Log Rotation

You can perform log rotation by specified limit and count at the same time. Former defines how big each log file can has and the latter define the number of log file output in a cycle (i.e. The number of file before overwriting the old logs).


Posted in JAVA. Tags: , . Leave a Comment »
Follow

Get every new post delivered to your Inbox.