Using WallpaperService to set wallpaper based on selected frequency by user - android

I am using WallpaperService to set the live wallpaper on homescreen. I am able to set 10 pics from resources and rotate them,
based on static frequency using thread. I want user to give the option of selecting multiple frequency. I tried to pass the thread sleep duration with user selected frequency. But the thread which is running is neither stopping or updating the values.
Its updating wallpaper with the previous selected frequency. Im stuck with this app from 3 weeks at various levels. I have tried wallpaper manager and setting bitmap with services, alarm manager but all are stopping to set the wallpaper after certain period of time. So finally using Wallpaper service. Help to overcome from this issue.
class WallpaperEngine extends Engine {
//frequency selected by user to update the wallpaper
private final int Wallpaper_DURATION = utils.getFrequency;
private int[] mImagesArray;
private int mImagesArrayIndex = 0;
private Thread mDrawWallpaper;
public WallpaperEngine() {
customWallpaperHelper = new CustomWallpaperHelper(getApplicationContext(), getResources());
mImagesArray = new int[] {R.drawable.one,R.drawable.two,R.drawable.three,R.drawable.four,R.drawable.five};
mDrawWallpaper = new Thread(new Runnable() {
#Override
public void run() {
try {
while (true) {
drawFrame();
incrementCounter();
Thread.sleep(Wallpaper_DURATION);
}
} catch (Exception e) {
//
}
}
});
mDrawWallpaper.start();
}
private void incrementCounter() {
mImagesArrayIndex++;
if (mImagesArrayIndex >= mImagesArray.length) {
mImagesArrayIndex = 0;
}
}
private void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas != null) {
drawImage(canvas);
}
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
private void drawImage(Canvas canvas)
{
Bitmap image = BitmapFactory.decodeResource(getResources(),
mImagesArray[mImagesArrayIndex]);
Bitmap b=Bitmap.createScaledBitmap(image, canvas.getWidth(), canvas.getHeight(), true);
canvas.drawBitmap(b, 0,0, null);
}

Related

OpenGL ES: Rotate the complete scene to match portrait mode

