Today I faced with a strange issue. I installed my application on three devices
- Asus Transformer Pad Infinity TF700T
- Samsung I9082 Galaxy Grand Duos
- LG Optimus L7 II Dual p715
First, I ran my app from Eclipse in the Debug mode with all of these devices. And it's ok.
Then, I ran my app in usual way (right on devices). And in this way all my bad have been started. For 1 and 2 devices all ok. But 3 device didn't work as I expected. The LogCat showed me the following fatal error:
01-14 01:36:47.529: E/dalvikvm(11294): threadid=1: stuck on threadid=14, giving up 01-14 01:36:47.529: A/libc(11294): Fatal signal 16 (SIGSTKFLT) at 0x00002c1e (code=-6), thread 11315 (AsyncTask #4)
I really cannot understand, what's going on and why both 1 and 2 devices work properly, but third doesn't.
Could anyone explain me, what this error means and how this can be corrected?
UPD1 I do not use NDK calls and any third-party libraries.
UPD2 The big code snippet that caused an error is(I include here all events invoke the draw process and stop it):
//I included all imports for sure that I don't use nothing instead of standard things
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.WindowManager;
public class MyLiveWallpaper extends WallpaperService {
public static final String CONFIGS = "configs";
private WallpaperEngine we;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
if(we.myDrawTask!=null){
we.myDrawTask.cancel(false);
}
}
@Override
public Engine onCreateEngine() {
we = new WallpaperEngine();
return we;
}
//DeviceScreenSize is some class where I just incapsulate screen width, height and depth obtained in getScreenSize() method
DeviceScreenSize dss = new DeviceScreenSize(0, 0, 0);
class WallpaperEngine extends Engine implements
OnSharedPreferenceChangeListener {
//Some definitions below
............................
public DrawTask myDrawTask = null;
............................
//Some definitions above
WallpaperEngine() {
..............
doubleTapDetector = new GestureDetector(HDLiveWallpaper.this,
new SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
if (mTouchEvents) {
mLastDrawTime = 0;
if(myDrawTask!=null){
myDrawTask.stopAnimationFlag = true;
}
else{
myDrawTask = new DrawTask(getSurfaceHolder(), mPaint);
myDrawTask.execute();
}
return true;
}
return false;
}
});
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
// Register receiver for media events
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
filter.addAction(Intent.ACTION_MEDIA_CHECKING);
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addAction(Intent.ACTION_MEDIA_EJECT);
filter.addAction(Intent.ACTION_MEDIA_NOFS);
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
filter.addDataScheme("file");
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_MEDIA_MOUNTED) || action.equals(Intent.ACTION_MEDIA_CHECKING)) {
mStorageReady = true;
setTouchEventsEnabled(true);
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
myDrawTask = new DrawTask(getSurfaceHolder(), mPaint);
myDrawTask.execute();
} else {
mStorageReady = false;
setTouchEventsEnabled(false);
if(myDrawTask!=null) myDrawTask.cancel(false);
}
}
};
registerReceiver(mReceiver, filter);
// Register receiver for screen on events
mScreenOnReciever = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
System.out.println(Intent.ACTION_SCREEN_ON);
if (mScreenWake) {
mLastDrawTime = 0;
}
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
myDrawTask = new DrawTask(getSurfaceHolder(), mPaint);
myDrawTask.execute();
}
};
registerReceiver(mScreenOnReciever, new IntentFilter(Intent.ACTION_SCREEN_ON));
mScreenOffReciever = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
System.out.println(Intent.ACTION_SCREEN_OFF);
if (mScreenWake) {
mLastDrawTime = 0;
}
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
}
};
registerReceiver(mScreenOffReciever, new IntentFilter(Intent.ACTION_SCREEN_OFF));
setTouchEventsEnabled(mStorageReady);
}
@Override
public void onDestroy() {
super.onDestroy();
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
unregisterReceiver(mReceiver);
unregisterReceiver(mScreenOnReciever);
unregisterReceiver(mScreenOffReciever);
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
}
@Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
myDrawTask = new DrawTask(getSurfaceHolder(), mPaint);
myDrawTask.execute();
mLastDrawTime = 0;
} else {
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
.....................
if (mBitmap != null) {
mBitmap.recycle();
}
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
myDrawTask = new DrawTask(getSurfaceHolder(), mPaint);
myDrawTask.execute();
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
myDrawTask = new DrawTask(getSurfaceHolder(), mPaint);
myDrawTask.execute();
mLastDrawTime = 0;
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xStep,
float yStep, int xPixels, int yPixels) {
mXOffset = xOffset;
mYOffset = yOffset;
if(myDrawTask!=null){
myDrawTask.cancel(false);
}
myDrawTask = new DrawTask(getSurfaceHolder(), mPaint);
myDrawTask.execute();
}
@Override
public void onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
this.doubleTapDetector.onTouchEvent(event);
}
public final Bitmap drawableToBitmap(Drawable drawable) {
int targetWidth = (mScroll) ? mMinWidth : dss.getWidth();
int targetHeight = (mScroll) ? mMinHeight : dss.getHeight();
Bitmap bitmap = Bitmap.createBitmap(targetWidth, targetHeight, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
// Rotate
.....................
// Scale bitmap
.....................
return bitmap;
}
void getScreenSize() {
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display d = wm.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
d.getMetrics(metrics);
// since SDK_INT = 1;
dss.setDeviceScreenParams(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
// includes window decorations (statusbar bar/menu bar)
if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17)
try {
dss.setDeviceScreenParams((Integer) Display.class.getMethod("getRawWidth").invoke(d), (Integer) Display.class.getMethod("getRawHeight").invoke(d), metrics.densityDpi);
} catch (Exception ignored) {}
// includes window decorations (statusbar bar/menu bar)
if (Build.VERSION.SDK_INT >= 17)
try {
DisplayMetrics realSize = new DisplayMetrics();
Display.class.getMethod("getRealMetrics", DisplayMetrics.class).invoke(d, realSize);
dss.setDeviceScreenParams(realSize.widthPixels, realSize.heightPixels, realSize.densityDpi);
} catch (Exception ignored) {}
}
//
// ************************
// DRAW AND ANIMATION TASK
// ************************
//
public class DrawTask extends AsyncTask<Void, Void, Void> {
private final SurfaceHolder _surfaceHolder;
private Paint myPaint;
boolean CancelFlag = false, stopAnimationFlag = false;
//HANDLERS
private final Handler mDrawHandler = new Handler(){
public void handleMessage(android.os.Message msg) { };
};
//WORKERS
private final Runnable mDrawWorker = new Runnable() {
public void run() {
if (mDuration > 0)
{
drawFrame();
}
}
};
DrawTask(SurfaceHolder holder, Paint paint){
_surfaceHolder = holder;
myPaint = paint;
CancelFlag = false;
}
SurfaceHolder getSurfHolder(){
return _surfaceHolder;
}
Paint getPaint(){
return myPaint;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Void doInBackground(Void... params) {
drawFrame();
while(!CancelFlag){}
return null;
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
CancelFlag = true;
}
void drawFrame() {
final SurfaceHolder holder = _surfaceHolder;
Canvas c = null;
boolean getImage = false;
try {
// Lock the canvas for writing
c = holder.lockCanvas();
// Do we need to get a new image?
if (mBitmap == null) {
getImage = true;
} else if (mDuration > 0 && mLastDrawTime < System.currentTimeMillis() - mDuration) {
getImage = true;
} else if (mLastDrawTime == 0) {
getImage = true;
}
// Get image to draw
if (getImage) {
// Get a list of files
String[] assets_files = null;
try {
assets_files = getApplicationContext().getAssets().list("");
} catch (IOException e) {
e.printStackTrace();
}
List<String> str_list = new ArrayList<String>();
for (int fi = 0; fi < assets_files.length; fi++) {
String ext = BitmapUtil.getExtension(assets_files[fi]);
if (ext != null) {
if (ext.equals("jpg") || ext.equals("jpeg") || ext.equals("png") || ext.equals("gif")) {
str_list.add(assets_files[fi]);
}
}
}
assets_files = str_list.toArray(new String[str_list.size()]);
// Increment counter
int nFiles = assets_files.length;
if (mRandom) {
int i = mIndex;
do {
mIndex = (int) (Math.random() * nFiles);
} while (nFiles > 1 && mIndex == i);
} else {
if (++mIndex >= nFiles) {
mIndex = 0;
}
}
InputStream ims = null;
try {
ims = getAssets().open(assets_files[mIndex]);
} catch (IOException e) {
e.printStackTrace();
}
d = Drawable.createFromStream(ims, null);
// Read file to bitmap
mBitmap=null;
mBitmap = drawableToBitmap(d);
// Save the current time
mLastDrawTime = System.currentTimeMillis();
} else if (mBitmap != null && mBitmap.isRecycled()){
mBitmap=null;
mBitmap = drawableToBitmap(d);
}
} catch (NullPointerException npe) {
holder.unlockCanvasAndPost(c);
return;
} catch (RuntimeException re) {
holder.unlockCanvasAndPost(c);
return;
}
try {
if (c != null) {
int xPos;
int yPos;
if (mScroll) {
xPos = 0 - (int) (mWidth * mXOffset);
yPos = 0 - (int) (mHeight * mYOffset);
} else {
xPos = 0;
yPos = 0;
}
try {
c.drawColor(Color.BLACK);
c.drawBitmap(mBitmap, xPos, yPos, myPaint);
} catch (Throwable t) {}
}
} finally {
if (c != null){
if((mPreviousBitmap==null) || (mPreviousBitmap==mBitmap))
holder.unlockCanvasAndPost(c);
else{
if(mTransition!=0)
startAnimation(mTransition, holder, c);
else
holder.unlockCanvasAndPost(c);
}
mPreviousBitmap=null;
mPreviousBitmap = mBitmap;
}
}
// Reschedule the next redraw
mDrawHandler.removeCallbacks(mDrawWorker);
if (mVisible) {
mDrawHandler.postDelayed(mDrawWorker, 1000 / 2);
}
else{CancelFlag=true;}
}
void startAnimation(int animNumber, SurfaceHolder holder, Canvas canvas)
{
switch(animNumber){
case 1:{
canvas.drawBitmap(mBitmap, 0, 0, myPaint);
int tmpPaintAlpha = myPaint.getAlpha();
myPaint.setAlpha(255);
canvas.drawBitmap(mPreviousBitmap, 0, 0, myPaint);
holder.unlockCanvasAndPost(canvas);
myPaint.setAlpha(tmpPaintAlpha);
int i=224;
while(i>=0){
canvas = holder.lockCanvas();
canvas.drawBitmap(mBitmap, 0, 0, myPaint);
myPaint.setAlpha(i);
canvas.drawBitmap(mPreviousBitmap, 0, 0, myPaint);
holder.unlockCanvasAndPost(canvas);
myPaint.setAlpha(255);
i-=18;
}
mLastDrawTime=System.currentTimeMillis();
break;
}
case 2:{
....................
break;
}
}
}
}
...................
}
...................
}
Here the error occures every time when we fall in the DrawTask.drawFrame() method even without any anymations (mTransition==0). And as the result I see:
(1) First step with initial image and next image is done properly;
(2) When it's time to change second to third etc. images, I just see the black screen during not constant period, and then I see again my initial image and repeat all in (2) infinitely. Any changes of settings don't change this behaviour.
UPD3 Hope my detailed descriptions(UPD1, UPD2) of this issue can help to find a reason of this strange behaviour. But, unfortunately, not for me now.
Big thanks in advance!