Android Service后台多线程压缩并提交图片及数据

手机端发帖,多张图片上传是个问题.最近重构项目代码,正好碰到这个,这里把解决的方案整理,以备后用.

方案原理:

  1. 创建上传任务表, 帖子内容发布的时候将数据存放到任务表中,并传递数据到service中.
  2. 启动服务,遍历任务表中内容,创建上传任务.如果接收到上传任务,创建任务,加入上传任务队列中.(上传任务顺序可以自定义)
  3. 包含图片的上传任务.开启多线程压缩(使用线程池管理压缩线程),压缩完毕后返回压缩后临时图片位置
  4. 上传成功后,根据返回值处理临时目录, 删除任务表中上传成功任务

Activity 和 Service 之间的数据通信使用的是 EventBus
数据上传使用的的是retrofit + okHttp


不多说,直接上代买

上传服务:


public class UploadService extends Service {

    private static final String TAG = UploadService.class.getSimpleName();

    private ExecutorService executorService = Executors.newFixedThreadPool(3);
    private ExecutorService singleTaskService = Executors.newSingleThreadExecutor();// 按顺序处理任务


    @Override
    public void onCreate() {
        super.onCreate();
        EventBus.getDefault().register(this);
        LogUtils.e(TAG, "UploadService is onCreate");
    }


    /**
     * 通过EventBus接受传递过来的数据
     *
     * @param submitPostBena
     */
    @Subscribe
    public void receiveUploadData(SubmitPostBena submitPostBena) {
        createSubmitTask(submitPostBena);
    }


    /**
     * 查询任务表中是否有数据   (未实现任务表)
     */
    private void queryTaskTable() {
        //mTaskCount = queryFromTable().size();//查询数据库
        //List postsBeen = new ArrayList<>();
        //for (int i = 0; i < postsBeen.size(); i++) { //循环创建提交帖子任务
        //    createSubmitTask(postsBeen.get(i));
        //}
    }

    /**
     * 创建提交帖子数据任务
     *
     * @param bean 要提交的提子数据
     */
    private void createSubmitTask(SubmitPostBena bean) {
        singleTaskService.execute(new TaskRunnable(bean));
    }

    /**
     * 任务
     */
    private class TaskRunnable implements Runnable {
        private CountDownLatch countDownLatch;
        private List newPath = Collections.synchronizedList(new ArrayList());//返回值
        private List faile = Collections.synchronizedList(new ArrayList());//提交失败返回值

        private SubmitPostBena bean;

        TaskRunnable(SubmitPostBena bean) {
            this.bean = bean;
            countDownLatch = new CountDownLatch(bean.getImagePaths().size());//这地方有个小坑,countDown 的数量一定要和压缩图片的数量一致
        }

        @Override
        public void run() {
            synchronized (UploadService.class) {
                try {
                    if (bean.isImagePost()) {
                        LogUtils.e(TAG, "开始任务处理图片压缩");
                        CompressTask(countDownLatch, newPath, bean.getImagePaths()); //处理压缩问题
                        countDownLatch.await();
                        bean.setImagePaths(newPath);
                        LogUtils.e(TAG, "压缩完成");
                   }
                    submitData(bean, faile); //提交
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

    /**
     * 创建处理压缩任务
     *
     * @param countDownLatch 线程同步辅助工具,用于记录图片压缩线程全部执行完毕后通知提交线程提交
     * @param newPath        处理过后的图片的地址
     * @param imagePath      原始图片地址
     */
    private void CompressTask(CountDownLatch countDownLatch, List newPath, List imagePath) {

        for (int i = 0; i < imagePath.size(); i++) {
            String path = imagePath.get(i);
            executorService.execute(new CompressRunnable(countDownLatch, path, newPath));
        }
    }


    /**
     * 压缩任务
     */
    private class CompressRunnable implements Runnable {

        private String filePath;
        private List newPath;
        private CountDownLatch countDownLatch;


        /**
         * 压缩图片处理
         *
         * @param countDownLatch 线程同步辅助类,用于基数当前线程是否完成,如果完成,线程数量减少1
         * @param imagePath      要处理图片的路径
         * @param newPath        处理后图片的新路径
         */
        CompressRunnable(CountDownLatch countDownLatch, String imagePath, List newPath) {
            this.newPath = newPath;
            this.filePath = imagePath;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            Bitmap smallBitmap = BitmapUtils.getSmallBitmap(filePath);
            String tempPath = BitmapUtils.compressImage(smallBitmap);
            if (!TextUtils.isEmpty(tempPath)) {
                newPath.add(tempPath);
                countDownLatch.countDown();
            }
        }
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
        LogUtils.e(TAG, "UploadService is onDestroy");
        executorService.shutdownNow();
        singleTaskService.shutdownNow();
    }


    private void submitData(SubmitPostBena mBean, List faile) {
        if (SystemUtils.checkNet(this)) {
          //上传方法
        }
    }
}

图片压缩:


public class BitmapUtils {


    /**
     * 根据路径获得图片并压缩,返回bitmap用于显示
     *
     * @param filePath 图片路径
     * @return
     */
    public static Bitmap getSmallBitmap(String filePath) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        options.inSampleSize = calculateInSampleSize(options, 480, 800);// Calculate inSampleSize
        options.inJustDecodeBounds = false;// Decode bitmap with inSampleSize set
        Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
        return bitmap;
    }

    /**
     * 计算图片的缩放值
     *
     * @param options
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        return inSampleSize;
    }


    public static String compressImage(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中

        int options = 90;
        while (baos.toByteArray().length / 1024 > 100) {//循环判断如果压缩后图片是否大于100kb,大于继续压缩
            baos.reset();//重置baos即清空baos
            options -= 10;
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
            if (options < 0) options = 0;

        }
        String tempName = UUID.randomUUID().toString().replace("-", "") + ".jpg";
        File fileDir = new File(Commons.PHOTOCACHE_TEMP);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        File file = new File(fileDir, tempName);
        try {
            if (file.exists())
                file.delete();
            FileOutputStream fos = new FileOutputStream(file);
            image.compress(Bitmap.CompressFormat.JPEG, 50, fos);
            fos.flush();
            fos.close();
            if (!image.isRecycled()) {
                image.recycle();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        LogUtils.e("压缩后图片大小 >>>> ", file.length() + "");
        return file.getPath();
    }
}

你可能感兴趣的:(开发笔记,Android)