In my app, I have an Activity that acts as a camera preview. I use a SurfaceView stretched across the entire screen, along with a bunch of items overlaid on top of it but nothing too complicated. This Activity can launch other Activities which will then return back to the camera preview.
I've been very careful to clean up resources, recycle bitmaps, avoid memory leaks, etc. I can run this app and test it like crazy, but when my phone has been on for a while and other apps are in memory, I get silent shut downs when resuming or creating the Activity that holds the camera preview. A common test case to reproduce the crash is use the app, snap photos (which triggers processing), launch sub activities, etc over and over again. Back out of the app, fire up something resource/graphics heavy, and then resume my app.
Here is some logcat output at the time of the crash:
03-29 14:20:02.109: ERROR/dalvikvm(6368): externalAllocPossible(): footprint 2756592 + extAlloc 15831356 + n 8640000 >= max 22409232 (space for 3821284)
03-29 14:20:02.109: ERROR/dalvikvm-heap(6368): 8640000-byte external allocation too large for this process.
03-29 14:20:02.109: ERROR/dalvikvm(6368): Out of memory: Heap Size=3835KB, Allocated=2835KB, Bitmap Size=15460KB, Limit=21884KB
03-29 14:20:02.109: ERROR/dalvikvm(6368): Trim info: Footprint=5383KB, Allowed Footprint=5383KB, Trimmed=1548KB
03-29 14:20:02.109: ERROR/GraphicsJNI(6368): VM won't let us allocate 8640000 bytes
My activity is logging at every step, so this happens in Activity.onCreate in between calling super.onCreate and setting the context view to my xml layout. My first thought was that the process of acquiring the SurfaceHolder or whatever happens in the SurfaceHolder methods might be too much in a tight memory situation, but this is way before that happens. It seems that it's occurring in setContentView while parsing my xml layout and building the View objects.
My camera preview code is taken from examples I've found in books and articles, so I'm wondering if there is any additional cleanup I need to do in surfaceDestroyed? Should I try and trigger a garbage collection at that point? The reason for this thinking is that the system has enough memory for the app to run in conditions where less apps are in memory. It either has to do with my own app not cleaning up after itself enough, or the system not being able to reclaim memory fast enough for my app. What I don't understand is why during setContentView is it attempting to allocate so much new memory.
Here is my surface callback code and a paraphrase of what goes on in the activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera_preview);
// crash occurs here
// ...other stuff
initControls();
}
private void initControls()
{
previewHolder = preview.getHolder();
previewHolder.addCallback(surfaceCallback);
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// ...other stuff
}
SurfaceHolder.Callback surfaceCallback = new Callback() {
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(ApplicationEx.LogTag, "surfaceDestroyed");
camera.stopPreview();
camera.release();
camera = null;
isPreviewRunning = false;
}
public void surfaceCreated(SurfaceHolder holder) {
Log.d(ApplicationEx.LogTag, "surfaceCreated");
camera = Camera.open();
try
{
camera.setPreviewDisplay(previewHolder);
}
catch(Throwable t)
{
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
Log.d(ApplicationEx.LogTag, "surfaceChanged");
if (isPreviewRunning)
{
Log.d(ApplicationEx.LogTag, "preview is running, stop preview");
camera.stopPreview();
isPreviewRunning = false;
}
Camera.Parameters parameters = camera.getParameters();
setPreviewAndPictureSize(parameters, width, height);
parameters.setPictureFormat(PixelFormat.JPEG);
parameters.setJpegQuality(85);
camera.setParameters(parameters);
camera.startPreview();
isPreviewRunning = true;
Log.d(ApplicationEx.LogTag, "end surfaceChanged");
}
};
Stop the preview and release the camera in onPause(), and acquire it in onResume(). Regardless of anything else, right now, you are going to tie up the camera and prevent other applications from using it when the user presses HOME.
(BTW, I had this wrong for a long time and corrected it in my books over the past few months)
It is possible that this will help with your memory situation, though I am not certain. If you could narrow down what exactly is asking for a 8640000-byte allocation (which is really really big), that might help. Perhaps some background image in your layout?
Related
I am creating a basic camera app as a small project I'm doing to get started with Android development.
When I click on the button to take a picture, there is about a 1-second delay in which the preview freezes before unfreezing again. There is no issue with crashing - just the freezing issue. Why is this happening and how can I fix it?
Below is the method where the camera is instantiated, as well as my SurfaceView class.
private void startCamera() {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
cameraPreviewLayout = (FrameLayout) findViewById(R.id.camera_preview);
camera = checkDeviceCamera();
camera.setDisplayOrientation(90);
mImageSurfaceView = new ImageSurfaceView(MainActivity.this, camera);
cameraPreviewLayout.addView(mImageSurfaceView);
ImageButton captureButton = (ImageButton)findViewById(R.id.imageButton);
captureButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
camera.takePicture(null, null, pictureCallback);
camera.stopPreview();
camera.startPreview();
}
});
}
public class ImageSurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
private Camera camera;
private SurfaceHolder surfaceHolder;
public ImageSurfaceView(Context context, Camera camera) {
super(context);
this.camera = camera;
this.surfaceHolder = getHolder();
this.surfaceHolder.addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
this.camera.setPreviewDisplay(holder);
this.camera.startPreview();
this.camera.setDisplayOrientation(90);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
EDIT: There is currently nothing in the pictureCallback.
Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
}
You don't need to call stopPreview() after takePicture(). And you don't need startPreview() on the next line. You do need startPreview() inside your onPictureTaken() callback (not in onClick() as in the posted code!!) if you want live preview to restart after the picture is captured into a Jpeg stream.
To keep your UI responsive while using camera, you should do all work with the camera on a background thread. But it is not enough to call Camera.open() or Camera.close() on some background thread. You must create a Handler thread and use it for Camera.open(). The same Looper will be used for all camera callbacks, including PictureCallback.onPictureTaken(). See my detailed walkthrough about the use of HandlerThread.
As I explained elsewhere, you can achieve even better performance if you use the new camera2 API on devices that fully support this API (but better use the old API with devices that provide only LEGACY level of camera2 support).
But if you want to get maximum from the camera ISP, this kind of freeze may be inevitable (this depends on many hardware and firmware design choices, made by the manufacturer). Some clever UI tweaks may help to conceal this effect.
You’ll need to enable access to the hidden “Developer options” menu on
your Android phone. To do that, simply tap the “About phone” option in
Settings. Then tap “Build number” seven times and you’re done. Now you
can just back out to the main Settings menu and you’ll find Developer
options somewhere near the bottom of the list.
==>Now that you’re done with that part, let the real fun begins. Tap the new Developer options menu you just enabled and scroll until you
see the following three settings (note that they may be located within
an “Advanced” subsection):
Window animation scale Transition animation scale Animator animation
scale
==>Did you see them? By default, each of those three options is set to “1x” but tapping them and changing them to “.5x” will dramatically
speed up your phone. This harmless tweak forces the device to speed up
all transition animations, and the entire user experience is faster
and smoother as a result
Please read the question before marking this as a duplicate. I'm trying to access the LED/Flashlight WITHOUT using the Camera methods shown in other code on this site. Thank you.
I'm trying to use the flashlight/torch in Android. I have found and implemented code that works for doing this. The problem I have is that I'm using an image recognition API that uses the camera as an image scanner and they don't have a light on/off function. When I try to override their methods and use the Camera methods to turn the torch on/off, this works, however, the Camera methods now control the window and their Scanner no longer has priority on the screen.
So what I'm trying to determine is... Is there another way to turn on the flashlight/torch without using the Camera methods and preview functions. Anyone have an idea how to bypass the Camera to use the flashlight/torch? Any information would be greatly appreciated.
Here is the code that I currently use, which is working to turn the flashlight on/off, but like I said...this overrides the scanner, and I need the camera/scanner to operate at the same time to recognize my images.
public class Flashlight extends AutoScannerSession {
Camera cam;
private CameraManager mCameraManager;
private Context context;
private Scanner scanner;
private AutoScannerSession.Listener listener;
private boolean advancedListener = false;
public Flashlight(Activity parent, Scanner scanner, Listener listener, SurfaceView preview) {
super(parent, scanner, listener, preview);
cam = Camera.open();
cam.setErrorCallback(new Camera.ErrorCallback() {
#Override
public void onError(int error, Camera camera) {
Log.e("erro", error +"");
}
});
this.context = parent;
this.mCameraManager = new CameraManager(parent, this, preview);
this.scanner = scanner;
this.listener = listener;
if(listener instanceof AutoScannerSession.AdvancedListener) {
this.advancedListener = true;
}
}
#Override
public void start() {
super.start();
//flashOn();
}
public void flashOn() {
android.hardware.Camera.Parameters p = cam.getParameters();
p.setFlashMode(android.hardware.Camera.Parameters.FLASH_MODE_TORCH);
cam.setParameters(p);
//cam.startPreview();
}
public void flashOff() {
cam.stopPreview();
cam.release();
}
#Override
public void stop() {
super.stop();
flashOff();
}
No there is no alternative way to work with flash. But probably you can "share" the camera object with the Scanner.
At any rate, Camera.open() in Activity.onCreate() and turning on flashlight in Activity.onStart() do not look correct. To be a good citizen among other apps, your app should not obtain camera before onResume() and release it no later than onPause().
Code Snippet to turn on camera flash light.
Camera cam = Camera.open();
Parameters p = cam.getParameters();
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
cam.setParameters(p);
cam.startPreview();
Code snippet to turn off camera led light.
cam.stopPreview();
cam.release();
I have a Symphony H20 smartphone officially running Android 4.4.2, Kernel 3.4.67, MT6582. Setting Parameters.FLASH_MODE_TORCH as a camera parameter did not work. After trying a few hours I decided to decompile its pre-installed FlashLight app. There, I found that they write 1 in file /sys/class/torch/torch/torch_level to turn on the Camera LED and 0 to turn it off.
Then I tried the same thing and voilla! It worked.
But the same technique did not work in my Winmax W800 Plus with Android 4.4.2, Kernel 3.4.67, MT6572. It even does not have a file like /sys/class/torch/torch/torch_level at all.
In my app, I am showing a dialog during a long-running background process which is modal.
This dialog is dismissed when android returns from the background task.
final ProgressDialog progressDialog = ProgressDialog.show(activity, "", "Doing something long running", true, true);
new Thread(new Runnable() {
public void run() {
someLongRunningCode(); // (not using AsyncTask!)
activity.runOnUiThread(new Runnable() {
public void run() {
progressDialog.dismiss();
}
});
}
}).start();
Now, however, when the user rotates his device while the background process is still running the activity is recreated and thus, the progressdialog gets detached from the activity.
When the long running process is done, android obviously tries to hide the (now detached) progress dialog which then results in an exception: java.lang.IllegalArgumentException: View not attached to window manager.
Is there any way to check if it is safe to dismiss the dialog?
Edit: Or better still, is there any way to attach the existing dialog to the newly created activity?
A new, cloned dialog would be fine aswell.
Just catch the exception and ignore it. Android's decision to restart the activity on rotation makes threads have a few odd exceptions like that. But the end result you want is no dialog box displayed. If you just catch the exception, no dialog box is displayed. So you're good.
If you aren't using a separate layout/drawables for landscape and portrait, you can just override configChange in your manifest for the activity so it doesn't destroy the activity (it will still correctly rotate and resize everything for you). That way the same dialog will be up and you shouldn't get the exception. The other option would require a lot of work around onSaveInstanceState and onRestoreInstanceState, and you'd need to be very careful of timing issues if the thread actually finishes during that time. The whole recreate activity on rotation idea doesn't work well with multithreading.
if (progressDialog != null) {
progressDialog.dismiss();
}
I've faced this issue before and solved using the below code.
private boolean isDialogViewAttachedToWindowManager() {
if (dialog.getWindow() == null) return false;
View decorView = progressDialog.getWindow().getDecorView();
return decorView != null && decorView.getParent() != null;
}
I know, this question was asked maaany times, but I do not think there was a solution for this. I'm developing application which should be targeted for all devices with android system and backfacing camera. The problem is I can test application on only two devices and have to be sure that it works on all devices.I think only reasonable solution would be to to find a code samples for camera api that are quaranteed to work on nearly all devices. Does anybody can provide such sources ... but sources ... that really are tested on maaaaany (ALL) devices ? I've lost all hairs from my head ... and .. and I'm loosing my mind i think... It is all because I've released app (only for tests in my company) which was tested on only two devices, and which uses camera api in a way that should work, but it appears there are some phones like for example HTC desire HD or HTC Evo 3d (with 3d camera) where the app simply crashes (because of fu..ing camera) or freezes (also because of fu..ing camera). If there is someone who have sources for camera api (taking a picture without user gui interaction, periodically) which are really tested, please be so kind and if You can, post the source or redirect me to proper place.
Hmm maybe The question should sound like this: "Is it technically possible to use camera api on all devices ?"
Maybe I will describe how I'm currently using api.
1) Initialize cam:
public void initCam()
{
LoggingFacility.debug("Attempting to initialize camera",this);
LoggingFacility.debug("Preview is enabled:"+isPreview,this);
try {
if (camera==null)
{
camera = Camera.open();
camera.setPreviewDisplay(mHolder);
if (camera!=null)
{
Camera.Parameters parameters = camera.getParameters();
List<Size> sizes = parameters.getSupportedPictureSizes();
if (sizes!=null)
{
Size min = sizes.get(0);
for (Size size : sizes)
if (size.width<min.width) min = size;
{
parameters.setPictureSize(min.width, min.height);
}
}
camera.setParameters(parameters);
setDisplayOrientation(90);
}
}
startPreview(aps);
} catch (Throwable e){
if (exceptionsCallback!=null)
exceptionsCallback.onException(e);
}
}
2) Start preview:
private void startPreview(AfterPreviewStarted after)
{
try {
if (!isPreview)
{
LoggingFacility.debug("Starting preview",this);
//camera.stopPreview();
camera.startPreview();
isPreview = true;
LoggingFacility.debug("Preview is enabled:"+isPreview,this);
}
if (after!=null) after.doAfter();
}catch(Throwable e)
{
if (exceptionsCallback!=null)
exceptionsCallback.onException(e);
}
}
3) Take picture:
public void takePicture(final PictureCallback callback)
{
LoggingFacility.debug("Attempting to take a picture",this);
if (camera!=null)
{
if (isPreview)
{
try
{
LoggingFacility.debug("preview is enabled jut before taking picture",this);
//AudioManager mgr = (AudioManager)ctx.getSystemService(Context.AUDIO_SERVICE);
//mgr.setStreamMute(AudioManager.STREAM_SYSTEM, true);
LoggingFacility.debug("Taking picture... preview will be stopped...",this);
isPreview = false;
camera.takePicture(null, new PictureCallback(){
public void onPictureTaken(byte[] arg0, Camera arg1)
{
//LoggingFacility.debug("Picture has been taken - 1t callback",CameraPreview.this);
}
}, callback);
//mgr.setStreamMute(AudioManager.STREAM_SYSTEM, false);
} catch (Throwable e){
if (exceptionsCallback!=null)
exceptionsCallback.onException(e);
}
}
}
4) Release camera after done, or after surface is disposed.
public void releaseCam()
{
LoggingFacility.debug("Attempting to release camera",this);
if (camera!=null)
{
isTakingPictures = false;
camera.stopPreview();
isPreview = false;
camera.release();
camera = null;
LoggingFacility.debug("A camera connection has been released...",this);
}
}
In 3rd code snippet in callback method Im invoking startPreview again since after taking picture a preview is disabled, and some smartphones require preview to be started to make a picture. All above method are part of class extending SurfaceView and implementing SurfaceHolder.Callback and is a part of activity.
SurfaceHolder.Callback is implemented as follows:
public void surfaceCreated(SurfaceHolder holder) {
initCam();
}
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCam();
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
}
Constructor of class
CameraPreview(Context context) {
super(context);
this.ctx = context;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
I was also considering another approach - to overcome taking picture, and instead of this to register onPreviewFrame callback and for example in this callback check the flag if a picture has been requested, if so - convert image to bitmap and use it in further processing. I was trying this approach, but then stuck with another problem - even If I register empty callback, a gui responds much slower.
For everyone who like me have problems using android camera api please refer to this link . It seems the code from this sample works on majority of smartphones.
final int PICTURE_TAKEN = 1;
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(filename)));
startActivityForResult(intent, PICTURE_TAKEN);
This works for me, haven't had complaints sofar.
I have an app that, for now, runs a loop where it updates the game state ~25 times/s and tries so draw it 40 times/s. the update works fine, it shows just a bit more than 25. but on the emulator, the game draws under 20 fps, no matter how much there is to draw. even if I only paint the background green it has low fps.
I sent the app to someone who tested it on his phone, there it runs correctly with 40fps drawing speed and 25fps updating speed.
I tested it on the emulator with different API levels, the drawing fps is always way too slow. I use eclipse. do I have to make any special setting for the emulator or so?
There was another problem too, the guy who tested it on his phone got an error message when starting the app, it said the app wouldn't react and the option for wait and shutdown. after waiting 5-10 seconds the content view would be displayed. I create the content view and assign the variable in the onCreate method, without starting the loop. In the onStart method I set the contentView and start the loop, but it runs an empty while loop until I set a boolean in the onResume method. any ideas?
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view= new MainMenu(this); }
protected void onStart() {
super.onStart();
this.setContentView(view);
view.startGameView();
}
public void startGameView() {
loop=new GameLoop(this);
loop.start();
}
protected void onResume() {
super.onResume();
view.resumeGameView();
}
public void resumeGameView() {
loop.b_pause=false;//loop starts to execute code for drawing/updating
}
the code for the activity change:
public void changeActivity(Class c, String str)
{
Intent i=new Intent(this, c);
startActivity(i);
}