接触Android开发的应该对图片加载这块都有过接触,也比较头疼,稍微处理不当就会遇到OOM。这两天在看Picasso,感觉收获很大,在这里记录一下。
1 2 3 4
| Picasso .with(getActivity()) .load("http://i1.mifile.cn/a1/T1pZJgBbZT1RXrhCrK.jpg") .into(imageView);
|
这是Picasso最简单的用法,先从入口看。
1 2 3 4 5 6 7 8 9 10
| public static Picasso with(Context context) { if (singleton == null) { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton; }
|
非常标准的双重检验锁单例模式实现。Picasso实例是通过构造者模式生成的,进去看一下build()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public Picasso build() { Context context = this.context; if (downloader == null) { downloader = Utils.createDefaultDownloader(context); } if (cache == null) { cache = new LruCache(context); } if (service == null) { service = new PicassoExecutorService(); } if (transformer == null) { transformer = RequestTransformer.IDENTITY; } Stats stats = new Stats(cache); Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats); return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats, defaultBitmapConfig, indicatorsEnabled, loggingEnabled); } }
|
这里简单说就是生成Picasso构造方法所需要的各个参数,有Downloader(图片下载器)、LruCache(LRU缓存算法实现)、PicassoExecutorService(线程池)、Dispatcher(任务调度)等,这些在后面的图片加载过程中都会用到。
到这儿,Picasso实例已经生成了,再看load()方法:
1 2 3 4 5 6 7 8 9
| public RequestCreator load(String path) { if (path == null) { return new RequestCreator(this, null, 0); } if (path.trim().length() == 0) { throw new IllegalArgumentException("Path must not be empty."); } return load(Uri.parse(path)); }
|
返回一个RequestCreator对象。看一下它的into()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| public void into(ImageView target) { into(target, null); } public void into(ImageView target, Callback callback) { long started = System.nanoTime(); checkMain(); if (target == null) { throw new IllegalArgumentException("Target must not be null."); } if (!data.hasImage()) { picasso.cancelRequest(target); if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } return; } if (deferred) { if (data.hasSize()) { throw new IllegalStateException("Fit cannot be used with resize."); } int width = target.getWidth(); int height = target.getHeight(); if (width == 0 || height == 0) { if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } picasso.defer(target, new DeferredRequestCreator(this, target, callback)); return; } data.resize(width, height); } Request request = createRequest(started); String requestKey = createKey(request); if (shouldReadFromMemoryCache(memoryPolicy)) { Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey); if (bitmap != null) { picasso.cancelRequest(target); setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled); if (picasso.loggingEnabled) { log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY); } if (callback != null) { callback.onSuccess(); } return; } } if (setPlaceholder) { setPlaceholder(target, getPlaceholderDrawable()); } Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); picasso.enqueueAndSubmit(action); }
|
先检查是不是主线程,然后根据RUI生成一个requestKey,根据requestKey看这次请求的图片能不能在内存缓存中取到,能的话直接返回,没有的话根据相关属性生成一个ImageViewAction,并调用Picasso的enqueueAndSubmit()方法,把生成的ImageViewAction传过去。
1 2 3 4 5 6 7 8 9 10 11 12 13
| void enqueueAndSubmit(Action action) { Object target = action.getTarget(); if (target != null && targetToAction.get(target) != action) { cancelExistingRequest(target); targetToAction.put(target, action); } submit(action); } void submit(Action action) { dispatcher.dispatchSubmit(action); }
|
这里有些需要注意,由ImageViewAction的构造方法可知action.getTarget()其实就是ImageView,第3行,从targetToAction的Map中取出ImageView对应的Action,如果该ImageView对应的有Action且和本次要请求的Action不相同,说明之前该ImageView已经绑定过Action,则调用cancelExistingRequest(target)方法取消之前绑定在ImageView上的Action,同时更新为新的Action,之前的访问请求如果已经添加到线程池中会被取消(采用的Future模式)。主要用途在于ListView的View复用,如果图片还没有下载下来就往下滑动导致View不可见View被复用时,取消访问之前的图片,加入新的Action,防止图片加载错乱。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private void cancelExistingRequest(Object target) { checkMain(); Action action = targetToAction.remove(target); if (action != null) { action.cancel(); dispatcher.dispatchCancel(action); } if (target instanceof ImageView) { ImageView targetImageView = (ImageView) target; DeferredRequestCreator deferredRequestCreator = targetToDeferredRequestCreator.remove(targetImageView); if (deferredRequestCreator != null) { deferredRequestCreator.cancel(); } } }
|
enqueueAndSubmit()方法最终调用了dispatcher的dispatchSubmit()方法。这里的dispatcher就是入口方法Picasso.with()中生成赋值的。来看一下。
1 2 3
| void dispatchSubmit(Action action) { handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action)); }
|
使用handler发了一个message,来看接收的地方:
1 2 3 4 5 6 7 8 9
| @Override public void handleMessage(final Message msg) { switch (msg.what) { case REQUEST_SUBMIT: { Action action = (Action) msg.obj; dispatcher.performSubmit(action); break; } }
|
这里只摘取REQUEST_SUBMIT逻辑处理部分,看到最终调用了performSubmit()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| void performSubmit(Action action) { performSubmit(action, true); } void performSubmit(Action action, boolean dismissFailed) { if (pausedTags.contains(action.getTag())) { pausedActions.put(action.getTarget(), action); if (action.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(), "because tag '" + action.getTag() + "' is paused"); } return; } BitmapHunter hunter = hunterMap.get(action.getKey()); if (hunter != null) { hunter.attach(action); return; } if (service.isShutdown()) { if (action.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down"); } return; } hunter = forRequest(action.getPicasso(), this, cache, stats, action); hunter.future = service.submit(hunter); hunterMap.put(action.getKey(), hunter); if (dismissFailed) { failedActions.remove(action.getTarget()); } if (action.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId()); } }
|
第15行,先从内存中根据requestKey取BitmapHunter,如果能取到数据则说明该URI正在处理中(取消、加载完成时会从hunterMap中remove),没必要再次处理,只需要把该次请求的Action添加进BitmapHunter所维护的actionList中,等BitmapHunter处理完之后会自动回调。这么做主要用在多个ImageView加载同一个url图片时,只需要一个任务去加载该url即可,图片下载成功后通知所有要显示该url的ImageView显示。
第28行,会根据传入的ImageViewAction调用forRequest()方法生成一个BitmapHunter,这个forRequest()方法我们一会再说,生成BitmapHunter之后,把这个BitmapHunter对象传给service的submit()方法。同样,这里的service就是前面Picasso中生成的PicassoExecutorService。看一下submit()方法。
1 2 3 4 5 6
| @Override public Future<?> submit(Runnable task) { PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task); execute(ftask); return ftask; }
|
由于BitmapHunter类实现了Runnable接口,最终会调用BitmapHunter的run()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Override public void run() { try { updateThreadName(data); if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this)); } result = hunt(); if (result == null) { dispatcher.dispatchFailed(this); } else { dispatcher.dispatchComplete(this); } } catch (Downloader.ResponseException e) { } finally { Thread.currentThread().setName(Utils.THREAD_IDLE_NAME); } }
|
主要处理在第10行,到hunt()方法中看一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| Bitmap hunt() throws IOException { Bitmap bitmap = null; if (shouldReadFromMemoryCache(memoryPolicy)) { bitmap = cache.get(key); if (bitmap != null) { stats.dispatchCacheHit(); loadedFrom = MEMORY; if (picasso.loggingEnabled) { log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache"); } return bitmap; } } data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy; RequestHandler.Result result = requestHandler.load(data, networkPolicy); if (result != null) { loadedFrom = result.getLoadedFrom(); exifRotation = result.getExifOrientation(); bitmap = result.getBitmap(); if (bitmap == null) { InputStream is = result.getStream(); try { bitmap = decodeStream(is, data); } finally { Utils.closeQuietly(is); } } } if (bitmap != null) { } return bitmap; }
|
开始再次从内存缓存中取一下,取不到的话继续。第35行是Bitmap获取之后的转换处理,暂时先不看。发现result主要是在第17行返回的。不过这个requestHandler又是什么呢?
别急,看一下发现是BitmapHunter的一个全局变量:
1
| final RequestHandler requestHandler;
|
但是这个final类型的全局变量是怎么来的呢?初始没有赋值,那么毫无疑问,就是在BitmapHunter的构造中赋值的了,也就是生成BitmapHunter对象的时候传入的。还记得刚才在dispatcher中调用BitmapHunter的run方法之前有个生成BitmapHunter对象的方法吗?没错,就是forRequest(),看一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) { Request request = action.getRequest(); List<RequestHandler> requestHandlers = picasso.getRequestHandlers(); for (int i = 0, count = requestHandlers.size(); i < count; i++) { RequestHandler requestHandler = requestHandlers.get(i); if (requestHandler.canHandleRequest(request)) { return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler); } } return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER); }
|
遍历Picasso中的requestHandlers这个List,找到一个能够处理这次请求的RequestHandler (requestHandler.canHandleRequest(request)返回true认为能处理),然后用这个RequestHandler生成一个BitmapHunter实例。那这个requestHandlers的List是哪来的呢?
在Picasso的构造方法中有如下代码:
1 2 3 4 5 6 7 8
| allRequestHandlers.add(new ResourceRequestHandler(context)); allRequestHandlers.add(new ContactsPhotoRequestHandler(context)); allRequestHandlers.add(new MediaStoreRequestHandler(context)); allRequestHandlers.add(new ContentStreamRequestHandler(context)); allRequestHandlers.add(new AssetRequestHandler(context)); allRequestHandlers.add(new FileRequestHandler(context)); allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats)); requestHandlers = Collections.unmodifiableList(allRequestHandlers);
|
可以看到,在一开始的时候Picasso会内置7种常见的RequestHandler。从名字就能看出每个能够处理的图片资源的类型了,resource资源图片、本地图片文件、网络图片、asset目录下的图片等。canHandleRequest()方法会根据load(URI uri)方法中传入的URI的scheme来判断自己能否处理该种请求类型。如NetworkRequestHandler的canHandleRequest()方法实现如下:
1 2 3 4 5 6 7 8
| private static final String SCHEME_HTTP = "http"; private static final String SCHEME_HTTPS = "https"; @Override public boolean canHandleRequest(Request data) { String scheme = data.uri.getScheme(); return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme)); }
|
如果是http或https开头的请求,则认为是要加载网络图片,交给自己处理,否则认为自己没有处理这种请求的能力,则返回false,交给别的RequestHandler。这么设计符合单一职责原则。
好了,绕了这么大一圈,再回到BitmapHunter的hunt()方法的这一行:
1
| RequestHandler.Result result = requestHandler.load(data, networkPolicy);
|
这下就清晰了,由于我们load(“http://i1.mifile.cn/a1/T1pZJgBbZT1RXrhCrK.jpg“) 传入的是网络图片,那么这里的requestHandler就应该是NetworkRequestHandler了。我们看一下它的load()方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @Override public Result load(Request request, int networkPolicy) throws IOException { Response response = downloader.load(request.uri, request.networkPolicy); if (response == null) { return null; } Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK; Bitmap bitmap = response.getBitmap(); if (bitmap != null) { return new Result(bitmap, loadedFrom); } InputStream is = response.getInputStream(); if (is == null) { return null; } if (loadedFrom == DISK && response.getContentLength() == 0) { Utils.closeQuietly(is); throw new ContentLengthException("Received response with 0 content-length header."); } if (loadedFrom == NETWORK && response.getContentLength() > 0) { stats.dispatchDownloadFinished(response.getContentLength()); } return new Result(is, loadedFrom); }
|
发现主要是调用了downloader的load()方法拿到的返回值。还记得Picasso.Builder中downloader是怎么生成的吗?
1
| downloader = Utils.createDefaultDownloader(context);
|
进去看一下:
1 2 3 4 5 6 7 8
| static Downloader createDefaultDownloader(Context context) { try { Class.forName("com.squareup.okhttp.OkHttpClient"); return OkHttpLoaderCreator.create(context); } catch (ClassNotFoundException ignored) { } return new UrlConnectionDownloader(context); }
|
如果项目中引入了OkHttp,那么使用OkHttp框架去发起网络请求,反之,使用UrlConnectionDownloader作为网络请求库。看一下UrlConnectionDownloader的load()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| @Override public Response load(Uri uri, int networkPolicy) throws IOException { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { installCacheIfNeeded(context); } HttpURLConnection connection = openConnection(uri); connection.setUseCaches(true); if (networkPolicy != 0) { String headerValue; if (NetworkPolicy.isOfflineOnly(networkPolicy)) { headerValue = FORCE_CACHE; } else { StringBuilder builder = CACHE_HEADER_BUILDER.get(); builder.setLength(0); if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) { builder.append("no-cache"); } if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) { if (builder.length() > 0) { builder.append(','); } builder.append("no-store"); } headerValue = builder.toString(); } connection.setRequestProperty("Cache-Control", headerValue); } int responseCode = connection.getResponseCode(); if (responseCode >= 300) { connection.disconnect(); throw new ResponseException(responseCode + " " + connection.getResponseMessage(), networkPolicy, responseCode); } long contentLength = connection.getHeaderFieldInt("Content-Length", -1); boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE)); return new Response(connection.getInputStream(), fromCache, contentLength); }
|
使用的是Android默认的HttpURLConnection,读取网络图片流后,生成Response()返回。
分析到这里,图片已经从网络上下载下来了,再次回到BitmapHunter的run()方法。
1 2 3 4 5 6
| result = hunt(); if (result == null) { dispatcher.dispatchFailed(this); } else { dispatcher.dispatchComplete(this); }
|
hunt()成功返回之后会回调dispatcher.dispatchComplete()方法,看一下。
1 2 3
| void dispatchComplete(BitmapHunter hunter) { handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter)); }
|
到Handler的handlerMessage()方法中
1 2 3 4 5
| case HUNTER_COMPLETE: { BitmapHunter hunter = (BitmapHunter) msg.obj; dispatcher.performComplete(hunter); break; }
|
继续看下去:
1 2 3 4 5 6 7 8 9 10
| void performComplete(BitmapHunter hunter) { if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) { cache.set(hunter.getKey(), hunter.getResult()); } hunterMap.remove(hunter.getKey()); batch(hunter); if (hunter.getPicasso().loggingEnabled) { log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion"); } }
|
如果开启了内存缓存(默认是开启的),则会把该次请求结果存到内存中,同时在batch()方法中发出一个HUNTER_DELAY_NEXT_BATCH的消息。
1 2 3 4 5 6 7 8 9
| private void batch(BitmapHunter hunter) { if (hunter.isCancelled()) { return; } batch.add(hunter); if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) { handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY); } }
|
1 2 3 4
| case HUNTER_DELAY_NEXT_BATCH: { dispatcher.performBatchComplete(); break; }
|
performBatchComplete()方法:
1 2 3 4 5 6
| void performBatchComplete() { List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch); batch.clear(); mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy)); logBatch(copy); }
|
这里通过Handler由子线程向主线程通信。这个mainThreadHandler是在Dispatcher的构造中赋值的,而生成Dispatcher同样是在Picasso.Builder中:
1
| Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
|
看看这个HANDLER的handlerMessage()方法:
1 2 3 4 5 6 7 8 9 10
| case HUNTER_BATCH_COMPLETE: { @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj; for (int i = 0, n = batch.size(); i < n; i++) { BitmapHunter hunter = batch.get(i); hunter.picasso.complete(hunter); } break; }
|
继续看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| void complete(BitmapHunter hunter) { Action single = hunter.getAction(); List<Action> joined = hunter.getActions(); boolean hasMultiple = joined != null && !joined.isEmpty(); boolean shouldDeliver = single != null || hasMultiple; if (!shouldDeliver) { return; } Uri uri = hunter.getData().uri; Exception exception = hunter.getException(); Bitmap result = hunter.getResult(); LoadedFrom from = hunter.getLoadedFrom(); if (single != null) { deliverAction(result, from, single); } if (hasMultiple) { for (int i = 0, n = joined.size(); i < n; i++) { Action join = joined.get(i); deliverAction(result, from, join); } } if (listener != null && exception != null) { listener.onImageLoadFailed(this, uri, exception); } } private void deliverAction(Bitmap result, LoadedFrom from, Action action) { if (action.isCancelled()) { return; } if (!action.willReplay()) { targetToAction.remove(action.getTarget()); } if (result != null) { if (from == null) { throw new AssertionError("LoadedFrom cannot be null."); } action.complete(result, from); if (loggingEnabled) { log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from); } } else { action.error(); if (loggingEnabled) { log(OWNER_MAIN, VERB_ERRORED, action.request.logId()); } } }
|
会回调action.complete(result, from)方法,这里请注意,如果多个ImageView加载同一张图片时,这里要遍历Action,分别回调,第23行。
这里的Action是into()方法里生成的ImageViewAction:
1 2
| Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); picasso.enqueueAndSubmit(action);
|
看下它的complete方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Override public void complete(Bitmap result, Picasso.LoadedFrom from) { if (result == null) { throw new AssertionError( String.format("Attempted to complete action with no result!\n%s", this)); } ImageView target = this.target.get(); if (target == null) { return; } Context context = picasso.context; boolean indicatorsEnabled = picasso.indicatorsEnabled; PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled); if (callback != null) { callback.onSuccess(); } }
|
再看setBitmap()方法:
1 2 3 4 5 6 7 8 9 10
| static void setBitmap(ImageView target, Context context, Bitmap bitmap, Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) { Drawable placeholder = target.getDrawable(); if (placeholder instanceof AnimationDrawable) { ((AnimationDrawable) placeholder).stop(); } PicassoDrawable drawable = new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging); target.setImageDrawable(drawable); }
|
把Bitmap设置到ImageView上,到此,所有的流程走完。
总结
针对ListView的优化?
通过ImageView和Action绑定,当ImageView被复用时取消之前绑定的Action,绑定、加载新的Action,节省后台开销,防止图片显示错乱。ListView滑动期间可以调用pauseTag()暂停加载图片,不滑动的时候再调用resumeTag()开启加载,防止滑动丢帧。
如何处理多个ImageView加载同一个URL图片?
每个请求加载图片的BitmapHunter会维护一个ActionList,当同一个URI对应的BitmapHunter还在处理图片时,再来一个相同的URI,BitmapHunter会把该Action添加到内部的ActionList中去,等处理完图片后遍历ActionList,回调需要接收该URI的ImageView。
Picasso是如何处理硬盘缓存的?
Picasso其实并没有实现DiskCache,它是借助底层的网络库OkHttp或HttpUrlConnection对缓存的处理。Picasso保存的也是完整图片的缓存,即使ImageView设置的大小不同,图片缓存都是一样的,通过读取完整图片缓存后再对BitMap进行处理。
子线程和UI线程是如果通信的?
通过Handler机制。子线程请求、解析图片完成后,通过Handler向UI线程sendMessage,然后在handlerMessage()方法里告知ImageView。
Picasso使用的是全局的ApplicationContext,在有些插件化架构的APP中会有些问题,无法加载plugin中的静态资源图片,这里需要优化一下。
1 2 3 4 5 6
| public Builder(Context context) { if (context == null) { throw new IllegalArgumentException("Context must not be null."); } this.context = context.getApplicationContext(); }
|
对线程池的处理。
根据用户网络不同,动态创建线程数量。这里其实可以加入CPU核数,和网络类型一起控制线程数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| void adjustThreadCount(NetworkInfo info) { if (info == null || !info.isConnectedOrConnecting()) { setThreadCount(DEFAULT_THREAD_COUNT); return; } switch (info.getType()) { case ConnectivityManager.TYPE_WIFI: case ConnectivityManager.TYPE_WIMAX: case ConnectivityManager.TYPE_ETHERNET: setThreadCount(4); break; case ConnectivityManager.TYPE_MOBILE: switch (info.getSubtype()) { case TelephonyManager.NETWORK_TYPE_LTE: case TelephonyManager.NETWORK_TYPE_HSPAP: case TelephonyManager.NETWORK_TYPE_EHRPD: setThreadCount(3); break; case TelephonyManager.NETWORK_TYPE_UMTS: case TelephonyManager.NETWORK_TYPE_CDMA: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_EVDO_A: case TelephonyManager.NETWORK_TYPE_EVDO_B: setThreadCount(2); break; case TelephonyManager.NETWORK_TYPE_GPRS: case TelephonyManager.NETWORK_TYPE_EDGE: setThreadCount(1); break; default: setThreadCount(DEFAULT_THREAD_COUNT); } break; default: setThreadCount(DEFAULT_THREAD_COUNT); } }
|