开发者

ImageView in Android Causing App to Crash

I have an app that is making use of photos stored on either the SD card or directly on the phone.

I have 2 Activities that seem to be having some kind of problem (Acitives for Viewing and Editing a record). A single record references a photo by storing a URI that gives the location of the related photo.

When I click on my View activity, I pass the URI, and then set the ImageView using the URI. When inside the View Activity, I have a menu option that allows you to reach the Edit Activity. When you click Edit, I again pass the URI of the photo, and set the ImageView using the URI. Both of these activities display fine on their own.

However, when I click the Back button to move from Edit to View, the app crashes. Initially the crash was an OutOfMemory Exception. I got past this exception by adding a call to ((BitmapDrawable)mImageView.getDrawable()).getBitmap().recycle() in the onPause function in both activities.

With the recycle code in place, execution moves from Edit back to View, and then just crashes somewhere. I've stepped through the lifecycle functions, and execution makes it through successfully, except for onCreate. Execution n开发者_Python百科ever reaches onCreate again.

I know it's something related to ImageView because when I remove the ImageView from the layout in my View activity, the crash no longer happens.

Here is the initial stack trace when the crash happens:

Thread [<3> main] (Suspended (exception RuntimeException))  
    ViewRoot.draw(boolean) line: 1356   
    ViewRoot.performTraversals() line: 1097 
    ViewRoot.handleMessage(Message) line: 1613  
    ViewRoot(Handler).dispatchMessage(Message) line: 99 
    Looper.loop() line: 123 
    ActivityThread.main(String[]) line: 4203    
    Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]  
    Method.invoke(Object, Object...) line: 521  
    ZygoteInit$MethodAndArgsCaller.run() line: 791  
    ZygoteInit.main(String[]) line: 549 
    NativeStart.main(String[]) line: not available [native method]  

After this stack trace, if I keep pushing "Step Over" in Eclipse, the stack trace ends up like this:

Thread [<3> main] (Suspended)   
    ThreadGroup.uncaughtException(Thread, Throwable) line: 884  
    NativeStart.main(String[]) line: not available [native method]  


I have found a way to get past this problem.

I was able to find this other solution thanks in part to Janusz's answer. In my View and Edit classes, I added a private Bitmap class variable (mBitmap). In onCreate, I retrieve and store the URI for the location of my image. In onPause, I recycle the bitmap. In onResume, I updated this function to make use of the new class variable mBitmap (and I'm sure there is a better way to setup the Bitmap, so everyone feel free to point it out). Also, with this new code, I was experiencing a crash in the Edit class initially. If you went to Edit, then clicked the "Home" button, the app would crash. I discovered that the solution to that problem was to set the image view's Bitmap to null in Edit.onPause. The problem was that the imageView was retaining a pointer to the Bitmap, even after I had recycled the bitmap. See Android "Trying to use recycled bitmap" error?:

protected void onResume()
{
    mBitmap = null;
    try
    {
        mBitmap = Bitmap.createBitmap(Media.getBitmap(this.getContentResolver(), mPicUri));
    }
    catch(Exception e)
    {}

    mImageView.setImageBitmap(mBitmap);
    mImageView.setAdjustViewBounds(true);
    mImageView.setMaxHeight(100);      //purposely showing image as 100x100 picture
    mImageView.setMaxWidth(100);
    mImageView.invalidate();

    super.onResume();
}

protected void onCreate()
{
    mPicUri = //the picuture location URI
}

protected void onPause()
{
    mBitmap.recycle();
    mImageView.setImageBitmap(null);  //in my Edit class
}


If you are changing from Activity First to Second, Activity First will be paused. The onPause method then will recycle the Bitmap.
If you switch Back from Activity Second to First Activity First already exists and there is no need to call the onCreateMethod again. Instead onlyt the onResume method is called telling the activity that it is again on the screen. At this moment the imageview tries to paint the image to the screen but because the image is recycled your app crashes.

If you really need to recycle the bitmap you have to reset it in the onResume method before calling the super onResume method of your activity.


The Best way to display a Bitmap image from a a stream and display it in an ImageView without causing any crashes is :

public void getImage(final  ImageView imageView , final String picUrl) {

    class GetImage extends AsyncTask<String,Void,Bitmap> {
      @Override
        protected void onPreExecute() {
            super.onPreExecute();
        Log.i("Download","Profile Picture Downloaded");
        }

        @Override
        protected void onPostExecute(Bitmap b) {
            super.onPostExecute(b);
            imageView.setImageBitmap(b);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            URL url = null;
            Bitmap image = null;
            Bitmap finalImage=null;
            try {
                url = new URL(picUrl);
                image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
                finalImage= resize(image,120,120);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return finalImage;
        }
    }

    GetImage gi = new GetImage();
    gi.execute();
}

The methods has 2 parameters, the ImageView that will contain our Image and the Image URL , you just need to call the method with the parameters and the image will be displayed.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