android pdf框架-9,解码优化

mupdf不支持直接的多线程.multithread.c我也没看明白,android直接调用多线程就会莫名其妙的事,要么崩溃,要么渲染出的页面不对.

pdfium倒是可以直接多线程.但是解码速度要慢不少.平均速度,100页,多个pdf对比,要慢50-200%,这个差距是明显的.

多线程,我最后发现,即使这方面已经做到极致,缓存也尽量最大化,渲染块的速度在最终的体验上,依然不尽如人意.可能是本人的能力问题吧.研究过radaee的sdk源码,商业产品效率与稳定性都极高,它已经加强了cjk的支持,原来不能显示的pdf也正常了,渲染速度之快,我比不了.静读天下就是用它的sdk.

解码优化

解码优化有好多方面可以做;

队列优化,把当前显示中间的项,优先进行解码.

如果有缩略图,要优化解码.

解码前判断是否可见,因为队列是单线程的,当轮到它解码时,可能页面已经滑动过去

队列优化

用recyclerview实现的页面,不需要这么复杂,直接切边,然后解码页面,甚至不需要缩略图,滑动起来效果还是不错的,前提是把额外空间设置一个页面的高度,利用它的预加载能力.

recyelerview已经处理了页面的布局,可见性,回收,预加载等操作,而自定义的view是没有这些的.

定义两个队列,一个是解码缩略图的.另一个解码具体node的.

private final LinkedHashMap<String, DecodeTask> nodeTasks = new LinkedHashMap<>(32, 0.75f, false);
private final LinkedHashMap<String, DecodeTask> pageTasks = new LinkedHashMap<>(32, 0.75f, false);

使用handlerthread来处理,因为单线程,所以也不开线程池了.

具体的消息调度:

public boolean handleMessage(Message msg) {
            int what = msg.what;
            if (what == MSG_DECODE_START) {
                addDecodeTask(msg);
            } else if (what == MSG_DECODE_SELECT) {
                selectDecodeTask(msg);
            } else if (what == MSG_DECODE_CANCEL) {
                cancelDecodeTask(msg);
            }
            return true;
        }

外部调用:

public void decodePage(String decodeKey, PageTreeNode node, int pageNumber, final DecodeCallback decodeCallback, float zoom, RectF pageSliceBounds, String thumbKey) {
        final DecodeTask decodeTask = new DecodeTask(node, pageNumber, decodeCallback, zoom, decodeKey, pageSliceBounds, thumbKey);
        Message message = Message.obtain();
        message.obj = decodeTask;
        message.what = MSG_DECODE_START;
        mHandler.sendMessage(message);
    }

这样就把任务添加进队列了.然后就是队列的循环去判断是否有缩略图的任务

添加任务的方法比较简单,就是判断有没有任务在.

private void addDecodeTask(Message msg) {
            final DecodeTask decodeTask = (DecodeTask) msg.obj;
            if (decodeTask.type == DecodeTask.TYPE_PAGE) {
                DecodeTask old = pageTasks.put(decodeTask.thumbKey, decodeTask);
                if (old != null) {
                    Log.d(TAG, String.format("old page task:%s-%s", pageTasks.size(), old));
                }
            } else {
                DecodeTask old = nodeTasks.put(decodeTask.decodeKey, decodeTask);
                if (old != null) {
                    Log.d(TAG, String.format("old node task:%s-%s", nodeTasks.size(), old));
                }
            }
            mHandler.sendEmptyMessage(MSG_DECODE_SELECT);
        }

选任务:

private void selectDecodeTask(Message msg) {
            if (isRecycled) {
                return;
            }

            DecodeTask selectTask = null;
            if (!pageTasks.isEmpty()) {
                selectTask = pageTasks.entrySet().iterator().next().getValue();
                pageTasks.remove(selectTask.thumbKey);
            }
            if (selectTask == null) {
                if (!nodeTasks.isEmpty()) {
                    selectTask = nodeTasks.entrySet().iterator().next().getValue();
                    nodeTasks.remove(selectTask.decodeKey);
                }
            }

            if (selectTask == null) {
                //mHandler.sendEmptyMessageDelayed(MSG_DECODE_SELECT, 5000L);
                Log.d(TAG, String.format("no task:%s-%s", pageTasks.size(), nodeTasks.size()));
            } else {
                Log.d(TAG, String.format("add task:%s-%s", selectTask.pageNumber, selectTask.type));
                try {
                    performDecode(selectTask);
                } catch (IOException e) {
                    Log.e(TAG, String.format("decode error:%s-%s", selectTask.pageNumber, selectTask.node));
                } finally {
                    mHandler.sendEmptyMessage(MSG_DECODE_SELECT);
                }
            }
        }

先判断缩略图的队列,如果不为空,则运行它,如果为空,判断node队列.最后都要发消息去进行下一轮选择.

原来的任务是否死亡的判断就要修改了:

private boolean isTaskDead(DecodeTask task) {
        boolean isPage = false;
        if (task.node == null) {
            isPage = true;
        }
        if (skipInvisible(task, isPage)) {
            return true;
        }
        return false;
    }
缩略图优化

涉及到体验,如果图片未解码,显示的是黑白色,看画布的颜色了.这体验是不好的.

所以优先设置一张缩略图,然后把这图先放上去,每一块解码时,把这些填充上,就会有一个渐变的过程,从模糊到清晰的过程,这种体验比较好.尤其在缩放值比较大的时候.比如放大3倍了.滑动的时候,每一块解码耗时都不小.

设置缩略图.就要先解析缩略图.

所以当node进行解码时,先判断有没有缩略图.没有的话,先解码缩略图.然后放入缓存中,再画出来.在vudroid的page中去画.pagetreenode中的decode,添加进参数.缩略图的key生成一个与page相关的唯一值.

绘制缩略图

protected String getKey() {

return String.format("%s-%s", index, documentView.decodeService);

}

这时的draw就要修改了

public void draw(Canvas canvas) {
        if (!isVisible()) {
            return;
        }
        //canvas.drawRect(bounds, fillPaint);
        Bitmap thumb = BitmapCache.getInstance().getBitmap(getKey());
        Log.d("", String.format("index:%s,%s, %s", index, getKey(), thumb));
        if (null != thumb) {
            Rect src = new Rect(0, 0, thumb.getWidth(), thumb.getHeight());
            Rect dst = new Rect((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom);
            canvas.drawBitmap(thumb, src, dst, null);
        } else {
            canvas.drawText("Page:" + (index + 1), bounds.centerX(), bounds.centerY(), textPaint);
        }

        node.draw(canvas);
        canvas.drawLine(bounds.left, bounds.bottom, bounds.right / 5, bounds.bottom, strokePaint);
        drawPageLinks(canvas);
    }
把小图画到大的范围,drawbitmap

通常有两种方法,一种是mat

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值