所以我在尝试在Android中捕获应用程序的屏幕时遇到问题。情况如下:
我想使用View.getDrawingCache()方法连续捕获应用程序屏幕,并检测屏幕的哪个部分已更改。我的应用程序基于一个包含5个活动的TabHost,我想捕获tabhost中发生的所有事情。我还希望屏幕捕获代码在后台运行,为此我创建了一个AsyncTask实例来捕获图像并检查是否有更改。问题是getDrawingCache()方法有时会导致应用程序崩溃,从而引发CalledFromWrongThreadException。
我做了一些测试,这就是我发现的东西。首先,我甚至没有在tabhost中的活动之间进行更改,因此只有第一个可见。第一个活动有一个ScrollView,一旦我禁用带有“android:scrollbars ='none'”的滚动条,捕获就会起作用,但是如果我重新启用它们就会崩溃并出现相同的异常。我认为问题在于我在doInBackground方法中进行屏幕捕获而且Android不喜欢它由于某种原因(好吧,我知道原因 - 它试图在除主要线程之外的线程中绘制缓存。 )。
所以我的问题是,是否有人知道如何在后台录制?
以下是使事情变得清晰的代码的主要部分......
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文件......
<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>
最后,这是抛出的异常......
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
非常感谢任何帮助:)
答案 0 :(得分:0)
死愚蠢而简单的方法是在runOnUiThread(...)