如何写图片压缩框架

链式编程

try {
    final InputStream is = getResources().getAssets().open("test-6.jpg");
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = mConfig;
    Bitmap originBitmap = BitmapFactory.decodeStream(is, null, options);
    //上面代码就是通过流得到Bitmap对象
    KmoCompress.FileCompressOptions compressOptions = new KmoCompress.FileCompressOptions();//设置自己的配置对象
    compressOptions.config = mConfig;
    KmoCompress.getInstance().source(originBitmap).asFile().withOptions(compressOptions).compress(new FileWithBitmapCallback() {
        @Override
        public void callback(boolean isSuccess, Bitmap bitmap, String outfile, Throwable t) {
            if (!isSuccess) {
                Logger.e("error: " + t.getMessage());
                mCompressTv.setText("compress file failed!");
                return;
            }
            File file = new File(outfile);
            setupCompressInfo(bitmap, outfile, file.length());
        }
    });
    is.close();
} catch (Exception e) {
    e.printStackTrace();
}

看到KmoCompress.getInstance().source(originBitmap).asFile().XXX这段就是链式编程。

首先由一个单例发出,单例里面有source这个方法

public synchronized CompressEngine source(Bitmap bitmap) {
    return new CompressEngine().source(bitmap);
}

这个方法很简单,就是new 一个对象调用该对象的source,这样就把业务转移到了CompressEngine这个对象身上。

public CompressEngine source(Bitmap bitmap) {
    mSourceType = SourceType.BITMAP;
    mSource = bitmap;
    return this;
}

这个对象的source方法就是存储一些东西。

所以我们小结如下:

链式编程在.号后面无非就是两种情况:

  • new 对象然后将业务转移到该对象身上。
  • this 就是当前对象实现业务
/**
 * @return  文件处理引擎,里面包含了图片源和图片类型
 */
public FileCompressEngine asFile() {
    return CompressEngineFactory.buildFileCompressEngine(mSource, mSourceType);
}
/**
 * @param options 配置文件
 * @return
 */
public FileCompressEngine withOptions(KmoCompress.FileCompressOptions options) {
    options.config = CompressUtils.filterConfig(options.config);//对config转化,如果是ARGB_8888,ARGB_4444,ALPHA_8都对应成8888,RGB_565就是RGB_565
    mCompressOptions = options;//并将配置保存
    return this;
}

此时链式编程已经传递到withOptions方法,他返回的是FileCompressEngine对象本身,那也就是说后面的compress方法也在该对象内部实现。

我们目前来看结果,上面代码到withOptions都是处理参数的保存,真正的压缩还没有执行。我们想一个问题哈,就是压缩是需要时间的,在Android里面耗时的操作不要 给主线程放,那么应该怎么让compress去通过我们传递进来的数据源在子线程中压缩数据呢?同时我们是需要返回结果的传统的new Thread(){run()}.start();已经不能满足我们,因为我们要将结果刷入主线程中,我们怎么做呢?

线程池+任务

根据上面所以这里就对应一个问题模型:数据源我们现在处理了,也传递到此时的处理类中,但是由于处理数据比较耗时,而且我们需要处理结果在主线程中,怎么办?

先看怎么写:

  1. 用单例的方式生成一个线程池
public class CompressExecutor {

    private CompressExecutor() {
        throw new RuntimeException("can not be a instance");
    }

    private static final ThreadPoolExecutor DEFAULT_COMPRESS_EXECUTOR;

    static {
        int nThreads = Runtime.getRuntime().availableProcessors() + 1;
        DEFAULT_COMPRESS_EXECUTOR = new CompressThreadPool(
                nThreads,
                nThreads,
                0L,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(),
                new CompressThreadFactory()
        );
    }

    public static ThreadPoolExecutor getExecutor() {
        return DEFAULT_COMPRESS_EXECUTOR;
    }

}
  1. 继承线程池

继承的目的是:我们需要在线程任务执行完成之后打印一些信息

public class CompressThreadPool extends ThreadPoolExecutor {

    public CompressThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public CompressThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public CompressThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public CompressThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    /**
     * Method invoked prior to executing the given Runnable in the given thread.
     *
     * @param t
     * @param r
     */
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        //ignore...
    }

    /**
     * Method invoked upon completion of execution of the given Runnable.
     *
     * @param r
     * @param t
     */
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        KmoCompressUtil.printExceptionMessage(t);
    }

    /**
     * Method invoked when the Executor has terminated.
     */
    @Override
    protected void terminated() {
        super.terminated();
        //ignore...
    }
}
public class FileCompressEngine extends CompressEngine {
    ...
    Bitmap bitmap = (Bitmap) mSource;
    CompressExecutor.getExecutor()
            .execute(new CompressFutureTask<CompressResult>(new FileCompressCallableTasks.BitmapAsFileCallable(mCompressOptions, shouldReturnBitmap, bitmap)
                    , new DefaultCallbackDispatcher<CompressResult>(callback)
            ));
    ...
}

我们观察一下new CompressFutureTask的继承结构:

public class CompressFutureTask<T> extends FutureTask<T> {

这下就和我们上一次的博客吻合,在线程任务执行过程中,work包装了我们的线程,最终调用了run方法,我们在FutureTask中看看,最终的run方法。

public void run() {
    if (state != NEW ||
        !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

看到是调用call和set方法。

所以我们的业务核心在call方法上

call方法是外界塞给FutureTask的对应接口。

  public static final class FileAsFileCallable extends BaseFileCompressCallable {
        private File mFile;

        public FileAsFileCallable(KmoCompress.FileCompressOptions options, boolean withBitmap, File file) {
            super(options, withBitmap);
            mFile = file;
        }

        @Override
        public CompressResult call() throws Exception {
            CompressResult result = null;
            FileInputStream fis = null;
            try {
                if (mCompressOptions != null && mCompressOptions.overrideSource)
                    mCompressOptions.outfile = mFile.getAbsolutePath();
                fis = new FileInputStream(mFile);
                result = new FileCompressor().compress(CompressUtils.transformToByteArray(fis), mCompressOptions, shouldReturnBitmap, true);
            } finally {
                try {
                    if (fis != null)
                        fis.close();
                } catch (IOException e) {
                    //ignore...
                }
            }
            return result;
        }
    }

所以call是压缩的核心那后面就不多说,主要是一些压缩比例转化等等问题以后我们在说压缩如何压缩。这个就直接偏向应用层,不需要架构的概念,我们今天只讨论架构的东西。OK就到这了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,384评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 一 潮水褪走了 风还没来 灯塔的余光淹没 寄居蟹的脚印 水鸟停靠在港口 成为唯一的证人 用一捧水 浇灌游鱼的梦 把...
    雨子1983阅读 158评论 2 2
  • 一叶知秋落海堤,萧瑟昏夕,渫雨凄迷。天开阴霁梦依稀,鸿雁轻啼,望断云低。 残热犹威汗染衣,茶饮神移...
    海1619阅读 242评论 2 7
  • 《遇见一棵花树》 ……………………………………………… 每次读席慕容的诗,那些柔软清丽的句子,犹如微风般轻轻拂来…...
    女侠K的解忧年画铺阅读 519评论 0 0