开发者

ActivityInstrumentationTestCase2 issues - test hangs because of invalidate() call in the code I'm writing a test for

I'm implementing a test for some code I've written which I have distilled down into a sample project pasted below. The problem I am having is that the test hangs the test runner and none of the test in my test case run.

I have tracked the problem down to a call to invalidate() in getChildStaticTransformation(View child, Transformation t) which I am overriding to do some scaling effects.

If I comment out the call to invalidate() then the tests run. The reason I'm calling invalidate() is to ensure that the view gets redrawn after I apply the scaling effect.

If I do not call invalidate() then the view dosen't look right until a user action forces the screen to get redrawn.

Two questions :

  1. Why is invalidate() blocking the test from running?
  2. Should I be doing the scaling of my view differently that does not require invalidate() to be called?

Following is the output of the Eclipse Android Console showing where the test hangs :

[2011-08-19 13:51:27 - HelloGalleryTest]
------------------------------
[2011-08-19 13:51:27 - HelloGalleryTest] Android Launch!
[2011-08-19 13:51:27 - HelloGalleryTest] adb is running normally.
[2011-08-19 13:51:27 - HelloGalleryTest] Performing
android.test.InstrumentationTestRunner JUnit launch
[2011-08-19 13:51:27 - HelloGalleryTest] Automatic Target Mode: using
device '428024642BF9457'
[2011-08-19 13:51:27 - HelloGalleryTest] Uploading
HelloGalleryTest.apk onto device '428024642BF9457'
[2011-08-19 13:51:27 - HelloGalleryTest] Installing
HelloGalleryTest.apk...
[2011-08-19 13:51:29 - HelloGalleryTest] Success!
[2011-08-19 13:51:29 - HelloGalleryTest] Project dependency found,
installing: HelloGallery
[2011-08-19 13:51:29 - HelloGallery] Uploading HelloGallery.apk onto
device '428024642BF9457'
[2011-08-19 13:51:29 - HelloGaller开发者_JS百科y] Installing HelloGallery.apk...
[2011-08-19 13:51:31 - HelloGallery] Success!
[2011-08-19 13:51:31 - HelloGalleryTest] Launching instrumentation
android.test.InstrumentationTestRunner on device 428024642BF9457
[2011-08-19 13:51:31 - HelloGalleryTest] Collecting test information
[2011-08-19 13:51:32 - HelloGalleryTest] Sending test information to
Eclipse
[2011-08-19 13:51:32 - HelloGalleryTest] Running tests...

Below is the sample project code and test case I've written that exhibits the problem I have described above. HelloGallery.getChildStaticTransformation(View child, Transformation t) is the method which has the call to invalidate().

The other files included below are for completeness incase you want to try and build the project and observe the behavior I have described.

START::HELLO_GALLERY_PROJECT-FILES

HelloGallery.java

package com.hello;

import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.Gallery;

public class HelloGallery extends Gallery {

       private Camera mCamera = new Camera();

       /**
        * The position (index) of the centered view in the gallery.
        */
       public int mCenterViewPositionIndex;

       public HelloGallery(Context context, AttributeSet attrs) {
               super(context, attrs);
               this.setStaticTransformationsEnabled(true);
       }

       /**
        * {@inheritDoc}
        *
        * @see #setStaticTransformationsEnabled(boolean)
        */
       protected boolean getChildStaticTransformation(View child,
Transformation t) {
               t.clear();

               mCamera.save();
               final Matrix imageMatrix = t.getMatrix();
               int position = getPositionForView(child);
               t.setTransformationType(Transformation.TYPE_MATRIX);

               if (child == getSelectedView()) {
                       mCenterViewPositionIndex = position;

                       t.setAlpha(1f);
                       mCamera.translate(-20, 20, -200);
               }
               else if(((mCenterViewPositionIndex - 1) == position )) {
                       t.setAlpha(0.5f);
                       // no need to zoom this view.
               }
               else if((mCenterViewPositionIndex + 1) == position ) {
                       t.setAlpha(0.5f);
                       // no need to zoom this view.
               }

               else if(((mCenterViewPositionIndex - 2) == position )) {
                       t.setAlpha(0.35f);
                       mCamera.translate(0, 0, 250);
               }
               else if((mCenterViewPositionIndex + 2) == position ) {
                       t.setAlpha(0.35f);
                       mCamera.translate(0, 0, 250);
               }

               else if(((mCenterViewPositionIndex - 3) == position )) {
                       t.setAlpha(0.1f);
                       mCamera.translate(0, 0, 350);
               }
               else if((mCenterViewPositionIndex + 3) == position ) {
                       t.setAlpha(0.2f);
                       mCamera.translate(0, 0, 350);
               }else {
                       t.setAlpha(0.1f);
                       mCamera.translate(0, 0, 450);
               }

               mCamera.getMatrix(imageMatrix);
               mCamera.restore();
//              invalidate();  //  <----  commenting out this line allows the Test
to run.   Uncommenting this line will not allow the Test to run.

               return true;
       }
}

