Android的View.getDrawingCache()抛出Call​​edFromWrongThreadException抛出、View、Android、getDrawingCache

2023-09-06 11:13:40 作者:称霸全服

所以我想捕捉我的应用程序的Andr​​oid中的屏幕问题。这里的情况:

So I have a problem trying to capture my application's screen in Android. Here's the situation:

我想用View.getDrawingCache()方法不断捕获应用程序屏幕,并检测其屏幕的一部分发生了变化。我的应用程序是基于它在5项活动的单一TabHost,我想捕捉在tabhost发生的一切。我也想在屏幕捕捉code在后台运行,为此我创立的AsyncTask实例来捕捉图像,并检查东西已经改变。的问题是,getDrawingCache()方法有时会导致该应用崩溃提高CalledFromWrongThreadException。

I would like to continuously capture the application screen using the View.getDrawingCache() method and detect which part of the screen has changed. My application is based on a single TabHost with 5 activities in it and I would like to capture everything that is happening in the tabhost. I also want the screen capturing code to run in the background and for this purpose I have created an AsyncTask instance to capture the image and check if something has changed. The problem is that the getDrawingCache() method sometimes causes the app to crash raising the CalledFromWrongThreadException.

我已经做了一些测试和这里是我发现..我的全部甚至在tabhost活动之间变化首先所以只有第一个是可见的。第一个活动,只要我禁用滚动条与具有滚动型和机器人:滚动条=无的捕捉作品,但如果我重新启用它们与它相同的异常崩溃。我认为问题是,我做的doInBackground方法截屏和Android是不顺心,由于某种原因(当然,我知道原因 - 它试图绘制缓存比主另一个线程.. )。

