【问题标题】:Android Listview is crashing on nonemulated DeviceAndroid Listview 在非模拟设备上崩溃
【发布时间】:2015-02-12 19:15:41
【问题描述】:

我一直在编写一个 Listview,其中显示一个图像以及一些文本,同时创建一个新项目,当您按下同一个 xml 中的按钮时。模拟我的应用程序会导致垃圾收集器“发疯”,并且我的应用程序不断跳帧。如标题所述,它甚至在 S3 上崩溃(拍照后)。尽管我一直在网上搜索可能的改进 (ViewHolder),但它们无济于事,甚至会导致错误。

所以这是我工作中的 MainActivity,其中我得到了最好的改进:

<!-- language: lang-java -->
public class MainActivity extends Activity {

List<FavImages> FavImages = new ArrayList<FavImages>();
ListView favImageListView;

Bitmap bitmap;

final Context context = this;

private SharedPreferences mPrefs;
private SharedPreferences.Editor mEditor;

//label logs
private static String logtag = "CameraApp";
//use main camera
private static int TAKE_PICTURE = 1;

private Uri imageUri;
public Uri imagePath = Uri.parse("android.resource://com.adrianopaulus/drawable/no_picture.png");
DataBaseHandler dbHandler;
int longClickedItemIndex;
ArrayAdapter<FavImages> favImagesAdapter;

private static final int EDIT = 0, DELETE = 1;

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    favImageListView = (ListView) findViewById(R.id.listView);
    dbHandler = new DataBaseHandler(getApplicationContext());

    mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    mEditor = mPrefs.edit();


    //enter an Item
    registerForContextMenu(favImageListView);
    //maybe without long
    favImageListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
        @Override
        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
            //position of Item
            longClickedItemIndex = position;
            return false;
        }
    });

    if (dbHandler.getFavCount() != 0) {
        FavImages.addAll(dbHandler.getAllFav());
    }

    //List<FavImages> addableFavs = dbHandler.getAllFav();
    //if (!addableFavs.isEmpty())
        populateList();

    //Button Action
    Button cameraButton = (Button)findViewById(R.id.button_camera);
    cameraButton.setOnClickListener(cameraListener);
}

private OnClickListener cameraListener = new OnClickListener(){
    public void onClick(View v){
        takePhoto(v);
    }
};

//launch native camera app
private void takePhoto(View v){
    final Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");

    //save Image and create file

    // in Progress
    LayoutInflater factory = LayoutInflater.from(this);
    final View textEntryView = factory.inflate(R.layout.text_entry, null);
    final EditText input1 = (EditText) textEntryView.findViewById(R.id.pictureName);
    final AlertDialog.Builder alert = new AlertDialog.Builder(this);
    //create Dialog
    alert
            .setTitle("Bitte bennenen Sie Ihr Bild!")
            .setView(textEntryView)
            .setPositiveButton(R.string.alert_dialog_ok,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            Log.i("AlertDialog","TextEntry 1 Entered "+input1.getText().toString());
                            /* User clicked OK so do some stuff */
                            String inputText = input1.getText().toString();

                            mEditor.putString("pictureName", inputText);
                            mEditor.commit();

                            File photo = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), replaceChars(mPrefs.getString("pictureName", "picture")) + ".jpg");
                            //access information of file
                            imageUri = Uri.fromFile(photo);
                            //save image path information
                            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

                            //for favImages
                            imagePath = imageUri;
                            mEditor.putString("picturePath", imagePath.toString());
                            mEditor.commit();
                            //
                            startActivityForResult(intent, TAKE_PICTURE);

                            //
                            Log.e("Dateipfad", imagePath.toString());
                            FavImages favImages = new FavImages(dbHandler.getFavCount(), mPrefs.getString("pictureName", "Bild"), imagePath);
                            dbHandler.createFav(favImages);

                        }
                    });
    //show Dialog
    alert.show();


}

public String replaceChars (String inputText){
    inputText = inputText.replace("ä","ae");
    inputText = inputText.replace("ö","oe");
    inputText = inputText.replace("ü","ue");
    inputText = inputText.replace("ß","ss");

    return inputText;
}

//deal with output
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent){
    super.onActivityResult(requestCode, resultCode, intent);
    //user hits ok button (picture accepted)
    if(resultCode == Activity.RESULT_OK){
        //Uri selectedImage = imageUri;
        //communication between apps
        getContentResolver().notifyChange(imageUri, null);

        /*get Image
        //ImageView imageView = (ImageView)findViewById(R.id.image_camera);
        //hold Image data
        ContentResolver cr = getContentResolver();
        Bitmap bitmap;

        //get bitmap data
        try {
            bitmap = MediaStore.Images.Media.getBitmap(cr, selectedImage);
            //set Image
            imageView.setImageBitmap(bitmap);
            //notify user of success
            Toast.makeText(MainActivity.this, selectedImage.toString(), Toast.LENGTH_LONG).show();
        }catch (Exception e){ //catch exceptions along the way
            Log.e(logtag, e.toString());
        } */

        List<FavImages> addableFavs = dbHandler.getAllFav();
        int favCount = dbHandler.getFavCount();

        //for(int i = 0; i < favCount; i++){
        FavImages.add(addableFavs.get(favCount-1));

        if (!addableFavs.isEmpty())
            populateList();
    }
}

