Android - 使用网格适配器和图像提高性能

时间:2016-05-03 15:42:41

标签: java android performance

有时,我正在构建的应用程序将耗尽内存或接收与字节有关的错误(例如,无法分配带有125240个空闲字节和122KB的2320024字节分配,直到OOM)。

该应用程序非常重要,依赖于将byte []转换为位图(一般不推荐我知道 - 但对于这个特定项目来说是必要的恶意)。我一直收到关于“在主线程上做得太多”的不断消息。当它崩溃时,源可以追溯到我的gridAdapter类,有时我的buildImage类(使用gridAdapter)

我意识到正确的方法是在另一个线程上运行进程,但是我一直遇到困难。当我试图在另一个线程上运行“fillGrid()”和“fillPartialGrid()”方法时 - 运行应用程序时没有任何内容出现。当我尝试在gridAdapter的getView方法中使用新线程时,我最终只得到了半个图像。此外,我甚至设法收到一个错误,指向我使用单独线程的位置(在buildImage中)。

如果有人能就如何改善表现提出一些建议,我将非常感激。

buildImage

    public class buildImage extends AppCompatActivity {
    private SimpleCursorAdapter adapter;
private ImageButton back;
    ImageView buildContainer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_build_image);
        super.onCreate(savedInstanceState);
        buildContainer = (ImageView) findViewById(R.id.fullSizeImage);
        back = (ImageButton) findViewById(R.id.backButton);



        back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                finish();
            }
        }, 60 * 2* 1000L);
    }

    protected void onStart() {
        super.onStart();

        Intent i = getIntent();
        if (i.hasExtra("position"))

        {
           final int position = i.getExtras().getInt("position");
            int lto = i.getExtras().getInt("lto");
            int location = i.getExtras().getInt("location");
            final gridAdapter adapter = new gridAdapter(this, lto, location);




            new Thread(new Runnable() {
                @Override
                public void run() {
                    final Bitmap buildImage = BitmapFactory.decodeByteArray(adapter.buildImages.get(position), 0, adapter.buildImages.get(position).length);
                    buildContainer.post(new Runnable() {
                        @Override
                        public void run() {
                            buildContainer.setImageBitmap(buildImage);
                        }
                    });
                }
            }).start();


        } else

        {


            byte[] imageId = i.getByteArrayExtra("imageID");
            Bitmap buildImage = BitmapFactory.decodeByteArray(imageId, 0, imageId.length);
            buildContainer.setImageBitmap(buildImage);

        }
    }
    }

gridAdapter

public class gridAdapter extends BaseAdapter {
    private Context mContext;
    private int isLto;
    private int board;
    public Cursor items;

public ArrayList<String> itemNames = new ArrayList<String>();

  public ArrayList<byte[]> icons = new ArrayList<byte[]>();
public ArrayList<byte[]> buildImages = new ArrayList<byte[]>();




    public gridAdapter(Context context, int lto, int location) {
mContext = context;
    isLto = lto;
        board = location;

                fillGrid();


    }

    public gridAdapter(Context context, int location, boolean homePage) {
        mContext = context;
        isLto = 1;
        board = location;

                fillPartialGrid();


    }

    private void fillGrid() {
        databaseHelper dbHelper = new databaseHelper(mContext);
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        items = db.rawQuery("SELECT " + databaseHelper.ITEM_NAME + ", " + databaseHelper.ICON + ", " + databaseHelper.IMAGE + " FROM " + databaseHelper.TABLE_NAME + " where " + databaseHelper.LTO + " = " + isLto + " and " + databaseHelper.LOCATION + " = " + board + " ORDER BY " + databaseHelper.ITEM_NAME + " ASC", null);
        items.moveToFirst();

int counter = 0;

        while(!items.isAfterLast()) {

            itemNames.add(items.getString(items.getColumnIndex(databaseHelper.ITEM_NAME)));


icons.add(items.getBlob(items.getColumnIndex(databaseHelper.ICON)));
             buildImages.add(items.getBlob(items.getColumnIndex(databaseHelper.IMAGE)));



            counter++;
            items.moveToNext();
        }
        items.close();
        db.close();
dbHelper.database.close();

    }