Note Im new to Android and OpenGL
Im building an Augmented Reality App based on ARToolKitX (Github: https://github.com/artoolkitx/artoolkitx/tree/8c6bd4e7be5e80c8439066b23473506aebbb496c/Source/ARXJ/ARXJProj/arxj/src/main/java/org/artoolkitx/arx/arxj).
The application shows the camera frame and displays objects with opengl on top.
My Problem:
ARToolKitX forces the app to be in landscape mode:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
but when I change the screen orientation to SCREEN_ORIENTATION_PORTRAIT, the camera image and the opengl objects dont rotate to the correct orientation and stay in landscape mode.
Inside the ARRenderer I can use the drawVideoSettings method to rotate the camera image by itself, but that doesnt apply to the opengl objects.
ARToolKitX also provides a SurfaceChanged method inside the CameraSurface class, with the comment: "This is where [...] to create transformation matrix to scale and then rotate surface view, if the app is going to handle orientation changes."
But I dont have any idea, how the transformation matrix has too look like and how to apply it.
Any help is appreciated.
ARRenderer:
public abstract class ARRenderer implements GLSurfaceView.Renderer {
private MyShaderProgram shaderProgram;
private int width, height, cameraIndex;
private int[] viewport = new int[4];
private boolean firstRun = true;
private final static String TAG = ARRenderer.class.getName();
/**
* Allows subclasses to load markers and prepare the scene. This is called after
* initialisation is complete.
*/
public boolean configureARScene() {
return true;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Transparent background
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.f);
this.shaderProgram = new MyShaderProgram(new MyVertexShader(), new MyFragmentShader());
GLES20.glUseProgram(shaderProgram.getShaderProgramHandle());
}
public void onSurfaceChanged(GL10 unused, int w, int h) {
this.width = w;
this.height = h;
if(ARController.getInstance().isRunning()) {
//Update the frame settings for native rendering
ARController.getInstance().drawVideoSettings(cameraIndex, w, h, false, false, false, ARX_jni.ARW_H_ALIGN_CENTRE, ARX_jni.ARW_V_ALIGN_CENTRE, ARX_jni.ARW_SCALE_MODE_FILL, viewport);
}
}
public void onDrawFrame(GL10 unused) {
if (ARController.getInstance().isRunning()) {
// Initialize artoolkitX video background rendering.
if (firstRun) {
boolean isDisplayFrameInited = ARController.getInstance().drawVideoInit(cameraIndex);
if (!isDisplayFrameInited) {
Log.e(TAG, "Display Frame not inited");
}
if (!ARController.getInstance().drawVideoSettings(cameraIndex, this.width, this.height, false, false,
false, ARX_jni.ARW_H_ALIGN_CENTRE, ARX_jni.ARW_V_ALIGN_CENTRE,
ARX_jni.ARW_SCALE_MODE_FILL, viewport)) {
Log.e(TAG, "Error during call of displayFrameSettings.");
} else {
Log.i(TAG, "Viewport {" + viewport[0] + ", " + viewport[1] + ", " + viewport[2] + ", " + viewport[3] + "}.");
}
firstRun = false;
}
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
if (!ARController.getInstance().drawVideoSettings(cameraIndex)) {
Log.e(TAG, "Error during call of displayFrame.");
}
draw();
}
}
/**
* Should be overridden in subclasses and used to perform rendering.
*/
public void draw() {
GLES20.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
//TODO: Check how to refactor near and far plane
shaderProgram.setProjectionMatrix(ARController.getInstance().getProjectionMatrix(10.0f, 10000.0f));
float[] camPosition = {1f, 1f, 1f};
shaderProgram.render(camPosition);
}
#SuppressWarnings("unused")
public ShaderProgram getShaderProgram() {
return shaderProgram;
}
public void setCameraIndex(int cameraIndex) {
this.cameraIndex = cameraIndex;
}
}
CameraSurface
class CameraSurfaceImpl implements CameraSurface {
/**
* Android logging tag for this class.
*/
private static final String TAG = CameraSurfaceImpl.class.getSimpleName();
private CameraDevice mCameraDevice;
private ImageReader mImageReader;
private Size mImageReaderVideoSize;
private final Context mAppContext;
private final CameraDevice.StateCallback mCamera2DeviceStateCallback = new CameraDevice.StateCallback() {
#Override
public void onOpened(#NonNull CameraDevice camera2DeviceInstance) {
mCameraDevice = camera2DeviceInstance;
startCaptureAndForwardFramesSession();
}
#Override
public void onDisconnected(#NonNull CameraDevice camera2DeviceInstance) {
camera2DeviceInstance.close();
mCameraDevice = null;
}
#Override
public void onError(#NonNull CameraDevice camera2DeviceInstance, int error) {
camera2DeviceInstance.close();
mCameraDevice = null;
}
};
/**
* Listener to inform of camera related events: start, frame, and stop.
*/
private final CameraEventListener mCameraEventListener;
/**
* Tracks if SurfaceView instance was created.
*/
private boolean mImageReaderCreated;
public CameraSurfaceImpl(CameraEventListener cameraEventListener, Context appContext){
this.mCameraEventListener = cameraEventListener;
this.mAppContext = appContext;
}
private final ImageReader.OnImageAvailableListener mImageAvailableAndProcessHandler = new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader)
{
Image imageInstance = reader.acquireLatestImage();
if (imageInstance == null) {
//Note: This seems to happen quite often.
Log.v(TAG, "onImageAvailable(): unable to acquire new image");
return;
}
// Get a ByteBuffer for each plane.
final Image.Plane[] imagePlanes = imageInstance.getPlanes();
final int imagePlaneCount = Math.min(4, imagePlanes.length); // We can handle up to 4 planes max.
final ByteBuffer[] imageBuffers = new ByteBuffer[imagePlaneCount];
final int[] imageBufferPixelStrides = new int[imagePlaneCount];
final int[] imageBufferRowStrides = new int[imagePlaneCount];
for (int i = 0; i < imagePlaneCount; i++) {
imageBuffers[i] = imagePlanes[i].getBuffer();
// For ImageFormat.YUV_420_888 the order of planes in the array returned by Image.getPlanes()
// is guaranteed such that plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V (Cr).
// The Y-plane is guaranteed not to be interleaved with the U/V planes (in particular, pixel stride is
// always 1 in yPlane.getPixelStride()). The U/V planes are guaranteed to have the same row stride and
// pixel stride (in particular, uPlane.getRowStride() == vPlane.getRowStride() and uPlane.getPixelStride() == vPlane.getPixelStride(); ).
imageBufferPixelStrides[i] = imagePlanes[i].getPixelStride();
imageBufferRowStrides[i] = imagePlanes[i].getRowStride();
}
if (mCameraEventListener != null) {
mCameraEventListener.cameraStreamFrame(imageBuffers, imageBufferPixelStrides, imageBufferRowStrides);
}
imageInstance.close();
}
};
#Override
public void surfaceCreated() {
Log.i(TAG, "surfaceCreated(): called");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mAppContext);
int defaultCameraIndexId = mAppContext.getResources().getIdentifier("pref_defaultValue_cameraIndex","string", mAppContext.getPackageName());
mCamera2DeviceID = Integer.parseInt(prefs.getString("pref_cameraIndex", mAppContext.getResources().getString(defaultCameraIndexId)));
Log.i(TAG, "surfaceCreated(): will attempt to open camera \"" + mCamera2DeviceID +
"\", set orientation, set preview surface");
/*
Set the resolution from the settings as size for the glView. Because the video stream capture
is requested based on this size.
WARNING: While coding the preferences are taken from the res/xml/preferences.xml!!!
When building for Unity the actual used preferences are taken from the UnityARPlayer project!!!
*/
int defaultCameraValueId = mAppContext.getResources().getIdentifier("pref_defaultValue_cameraResolution","string",mAppContext.getPackageName());
String camResolution = prefs.getString("pref_cameraResolution", mAppContext.getResources().getString(defaultCameraValueId));
String[] dims = camResolution.split("x", 2);
mImageReaderVideoSize = new Size(Integer.parseInt(dims[0]),Integer.parseInt(dims[1]));
// Note that maxImages should be at least 2 for acquireLatestImage() to be any different than acquireNextImage() -
// discarding all-but-the-newest Image requires temporarily acquiring two Images at once. Or more generally,
// calling acquireLatestImage() with less than two images of margin, that is (maxImages - currentAcquiredImages < 2)
// will not discard as expected.
mImageReader = ImageReader.newInstance(mImageReaderVideoSize.getWidth(),mImageReaderVideoSize.getHeight(), ImageFormat.YUV_420_888, /* The maximum number of images the user will want to access simultaneously:*/ 2 );
mImageReader.setOnImageAvailableListener(mImageAvailableAndProcessHandler, null);
mImageReaderCreated = true;
} // end: public void surfaceCreated(SurfaceHolder holder)
/* Interface implemented by this SurfaceView subclass
holder: SurfaceHolder instance associated with SurfaceView instance that changed
format: pixel format of the surface
width: of the SurfaceView instance
height: of the SurfaceView instance
*/
#Override
public void surfaceChanged() {
Log.i(TAG, "surfaceChanged(): called");
// This is where to calculate the optimal size of the display and set the aspect ratio
// of the surface view (probably the service holder). Also where to Create transformation
// matrix to scale and then rotate surface view, if the app is going to handle orientation
// changes.
if (!mImageReaderCreated) {
surfaceCreated();
}
if (!isCamera2DeviceOpen()) {
openCamera2(mCamera2DeviceID);
}
if (isCamera2DeviceOpen() && (null == mYUV_CaptureAndSendSession)) {
startCaptureAndForwardFramesSession();
}
}
private void openCamera2(int camera2DeviceID) {
Log.i(TAG, "openCamera2(): called");
CameraManager camera2DeviceMgr = (CameraManager)mAppContext.getSystemService(Context.CAMERA_SERVICE);
try {
if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(mAppContext, Manifest.permission.CAMERA)) {
camera2DeviceMgr.openCamera(Integer.toString(camera2DeviceID), mCamera2DeviceStateCallback, null);
return;
}
} catch (CameraAccessException ex) {
Log.e(TAG, "openCamera2(): CameraAccessException caught, " + ex.getMessage());
} catch (Exception ex) {
Log.e(TAG, "openCamera2(): exception caught, " + ex.getMessage());
}
if (null == camera2DeviceMgr) {
Log.e(TAG, "openCamera2(): Camera2 DeviceMgr not set");
}
Log.e(TAG, "openCamera2(): abnormal exit");
}
private int mCamera2DeviceID = -1;
private CaptureRequest.Builder mCaptureRequestBuilder;
private CameraCaptureSession mYUV_CaptureAndSendSession;
private void startCaptureAndForwardFramesSession() {
if ((null == mCameraDevice) || (!mImageReaderCreated) /*|| (null == mPreviewSize)*/) {
return;
}
closeYUV_CaptureAndForwardSession();
try {
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
List<Surface> surfaces = new ArrayList<>();
Surface surfaceInstance;
surfaceInstance = mImageReader.getSurface();
surfaces.add(surfaceInstance);
mCaptureRequestBuilder.addTarget(surfaceInstance);
mCameraDevice.createCaptureSession(
surfaces, // Output surfaces
new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession session) {
try {
if (mCameraEventListener != null) {
mCameraEventListener.cameraStreamStarted(mImageReaderVideoSize.getWidth(), mImageReaderVideoSize.getHeight(), "YUV_420_888", mCamera2DeviceID, false);
}
mYUV_CaptureAndSendSession = session;
// Session to repeat request to update passed in camSensorSurface
mYUV_CaptureAndSendSession.setRepeatingRequest(mCaptureRequestBuilder.build(), /* CameraCaptureSession.CaptureCallback cameraEventListener: */null, /* Background thread: */ null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession session) {
Toast.makeText(mAppContext, "Unable to setup camera sensor capture session", Toast.LENGTH_SHORT).show();
}
}, // Callback for capture session state updates
null); // Secondary thread message queue
} catch (CameraAccessException ex) {
ex.printStackTrace();
}
}
#Override
public void closeCameraDevice() {
closeYUV_CaptureAndForwardSession();
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mImageReader) {
mImageReader.close();
mImageReader = null;
}
if (mCameraEventListener != null) {
mCameraEventListener.cameraStreamStopped();
}
mImageReaderCreated = false;
}
private void closeYUV_CaptureAndForwardSession() {
if (mYUV_CaptureAndSendSession != null) {
mYUV_CaptureAndSendSession.close();
mYUV_CaptureAndSendSession = null;
}
}
/**
* Indicates whether or not camera2 device instance is available, opened, enabled.
*/
#Override
public boolean isCamera2DeviceOpen() {
return (null != mCameraDevice);
}
#Override
public boolean isImageReaderCreated() {
return mImageReaderCreated;
}
}
Edit:
/**
* Override the draw function from ARRenderer.
*/
#Override
public void draw() {
super.draw();
fpsCounter.frame();
if(maxfps<fpsCounter.getFPS()){
maxfps= fpsCounter.getFPS();
}
logger.log(Level.INFO, "FPS: " + maxfps);
// Initialize GL
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glFrontFace(GLES20.GL_CCW);
// Look for trackables, and draw on each found one.
for (int trackableUID : trackables.keySet()) {
// If the trackable is visible, apply its transformation, and render the object
float[] modelViewMatrix = new float[16];
if (ARController.getInstance().queryTrackableVisibilityAndTransformation(trackableUID, modelViewMatrix)) {
float[] projectionMatrix = ARController.getInstance().getProjectionMatrix(10.0f, 10000.0f);
trackables.get(trackableUID).draw(projectionMatrix, modelViewMatrix);
}
}
}