I have done some testing and here's what I found.. First of all I am not even changing between the activities in the tabhost so only the first one is visible. The first activity has a ScrollView and as soon as I disable the scrollbars with "android:scrollbars='none'" the capturing works but if I reenable them it crashes with the same exception. I think the problem is that I am doing the screen capture in the doInBackground method and Android isn't liking that for some reason (well, I know the reason - it's trying to draw the cache in a thread other than the main one...).

所以我的问题是,没有任何人有一个想法,我怎么能实现在后台记录?

So my question is does anyone have an idea how I can achieve recording in the background?

这里的code的主要部分,以把事情说清楚......

Here's the main parts of the code to make things clear...

public class TRCTargetMainActivity extends TabActivity {
private static final Logger tracelog = Logger.getLogger( TRCTargetMainActivity.class.getName() );

private boolean isVisible;
private int selectedIndex;
private Bitmap latestScreen, currentScreen;
private boolean stop = false;
private ScreenCaptureThread scThread;

private TabHost mTabHost;
private MyNotificationReceiver receiver;
private BadgeView chatBadge, transfersBadge;

public void onCreate( Bundle savedInstanceState ) {
    super.onCreate(savedInstanceState);

    requestWindowFeature( Window.FEATURE_NO_TITLE );
    setContentView( R.layout.activity_main );

    mTabHost = getTabHost();

    buildTab( "deviceSpec", getString( R.string.activity_trctargetmain_title_info ), R.drawable.ic_tab_device_info, DeviceInfoActivity.class );
    buildTab( "fileExplorerSpec", getString( R.string.activity_trctargetmain_title_transferfolder ), R.drawable.ic_tab_transfer, FileExplorerActivity.class );
    buildTab( "chatSpec", getString( R.string.activity_trctargetmain_title_chat ), R.drawable.ic_tab_chat, ChatActivity.class );
    buildTab( "transfersSpec", getString( R.string.activity_trctargetmain_title_transfers ), R.drawable.ic_tab_transfers, TransfersActivity.class );
    buildTab( "settingsSpec", getString( R.string.activity_trctargetmain_title_settings ), R.drawable.ic_tab_settings, SettingsActivity.class );

    Bundle extras = getIntent().getExtras();
    if ( extras != null ) {
        selectedIndex = extras.getInt( Settings.EXTRA_SELECTED_TAB );

        mTabHost.setCurrentTab( selectedIndex );
    }

    mTabHost.setOnTabChangedListener( new OnTabChangeListener() {
        public void onTabChanged( String arg0 ) {         
            selectedIndex = mTabHost.getCurrentTab();
        }       
    } );  

    chatBadge = new BadgeView( this, mTabHost.getTabWidget(), 2 );
    transfersBadge = new BadgeView( this, mTabHost.getTabWidget(), 3 );

    initializeBroadcastReceiver();
    configureTabHostBackground();
    checkDirectoryExistence();
    checkTargetServiceState();

    doStartScreenCapture();
}

...

public void doStartScreenCapture() {
    stop = false;

    if ( scThread == null ) {
        scThread = new ScreenCaptureThread();
        scThread.execute();
    }
}
...

private Bitmap getScreenBitmap() {
    RelativeLayout r = (RelativeLayout) this.findViewById( R.id.rootLayout );
    View v = r.getRootView();
    v.setDrawingCacheEnabled( true );

    Bitmap bm = v.getDrawingCache();
    if ( bm == null )
        return bm;

    int[] pixels = new int[ bm.getWidth() * bm.getHeight() ];
    bm.getPixels( pixels, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight() );

    return Bitmap.createBitmap( pixels, bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888 );
}
...

private class ScreenCaptureThread extends AsyncTask<String,Integer,Boolean> {

    private void sendScreenUpdate( int[] data, int row, int col ) {
        ByteBuffer byteBuffer = ByteBuffer.allocate( data.length * 4 );        
        IntBuffer intBuffer = byteBuffer.asIntBuffer();
        intBuffer.put( data );

        byte[] array = byteBuffer.array();

        // send the screen update
    }

    private boolean checkForScreenChanges() {
        int[] pixels1 = new int[ Settings.SCREEN_BLOCK_SIZE * Settings.SCREEN_BLOCK_SIZE ];
        int[] pixels2 = new int[ Settings.SCREEN_BLOCK_SIZE * Settings.SCREEN_BLOCK_SIZE ];

        int numCols = currentScreen.getWidth() / Settings.SCREEN_BLOCK_SIZE;
        int numRows = currentScreen.getHeight() / Settings.SCREEN_BLOCK_SIZE;
        int x = 0;
        int y = 0;

        boolean differenceFound = false;
        for ( int i = 0; i < numRows; i++ ) {
            for ( int j = 0; j < numCols; j++ ) {
                latestScreen.getPixels( pixels1, 0, Settings.SCREEN_BLOCK_SIZE, x, y, Settings.SCREEN_BLOCK_SIZE, Settings.SCREEN_BLOCK_SIZE );
                currentScreen.getPixels( pixels2, 0, Settings.SCREEN_BLOCK_SIZE, x, y, Settings.SCREEN_BLOCK_SIZE, Settings.SCREEN_BLOCK_SIZE );

                if ( !Arrays.equals( pixels1, pixels2 ) ) {
                    sendScreenUpdate( pixels2, i, j );

                    differenceFound = true;
                }

                Arrays.fill( pixels1, 0 );
                Arrays.fill( pixels2, 0 );

                x += Settings.SCREEN_BLOCK_SIZE;
            }

            x = 0;
            y += Settings.SCREEN_BLOCK_SIZE;
        }

        return differenceFound;
    }

    @Override
    protected Boolean doInBackground( String... arg0 ) {
        System.out.println( "SCREEN CAPTURE THREAD STARED" );

        currentScreen = getScreenBitmap();
        latestScreen = getScreenBitmap();

        while ( !stop ) {
            try {
                Thread.sleep( 3000 );
            } catch ( Exception e ) {}
            if ( latestScreen != null && currentScreen != null ) {
                if ( checkForScreenChanges() ) {
                    latestScreen = getScreenBitmap();
                    System.out.println( "SCREEN UPDATED" );
                }
            }
            if ( currentScreen != null && !currentScreen.isRecycled() )
                currentScreen.recycle();

            currentScreen = getScreenBitmap();
        }

        return true;
    }

    @Override
    protected void onPostExecute( Boolean result ) {
        System.out.println( "SCREEN CAPTURE THREAD STOPPED" );
    }
    }
}

和这里的活动的xml文件...

and here's the activity's xml file...

<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/rootLayout"
        >

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_above="@android:id/tabs">

            <RelativeLayout android:id="@+id/emptylayout1" android:orientation="vertical"
                 android:layout_width="fill_parent" android:layout_height="fill_parent"/>
            <RelativeLayout android:id="@+id/emptylayout2" android:orientation="vertical"
                 android:layout_width="fill_parent" android:layout_height="fill_parent"/>
            <RelativeLayout android:id="@+id/emptylayout3" android:orientation="vertical"
                 android:layout_width="fill_parent" android:layout_height="fill_parent"/>
        </FrameLayout>

        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="-4dip" />
    </RelativeLayout>
</TabHost>

最后,这里所引发的异常...

Finally, here's the exception that is thrown...

01-21 18:11:40.070: E/AndroidRuntime(11279): FATAL EXCEPTION: AsyncTask #1
01-21 18:11:40.070: E/AndroidRuntime(11279): java.lang.RuntimeException: An error occured while executing doInBackground()
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.os.AsyncTask$3.done(AsyncTask.java:200)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at java.lang.Thread.run(Thread.java:1019)
01-21 18:11:40.070: E/AndroidRuntime(11279): Caused by: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewRoot.checkThread(ViewRoot.java:3055)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewRoot.invalidateChild(ViewRoot.java:657)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:683)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.invalidateChild(ViewGroup.java:2514)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.invalidate(View.java:5479)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.awakenScrollBars(View.java:5372)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.awakenScrollBars(View.java:5264)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.widget.ScrollView.onOverScrolled(ScrollView.java:820)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.overScrollBy(View.java:9100)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.widget.ScrollView.computeScrollBounce(ScrollView.java:1325)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.widget.ScrollView.computeScroll(ScrollView.java:1366)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.drawChild(ViewGroup.java:1562)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.draw(View.java:7083)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.draw(View.java:7083)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.widget.FrameLayout.draw(FrameLayout.java:357)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.drawChild(ViewGroup.java:1644)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.drawChild(ViewGroup.java:1644)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.drawChild(ViewGroup.java:1644)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.drawChild(ViewGroup.java:1644)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.draw(View.java:7083)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.widget.FrameLayout.draw(FrameLayout.java:357)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.draw(View.java:7083)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.widget.FrameLayout.draw(FrameLayout.java:357)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2119)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.buildDrawingCache(View.java:6842)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.getDrawingCache(View.java:6628)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.view.View.getDrawingCache(View.java:6593)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at com.ibm.trctarget.TRCTargetMainActivity.getScreenBitmap(TRCTargetMainActivity.java:158)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at com.ibm.trctarget.TRCTargetMainActivity.access$6(TRCTargetMainActivity.java:153)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at com.ibm.trctarget.TRCTargetMainActivity$ScreenCaptureThread.doInBackground(TRCTargetMainActivity.java:391)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at com.ibm.trctarget.TRCTargetMainActivity$ScreenCaptureThread.doInBackground(TRCTargetMainActivity.java:1)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at android.os.AsyncTask$2.call(AsyncTask.java:185)
01-21 18:11:40.070: E/AndroidRuntime(11279):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
01-21 18:11:40.070: E/AndroidRuntime(11279):    ... 4 more

任何帮助是非常AP preciated:)

any help is very appreciated :)

推荐答案

死蠢而简单的方法是将换东西需要在 runOnUiThread主线程上运行(......)

The dead stupid and simple way would be to wrap things that need to run on the main thread in runOnUiThread(...)