链式编程
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();已经不能满足我们,因为我们要将结果刷入主线程中,我们怎么做呢?
线程池+任务
根据上面所以这里就对应一个问题模型:数据源我们现在处理了,也传递到此时的处理类中,但是由于处理数据比较耗时,而且我们需要处理结果在主线程中,怎么办?
先看怎么写:
- 用单例的方式生成一个线程池
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;
}
}
- 继承线程池
继承的目的是:我们需要在线程任务执行完成之后打印一些信息
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就到这了