How do i reduce or fix the amount of graphics memory used by GlSurfaceView when applying android media effects to images in android?

I'm making an android application that uses GLSurfaceView and android media effects to apply filters to images.The implementation is working fine except it appears as if there's a memory leak in the app.With each addition of an effect the graphics memory used by the app increases significantly and this memory is never released.
Does any one know how to reduce the memory footprint from this class or at least knows what is causing the high usage of memory and point me in the right direction to solving this?
I've tried to call GLES20.glDeleteTextures(2, mTextures, 0) just before GLES20.glGenTextures(2, mTextures, 0) but it didn't help.I've also experiment with GLES20.glFinish() and GLES20.glFlush() and mEffectContext.release() with no success or any observable change.
I've attached a screenshot from the profiling I've performed and also relevant pieces of the class itself below.
This is my first stackoverflow question so please feel free to correct me incase i've broken any community guidelines.
class ImageFilterView extends GLSurfaceView implements GLSurfaceView.Renderer {
private int[] mTextures = new int[2];
private void init() {
setEGLContextClientVersion(2);
setRenderer(this);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
setFilterEffect(NONE);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if (mTexRenderer != null) {
mTexRenderer.updateViewSize(width, height);
}
}
#Override
public void onDrawFrame(GL10 gl) {
if (!mInitialized) {
//Only need to do this once
mEffectContext = EffectContext.createWithCurrentGlContext();
mTexRenderer.init();
loadTextures();
mInitialized = true;
}
if (mCurrentEffect != NONE || mCustomEffect != null) {
//if an effect is chosen initialize it and apply it to the texture
initEffect();
applyEffect();
}
renderResult();
if (isSaveImage) {
final Bitmap mFilterBitmap = BitmapUtil.createBitmapFromGLSurface(this, gl);
Log.e(TAG, "onDrawFrame: " + mFilterBitmap);
isSaveImage = false;
if (mOnSaveBitmap != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
mOnSaveBitmap.onBitmapReady(mFilterBitmap);
}
});
}
}
}
void setFilterEffect(PhotoFilter effect) {
mCurrentEffect = effect;
mCustomEffect = null;
requestRender();
}
void saveBitmap(OnSaveBitmap onSaveBitmap) {
mOnSaveBitmap = onSaveBitmap;
isSaveImage = true;
requestRender();
}
private void loadTextures() {
GLES20.glGenTextures(2, mTextures, 0);
// Load input bitmap
if (mSourceBitmap != null) {
mImageWidth = mSourceBitmap.getWidth();
mImageHeight = mSourceBitmap.getHeight();
mTexRenderer.updateTextureSize(mImageWidth, mImageHeight);
// Upload to texture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mSourceBitmap, 0);
// Set texture parameters
GLToolbox.initTexParams();
}
}
private void initEffect() {
EffectFactory effectFactory = mEffectContext.getFactory();
if (mEffect != null) {
mEffect.release();
}
if (mCustomEffect != null) {
mEffect = effectFactory.createEffect(mCustomEffect.getEffectName());
Map<String, Object> parameters = mCustomEffect.getParameters();
for (Map.Entry<String, Object> param : parameters.entrySet()) {
mEffect.setParameter(param.getKey(), param.getValue());
}
} else {
// Initialize the correct effect based on the selected menu/action item
switch (mCurrentEffect) {
case VIGNETTE:
mEffect = effectFactory.createEffect(EFFECT_VIGNETTE);
mEffect.setParameter("scale", .5f);
break;
}
}
}
private void applyEffect() {
mEffect.apply(mTextures[0], mImageWidth, mImageHeight, mTextures[1]);
}
private void renderResult() {
if (mCurrentEffect != NONE || mCustomEffect != null) {
// if no effect is chosen, just render the original bitmap
mTexRenderer.renderTexture(mTextures[1]);
} else {
// render the result of applyEffect()
mTexRenderer.renderTexture(mTextures[0]);
}
}
}
Screenshot of profiling:Memory increases every time a filter is added

