facebook/fresco

bitmap's byte[] leak when use RoundedBitmapDrawable in android 6.0

Open

#2,429 建立於 2019年10月31日

在 GitHub 查看
 (3 留言) (0 反應) (0 負責人)Java (17,072 star) (3,750 fork)batch import
bughelp wantedstarter-task

描述

Description

when wrap a drawable with RoundedBitmapDrawable in fresco ,after call bitmap.recycle(), it's byte[] data is still hold by native stack which cause a memory leak.

here is the memry when show and destroy a image:

frescoRoundDrawableMemory

here is the hprof,the byte[] 's GC root is Native Stack,and without anyother references : hprof_pic

Reproduction

app's gradle file

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.example.imagetest"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'com.github.bumptech.glide:glide:4.10.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
    implementation 'com.facebook.fresco:fresco:1.8.0'
}

sample code


import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

import androidx.appcompat.app.AppCompatActivity;

import com.facebook.drawee.drawable.RoundedBitmapDrawable;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;


public class MainActivity extends AppCompatActivity {
    ViewGroup root;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        root = findViewById(R.id.root);
    }

    private Bitmap mBitmap;
    private ImageView mView;

    private Bitmap decode(String url) throws IOException {
        InputStream is = null;
        Bitmap bmp;
        try {
            URL url1 = new URL(url);
            HttpURLConnection con = (HttpURLConnection) url1.openConnection();
            is = con.getInputStream();
            bmp = BitmapFactory.decodeStream(is);
        } finally {
            if (is != null) {
                is.close();
            }
        }
        return bmp;
    }

    private BitmapDrawable getDrawable(Bitmap map) {
        return new BitmapDrawable(getResources(), map);
    }

    private RoundedBitmapDrawable getRoundDrawable(BitmapDrawable drawable) {
        RoundedBitmapDrawable bitmapDrawable = new RoundedBitmapDrawable(getResources(), drawable.getBitmap(), drawable.getPaint());
        bitmapDrawable.setCircle(false);
        float[] roundd = {15.36f, 15.36f, 15.36f, 15.36f, 15.36f, 15.36f, 15.36f, 15.36f};
        bitmapDrawable.setRadii(roundd);
        bitmapDrawable.setBorder(0, 0);
        bitmapDrawable.setPadding(0);
        bitmapDrawable.setScaleDownInsideBorders(false);
        return bitmapDrawable;
    }


    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.create:
                if (mView != null) {
                    return;
                }
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            mBitmap = decode("https://images.manmanapp.com/banner/5d8daf812ff15.jpg");
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    mView = new ImageView(MainActivity.this);
                                    mView.setImageDrawable(
                                            getDrawable(mBitmap));
                                    mView.setLayoutParams(new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
                                    root.addView(mView);
                                }
                            });
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
                break;
            case R.id.delete:
                View v1 = mView;
                if (v1 != null) {
                    mView = null;
                    root.removeView(v1);
                }
                Bitmap bit = mBitmap;
                if (bit != null) {
                    mBitmap = null;
                    bit.recycle();
                }
                break;
            default:
                break;
        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <LinearLayout
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Hello World!"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/create"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="create"
            android:onClick="onClick"
            app:layout_constraintBottom_toBottomOf="parent"
            android:background="@color/colorAccent"
            android:layout_margin="10dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <TextView
            android:id="@+id/delete"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="delete"
            android:onClick="onClick"
            app:layout_constraintBottom_toBottomOf="parent"
            android:background="@color/colorAccent"
            android:layout_margin="10dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
        <!--
            <ImageView
                android:id="@+id/img"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        -->

    </LinearLayout>

</ScrollView>

Additional Information

  • Fresco version: [1.8.0]
  • Platform version: [android 6.0]

貢獻者指南