//
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo){
    super.onCreateContextMenu(menu, view, menuInfo);

    menu.setHeaderTitle("Favorit bearbeiten");
    menu.add(Menu.NONE, DELETE, menu.NONE, "Favorit löschen");
}

public boolean onContextItemSelected (MenuItem item){
    switch (item.getItemId()){
        case EDIT:
            //TODO: edit Favorite
            break;
        case DELETE:
            //delete Favorite
            dbHandler.deleteFav(FavImages.get(longClickedItemIndex));
            FavImages.remove(longClickedItemIndex);
            favImagesAdapter.notifyDataSetChanged();
            break;
    }
    return super.onContextItemSelected(item);
}

private void populateList(){
    //ArrayAdapter<FavImages> adapter = new favImagesListAdapter();
    //favImageListView.setAdapter(adapter);
    favImagesAdapter = new favImagesListAdapter();
    favImageListView.setAdapter(favImagesAdapter);
}

static class ViewHolder {
    private TextView imageTextView;
    private ImageView favImageView;
}

//Constructor for List Items
private class favImagesListAdapter extends ArrayAdapter<FavImages>{
    public favImagesListAdapter(){
        super (MainActivity.this, R.layout.listview_item, FavImages);
    }



    @Override
    public View getView (int position, View view, ViewGroup parent){
        if (view == null) {
            view = getLayoutInflater().inflate(R.layout.listview_item, parent, false);
        }

        FavImages currentFav = FavImages.get(position);

        TextView favName = (TextView) view.findViewById(R.id.favName);
        favName.setText(currentFav.getImageName());
        ImageView ivFavsImage = (ImageView) view.findViewById(R.id.favImage);

        //hold Image data
        ContentResolver cr = getContentResolver();

        try {
            bitmap = MediaStore.Images.Media.getBitmap(cr, currentFav.getImagePath());
            //set Image
            ivFavsImage.setImageBitmap(bitmap);
        }catch (Exception e){ //catch exceptions along the way
            Log.e(logtag, e.toString());
        }

        return view;
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu){
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
   }

}

总结一下: 如何使我的应用程序占用更少的内存?

编辑:来自 logcat 的部分堆栈跟踪

    02-12 20:00:19.945      948-948/com.adrianopaulus.favoriten I/AlertDialog﹕ TextEntry 1 Entered b
    02-12 20:00:20.095      948-948/com.adrianopaulus.favoriten E/Dateipfad﹕ file:///mnt/sdcard/Pictures/b.jpg
    02-12 20:00:23.295      948-948/com.adrianopaulus.favoriten W/IInputConnectionWrapper﹕ showStatusIcon on inactive InputConnection
    02-12 20:00:29.335      948-948/com.adrianopaulus.favoriten D/dalvikvm﹕ GC_FOR_ALLOC freed 1337K, 6% free 23771K/25287K, paused 31ms, total 49ms
    02-12 20:00:29.436      948-948/com.adrianopaulus.favoriten D/dalvikvm﹕ GC_FOR_ALLOC freed 1311K, 7% free 23693K/25287K, paused 31ms, total 48ms
    02-12 20:00:29.526      948-948/com.adrianopaulus.favoriten D/dalvikvm﹕ GC_FOR_ALLOC freed 1239K, 7% free 23686K/25287K, paused 29ms, total 30ms
    02-12 20:00:29.616      948-948/com.adrianopaulus.favoriten D/dalvikvm﹕ GC_FOR_ALLOC freed 1232K, 7% free 23687K/25287K, paused 28ms, total 29ms
    02-12 20:00:29.766      948-948/com.adrianopaulus.favoriten W/EGL_emulation﹕ eglSurfaceAttrib not implemented
    02-12 20:00:29.865      948-948/com.adrianopaulus.favoriten D/dalvikvm﹕ GC_FOR_ALLOC freed 1233K, 7% free 23687K/25287K, paused 97ms, total 101ms
    02-12 20:00:30.075      948-948/com.adrianopaulus.favoriten D/dalvikvm﹕ GC_FOR_ALLOC freed 1233K, 7% free 23690K/25287K, paused 35ms, total 38ms
    02-12 20:00:30.175      948-948/com.adrianopaulus.favoriten D/dalvikvm﹕ GC_FOR_ALLOC freed 32K, 2% free 24895K/25287K, paused 28ms, total 29ms
    02-12 20:00:30.275      948-948/com.adrianopaulus.favoriten D/dalvikvm﹕ GC_FOR_ALLOC freed 32K, 2% free 26099K/26503K, paused 31ms, total 32ms
    02-12 20:00:30.345      948-948/com.adrianopaulus.favoriten I/Choreographer﹕ Skipped 64 frames!  The application may be doing too much work on its main thread.

