I need to load images from my database, I store them in blobs just like android. Each image is represented by my costume URI. How can I work it out with Glide?
I want to benefit from Glide cache and fast loading.
Is there a proper way of doing this?
you can register customize ModelLoader class to Glide by calling Glide.get(context).register() method. and in your ModelLoader, you can tell Glide how to load image resources from your database by implement getResourceFetcher method and return a customize DataFetcher instance.
here's a example:
DBImageUri class:
public class DBImageUri {
private String uriString;
public DBImageUri(String uriString){
this.uriString = uriString;
}
#Override
public String toString(){
return uriString;
}
}
DBDataFetcher class:
public class DBDataFetcher implements DataFetcher<InputStream> {
private DBImageUri uri;
private int width;
private int height;
private InputStream stream;
public DBDataFetcher(DBImageUri uri, int width, int height){
this.uri = uri;
this.width = width;
this.height = height;
}
#Override
public InputStream loadData(Priority priority){
String uriString = this.uri.toString();
stream = //**load image based on uri, and return InputStream for this image. this is where you do the actual image from database loading process**;
return stream;
}
#Override
public String getId(){
//width & height should be ignored if you return same image resources for any resolution (return uri.toString();)
return uri.toString() + "_" + width + "_" + height;
}
#Override
public void cleanup(){
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignore
}
}
}
#Override
public void cancel(){
}
}
DBModelLoader class:
public class DBModelLoader implements ModelLoader<DBImageUri, InputStream> {
#Override
public DataFetcher<InputStream> getResourceFetcher(DBImageUri model, int width, int height){
return new DBDataFetcher(model, width, height);
}
public static class Factory implements ModelLoaderFactory<DBImageUri, InputStream>{
#Override
public ModelLoader<DBImageUri, InputStream> build(Context context, GenericLoaderFactory factories){
return new DBModelLoader();
}
#Override
public void teardown(){
}
}
}
and then you add ModelLoader to Glide registry by calling:
Glide.get(context).register(DBImageUri.class, InputStream.class, new DBModelLoader.Factory());
now you can load you database images:
Glide.with(context).load(new DBImageUri(/*your unique id string for database image*/)).into(imageview);
Related
I'm reading the s3 android guide and im really confused on how to download my files.
They provide this code:
TransferObserver observer = transferUtility.download(
MY_BUCKET, /* The bucket to download from */
OBJECT_KEY, /* The key for the object to download */
MY_FILE /* The file to download the object to */
);
So what is MY_FILE? am i suppose to make a local empty file object and supply it into that transferUtility download function and it fills that empty file to the one download?
And, when i finish getting the file, (particularly for images) how do i upload that file into an imageView using glide or Picasso?
I am not sure how to use the TransferObserver object.
Hope someone can provide a working example, please!
cheers!
Although I am quite late answering this question. Hope this helps someone who is stuck in this problem.
You don't need to make the bucket public. You can directly show the image via Glide. Here is my repo to load image from amazon s3 bucket via Glide.
https://github.com/jontyankit/Glide-Amazon-Image-Load
You need to override GlideModule and register our component
public class CustomGlideModule implements GlideModule {
#Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
#Override
public void registerComponents(Context context, Glide glide) {
glide.register(ImageModel.class, InputStream.class, new ImageLoader.Factory());
}
}
Make custom ModelLoader class. This class fetches the image on the basis of model described above instead of URL
public class ImageLoader implements ModelLoader<ImageModel, InputStream> {
private final ModelCache<ImageModel, ImageModel> mModelCache;
public ImageLoader(ModelCache<ImageModel, ImageModel> mModelCache) {
this.mModelCache = mModelCache;
}
#Override
public DataFetcher<InputStream> getResourceFetcher(ImageModel model, int width, int height) {
ImageModel imageModel = model;
if (mModelCache != null) {
imageModel = mModelCache.get(model, 0, 0);
if (imageModel == null) {
mModelCache.put(model, 0, 0, model);
imageModel = model;
}
}
return new ImageFetcher(imageModel);
}
public static class Factory implements ModelLoaderFactory<ImageModel, InputStream> {
private final ModelCache<ImageModel, ImageModel> mModelCache = new ModelCache<>(500);
#Override
public ModelLoader<ImageModel, InputStream> build(Context context, GenericLoaderFactory factories) {
return new ImageLoader(mModelCache);
}
#Override
public void teardown() {
}
}
}
And at last make custom class DataFetcher. public InputStream loadData(Priority priority) is the method which will download image from Amazon.
public class ImageFetcher implements DataFetcher<InputStream> {
private final ImageModel imageModel;
private InputStream mInputStream;
boolean downloadComplete = false;
int transferId = 0;
public ImageFetcher(ImageModel imageModel) {
this.imageModel = imageModel;
}
#Override
public InputStream loadData(Priority priority) throws Exception {
return fetchStream(imageModel);
}
private InputStream fetchStream(final ImageModel imageModel) {
TransferUtility transferUtility = AmazonClient.getClient().getTransferUtility();
TransferObserver bolomessages = transferUtility.download(imageModel.getBucketName(), imageModel.getId(), new File(imageModel.getLocalPath()));
transferId = bolomessages.getId();
bolomessages.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
Log.wtf("AWSS3", "onStateChanged = " + state);
if (state == TransferState.COMPLETED) {
File initialFile = new File(imageModel.getLocalPath());
try {
mInputStream = new FileInputStream(initialFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
downloadComplete = true;
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
}
#Override
public void onError(int id, Exception ex) {
// do something
Log.wtf("AWSS3", "onError");
ex.printStackTrace();
downloadComplete = true;
}
});
while (!downloadComplete){}
return mInputStream;
}
#Override
public void cleanup() {
if (mInputStream != null) {
try {
mInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
mInputStream = null;
}
}
}
#Override
public String getId() {
return imageModel.getId();
}
#Override
public void cancel() {
AmazonClient.getClient().getTransferUtility().cancel(transferId);
}
}
So I am able to use Glide or picasso to load the image using the url of the image in the s3 bucket. But you have to make the bucket public.
Here is how you upload the image:
Glide.with(getActivity().getBaseContext()).load("IMAGE URL FROM S3").centerCrop().into(cardImage);
And thanks to #KNeerajLal here is how you can make your bucket public.
Here:
making bucket public
I created a custom TextView to show and images on it. I have to download the images and then show them on the textview. So I thought that I can do my job with a AsyncTask class. On the doInBackground I download and save the image on the Internal storage (for offline mode), and on the onPostExecute I call a method which shows the images on the textview. But it doesn't work. I mean instead of showing the images it shows the img tags.
I also tried to get the status from the AsyncTask class on the onPostExecute method and it's RUNNING. Isn't that weird? I thought that it will be FINISHED. Am I missing something about AsyncTask class?
Below is addImages method, where I find the img tags from the text and store the data on a List, and call the DownLoadImage class which extends the AsyncTask.
private boolean add(final Context context, final Spannable spannable) {
path = context.getFilesDir();
Pattern refImgPattern = Pattern.compile("<img .+?\\/>");
hasChanges = false;
refImgMatcher = refImgPattern.matcher(spannable);
while (refImgMatcher.find()) {
set = true;
for (ImageSpan span : spannable.getSpans(refImgMatcher.start(), refImgMatcher.end(), ImageSpan.class)) {
if (spannable.getSpanStart(span) >= refImgMatcher.start()
&& spannable.getSpanEnd(span) <= refImgMatcher.end()
) {
spannable.removeSpan(span);
} else {
set = false;
break;
}
}
String imageUrl = spannable.subSequence(refImgMatcher.start(0), refImgMatcher.end(0)).toString().trim();
width = 0;
Pattern widthPattern = Pattern.compile("width=\"[0-9]+?\"");
Matcher widthMatcher = widthPattern.matcher(imageUrl);
if (widthMatcher.find()) {
String w = widthMatcher.group(0);
w = w.replaceAll("width=", "");
w = w.replaceAll("\"", "");
width = Integer.valueOf(w);
}
height = 0;
Pattern heightPattern = Pattern.compile("height=\"[0-9]+?\"");
Matcher heightMatcher = heightPattern.matcher(imageUrl);
if (heightMatcher.find()) {
String h = heightMatcher.group(0);
h = h.replaceAll("height=", "");
h = h.replaceAll("\"", "");
height = Integer.valueOf(h);
}
Pattern urlPattern = Pattern.compile("(http|ftp|https):\\/\\/([\\w_-]+(?:(?:\\.[\\w_ -]+)+))([\\w.,#?^=%&:\\/~+#-]*[\\w#?^=%&\\/~+#-])?");
Matcher urlMatcher = urlPattern.matcher(imageUrl);
if (urlMatcher.find())
imageUrl = urlMatcher.group(0);
imageName = siteData.getId() + "_" + imageUrl.substring(imageUrl.lastIndexOf("/") + 1, imageUrl.length());
images.add(new Image(imageUrl, imageName, width, height, refImgMatcher.start(0), refImgMatcher.end(0)));
}
if (images.size() > 0) {
for (final Image img : images) {
image = new File(path, img.name);
if (!image.exists()) {
new DownLoadImage(context, spannable, img).execute();
} else
addImages(spannable, context, img);
}
}
return hasChanges;
}
This is the addImages method where I replace the tags with images
private void addImages(Spannable spannable, Context context, Image im) {
image = new File(path, im.name);
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bitmap = BitmapFactory.decodeFile(image.getAbsolutePath(), bmOptions);
if (im.width > 0 && im.height > 0)
bitmap = Bitmap.createScaledBitmap(bitmap, im.width * 3, im.height * 3, true);
if (set) {
hasChanges = true;
spannable.setSpan(new ImageSpan(context, bitmap),
im.startIndex,
im.endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
and the DownLoadImage class
private class DownLoadImage extends AsyncTask<Void, Void, Void> {
private Connection connection = Connection.getInstance();
private Context context;
private Spannable spannable;
private Image image;
public DownLoadImage(Context context, Spannable spannable, Image image) {
this.spannable = spannable;
this.context = context;
this.image = image;
}
#Override
protected Void doInBackground(Void... params) {
try {
connection.openConnection(image.path, ConnectionType.GET, false, false, null);
Integer status = connection.getResponseCode();
if (status >= 200 && status < 300) {
InputStream inputStream = new BufferedInputStream(connection.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Actions.saveImage(context, bitmap, image.name);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
connection.closeConnection();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Log.i("status", this.getStatus().toString());
addImages(spannable, context, image);
}
}
***** EDIT *****
the getTextWithImages where I call the add method
private Spannable getTextWithImages(Context context, CharSequence text) {
images = new ArrayList<>();
Spannable spannable = spannableFactory.newSpannable(text);
add(context, spannable);
return spannable;
}
and the setText method where I call the getTextWithImages
#Override
public void setText(CharSequence text, BufferType type) {
Spannable s = getTextWithImages(getContext(), text);
super.setText(s, BufferType.SPANNABLE);
}
You could create an interface that invokes a callback to the UI thread, instead of using the context. For example, in your AsyncTask:
private class DownLoadImage extends AsyncTask<Void, Void, Void> {
private Connection connection = Connection.getInstance();
private Context context;
private Spannable spannable;
private Image image;
private OnImageDownloadedListener mOnImageDownloadedListener;
...
#Override
protected Void doInBackground(Void... params) {
...
...
}
// Interface the task will use to communicate with your activity method.
public interface OnImageDownloadedListener {
void onImageDownloaded(Spannable spannable, Image image); // No need for context.
}
#Override
protected void onPostExecute(Void aVoid) {
if (mOnImageDownloadedListener != null) {
// If we set a listener, invoke it.
mOnImageDownloadedListener.onImageDownloaded(spannable, image);
}
}
// Setter.
public setOnImageDownloadedListener(OnImageDownloadedListener listener) {
mOnImageDownloadedListener = listener;
}
}
Then when you create your task try:
if (!image.exists()) {
// Create the task.
DownloadImage downloadTask = new DownLoadImage(context, spannable, img);
// Set your listener.
downloadTask.setOnImageDownloadedListener(new OnImageLoadedListener() {
#Override
public void onImageDownloaded(Spannable spannable, Image image) {
// Add the images.
addImages(spannable, **YourContextHere(Activity/etc)**.this, image)
}
});
// Execute.
downloadTask.execute();
} else
addImages(spannable, context, img);
Hope this helps.
I'm currently using Retrofit by Square for Android network communications. Is there a way to get its progress during a task to create a progress notification, something similar to that which Facebook uses when uploading an image?
Use Case would be to load an image hopefully of full image quality without compression or scaling.
I see how it is possible with an asynctask but that would defeat the purpose of using Retrofit. However that might be the route I would have to take.
This answer is for Retrofit 1. For solution compatible with Retrofit 2 see this answer.
I had the same problem and finally managed to do it. I was using spring lib before and what I show below kind worked for Spring but was inconsistent since I made a mistake on using it for the InputStream. I moved all my API's to use retrofit and upload was the last one on the list, I just override TypedFile writeTo() to update me on the bytes read to the OutputStream. Maybe this can be improved but as I said I made it when I was using Spring so I just reused it.
This is the code for upload and it's working for me on my app, if you want download feedback then you can use #Streaming and read the inputStream.
ProgressListener
public interface ProgressListener {
void transferred(long num);
}
CountingTypedFile
public class CountingTypedFile extends TypedFile {
private static final int BUFFER_SIZE = 4096;
private final ProgressListener listener;
public CountingTypedFile(String mimeType, File file, ProgressListener listener) {
super(mimeType, file);
this.listener = listener;
}
#Override public void writeTo(OutputStream out) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
FileInputStream in = new FileInputStream(super.file());
long total = 0;
try {
int read;
while ((read = in.read(buffer)) != -1) {
total += read;
this.listener.transferred(total);
out.write(buffer, 0, read);
}
} finally {
in.close();
}
}
}
MyApiService
public interface MyApiService {
#Multipart
#POST("/files")
ApiResult uploadFile(#Part("file") TypedFile resource, #Query("path") String path);
}
SendFileTask
private class SendFileTask extends AsyncTask<String, Integer, ApiResult> {
private ProgressListener listener;
private String filePath;
private FileType fileType;
public SendFileTask(String filePath, FileType fileType) {
this.filePath = filePath;
this.fileType = fileType;
}
#Override
protected ApiResult doInBackground(String... params) {
File file = new File(filePath);
totalSize = file.length();
Logger.d("Upload FileSize[%d]", totalSize);
listener = new ProgressListener() {
#Override
public void transferred(long num) {
publishProgress((int) ((num / (float) totalSize) * 100));
}
};
String _fileType = FileType.VIDEO.equals(fileType) ? "video/mp4" : (FileType.IMAGE.equals(fileType) ? "image/jpeg" : "*/*");
return MyRestAdapter.getService().uploadFile(new CountingTypedFile(_fileType, file, listener), "/Mobile Uploads");
}
#Override
protected void onProgressUpdate(Integer... values) {
Logger.d(String.format("progress[%d]", values[0]));
//do something with values[0], its the percentage so you can easily do
//progressBar.setProgress(values[0]);
}
}
The CountingTypedFile is just a copy of TypedFile but including the ProgressListener.
If you want to get the max value in order to show it on a ProgressDialog, Notification, etc.
ProgressListener
public interface ProgressListener {
void transferred(long num, long max);
}
CountingTypedFile
public class CountingTypedFile extends TypedFile {
private static final int BUFFER_SIZE = 4096;
private final ProgressListener listener;
public CountingTypedFile(String mimeType, File file, ProgressListener listener) {
super(mimeType, file);
this.listener = listener;
}
#Override
public void writeTo(OutputStream out) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
FileInputStream in = new FileInputStream(super.file());
long total = 0;
try {
int read;
while ((read = in.read(buffer)) != -1) {
total += read;
this.listener.transferred(total, super.file().length());
out.write(buffer, 0, read);
}
} finally {
in.close();
}
}
}
I am new to OSM and OSMdroid.
I was following this pretty good tutorial to show offline maps. So basically what I have done is:
Created a tile package in zip format with Mobile Atlas Creator
Used MapQuest source, JPEG format
Put the zip into the right folder: /mnt/sdcard/osmdroid/
The problem was the tiles were not rendered. I got a blank page.
I found this solution, to solve my problem.
But now, it is bothering me that I have to use PNG files, that takes significantly more space. It is not really efficient for my app because the user will have to download a much larger package.
MY QUESTION IS: How can I use JPEG tiles with OSMDroid and MapQuest?
Thanks in advance.
This works to get JPGs instead of PNGs:
MapView myOpenMapView;
myOpenMapView = (MapView) findViewById(R.id.openmapview);
myOpenMapView.setTileSource(new XYTileSource("MapquestOSM", ResourceProxy.string.mapquest_osm, 0, 18, 256, ".jpg", new String[] {
"http://otile1.mqcdn.com/tiles/1.0.0/map/", "http://otile2.mqcdn.com/tiles/1.0.0/map/", "http://otile3.mqcdn.com/tiles/1.0.0/map/",
"http://otile4.mqcdn.com/tiles/1.0.0/map/" }));
Notice ".jpg" in line 3.
I created a tile source that suppoort jpg, you can take a look and adapt your case,
Please note that getTileRelativeFilenameString won't contain .title ext. That part will be added by (MapTileFilesystemProvider)
import java.io.File;
import java.io.InputStream;
import java.util.Random;
import org.osmdroid.ResourceProxy;
import org.osmdroid.ResourceProxy.string;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.tilesource.BitmapTileSourceBase.LowMemoryException;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
public class MapTilerCustomDataSource implements ITileSource {
private static int globalOrdinal = 0;
private final int mMinimumZoomLevel;
private final int mMaximumZoomLevel;
private final int mOrdinal;
protected final String mName;
protected final String mImageFilenameEnding;
protected final Random random = new Random();
private final int mTileSizePixels;
private final string mResourceId;
public MapTilerCustomDataSource() {
mResourceId = null;
mOrdinal = globalOrdinal++;
mName = "MapquestOSM";
mMinimumZoomLevel = 0;
mMaximumZoomLevel = 20;
mTileSizePixels = 256;
mImageFilenameEnding = ".jpg";
}
#Override
public String getTileRelativeFilenameString(final MapTile tile) {
final StringBuilder sb = new StringBuilder();
sb.append(pathBase());
sb.append('/');
sb.append(tile.getZoomLevel());
sb.append('/');
sb.append(tile.getX());
sb.append('/');
sb.append(tile.getY());
sb.append(imageFilenameEnding());
return sb.toString();
}
#Override
public Drawable getDrawable(String aFilePath) throws LowMemoryException {
try {
// default implementation will load the file as a bitmap and create
// a BitmapDrawable from it
final Bitmap bitmap = BitmapFactory.decodeFile(aFilePath);
if (bitmap != null) {
return new BitmapDrawable(bitmap);
} else {
// if we couldn't load it then it's invalid - delete it
try {
new File(aFilePath).delete();
} catch (final Throwable e) {
}
}
} catch (final OutOfMemoryError e) {
System.gc();
}
return null;
}
#Override
public Drawable getDrawable(InputStream aFileInputStream) throws LowMemoryException {
try {
// default implementation will load the file as a bitmap and create
// a BitmapDrawable from it
final Bitmap bitmap = BitmapFactory.decodeStream(aFileInputStream);
if (bitmap != null) {
return new BitmapDrawable(bitmap);
}
} catch (final OutOfMemoryError e) {
System.gc();
}
return null;
}
#Override
public int ordinal() {
return mOrdinal;
}
#Override
public String name() {
return mName;
}
public String pathBase() {
return mName;
}
public String imageFilenameEnding() {
return mImageFilenameEnding;
}
#Override
public int getMinimumZoomLevel() {
return mMinimumZoomLevel;
}
#Override
public int getMaximumZoomLevel() {
return mMaximumZoomLevel;
}
#Override
public int getTileSizePixels() {
return mTileSizePixels;
}
#Override
public String localizedName(final ResourceProxy proxy) {
return proxy.getString(mResourceId);
}
}
Download 'xxx.JPG.tile' files and rename them to 'xxx.PNG.tile'.
I'm loading images in gridviev asynchronusly.But my gridview displaying only a single image in the last cell of gridview.My adapter class and asynchronus class is given below, thanks.
Adapter class:
class OrderAdapter extends ArrayAdapter<String>
{
LayoutInflater inflater;
String name3[];
public OrderAdapter(Context context,int resource,LayoutInflater inflater,String name2[])
{
super(context, resource,R.id.img,name2);
this.inflater=inflater;
this.name3=name2;
}
public View getView(int position, View convertView, ViewGroup parent)
{
View row=inflater.inflate(R.layout.row,parent,false);
final ImageView img=(ImageView)row.findViewById(R.id.img);
String imgurl=name3[position];
Log.e("urlchandan",name3[position]);
AsyncImageLoaderv asyncImageLoaderv=new AsyncImageLoaderv();
Bitmap cachedImage = asyncImageLoaderv.loadDrawable(imgurl, new AsyncImageLoaderv.ImageCallback()
{
public void imageLoaded(Bitmap imageDrawable, String imageUrl) {
img.setImageBitmap(imageDrawable);
}
});
img.setImageBitmap(cachedImage);
return row;
}
}
Asynchronous class
public class AsyncImageLoaderv {
private HashMap<String, SoftReference<Bitmap>> imageCache;
public AsyncImageLoaderv() {
imageCache = new HashMap<String, SoftReference<Bitmap>>();
}
public Bitmap loadDrawable(final String imageUrl, final ImageCallback imageCallback) {
if (imageCache.containsKey(imageUrl)) {
SoftReference<Bitmap> softReference = imageCache.get(imageUrl);
Bitmap drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
final Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
imageCallback.imageLoaded((Bitmap) message.obj, imageUrl);
}
};
new Thread() {
#Override
public void run() {
try{
Log.d("ur",imageUrl);
Bitmap drawable = loadImageFromUrl(imageUrl);
imageCache.put(imageUrl, new SoftReference<Bitmap>(drawable));
Message message = handler.obtainMessage(0, drawable);
handler.sendMessage(message);
}catch(Exception e){Log.e("thread stellent",e.toString());}
}
}.start();
return null;
}
public static Bitmap loadImageFromUrl(String url) {
InputStream inputStream;Bitmap b;
try {
inputStream = (InputStream) new URL(url).getContent();
BitmapFactory.Options bpo= new BitmapFactory.Options();
bpo.inSampleSize=2;
b=BitmapFactory.decodeStream(new PatchInputStream(inputStream), null,bpo );
return b;
} catch (IOException e) {
throw new RuntimeException(e);
}
//return null;
}
public interface ImageCallback {
public void imageLoaded(Bitmap imageBitmap, String imageUrl);
}
}
You can't do it the way you're trying. You need to have your asynchronous loader store the resulting image in some data structure your adapter can access by position (e.g. a list, a hashmap, whatever). Your getView() should then simply pull the image from the correct position. Your asynchronous loader will populate the data structure and perform a notifyDataSetChanged() to have the list redraw itself with the newly loaded image.
I got the solution by making the ImageView img in adatper inflater as final because it avoids
the images to display at a single cell in gridview . And my images was of big size and got the error decoder return false and this error is solved by taking another class
--
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class PatchInputStream extends FilterInputStream {
public PatchInputStream(InputStream in) {
super(in);
}
public long skip(long n) throws IOException {
long m = 0L;
while (m < n) {
long _m = in.skip(n-m);
if (_m == 0L) break;
m += _m;
}
return m;
}
}
this class is used in AsyncImageLoaderv given above .
b=BitmapFactory.decodeStream(new PatchInputStream(inputStream), null,bpo );