ios加载本地html懒加载图片方案,关于图片加载优化相关方案 -- android,ios

1、快速滑动图片加载性能优化方案

两种方案:

1:加载策略

2:手动控制

1.1、 加载策略

1):FIFO first in first out

2):LIFO last in first out

后进先出,针对滑动加载图片的,这个比较合适,滑动速度越快,越能体现这种方案的优势。当前呈现给用户的,最新加载;当前未呈现的,选择加载。这种方案需要自己写工具类控制线程调度,也就相当于控制多线程并发。如下:/**

* Created by zyj on 2017/9/21.

*/public class TaskDispatcher {    private LinkedList mTaskList;         // 任务队列

private ExecutorService mThreadPool;            // 线程池

private Thread mPollingThead;                   // 轮询线程

private Handler mPollingHanler;                 // 轮询线程中的Handler

private static int mThreadCount = 3;            // 线程池的线程数量,默认为3

private Type mType = Type.FIFO;                 // 队列的调度方式,默认为LIFO

private volatile Semaphore mPollingSemaphore;   // 信号量,由于线程池内部也有一个阻塞线程,若加入任务的速度过快,LIFO效果不明显

private volatile Semaphore mSemaphore = new Semaphore(0);  //信号量,防止mPoolThreadHander未初始化完成

private static TaskDispatcher mInstance;    public enum Type { FIFO, LIFO }    /**

* 单例获得实例对象

* @return   实例对象

*/

public static TaskDispatcher getInstance() {        if (mInstance == null) {            synchronized (TaskDispatcher.class) {                if (mInstance == null) {

mInstance = new TaskDispatcher(mThreadCount, Type.FIFO);

}

}

}        return mInstance;

}    /**

* 单例获得实例对象

* @param threadCount    线程池的线程数量

* @param type           队列的调度方式

* @return   实例对象

*/

public static TaskDispatcher getInstance(int threadCount, Type type) {        if (mInstance == null) {            synchronized (TaskDispatcher.class) {                if (mInstance == null) {

mInstance = new TaskDispatcher(threadCount, type);

}

}

}        return mInstance;

}    /**

* 构造函数

* @param threadCount    线程池的线程数量

* @param type           队列的调度方式

*/

private TaskDispatcher(int threadCount, Type type) {

init(threadCount, type);

}    /**

* 初始化

* @param threadCount    线程池的线程数量

* @param type           队列的调度方式

*/

private void init(int threadCount, Type type) {

mThreadPool = Executors.newFixedThreadPool(threadCount);

mPollingSemaphore = new Semaphore(threadCount);

mTaskList = new LinkedList();        if (type == null) {

mType = Type.LIFO;

} else {

mType = type;

}        // 开启轮询线程

mPollingThead = new Thread() {            @Override

public void run() {

Looper.prepare();

mPollingHanler = new Handler() {                    @Override

public void handleMessage(Message msg) {

mThreadPool.execute(getTask());                        try {

mPollingSemaphore.acquire();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

};

mSemaphore.release();   // 释放一个信号量

Looper.loop();

}

};

mPollingThead.start();

}    /**

* 添加一个任务

* @param task   任务

*/

private synchronized void addTask(Runnable task) {        try {            // mPollingHanler为空时,请求信号量,因为mPollingHanler创建完成会释放一个信号量

if (mPollingHanler == null) mSemaphore.acquire();

} catch (InterruptedException e) {

e.printStackTrace();

}

mTaskList.add(task);

mPollingHanler.sendEmptyMessage(0x110);

}    /**

* 取出一个任务

* @return 需要执行的任务

*/

private synchronized Runnable getTask() {        if (mType == Type.LIFO) {            return mTaskList.removeLast();

} else if (mType == Type.FIFO) {            return mTaskList.removeFirst();

}        return null;

}    /**

* 执行自定义的任务

*/

public void doTask(Runnable runnable) {

addTask(runnable);

}    public void releaseSemaphore() {

mPollingSemaphore.release();

}

}

调用private TaskDispatcher td;

....        for (int i = 0; i 

td = TaskDispatcher.getInstance();

td.doTask(new Runnable() {                @Override

public void run() {

...                    //这句必须有,任务处理完成后释放一个信号量,初始化时候可以指定线程数,如果为1的,下面这句不用

//                    td.releaseSemaphore();

}

});

}

1.2、 手动从交互上控制

监听列表滚动,当列表滚动时候,禁止图片的加载,滚动停止时候,开始加载图片

1.2.1、 android 端 - picasso

android中使用picasso处理图片加载,可以用tag标记(pauseTag,resumeTag)处理。在滑动时候禁止图片加载,滑动停止启用图片加载。这个效果很明显,滑动不再卡了

场景一: 比如一个照片流列表,当我们快速滑动列表浏览照片的时候,后台会一直发起请求加载照片的,这可能会导致卡顿,那么我们就可以为每个请求设置一个相同的Tag,在快速滑动的时候,调用pauseTag暂停请求,当滑动停止的时候,调用resumeTag恢复请求,这样的体验是不是就会更好一些呢。

Adapter中添加如下代码:Picasso.with(this).load(mData.get(position))

.placeholder(R.drawable.default_bg)

.error(R.drawable.error_iamge)

.tag("PhotoTag")

.into(holder.mImageView);

Activity中为RecyclerView添加滑动监听:mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {            @Override

public void onScrollStateChanged(RecyclerView recyclerView, int newState) {                final Picasso picasso = Picasso.with(MainActivity.this);                if (newState == SCROLL_STATE_IDLE) {

picasso.resumeTag("PhotoTag");

} else {

picasso.pauseTag("PhotoTag");

}

}

});

场景二: 比如一个照片流列表界面,在弱网环境下,加载很慢,退出这个界面时可能会有很多请求没有完成,这个时候我们就可 以通过tag 来取消请求了。@Override

protected void onDestroy() {        super.onDestroy();

Picasso.with(this).cancelTag("PhotoTag");

}

1.2.2、 ios 端

基于监听Y,来算速度,然后再controller里面设置一个全局变量,当速度低于多少的时候把这个全局变量布尔值打开,然后再绘制cell的时候根据这个全局变量去选择是否使用Sd去加载图片

2、 列表cell/item复用导致图片错乱解决方案

解决方案:

1:占位图

2:复用视图设置tag。以图片url为tag

2.1、 android 端

问题原理:

RecyclerView,包括之前用的ListView都存在这个问题。因为有ViewHolder的重用机制,每一个item在移除屏幕后都会被重新使用以节省资源,避免滑动卡顿。而在图片的异步加载过程中,从发出网络请求到完全下载并加载成Bitmap的图片需要花费很长时间,而这时候很有可能原先需要加载图片的item已经划出界面并被重用了。而原先下载的图片在被加载进ImageView的时候没有判断当前的ImageView是不是原先那个要求加载的,故可能图片被加载到被重用的item上,就产生了图片错位的问题。

解决方案:

解决思路也很简单,就是在下载完图片,准备给ImageView装上的时候检查一下这个ImageView

recyclerview,item,viewholder:设置hint图,给view设置tag

解决思路:

开始->当前item1给ImageView打tag

->当前item1发起网络请求

->异步处理网络请求

->当前item1滑出屏幕

->划出屏幕的item重用并滑进屏幕命名为item2

->item2重新给ImageView打上tag

->item1的图片下载完成,因为重用的原因,图片将要加载给item2

-> 用原先传入的item1设置的tag和新覆盖的tag比较,发现不相同

->不给当前item2设置图片,避免了图片错位

2.2、 ios 端

同理android,其实ios也可以设置tag

ios中一般用的都是sdwebimage。早年时候,sd加载图片会出现闪烁的情况。很棘手的问题,原因就是缓存的operation在前一个请求刚加载完成显示图片的时候,后一个请求开始了。。sd做了处理,就是在每次请求下载图片请求时候,都会将其哪一个请求cancle掉。

一般有4中方案:

1:不用cell复用方案

问题显而易见,就是cell数量大的话,内存是个问题。当然,我们在入门ios时候,第一次学习tableview使用的时候用的也是这种。当你觉得这种很水的时候,说明你现在也已经很溜了- (UITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identifier

换为

-(UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath

2:特定重用标识方案

采用cell复用策略。但是在初始化cell的时候,identifier指定为唯一确定cell的字符串,比如:cell的section + row。这种方式的复用,也就是相当于cell自我复用。这种方案相对于第一种有进步,但是依然很不合理。- (UITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identifier

3:cell复用,从新初始化子视图方案

思路就是,采用cell复用。但是拿到复用的cell后,将cell的所有自定义子视图移除,初始化数据需要的视图。这种方案,相对于前两种,进步挺大。但是深入想一想就知道喽。频繁的初始化视图,渲染。cpu,gpu累不累。如果你要是用的masonry约束。那完了。

4:cell复用,根据数据动态调整cell子视图方案

前面三种方案,如果是针对简单的cell视图结构,数据量又不大,采用哪种都可以实现,也没有什么差别。只是直观的体现了你的水准而已,嘿嘿。

应用场景:比如微信,做两天界面时候,cell类型比较多。文本,图片,语音,视频,地图等。这种cell,如果要柔和到一个cell里,里面的逻辑控制会写死人的。我处理这块的时候,经历过三个阶段:

1):收发cell是一个cell。用的还是autolayout。当时cell比较简单,就文本,图片,语音。但是已经哭死了。这种写法:逻辑不好控制,并且不好适配

2):后来约束用了masonry。cell也分开了。receiveCell,sendCell。这种方案现在想想也是水啊。每个cell中依然柔和很多类型数据,健壮性一点都不好,特别是sendCell,有好多种临时视图,比如发送indicator,语音下载indicator,语音播放视图,发送失败视图等。单masonry控制视图状态就够累了

3):前面两种方案都不好,最后重构了。还是masonry约束视图。根据消息类型分cell类型。比如:receiveTextCell,sendAudioCell等。每类型cell只控制自己的东西,并且不会有太大变化,毕竟语音cell处理的都是语音的视图/数据嘛。并且对于那些临时视图,采用懒加载。masonry约束他们也不采用remake(通知系统刷新约束,麻烦)。直接update。瞬间清爽了。代码可读性,拓展性,稳定性都有了

作者:DaZenD

链接:https://www.jianshu.com/p/0bae27ff217f

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值