EmbeddedPicture & java.lang.RuntimeException: Canvas: trying to use a recycled bitmap

I'm developing music player and I use picture from audio files to display it in UI of my app, if I change music very fast (previous, next button) then I can get java.lang.RuntimeException: Canvas: trying to use a recycled bitmap, but popular players from Play market doesn't have this problem if I change music very fast. How can I avoid this error as well as in other music apps?
Similar question with the same error didn't help me
MediaService class
MediaMetadataRetriever mMetaRetriever = new MediaMetadataRetriever();
mMetaRetriever.setDataSource(songPath);
byte[] art = mMetaRetriever.getEmbeddedPicture();
Bitmap iconUrl = null;
try {
if (art != null) {
iconUrl = BitmapFactory.decodeByteArray(art, 0, art.length);
}
} catch (Exception e) {
}
UI class
try {
// with or without it error happens anyway
/*if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
mBitmap = null;
mLogo.setImageBitmap(null);
}*/
mBitmap = metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
mLogo.setImageBitmap(mBitmap);
} catch (Exception e) {
}
I just decided to use AsyncTask and cancel execution (mImageViewAsync.cancel(true);) for current instance when a new instance is created and executed, so in this case onPostExecute is triggered less then doInBackground, so now it be difficult for app to crash (but it still can in some cases, but it's not important so)
private ImageViewAsync mImageViewAsync;
private void updateMetaData(MediaMetadataCompat metadata) {
//...
mLogo.setImageBitmap(null);
if (mImageViewAsync != null) {
mImageViewAsync.cancel(true);
}
mImageViewAsync = new ImageViewAsync();
mImageViewAsync.execute(metadata);
}
private class ImageViewAsync extends AsyncTask<MediaMetadataCompat,Void,Bitmap> {
#Override
protected Bitmap doInBackground(MediaMetadataCompat... meta) {
return meta[0].getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
}
#Override
protected void onPostExecute(Bitmap bitmap) {
mLogo.setImageBitmap(bitmap);
}
}

android Position an image inside an ImageView animation

i have imageView
<ImageView
android:id="#+id/loading"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerCrop"
android:layout_gravity="center_horizontal"
android:src="#drawable/spinner"/>
and image is
i want create an animation which change position of image inside imageView
i tried this
android:scrollX="-80dp"
this in xml and static i want animation
You can do it by creating a thread and a handler where the thread sleeps for a certain amount of time and informs handler by sending message when it wakes up and handler will update the image in image view.
Create 4 Fields like below :
private ImageView mImageView;
private Handler mImageChangingHandler;
private int mCurrentImageIndex = 0;
private BackgroundThread mImageUpdateThread;
In onCreate() get the ImageView, spilt your bitmaps and add it to a list and create a handler which updates imageview. Here you should know the number of individual bitmaps which is in main Bitmap. Like :
mImageView = (ImageView) findViewById(R.id.img);
Bitmap mainBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.anim);
//number of individual bitmaps, which you should know.
final int numberOfImages = 21;
//individual bitmap width
int bitmapWidth = mainBitmap.getWidth() / numberOfImages;
//individual bitmap height
int bitmapHeight = mainBitmap.getHeight();
//list which holds individual bitmaps.
final List<Bitmap> animBitmaps = new ArrayList<>(numberOfImages);
//split your bitmap in to individual bitmaps
for (int index = 0; index < numberOfImages; index++) {
animBitmaps.add(Bitmap.createBitmap(mainBitmap, index * bitmapWidth, 0, bitmapWidth, bitmapHeight));
}
//set 1st bitmap to imageView.
mImageView.setImageBitmap(animBitmaps.get(mCurrentImageIndex));
mImageChangingHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
//increament current bitmap index
mCurrentImageIndex++;
//if current index is greater than the number of images, reset it to 0
if (mCurrentImageIndex > numberOfImages - 1) {
mCurrentImageIndex = 0;
}
mImageView.setImageBitmap(animBitmaps.get(mCurrentImageIndex));
return true;
}
});
//Create the background thread by passing the handler and start.
mImageUpdateThread = new BackgroundThread(mImageChangingHandler);
mImageUpdateThread.setRunning(true);
mImageUpdateThread.start();
And This will be your BackgroundThread.class :
public static class BackgroundThread extends Thread {
private Handler mHandler;
private boolean mRunning;
public BackgroundThread(Handler handler) {
mHandler = handler;
}
public void setRunning(boolean running) {
mRunning = running;
}
#Override
public void run() {
super.run();
while (mRunning) {
try {
//this sleeps for 100 milliseconds
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//send message to handler after sleep
mHandler.sendEmptyMessage(0);
}
}
}
}
Finally in onDestroy() stop the thread. Like :
mImageUpdateThread.setRunning(false);
boolean stopThread = true;
while (stopThread) {
try {
mImageUpdateThread.join();
//thread already stopped
stopThread = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mImageUpdateThread = null;
Note : If you want to increase the update speed, reduce the sleep time in thread.

Android loading the same bitmap images crash

I am a beginner in Android app dev. Recently I am trying to make a face detection app using FaceDetector.
In the code below is my main activity class. I have a Runnable to loop through a set of images, convert to bitmaps and send them to FaceOverlayView to find and draw faces. The loop can go through all 4 items.
However, as I tried to make it loop all over again by setting index to 0 after the last item, the app crashes. I observed the behavior that the app will crash trying to reload an image loaded before. Would you please give me any insight on this? Many thanks in advance.
public class MainActivity extends AppCompatActivity {
private FaceOverlayView mFaceOverlayView;
Handler handler;
Thread thread;
ArrayList<Integer> imgID = new ArrayList<Integer>(Arrays.asList(R.raw.face1, R.raw.face2, R.raw.face3, R.raw.face4));
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFaceOverlayView = (FaceOverlayView) findViewById(R.id.face_overlay);
thread = new Thread(new myThread());
thread.start();
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.arg1 == -1) {
//thread.interrupt();
}
Bitmap bitmap = (Bitmap) msg.obj;
mFaceOverlayView.setBitmap(bitmap);
}
};
}
class myThread implements Runnable {
#Override
public void run() {
int index = 0;
while (index < imgID.size()) {
InputStream stream = getResources().openRawResource(imgID.get(index));
Bitmap bitmap = BitmapFactory.decodeStream(stream);
Message msg = Message.obtain();
msg.obj = bitmap;
handler.sendMessage(msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
index++;
if (index >= imgID.size()) {
index = 0;
}
bitmap.recycle();
bitmap = null;
}
}
}
}

Categories

Resources