Why does getActivity () block during a JUnit test when a custom ImageView calls startAnimation?

I wrote an Android application that displays a custom ImageViewone that rotates periodically using startAnimation(Animation). The application works fine, but if I create a JUnit type ActivityInstrumentationTestCase2test and test calls getActivity(), the call getActivity()will never return until the application goes to the background (for example, the device’s home button is pressed).

After a long time and frustration, I found that it getActivity()returns immediately if I comment on the call startAnimation(Animation)in my custom class ImageView. But this will defeat my custom goal ImageView, because I need to revive it.

Can someone tell me why it getActivity()blocks during my JUnit test, but only when used startAnimation? Thanks in advance to anyone who can suggest a workaround or tell me what I'm doing wrong.

Note. The solution should work with a minimum API level at the API level.

Here is all the source code you need to run (put any PNG image in res / drawable and name 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);
    }

}
+5
source share
5 answers

, . , 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;
    }
+9

, , , - , .

, getActivity(), , InstrumentationTestCase.java

public final <T extends Activity> T launchActivityWithIntent(
            String pkg,
            Class<T> activityCls,
            Intent intent) {
        intent.setClassName(pkg, activityCls.getName());
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        T activity = (T) getInstrumentation().startActivitySync(intent);
        getInstrumentation().waitForIdleSync();
        return activity;
    }

, :

getInstrumentation().waitForIdleSync();

- ! ? , , . , , , , , ! , .

+1

UPDATE: @nebula : fooobar.com/questions/1519633/...


: , Animation:

Android:

, . - , ActivityInstrumentationTestCase2.getActivity(), Activity Animation ImageView, SSCCE , , .

0

, , , all;)

public class TestApk extends ActivityInstrumentationTestCase2 {

    private static final String LAUNCHER_ACTIVITY_FULL_CLASSNAME =
        "com.notepad.MainActivity";
    private static Class launcherActivityClass;
    static {

        try {
            launcherActivityClass = Class
                    .forName(LAUNCHER_ACTIVITY_FULL_CLASSNAME);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public TestApk () throws ClassNotFoundException {
        super(launcherActivityClass);
    }

    private Solo solo;

    @Override
    protected void setUp() throws Exception {
        solo = new Solo(getInstrumentation());
        Intent intent = new Intent(getInstrumentation().getTargetContext(), launcherActivityClass);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        getInstrumentation().getTargetContext().startActivity(intent);
    }

    public void test_ookla_speedtest() {
        Boolean expect = solo.waitForText("Login", 0, 60*1000);
        assertTrue("xxxxxxxxxxxxxxxxxxx", expect);
    }

    @Override
    public void tearDown() throws Exception {
        solo.finishOpenedActivities();
        super.tearDown();
    }

}
0

, . ? : , . , ? , - , . AndroidJUnit ( , ActivityInstrumentationTestCase2 , , , AndroidJUnit ; , AndroidJUnit waitForIdleSync, ' )

@Rule
public ActivityTestRule<MainActivity> mActivityRule =
        new ActivityTestRule<>(MainActivity.class, true, false);

@Before
    public init() {
    Activity mActivity;
    Intent intent = new Intent();
    intent.put("isTestMode, true);
    mActivity = mActivityRule.launchActivity(intent);
}

, MainActivity onCreate :

Boolean isTestMode = (Boolean)savedInstanceState.get("isTestMode");
if (isTestMode == null || !isTestMode) {
    rotatingImageView.startRotation();
}

After starting the action, you can use other means to start the rotation, if it is important for you.

0
source

All Articles