1. 程式人生 > >Android Service後臺多執行緒壓縮並提交圖片及資料

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<SubmitPostBena> 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<String> newPath = Collections.synchronizedList(new ArrayList<String>());//返回值 private List<String> faile = Collections.synchronizedList(new ArrayList<String>());//提交失敗返回值 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<String> newPath, List<String> 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<String> newPath; private CountDownLatch countDownLatch; /** * 壓縮圖片處理 * * @param countDownLatch 執行緒同步輔助類,用於基數當前執行緒是否完成,如果完成,執行緒數量減少1 * @param imagePath 要處理圖片的路徑 * @param newPath 處理後圖片的新路徑 */ CompressRunnable(CountDownLatch countDownLatch, String imagePath, List<String> 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<String> 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();
    }
}