【问题标题】:Android - Save bitmap to SD Card with determinated ProgressDialogAndroid - 使用确定的 ProgressDialog 将位图保存到 SD 卡
【发布时间】:2013-12-13 16:25:59
【问题描述】:

我正在使用此 AsyncTask 将我的图像资源保存到 SD 卡:

public class SaveImageAsync extends AsyncTask<String, String, String> {

    private Context mContext;

    int imageResourceID;

    private ProgressDialog mProgressDialog;

    public SaveImageAsync(Context context, int image) 
    {
        mContext = context;
        imageResourceID = image;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mProgressDialog = new ProgressDialog(mContext);
        mProgressDialog.setMessage("Saving Image to SD Card");
        mProgressDialog.setMax(100);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setIndeterminate(true);
        mProgressDialog.setCancelable(false);
        mProgressDialog.show();
    }

    @SuppressLint("NewApi")
    @Override
    protected String doInBackground(String... filePath) {
        try {


            Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResourceID);

            ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
            bitmap.compress(CompressFormat.JPEG, 100, bos); 
            byte[] bitmapdata = bos.toByteArray();
            ByteArrayInputStream bis = new ByteArrayInputStream(bitmapdata);

            int lenghtOfFile = bitmap.getByteCount();
            Log.d("LOG", "File Lenght = " + lenghtOfFile);

            byte[] buffer = new byte[64];
            int len1 = 0;
            long total = 0;

            while ((len1 = bis.read(buffer)) > 0) {
                total += len1;
                publishProgress("" + (int) ((total * 100) / lenghtOfFile));
                bos.write(buffer, 0, len1);
            }
            bos.flush();
            bos.close();
            bitmap.recycle();
            bis.close();

            return getTempUri().getPath();
        } catch (Exception e) {
            return null;
        }


    }

    protected void onProgressUpdate(String... progress) {
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setProgress(Integer.parseInt(progress[0]));
    }

    @Override
    protected void onPostExecute(String filename) {
        // dismiss the dialog after the file was saved
        try {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Uri getTempUri() {
        return Uri.fromFile(getTempFile());
    }

    private File getTempFile() {
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

            File directory = new File(mContext.getExternalCacheDir().getPath());
            directory.mkdirs();
            File file = new File(directory , "temp.jpg");
            try  {
                file.createNewFile();
            }  catch (IOException e) {}
            return file;
        } else  {
            return null;
        }
    }   
}

我从我的活动中调用它:

new SaveImageAsync(this, R.drawable.my_image_resource).execute();

它工作正常,问题是bitmap.getByteCount();返回的位图大小与保存文件的最终大小完全不同。过程完成时的结果,指示的进度仅多或少 20%。

有什么方法可以在保存之前知道文件的最终大小?谢谢。

【问题讨论】:

    标签: android bitmap android-asynctask progressdialog sd-card


    【解决方案1】:

    您使用 PNG 压缩,这种压缩效果的好坏取决于图像的实际内容,即仅包含空白的空白图像尺寸较小,所有像素不同的彩色图像尺寸较大。这么长的故事...bitmap.getByteCount() 为您提供了将多少字节用于将实际图像存储在内存中(未压缩)而不是 SD 卡(压缩)的信息。您的期望与实际所得之间的差异解释了约 20% 的转折点。

    如果我猜想一个解决方案可能是使用位图数据数组的长度。我修改了你的代码:

    Bitmap bitmap = null;
    
    ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
    bitmap.compress(CompressFormat.JPEG, 100, bos); 
    byte[] bitmapdata = bos.toByteArray();
    ByteArrayInputStream bis = new ByteArrayInputStream(bitmapdata);
    
    int lenghtOfFile = bitmapdata.length;
    byte[] buffer = new byte[64];
    int currentProcess = 0;
    int totalReadedYet = 0;
    
    try {
    while ((currentProcess = bis.read(buffer)) > 0) {
        totalReadedYet += currentProcess;
        publishProgress(Integer.toString((int) ((totalReadedYet) / lenghtOfFile)));
        bos.write(buffer, 0, currentProcess);
    }
    
    bos.flush();
    bos.close();
    bitmap.recycle();
    bis.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    【讨论】:

    • 我知道,出于这个原因,我正在寻求一种方法来了解文件的最终大小。
    • 谢谢,'bitmapdata.length;'正是我想要的。
    【解决方案2】:

    感谢 Baschi 的回答,bitmapdata.length; 正是我所需要的,这是我的AsyncTask 用于将bitmapDeterminated ProgressBar 保存到 SD 卡中,我希望有人会觉得它有用:

    public class SaveImageAsync extends AsyncTask<Void, String, Void> {
    
        private Context mContext;
        private int imageResourceID;
    
        private ProgressDialog mProgressDialog;
    
        public SaveImageAsync(Context context, int image) {
            mContext = context;
            imageResourceID = image;
        }
    
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mProgressDialog = new ProgressDialog(mContext);
            mProgressDialog.setMessage("Saving Image to SD Card");
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.setIndeterminate(true);
            mProgressDialog.setCancelable(false);
            mProgressDialog.show();
        }
    
        @Override
        protected Void doInBackground(Void... filePath) {
            try {
                Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResourceID);
    
                ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); 
                bitmap.compress(CompressFormat.JPEG, 100, byteOutputStream); 
                byte[] mbitmapdata = byteOutputStream.toByteArray();
                ByteArrayInputStream inputStream = new ByteArrayInputStream(mbitmapdata);
    
                String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();
                String fileName = "mySavedImage.jpg";
    
                OutputStream outputStream = new FileOutputStream(baseDir + File.separator + fileName);
                byteOutputStream.writeTo(outputStream);
    
                byte[] buffer = new byte[128]; //Use 1024 for better performance
                int lenghtOfFile = mbitmapdata.length;
                int totalWritten = 0;
                int bufferedBytes = 0;
    
                while ((bufferedBytes = inputStream.read(buffer)) > 0) {
                    totalWritten += bufferedBytes;
                    publishProgress(Integer.toString((int) ((totalWritten * 100) / lenghtOfFile)));
                    outputStream.write(buffer, 0, bufferedBytes);
                }
    
            } catch (IOException e) { e.printStackTrace(); }
            return null;
    
        }
    
        protected void onProgressUpdate(String... progress) {
            mProgressDialog.setIndeterminate(false);
            mProgressDialog.setProgress(Integer.parseInt(progress[0]));
        }
    
        @Override
        protected void onPostExecute(Void filename) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
    }
    

    在您的Activity 中使用这一行来保存图像:

    new SaveImageAsync(this, R.drawable.your_image_resource).execute();
    

    【讨论】:

    • 我能问一下...为什么在代码中从不使用filePath 时使用(Void... filePath)?可以说是使doInBackground() 参数无效吗?我也在尝试保存一个文件,想知道我是否需要一个 AsyncTask 来释放主线程,但是使用他们推荐的 (String... params) 对我不起作用,因为我一次只保存一个文件(不是整个数组)。但不确定如何使用可行的东西。谢谢,如果你知道。
    猜你喜欢
    • 2012-08-04
    • 2012-10-21
    • 2011-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多