I wrote an Android app that displays a custom ImageView
that rotates itself periodically, using startAnimation(Animation)
. The app works fine, but if I create a JUnit test of type ActivityInstrumentationTestCase2
and the test calls getActivity()
, that call to getActivity()
never returns until the app goes to the background (for example, the device's home button is pressed).
After much time and frustration, I found that getActivity()
returns immediately if I comment out the call to startAnimation(Animation)
in my custom ImageView
class. But that would defeat the purpose of my custom ImageView
, because I do need to animate it.
Can anyone tell me why getActivity()
blocks during my JUnit test but only when startAnimation
is used? Thanks in advance to anyone who can suggest a workaround or tell me what I'm doing wrong.
Note: the solution needs to work with Android API level 10 minimum.
Here is all the source code you need to run it (put any PNG image in res/drawable and call it the_image.png):
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<com.example.rotatingimageviewapp.RotatingImageView
android:id="@+id/rotatingImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/the_image" />
</RelativeLayout>
MainActivity.java:
package com.example.rotatingimageviewapp;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends Activity {
private RotatingImageView rotatingImageView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rotatingImageView = (RotatingImageView) findViewById(
R.id.rotatingImageView);
rotatingImageView.startRotation();
}
@Override
protected void onPause() {
super.onPause();
rotatingImageView.stopRotation();
}
@Override
protected void onResume() {
super.onResume();
rotatingImageView.startRotation();
}
}
RotatingImageView.java (custom ImageView):
package com.example.rotatingimageviewapp;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
public class RotatingImageView extends ImageView {
private static final long ANIMATION_PERIOD_MS = 1000 / 24;
//The Handler that does the rotation animation
private final Handler handler = new Handler() {
private float currentAngle = 0f;
private final Object animLock = new Object();
private RotateAnimation anim = null;
@Override
public void handleMessage(Message msg) {
float nextAngle = 360 - msg.getData().getFloat("rotation");
synchronized (animLock) {
anim = new RotateAnimation(
currentAngle,
nextAngle,
Animation.RELATIVE_TO_SELF,
.5f,
Animation.RELATIVE_TO_SELF,
.5f);
anim.setDuration(ANIMATION_PERIOD_MS);
/**
* Commenting out the following line allows getActivity() to
* return immediately!
*/
startAnimation(anim);
}
currentAngle = nextAngle;
}
};
private float rotation = 0f;
private final Timer timer = new Timer(true);
private TimerTask timerTask = null;
public RotatingImageView(Context context) {
super(context);
}
public RotatingImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RotatingImageView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public void startRotation() {
stopRotation();
/**
* Set up the task that calculates the rotation value
* and tells the Handler to do the rotation
*/
timerTask = new TimerTask() {
@Override
public void run() {
//Calculate next rotation value
rotation += 15f;
while (rotation >= 360f) {
rotation -= 360f;
}
//Tell the Handler to do the rotation
Bundle bundle = new Bundle();
bundle.putFloat("rotation", rotation);
Message msg = new Message();
msg.setData(bundle);
handler.sendMessage(msg);
}
};
timer.schedule(timerTask, 0, ANIMATION_PERIOD_MS);
}
public void stopRotation() {
if (null != timerTask) {
timerTask.cancel();
}
}
}
MainActivityTest.java:
package com.example.rotatingimageviewapp.test;
import android.app.Activity;
import android.test.ActivityInstrumentationTestCase2;
import com.example.rotatingimageviewapp.MainActivity;
public class MainActivityTest extends
ActivityInstrumentationTestCase2<MainActivity> {
public MainActivityTest() {
super(MainActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
}
protected void tearDown() throws Exception {
super.tearDown();
}
public void test001() {
assertEquals(1 + 2, 3 + 0);
}
public void test002() {
//Test hangs on the following line until app goes to background
Activity activity = getActivity();
assertNotNull(activity);
}
public void test003() {
assertEquals(1 + 2, 3 + 0);
}
}
See Question&Answers more detail:
os