HelloGalleryActivity.java

package com.hello;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Gallery;

public class HelloGalleryActivity extends Activity implements OnItemClickListener {
    private Gallery mGallery;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mGallery=(Gallery)findViewById(R.id.gallery);

        //String array holding the values
        String [] text=new String[]{"Hello","Hi","Alloha","Bonjour","Hallo","¡Hola"};

        //Circular Array adapter to display our values in the gallery control
        HomeGalleryAdapter adapter = new HomeGalleryAdapter(this, R.layout.gallery_item_layout, text);
        mGallery.setAdapter(adapter);
        mGallery.setSelection(adapter.MIDDLE);
        mGallery.setOnItemClickListener(this);
        mGallery.setOnTouchListener(new OnTouchListener() {

            public boolean onTouch(View arg0, MotionEvent arg1) {
                Log.d("HelloGalleryActivity", " --> onTouch");
                return false;
            }
        });        
    }

    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        Log.d("HelloGalleryActivity", " --> onItemClick");  
    }
}

HelloGalleryAdapter.java

package com.hello;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class HomeGalleryAdapter extends ArrayAdapter<String> {
       private LayoutInflater mLayoutInflater;
       private TextView mText;
       private int mLayoutId;

       public static final int HALF_MAX_VALUE = Integer.MAX_VALUE/2;
   public final int MIDDLE;
   private String[] mObjects;

       public HomeGalleryAdapter(Context context, int layoutId, String[]
lables) {
               super(context, 0, lables);

               mLayoutInflater = ((Activity) context).getLayoutInflater();
               mLayoutId = layoutId;

           mObjects = lables;
           MIDDLE = HALF_MAX_VALUE - HALF_MAX_VALUE % lables.length;
       }

       public int getCount() {
               return Integer.MAX_VALUE;
       }

       public String getItem(int position) {
               return mObjects[position % mObjects.length];
       }

       public long getItemId(int position) {
               return position % mObjects.length;
       }

       public View getView(int position, View convertView, ViewGroup group)
{
               if (convertView == null) {
                       convertView = mLayoutInflater.inflate(mLayoutId, group, false);
               }

               mText = (TextView) convertView.findViewById(R.id.gallery_item_text);

               mText.setText((String) getItem(position));
               mText.setTextColor(Color.WHITE);

               convertView.setTag(mText.getText());

               return convertView;
       }
}

res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:orientation="vertical"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent">

       <TextView
               android:id="@+id/textview"
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
               android:text="@string/hello" />

       <com.hello.HelloGallery
               android:id="@+id/gallery"
               android:layout_gravity="center"
               android:spacing="0dp"
               android:layout_width="fill_parent"
               android:layout_height="fill_parent" />
</LinearLayout>

res/layout/gallery_item_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@+id/home_gallery_item_layout"
       android:orientation="vertical"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center">

       <RelativeLayout
               android:id="@+id/gallery_item_background"
               android:layout_width="100dip"
               android:layout_height="100dip"
               android:orientation="horizontal"
               android:background="@drawable/gallery_item_selector">

               <ImageView
                       android:src="@drawable/icon"
                       android:id="@+id/gallery_item_icon"
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:layout_centerInParent="true" />

       </RelativeLayout>

       <TextView
               android:id="@+id/gallery_item_text"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_gravity="center"
               android:textSize="20sp" />
</LinearLayout>

res/drawable/gallery_item_selector.xml

 <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
          android:drawable="@drawable/background_selected" /> <!--
pressed -->
    <item android:drawable="@drawable/background_normal" /> <!--
default -->
 </selector>

