所以我在尝试在 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
非常感谢任何帮助:)