From 3d0882ffa7857a7f6468b67ac0956f3dad7f93e0 Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Wed, 15 Apr 2015 16:27:27 +0300 Subject: [PATCH] Rewrote image-cache to use the native image caching features, i.e. LruCache and NSCache. --- apps/cuteness.io/reddit-item-view-model.ts | 13 ++++-- .../tests/ui/image-cache/image-cache-tests.ts | 12 +++--- ui/image-cache/image-cache-common.ts | 42 ++++++++---------- ui/image-cache/image-cache.android.ts | 43 +++++++++++++++---- ui/image-cache/image-cache.d.ts | 8 ++-- ui/image-cache/image-cache.ios.ts | 34 ++++++++++++--- 6 files changed, 101 insertions(+), 51 deletions(-) diff --git a/apps/cuteness.io/reddit-item-view-model.ts b/apps/cuteness.io/reddit-item-view-model.ts index 9004c30e1..3c36bc9ed 100644 --- a/apps/cuteness.io/reddit-item-view-model.ts +++ b/apps/cuteness.io/reddit-item-view-model.ts @@ -50,7 +50,11 @@ export class RedditViewModel extends observable.Observable { } else if (redditAppViewModel.cache) { var url = this._source.thumbnail; - var imgSource = redditAppViewModel.cache.get(url); + var imgSource: imageSource.ImageSource; + var image = redditAppViewModel.cache.get(url); + if (image) { + imgSource = imageSource.fromNativeSource(image); + } if (imgSource) { this._thumbnailImageSource = imgSource; @@ -61,11 +65,12 @@ export class RedditViewModel extends observable.Observable { redditAppViewModel.cache.push({ key: url, url: url, - completed: (result: imageSource.ImageSource, key: string) => { + completed: (image: any, key: string) => { if (url === key) { this.isLoading = false; - this._thumbnailImageSource = result; - this.notify({ object: this, eventName: observable.knownEvents.propertyChange, propertyName: THUMBNAIL_IMAGE_SOURCE, value: result }); + var imgSource = imageSource.fromNativeSource(image); + this._thumbnailImageSource = imgSource; + this.notify({ object: this, eventName: observable.knownEvents.propertyChange, propertyName: THUMBNAIL_IMAGE_SOURCE, value: imgSource }); } } }); diff --git a/apps/tests/ui/image-cache/image-cache-tests.ts b/apps/tests/ui/image-cache/image-cache-tests.ts index 5b937accd..faa239d70 100644 --- a/apps/tests/ui/image-cache/image-cache-tests.ts +++ b/apps/tests/ui/image-cache/image-cache-tests.ts @@ -19,22 +19,22 @@ export function test_DummyTestForSnippetOnly() { //// Enable download while not scrolling cache.enableDownload(); - var src: imageSource.ImageSource; + var imgSouce: imageSource.ImageSource; var url = "https://github.com/NativeScript.png"; //// Try to read the image from the cache - var result = cache.get(url); - if (result) { + var image = cache.get(url); + if (image) { //// If present -- use it. - src = result; + imgSouce = imageSource.fromNativeSource(image); } else { //// If not present -- request its download. cache.push({ key: url, url: url, - completed: (result: imageSource.ImageSource, key: string) => { + completed: (image: any, key: string) => { if (url === key) { - src = result; + imgSouce = imageSource.fromNativeSource(image); } } }); diff --git a/ui/image-cache/image-cache-common.ts b/ui/image-cache/image-cache-common.ts index 8c243a137..45ba2414b 100644 --- a/ui/image-cache/image-cache-common.ts +++ b/ui/image-cache/image-cache-common.ts @@ -9,7 +9,7 @@ export module knownEvents { export interface DownloadRequest { url: string; key: string; - completed?: (result: imageSource.ImageSource, key: string) => void; + completed?: (image: any, key: string) => void; } export class Cache extends observable.Observable implements definition.Cache { @@ -17,7 +17,6 @@ export class Cache extends observable.Observable implements definition.Cache { public maxRequests = 5; private _enabled = true; - private _cache = {}; private _pendingDownloads = {}; private _queue: Array = []; private _currentDownloads = 0; @@ -98,47 +97,42 @@ export class Cache extends observable.Observable implements definition.Cache { } } - public get(key: string): imageSource.ImageSource { - var value = this._cache[key]; - if (value) { - return value; - } - - return undefined; + public get(key: string): any { + // This method is intended to be overridden by the android and ios implementations + throw new Error("Abstract"); } - public set(key: string, source: imageSource.ImageSource): void { - this._cache[key] = source; + public set(key: string, image: any): void { + // This method is intended to be overridden by the android and ios implementations + throw new Error("Abstract"); } public remove(key: string): void { - delete this._cache[key]; + // This method is intended to be overridden by the android and ios implementations + throw new Error("Abstract"); } public clear() { - var keys = Object.keys(this._cache); - var i; - var length = keys.length; - - for (i = 0; i < length; i++) { - delete this._cache[keys[i]]; - } + // This method is intended to be overridden by the android and ios implementations + throw new Error("Abstract"); } /* tslint:disable:no-unused-variable */ public _downloadCore(request: definition.DownloadRequest) { // This method is intended to be overridden by the android and ios implementations + throw new Error("Abstract"); } /* tslint:enable:no-unused-variable */ - public _onDownloadCompleted(key: string, result: imageSource.ImageSource) { + public _onDownloadCompleted(key: string, image: any) { var request = this._pendingDownloads[key]; - this._cache[request.key] = result; + this.set(request.key, image); + this._currentDownloads--; if (request.completed) { - request.completed(result, request.key); + request.completed(image, request.key); } if (this.hasListeners(knownEvents.downloaded)) { @@ -146,7 +140,7 @@ export class Cache extends observable.Observable implements definition.Cache { eventName: knownEvents.downloaded, object: this, key: key, - image: result + image: image }); } @@ -156,7 +150,7 @@ export class Cache extends observable.Observable implements definition.Cache { } private _shouldDownload(request: definition.DownloadRequest, onTop: boolean): boolean { - if (request.key in this._cache || request.key in this._pendingDownloads) { + if (this.get(request.key) || request.key in this._pendingDownloads) { return false; } diff --git a/ui/image-cache/image-cache.android.ts b/ui/image-cache/image-cache.android.ts index 6615776b9..763446ceb 100644 --- a/ui/image-cache/image-cache.android.ts +++ b/ui/image-cache/image-cache.android.ts @@ -1,20 +1,38 @@ import common = require("ui/image-cache/image-cache-common"); -import imageSource = require("image-source"); module.exports.knownEvents = common.knownEvents; +class LruBitmapCache extends android.util.LruCache { + constructor(cacheSize: number) { + super(cacheSize); + return global.__native(this); + } + + protected sizeOf(key: string, bitmap: android.graphics.Bitmap): number { + // The cache size will be measured in kilobytes rather than + // number of items. + var result = Math.round(bitmap.getByteCount() / 1024); + return result; + } +}; + export class Cache extends common.Cache { private _callback: any; + private _cache: LruBitmapCache; constructor() { super(); + var maxMemory = java.lang.Runtime.getRuntime().maxMemory() / 1024; + var cacheSize = maxMemory / 8; + this._cache = new LruBitmapCache(cacheSize); + var that = new WeakRef(this); this._callback = new (com).tns.Async.CompleteCallback({ onComplete: function (result: any, context: any) { var instance = that.get(); if (instance) { - instance._onBitmapDownloaded(result, context); + instance._onDownloadCompleted(context, result) } } }); @@ -24,11 +42,20 @@ export class Cache extends common.Cache { (com).tns.Async.DownloadImage(request.url, this._callback, request.key); } - /* tslint:disable:no-unused-variable */ - private _onBitmapDownloaded(result: android.graphics.Bitmap, context: any) { - // as a context we are receiving the key of the request - var source = imageSource.fromNativeSource(result); - this._onDownloadCompleted(context, source); + public get(key: string): any { + var result = this._cache.get(key); + return result; + } + + public set(key: string, image: any): void { + this._cache.put(key, image); + } + + public remove(key: string): void { + this._cache.remove(key); + } + + public clear() { + this._cache.evictAll(); } - /* tslint:enable:no-unused-variable */ } \ No newline at end of file diff --git a/ui/image-cache/image-cache.d.ts b/ui/image-cache/image-cache.d.ts index ce69ca354..0ed9bff65 100644 --- a/ui/image-cache/image-cache.d.ts +++ b/ui/image-cache/image-cache.d.ts @@ -20,7 +20,7 @@ declare module "ui/image-cache" { /** * An optional function to be called when the download is complete. */ - completed?: (result: imageSource.ImageSource, key: string) => void; + completed?: (image: any, key: string) => void; } /** @@ -57,11 +57,11 @@ declare module "ui/image-cache" { /** * Gets the image for the specified key. May be undefined if the key is not present in the cache. */ - get(key: string): imageSource.ImageSource; + get(key: string): any; /** * Sets the image for the specified key. */ - set(key: string, source: imageSource.ImageSource): void; + set(key: string, image: any): void; /** * Removes the cache for the specified key. */ @@ -73,7 +73,7 @@ declare module "ui/image-cache" { //@private _downloadCore(request: DownloadRequest); - _onDownloadCompleted(key: string, result: imageSource.ImageSource); + _onDownloadCompleted(key: string, image: any); //@endprivate } diff --git a/ui/image-cache/image-cache.ios.ts b/ui/image-cache/image-cache.ios.ts index cfff270d1..e9dd60330 100644 --- a/ui/image-cache/image-cache.ios.ts +++ b/ui/image-cache/image-cache.ios.ts @@ -1,15 +1,39 @@ import common = require("ui/image-cache/image-cache-common"); -import imageSource = require("image-source"); +import httpRequest = require("http/http-request"); module.exports.knownEvents = common.knownEvents; export class Cache extends common.Cache { + private _cache: NSCache; + + constructor() { + super(); + + this._cache = new NSCache(); + } + public _downloadCore(request: common.DownloadRequest) { - // TODO: WeakRef? var that = this; - imageSource.fromUrl(request.url). - then(function (value) { - that._onDownloadCompleted(request.key, value); + httpRequest.request({ url: request.url, method: "GET" }) + .then(response => { + var image = UIImage.imageWithData(response.content.raw); + that._onDownloadCompleted(request.key, image); }); } + + public get(key: string): any { + return this._cache.objectForKey(key); + } + + public set(key: string, image: any): void { + this._cache.setObjectForKey(image, key); + } + + public remove(key: string): void { + this._cache.removeObjectForKey(key); + } + + public clear() { + this._cache.removeAllObjects(); + } } \ No newline at end of file