我写了一个Android应用程序,显示自定义的的ImageView
定期自身旋转,使用 startAnimation(动画)
。该应用程序工作正常,但如果我创建类型的JUnit测试 ActivityInstrumentationTestCase2
和测试呼叫 getActivity()
,即来电 getActivity()
永远不会返回,直到应用程序进入到背景(例如,设备的主按钮是pressed)。
在很多时间和挫折,我发现 getActivity()
立即返回,如果我注释掉调用 startAnimation(动画)
在我的自定义的ImageView
类。但是,这会破坏我的自定义的目的的ImageView
,因为我确实需要制作动画。
谁能告诉我,为什么只有当 startAnimation
用于 getActivity()
我的JUnit测试过程中块,但?在此先感谢任何人谁可以提出一个解决办法或告诉我,我做错了。
注:该解决方案需要与Android API级别10最小工作
下面是你需要运行它的源$ C $ C(投入任何资源PNG图像/绘制并调用它the_image.png):
activity_main.xml:
< RelativeLayout的的xmlns:机器人=http://schemas.android.com/apk/res/android
的xmlns:工具=http://schemas.android.com/tools
机器人:layout_width =match_parent
机器人:layout_height =match_parent
机器人:paddingBottom会=@扪/ activity_vertical_margin
机器人:以下属性来=@扪/ activity_horizontal_margin
机器人:paddingRight =@扪/ activity_horizontal_margin
机器人:paddingTop =@扪/ activity_vertical_margin
工具:上下文=MainActivity。>
< com.example.rotatingimageviewapp.RotatingImageView
机器人:ID =@ + ID / rotatingImageView
机器人:layout_width =WRAP_CONTENT
机器人:layout_height =WRAP_CONTENT
机器人:背景=@可绘制/ the_image/>
< / RelativeLayout的>
MainActivity.java:
包com.example.rotatingimageviewapp;
进口android.app.Activity;
进口android.os.Bundle;
进口android.util.Log;
公共类MainActivity延伸活动{
私人RotatingImageView rotatingImageView = NULL;
@覆盖
保护无效的onCreate(包savedInstanceState){
super.onCreate(savedInstanceState);
的setContentView(R.layout.activity_main);
rotatingImageView =(RotatingImageView)findViewById(
R.id.rotatingImageView);
rotatingImageView.startRotation();
}
@覆盖
保护无效的onPause(){
super.onPause();
rotatingImageView.stopRotation();
}
@覆盖
保护无效onResume(){
super.onResume();
rotatingImageView.startRotation();
}
}
RotatingImageView.java(自定义ImageView的):
包com.example.rotatingimageviewapp;
进口java.util.Timer中;
进口java.util.TimerTask中;
进口android.content.Context;
进口android.os.Bundle;
进口android.os.Handler;
进口android.os.Message;
进口android.util.AttributeSet;
进口android.view.animation.Animation;
进口android.view.animation.RotateAnimation;
进口android.widget.ImageView;
公共类RotatingImageView扩展ImageView的{
私有静态最后长ANIMATION_PERIOD_MS = 1000至24年;
//,做旋转动画的处理程序
私人最终处理程序处理程序=新的处理程序(){
私人浮动currentAngle中= 0F;
私人最终对象animLock =新的对象();
私人RotateAnimation动画= NULL;
@覆盖
公共无效的handleMessage(信息MSG){
浮nextAngle = 360 - msg.getData()getFloat(旋转)。
同步(animLock){
动画=新RotateAnimation(
currentAngle中,
nextAngle,
Animation.RELATIVE_TO_SELF,
.5f,
Animation.RELATIVE_TO_SELF,
.5f);
anim.setDuration(ANIMATION_PERIOD_MS);
/ **
*注释掉以下行允许getActivity()来
*立即返回!
* /
startAnimation(动画);
}
currentAngle中= nextAngle;
}
};
私人浮动旋转= 0F;
私人最终定时器定时=新的定时器(真正的);
私人的TimerTask的TimerTask = NULL;
公共RotatingImageView(上下文的背景下){
超(上下文);
}
公共RotatingImageView(上下文的背景下,ATTRS的AttributeSet){
超(背景下,ATTRS);
}
公共RotatingImageView(上下文的背景下,ATTRS的AttributeSet,
INT defStyle){
超(背景下,ATTRS,defStyle);
}
公共无效startRotation(){
stopRotation();
/ **
*设置,计算旋转值任务
*并告诉处理器做旋转
* /
TimerTask的=新的TimerTask(){
@覆盖
公共无效的run(){
//计算下一个旋转值
旋转+ = 15F;
而(旋转> = 360°F){
旋转 - = 360F;
}
//告诉处理器做旋转
束束=新包();
bundle.putFloat(旋转,旋转);
消息味精=新的Message();
msg.setData(包);
handler.sendMessage(MSG);
}
};
timer.schedule(TimerTask的,0,ANIMATION_PERIOD_MS);
}
公共无效stopRotation(){
如果(NULL!=的TimerTask){
timerTask.cancel();
}
}
}
MainActivityTest.java:
包com.example.rotatingimageviewapp.test;
进口android.app.Activity;
进口android.test.ActivityInstrumentationTestCase2;
进口com.example.rotatingimageviewapp.MainActivity;
公共类MainActivityTest扩展
ActivityInstrumentationTestCase2< MainActivity> {
公共MainActivityTest(){
超(MainActivity.class);
}
保护无效设置()抛出异常{
super.setUp();
}
保护无效tearDown的()抛出异常{
super.tearDown();
}
公共无效test001(){
的assertEquals(1 + 2,+ 3 0);
}
公共无效test002(){
以下行//测试挂起,直到应用程序被切换到后台
活性活性= getActivity();
assertNotNull(活动);
}
公共无效test003(){
的assertEquals(1 + 2,+ 3 0);
}
}
解决方案
不知道,如果你们解决这个问题。 但是,这是我的解决方案,只是重写方法getActivity():
@覆盖
公共MyActivity getActivity(){
如果(mActivity == NULL){
意向意图=新的意图(getInstrumentation()getTargetContext(),MyActivity.class。);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//寄存器活性需要被监视。
监测= getInstrumentation()添加监视器(MyActivity.class.getName(),空,假的)。
。getInstrumentation()getTargetContext()startActivity(意向)。
mActivity =(MyActivity)getInstrumentation()waitForMonitor(显示器)。
setActivity(mActivity);
}
返回mActivity;
}
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);
}
}
解决方案
not sure if you guys solve this. But this is my solution, just override method getActivity():
@Override
public MyActivity getActivity() {
if (mActivity == null) {
Intent intent = new Intent(getInstrumentation().getTargetContext(), MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// register activity that need to be monitored.
monitor = getInstrumentation().addMonitor(MyActivity.class.getName(), null, false);
getInstrumentation().getTargetContext().startActivity(intent);
mActivity = (MyActivity) getInstrumentation().waitForMonitor(monitor);
setActivity(mActivity);
}
return mActivity;
}