res/drawable/background_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="rectangle">
   <gradient
       android:startColor="#80000000"
       android:endColor="#80FFFFFF"
       android:angle="45"/>
   <padding android:left="7dp"
       android:top="7dp"
       android:right="7dp"
       android:bottom="7dp" />
   <corners android:radius="8dp" />
</shape>

res/drawable/background_selected.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="rectangle">
   <gradient
       android:startColor="#80000000"
       android:endColor="#807EB6FF"
       android:angle="45"/>
   <padding android:left="7dp"
       android:top="7dp"
       android:right="7dp"
       android:bottom="7dp" />
   <corners android:radius="8dp" />
</shape>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
       xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.hello"
       android:versionCode="1"
       android:versionName="1.0">
       <uses-sdk
               android:minSdkVersion="7" />

       <application
               android:icon="@drawable/icon"
               android:label="@string/app_name">
               <activity
                       android:name=".HelloGalleryActivity"
                       android:label="@string/app_name">
                       <intent-filter>
                               <action
                                       android:name="android.intent.action.MAIN" />
                               <category
                                       android:name="android.intent.category.LAUNCHER" />
                       </intent-filter>
               </activity>

       </application>
</manifest>

END::HELLO_GALLERY_PROJECT-FILES

.

START::HELLO_GALLERY_TEST_PROJECT-FILES

HelloGalleryActivityTest.java ( <---- this file goes in the test project !!)

package com.hello.test;

import android.test.ActivityInstrumentationTestCase2;
import android.widget.Gallery;
import android.widget.SpinnerAdapter;

import com.hello.HelloGalleryActivity;

public class HelloGalleryActivityTest extends
ActivityInstrumentationTestCase2<HelloGalleryActivity> {
       private HelloGalleryActivity mActivity; // the activity under test
       private Gallery mGallery;
       private String[] mGalleryDataTestArray;
       private SpinnerAdapter mGalleryData;

       public HelloGalleryActivityTest() {
               super("com.hello", HelloGalleryActivity.class);
               mGalleryDataTestArray = new String[] { "Hello", "Hi", "Alloha",
"Bonjour", "Hallo", "¡Hola" };
       }

       @Override
       protected void setUp() throws Exception {
               super.setUp();
               setActivityInitialTouchMode(false);

               mActivity = this.getActivity();
               mGallery = (Gallery) mActivity.findViewById(com.hello.R.id.gallery);
               mGalleryData = (SpinnerAdapter) mGallery.getAdapter();
       }

       public void testPreconditions() {
               assertNotNull(mActivity);
               assertNotNull(mGallery);
               assertNotNull(mGallery.getOnItemClickListener());
               assertNotNull(mGalleryData);
       }

       public void testInitialGalleryPosition() {
               int position = mGallery.getSelectedItemPosition();
               String galleryItemLabel = (String)
mGallery.getItemAtPosition(position);

               assertEquals(mGalleryDataTestArray[0], galleryItemLabel);
       }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.hello.test"
     android:versionCode="1"
     android:versionName="1.0">
   <uses-sdk android:minSdkVersion="12" />
   <instrumentation android:targetPackage="com.hello"
android:name="android.test.InstrumentationTestRunner" />
   <application android:icon="@drawable/icon" android:label="@string/
app_name">

   <uses-library android:name="android.test.runner" />
   </application>
</manifest>

END::HELLO_GALLERY_TEST_PROJECT-FILE


Try postInvalidate() instead of invalidate().

I'm not 100% sure what's going on but I suspect there's something in your code that's blocking the UI thread. Using postInvalidate() will return right away and let your code finish, which will unblock the UI thread.


Problem solved!

the invalidate call is NOT needed.. it screws things up PERIOD!

The trick was to add a method to HelloGallery.java to set the center position and to call setPosition on the super class.

HelloGallery.java

public void setSelectionToCenter(int position) {
    mCenterViewPositionIndex = position;
    setSelection(mCenterViewPositionIndex);
}

Then in HelloGalleryActivity call the setSelectionToCenter method rather than setSelection.

HelloGalleryActivity

public void onCreate(Bundle savedInstanceState) {
    ...
    ((HelloGallery)mGallery).setSelectionToCenter(adapter.MIDDLE);
    ...
}

The test runs like a charm now! Woot!
Thanks goes out to Betsy the mystery developer for tracking this down for me!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