Skip to content

Commit

Permalink
Merge pull request #8 from ddfreiling/feat/prefetch
Browse files Browse the repository at this point in the history
Feature: Prefetching
  • Loading branch information
farfromrefug authored May 3, 2019
2 parents 4cff7dc + 1c515c7 commit 88ae88d
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 24 deletions.
3 changes: 2 additions & 1 deletion demo-angular/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import * as imageModule from 'nativescript-image';
import * as applicationModule from 'tns-core-modules/application';

if (applicationModule.android) {
applicationModule.on('launch', () => {
applicationModule.on(applicationModule.launchEvent, () => {
console.log('initialize pipeline');
imageModule.initialize();
});
}
Expand Down
30 changes: 18 additions & 12 deletions demo-angular/app/components/app.component.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
<ActionBar title="NativeScript-Image Angular sample app">
</ActionBar>
<GridLayout rows="*, auto, auto, auto, auto, auto">
<NSImg #imageView
verticalAlignment="center"
placeholderImageUri="res://ns_logo"
src="https://raw.githubusercontent.com/Akylas/nativescript-image/master/examples-data/breakfast1.jpg">
</NSImg>
<Button text="Set 1" row="1" (tap)="onSeOpacityTo1()"></Button>
<Button text="Set 0.5" row="2" (tap)="onSeOpacityTo05()"></Button>
<Button text="Set 0.1" row="3" (tap)="onSeOpacityTo01()"></Button>
<Button text="Animate to 0.1" row="4" (tap)="onAnimateTo01()"></Button>
<Button text="Animate to 1" row="5" (tap)="onAnimateTo1()"></Button>
</GridLayout>
<ScrollView>
<GridLayout rows="*, auto, auto, auto, auto, auto, auto, auto">
<GridLayout rows="*" columns="*" *ngIf="showImage | async">
<NSImg #imageView
verticalAlignment="center"
placeholderImageUri="res://ns_logo"
src="https://raw.githubusercontent.com/Akylas/nativescript-image/master/examples-data/breakfast1.jpg">
</NSImg>
</GridLayout>
<Button text="Set 1" row="1" (tap)="onSeOpacityTo1()"></Button>
<Button text="Set 0.5" row="2" (tap)="onSeOpacityTo05()"></Button>
<Button text="Set 0.1" row="3" (tap)="onSeOpacityTo01()"></Button>
<Button text="Animate to 0.1" row="4" (tap)="onAnimateTo01()"></Button>
<Button text="Animate to 1" row="5" (tap)="onAnimateTo1()"></Button>
<Button text="Prefetch to Disk" row="6" (tap)="prefetchToDisk()"></Button>
<Button text="Prefetch to Memory" row="7" (tap)="prefetchToMemory()"></Button>
</GridLayout>
</ScrollView>
41 changes: 41 additions & 0 deletions demo-angular/app/components/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import * as imageModule from 'nativescript-image';

// tslint:disable:no-console

const uri = 'https://www.dccomics.com/sites/default/files/BM_Cv44_A_gallery_5abd2134f11d95.39493991.jpg';

@Component({
selector: 'my-app',
Expand Down Expand Up @@ -37,4 +42,40 @@ export class AppComponent implements OnInit {
duration: 500
});
}

prefetchToDisk() {
const pipeline = imageModule.getImagePipeline();
pipeline.evictFromCache(uri);

console.time('prefetchToDisk');
pipeline
.prefetchToDiskCache(uri)
.then(() => {
console.timeEnd('prefetchToDisk');
console.log('in memory cache?', pipeline.isInBitmapMemoryCache(uri));
console.log('in disk cache?', pipeline.isInDiskCache(uri));
})
.catch((err) => {
console.log('failed!', err);
});

pipeline.evictFromCache(uri);
}

prefetchToMemory() {
const pipeline = imageModule.getImagePipeline();
pipeline.evictFromCache(uri);

console.time('prefetchToMemory');
pipeline
.prefetchToMemoryCache(uri)
.then(() => {
console.timeEnd('prefetchToMemory');
console.log('in memory cache?', pipeline.isInBitmapMemoryCache(uri));
console.log('in disk cache?', pipeline.isInDiskCache(uri));
})
.catch((err) => {
console.log('failed!', err);
});
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
"ngc": "node --max-old-space-size=8192 ./node_modules/.bin/ngc",
"clean": "rimraf ./plugin/**/*.d.ts ./plugin/**/*.js ./plugin/**/*.js.map plugin/node_modules plugin/package-lock.json plugin/README.md demo/node_modules demo-angular/node_modules",
"tsc": "tsc -skipLibCheck && cpy '**/*.d.ts' '../plugin' --parents --cwd=src",
"build": "npm i && npm run tsc && npm run ngc && cp README.md",
"build": "npm i && npm run tsc && npm run ngc && cp README.md ./plugin",
"tslint": "tslint \"**/*.ts\" --config tslint.json --exclude \"**/node_modules/**\" --exclude '**/platforms/**'",
"plugin.tscwatch": "tsc -skipLibCheck -w",
"demo.android": "npm run build && cd demo && tns run android --syncAllFiles",
"demo.android.watch": "npm run build && npm run plugin.tscwatch && cd demo && tns run android --syncAllFiles",
"demo.reset": "cd ../demo && rimraf platforms",
"demo.ng.android": "npm run build && cd demo-angular && tns run android --syncAllFiles",
"demo.ng.android.watch": "pm run build && npm run plugin.tscwatch && cd demo-angular && tns run android --syncAllFiles",
"demo.ng.android.watch": "npm run build && npm run plugin.tscwatch && cd demo-angular && tns run android --syncAllFiles",
"demo.ng.reset": "cd ../demo-angular && rimraf platforms",
"plugin.prepare": "npm run build && cd demo-angular && tns plugin remove nativescript-image && tns plugin add ../plugin && cd ../demo && tns plugin remove nativescript-image && tns plugin add ../plugin",
"publish": "npm run build && lerna publish",
Expand Down
74 changes: 68 additions & 6 deletions src/image.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import * as imageSource from 'tns-core-modules/image-source';
import * as fs from 'tns-core-modules/file-system';
import { Color } from 'tns-core-modules/color/color';

let BaseDataSubscriber: new (onNewResult: () => void, onFailure: () => void) => com.facebook.datasource.BaseDataSubscriber<any>;

export function initialize(config?: ImagePipelineConfigSetting): void {
if (application.android) {
if (config && config.isDownsampleEnabled) {
Expand All @@ -17,6 +19,7 @@ export function initialize(config?: ImagePipelineConfigSetting): void {
} else {
com.facebook.drawee.backends.pipeline.Fresco.initialize(application.android.context);
}
initializeBaseDataSubscriber();
}
}

Expand All @@ -38,12 +41,42 @@ export function shutDown(): void {
com.facebook.drawee.backends.pipeline.Fresco.shutDown();
}

function initializeBaseDataSubscriber() {
if (BaseDataSubscriber) {
return;
}
class BaseDataSubscriberImpl extends com.facebook.datasource.BaseDataSubscriber<any> {
private _onNewResult: () => void;
private _onFailure: () => void;
constructor(onNewResult: () => void, onFailure: () => void) {
super();
this._onNewResult = onNewResult;
this._onFailure = onFailure;
return global.__native(this);
}
public onNewResultImpl(_dataSource: com.facebook.datasource.DataSource<any>): void {
// Store image ref to be released later.
//const mCloseableImageRef = _dataSource.getResult();
if (this._onNewResult) {
this._onNewResult();
}
}

public onFailureImpl(_dataSource: com.facebook.datasource.DataSource<any>): void {
if (this._onFailure) {
this._onFailure();
}
}
};
BaseDataSubscriber = BaseDataSubscriberImpl;
}

function getUri(src: string) {
let uri;
let uri: android.net.Uri;
if (utils.isFileOrResourcePath(src)) {
const res = utils.ad.getApplicationContext().getResources();
if (!res) {
return;
return null;
}

if (src.indexOf(utils.RESOURCE_PREFIX) === 0) {
Expand All @@ -69,7 +102,7 @@ function getUri(src: string) {
export class ImagePipeline {
private _android: com.facebook.imagepipeline.core.ImagePipeline;

isInDiskCacheSync(uri: string) {
isInDiskCache(uri: string): boolean {
return this._android.isInDiskCacheSync(android.net.Uri.parse(uri));
}

Expand Down Expand Up @@ -101,6 +134,35 @@ export class ImagePipeline {
this._android.clearDiskCaches();
}

prefetchToDiskCache(uri: string): Promise<void> {
return this.prefetchToCache(uri, true);
}

prefetchToMemoryCache(uri: string): Promise<void> {
return this.prefetchToCache(uri, false);
}

private prefetchToCache(uri: string, toDiskCache: boolean): Promise<void> {
return new Promise((resolve, reject) => {
try {
const nativeUri = android.net.Uri.parse(uri);
const request = com.facebook.imagepipeline.request.ImageRequestBuilder.newBuilderWithSource(nativeUri).build();
let datasource: com.facebook.datasource.DataSource<java.lang.Void>;
if (toDiskCache) {
datasource = this._android.prefetchToDiskCache(request, null);
} else {
datasource = this._android.prefetchToBitmapCache(request, null);
}
datasource.subscribe(
new BaseDataSubscriber(resolve, reject),
com.facebook.common.executors.CallerThreadExecutor.getInstance()
);
} catch (error) {
reject(error);
}
});
}

get android(): any {
return this._android;
}
Expand Down Expand Up @@ -154,9 +216,9 @@ export class ImagePipeline {
}

export class ImageError implements ImageErrorBase {
private _stringValue;
private _message;
private _errorType;
private _stringValue: string;
private _message: string;
private _errorType: string;

constructor(throwable: java.lang.Throwable) {
this._message = throwable.getMessage();
Expand Down
12 changes: 11 additions & 1 deletion src/image.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ export class ImagePipeline {
/**
* Returns whether the image is stored in the disk cache.
*/
private isInDiskCacheSync(uri: string): boolean;
isInDiskCache(uri: string): boolean;

/**
* Removes all images with the specified Uri from memory cache.
Expand Down Expand Up @@ -387,6 +387,16 @@ export class ImagePipeline {
* Clear disk caches.
*/
clearDiskCaches(): void;

/**
* Prefetch to disk cache.
*/
prefetchToDiskCache(uri: string): Promise<void>;

/**
* Prefetch to memory cache.
*/
prefetchToMemoryCache(uri: string): Promise<void>;
}

/**
Expand Down
27 changes: 25 additions & 2 deletions src/image.ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ export class ImagePipeline {
this._ios = SDImageCache.sharedImageCache;
}

// Currently not available in 0.9.0+
isInDiskCacheSync(uri: string) {
isInDiskCache(uri: string): boolean {
return this._ios.diskImageDataExistsWithKey(uri);
}

Expand Down Expand Up @@ -200,6 +199,30 @@ export class ImagePipeline {
clearDiskCaches() {
this._ios.clearDiskOnCompletion(null);
}

prefetchToDiskCache(uri: string): Promise<void> {
return this.prefetchToCacheType(uri, SDImageCacheType.Disk);
}

prefetchToMemoryCache(uri: string): Promise<void> {
return this.prefetchToCacheType(uri, SDImageCacheType.Memory);
}

private prefetchToCacheType(uri: string, cacheType: SDImageCacheType): Promise<void> {
return new Promise((resolve, reject) => {
const context = NSMutableDictionary.alloc<string, any>().initWithCapacity(1);
context.setObjectForKey(cacheType, SDWebImageContextStoreCacheType);
SDWebImagePrefetcher.sharedImagePrefetcher.context = context;
SDWebImagePrefetcher.sharedImagePrefetcher.prefetchURLsProgressCompleted([getUri(uri)], null, (finished, skipped) => {
if (finished && !skipped) {
resolve();
} else {
reject(`prefetch failed for URI: ${uri}`);
}
});
});
}

get ios() {
return this._ios;
}
Expand Down

0 comments on commit 88ae88d

Please sign in to comment.