    private void fillPartialGrid() {
        databaseHelper dbHelper = new databaseHelper(mContext);
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        items = db.rawQuery("SELECT " + databaseHelper.ITEM_NAME + ", " + databaseHelper.ICON + ", " + databaseHelper.IMAGE + " FROM " + databaseHelper.TABLE_NAME + " where " + databaseHelper.LTO + " = " + isLto + " and " + databaseHelper.LOCATION + " = " + board +  " ORDER BY " + databaseHelper.ITEM_NAME + " ASC LIMIT 3", null);
        items.moveToFirst();
        int counter = 0;
        while(!items.isAfterLast()) {  itemNames.add(items.getString(items.getColumnIndex(databaseHelper.ITEM_NAME)));


            icons.add(items.getBlob(items.getColumnIndex(databaseHelper.ICON)));
            buildImages.add(items.getBlob(items.getColumnIndex(databaseHelper.IMAGE)));



            counter++;
            items.moveToNext();
        }
        items.close();

        db.close();
        dbHelper.database.close();
    }
    @Override
    public int getCount() {
return itemNames.size();
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public Object getItem(int position) {
        return buildImages.get(position);
    }

    @Override
    public View getView(int position, View view, ViewGroup parent) {
        LayoutInflater layout = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = layout.inflate(R.layout.grid_item, parent, false);

        TextView gridItemName = (TextView)view.findViewById(R.id.gridItemName);


        ImageView iconContainer = (ImageView)view.findViewById(R.id.gridItemIcon);

        gridItemName.setText(itemNames.get(position));

Bitmap iconBitmap = BitmapFactory.decodeByteArray(icons.get(position), 0, icons.get(position).length);
iconContainer.setImageBitmap(iconBitmap);


        return view;
    }
}

以及使用gridAdapter并导致buildImage的另一个活动的示例

public class lunchStandard extends AppCompatActivity {
    private String release = Build.VERSION.RELEASE;
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()) {
            case R.id.configure_icon:

                Intent i = new Intent(this, preferences.class);
                startActivity(i);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
private  int discriminator;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lunch_standard);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        toolbar.setClipChildren(false);
        toolbar.setNavigationIcon(R.drawable.back);
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(lunchStandard.this, launch_activity.class);
                startActivity(i);
            }
        });
                GridView grid = (GridView) findViewById(R.id.lunchStandardGrid);
        SharedPreferences preferences = getSharedPreferences("config", MODE_PRIVATE);


        if(preferences.getInt("Location", 2) == 3) {
            discriminator = 3;
        } else if(preferences.getInt("Location", 2) == 2){
            discriminator = 2;
        }
        if(discriminator == 2) {
            setTitle("Main Boards Builds " + release);
        } else if (discriminator == 3) {
            setTitle("Speciality Builds " + release);
        }


        gridAdapter adapter = new gridAdapter(this, 0, discriminator);
        grid.setAdapter(adapter);


        grid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = new Intent(lunchStandard.this, buildImage.class);
                intent.putExtra("location", discriminator);
                intent.putExtra("lto", 0);
                intent.putExtra("position", position);
                startActivity(intent);
            }
        });

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent i = new Intent(lunchStandard.this, midamcorp.com.burgerkingapp.preferences.class);
                startActivity(i);
            }
        });

    }

}

2 个答案:

答案 0 :(得分:0)

我能够在您提供的代码中识别一些减速和内存密集区域。我会尝试列出你可以获得最大收益的问题。

  • 在主线程上执行SQL查询并迭代它 - 通常,执行单个SQL查询不会减慢你的速度,但是因为你在fillGrid()中迭代它,所以渲染活动的时间随返回的结果数量线性增加。对此的一般解决方案是使用LoaderManager/CursorAdapter作为网格视图。
  • 使用位图而不首先检查大小 - 这个总是让我。某些设备上的Android内存堆大小非常有限,如果不小心,位图可以快速占用整个内存。一种常见的策略是在使用之前检查位图的大小,并在必要时缩小位图。请参阅其他答案,例如Strange out of memory issue while loading an image to a Bitmap object,以获取有关此问题的帮助。
  • 将byte []转换为位图转换 - 如果您能够避免这种情况,那么移动到将文件存储在文件存储中而不是作为blob存储在SQL数据库中,也可以获得性能提升。您还可以利用许多出色的第三方图像库,例如FrescoGlidePicasso等...来处理所有繁重的工作(加载图像)关闭主线程,缩小尺寸/调整大小,网络请求,加载指标等......)

答案 1 :(得分:0)

为什么不使用像Glide这样的图片加载库并让它处理加载过程?并且你永远不会得到OOM错误。

它非常容易设置,只需在gradle.build

中编译此依赖项
compile 'com.github.bumptech.glide:glide:3.7.0'

然后您需要做的就是

 Glide
.with(Context)
.load(adapter.buildImages.get(position))
.into(buildContainer);

我参与了处理图像的多个应用程序,这是避免我知道OOM的最佳方法。

希望有所帮助