编辑 2:新的 ListAdapter

static class ViewHolder {
    TextView imageTextView;
    ImageView favImageView;
}

//Constructor for List Items
private class favImagesListAdapter extends ArrayAdapter<FavImages>{
    private LayoutInflater mInflater = LayoutInflater.from(context);

    public favImagesListAdapter(){
        super (MainActivity.this, R.layout.listview_item, FavImages);
    }



    @Override
    public View getView (int position, View convertView, ViewGroup parent){

        ViewHolder holder;

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.listview_item, parent, false);
            holder = new ViewHolder();
            holder.imageTextView = (TextView) convertView.findViewById(R.id.favName);
            holder.favImageView = (ImageView) convertView.findViewById(R.id.favImage);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        FavImages currentFav = FavImages.get(position);

        holder.imageTextView.setText(currentFav.getImageName());
        ContentResolver cr = getContentResolver();
        try {
            holder.favImageView.setImageBitmap(MediaStore.Images.Media.getBitmap(cr,   currentFav.getImagePath()));
        } catch (IOException e) {
            e.printStackTrace();
        }




        return convertView;
    }
}

【问题讨论】:

  • 至少从 logcat 发布堆栈跟踪。
  • 好的,只需再次运行应用程序 - 片刻
  • 您发布的Logcat中没有错误。 “崩溃”在哪里?
  • 崩溃发生在 Android 设备上,目前不可用,发布的日志来自模拟设备。
  • 如果您的标题显示“崩溃”,请从“崩溃”发布堆栈跟踪

标签: android performance listview


【解决方案1】:

您的适配器未针对性能进行调整,可能导致您遇到的所有延迟。

你需要的东西:

  • 使用getView 方法提供的convertViews 进行适当的缓存
  • 使用ViewHolder 模式来避免findViewById 重复。

所有这些问题都已经在 SO 上得到了多次回答,但 IMO 最好的学习来源仍然是 Google 团队的 the video

祝你好运!

【讨论】:

  • 虽然我按照您的提示操作,但它仍然会跳帧等。正如我所说的我正在拍照,我是否必须注意那里的特殊性能问题。
【解决方案2】:

我可以建议您尝试在内部存储中缓存您的位图。就像我做的那样。

ImageCacheHelper.

public class ImageCacheHelper {

    Context ctx;


    public ImageCacheHelper(Context ctx)
    {
        this.ctx=ctx;
    }


    public String saveToInternalSorage(Bitmap bitmapImage,String fileName){
        ContextWrapper cw = new ContextWrapper(ctx.getApplicationContext());
         // path to /data/data/yourapp/app_data/imageDir
        File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
        // Create imageDir
        File mypath=new File(directory,fileName);

        FileOutputStream fos = null;
        try {           

            fos = new FileOutputStream(mypath);

       // Use the compress method on the BitMap object to write image to the OutputStream
            bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return directory.getAbsolutePath();
    }

    public Bitmap loadImageFromStorage(String fileName)
    {

        try {
            ContextWrapper cw=new ContextWrapper(ctx);
            File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
            File myFile=new File(directory,fileName);

            InputStream istr=new BufferedInputStream(new FileInputStream(myFile));
            Bitmap image=BitmapFactory.decodeStream(istr);
            return image;
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
            return null;
        }

    }

}

然后在你的 getView 方法中

        @Override
            public View getView(int position, View convertView, ViewGroup parent) {
            if(convertView==null){.....}
            else {.....}

           Bitmap imageAnswer = new ImageCacheHelper(cContext)
    .loadImageFromStorage(cQuestions.get(position)
            .getImageUrl.toString());
    if(imageAnswer==null)
    imgDownloader.execute(tempView);
    else
        tempView.iView.setImageBitmap(imageAnswer);
    }

并在您的 AsyncTask 对象中获取您的位图

private class imageDownloader extends AsyncTask<ViewHolder, Void, Bitmap> {

        ViewHolder v;

        @Override
        protected Bitmap doInBackground(ViewHolder... params) {
            // TODO Auto-generated method stub


                and writing to storage


                    v = params[0];
                    InputStream is = (InputStream) new URL(params[0].ImageUrl)
                            .getContent();
                    Bitmap b = BitmapFactory.decodeStream(is);
                    new ImageCacheHelper(cContext).saveToInternalSorage(b,
                            cQuestions.get(params[0].position)
                                    .getImageId.toString()); 

   }
}

....

希望我的朋友能帮助你。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 2013-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多