Good day everyone. I am writing an application for the site. The meaning is simple. From APi site in JSON format download all the materials. And then from the database we show the user. But the problem crept up from where I did not expect. When opening material with 1-10 pictures everything seems to be normal. But there are materials where there are a lot of images. It happens more than 50. And at this stage the application is closed due to a lack of device memory. Below is the Activity code in which the display should occur.

public class FullTextActivity extends ActionBarActivity implements Html.ImageGetter { TextView tv; WebView ww; TextView content; private final static String TAG = "TestImageGetter"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_full_text); ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); tv = (TextView)findViewById(R.id.title); tv.setText(getIntent().getStringExtra("title")); Spanned spanned = Html.fromHtml(getIntent().getStringExtra("content"), this, null); tv = (TextView) findViewById(R.id.content); tv.setText(spanned); } @Override public Drawable getDrawable(String source) { LevelListDrawable d = new LevelListDrawable(); Drawable empty = getResources().getDrawable(R.drawable.ic_launcher); d.addLevel(0, 0, empty); d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight()); new LoadImage().execute(source, d); return d; } class LoadImage extends AsyncTask<Object, Void, Bitmap> { private LevelListDrawable mDrawable; @Override protected Bitmap doInBackground(Object... params) { String sourceT = (String) params[0]; String source = sourceT.replace("../..", "http://mediananny.com"); mDrawable = (LevelListDrawable) params[1]; Log.d(TAG, "doInBackground " + source); try { InputStream is = new URL(source).openStream(); return BitmapFactory.decodeStream(is); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Bitmap bitmap) { Log.d(TAG, "onPostExecute drawable " + mDrawable); Log.d(TAG, "onPostExecute bitmap " + bitmap); if (bitmap != null) { BitmapDrawable d = new BitmapDrawable(bitmap); mDrawable.addLevel(1, 1, d); mDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); mDrawable.setLevel(1); // i don't know yet a better way to refresh TextView // mTv.invalidate() doesn't work as expected CharSequence t = tv.getText(); tv.setText(t); } } } 

}

To understand all the depths of the tragedy I show what exactly should be displayed in the application http://mediananny.com/reportazhi/2309660/

and here is what I have from the API http://mediananny.com/api/category/?offset=0&category_id=83&debug=true

  • Maybe someone will tell a different way of implementation? The thought comes to use WebView. But there is a snag: there the address of the image is represented as ../../content/images_new/news/original/115746.jpg, i.e. without specifying a domain - Benya Michelson
  • WebView.loadDataWithBaseURL (String baseUrl, String data, String mimeType, String encoding, String historyUrl). Using this method, you can use relative links rather than complete ones. In baseUrl you need to pass the base URL where the pictures are - Vladyslav Matviienko

6 answers 6

I would do as follows:

  1. Pulled links to images. If offline work is not needed, then immediately to p.3
  2. I would download images to devices as files without reading them completely into memory and save, where necessary, the path to the image on the device.
  3. I would use one of the libraries for display:

They know how to load images as from the Internet, caching them, or directly from the device. And you will not need to worry about the memory eaten.

As for the display of all content directly in TextView , I would not do that. You write for a specific site, you can sharpen for it. Select key items to display and use ListView or RecyclerView .

A more detailed approach is described here.

  • So. Thanks to everyone who answered. Now the situation is cleared up. Last question. As a novice in this matter is not entirely clear. I managed to display this content in WebView. And even everything shows beautifully. How good is this solution? what are the pros and cons. And is it really worth the trouble and follow the same advice using libraries and not using a ready-made WebView - Benya Michelson
  • At first glance, the logic of working with WebView is simpler, because you need to generate html and that's it. But such an approach over time will become more difficult and more difficult to customize. You will not be able to manage all the elements and memory. The speed of work will not be what you want. I did this and immediately took advantage of the option I proposed. - andreich

In WebView, display, and even better in ListView.

But there is a snag: there the address of the image is represented as ../../content/images_new/news/original/115746.jpg, i.e. without domain

Once TextView can display pictures then WebView can.

  • When using the standard HTML.ImageGet parser, I can change this line by removing the colon with slashes from the beginning and replacing them with the site address. And how in WebView to give the correct line. Moreover, there are many of them. - Benya Michelson
  • WebView can be fed just html file. - anber

The problem is known. Its root is in:

 BitmapFactory.decodeStream(is); 

Many bitmaps eat memory a lot. There used to be such a flag in BitmapFactory.Options inPurgeable=true , which allowed bitmaps to be uploaded to the disk in case of low memory. In the new versions it was deprived, but it seems there should be a replacement. Read carefully the dock for the BitmapFactory.Options class - there are many different parameters and flags with which you can adjust the settings for generating bitmaps.

Update

You can try to still apply the Universal-Image-Loader

  • If there are a lot of bitmaps at the same time, it will still fall with OOMe, then you need to change the approach in principle. - anber
  • see the answer update - Barmaley

When uploading images from outside, you should always scale them.
Code example:

 private Bitmap getBitmap(String path) { Uri uri = getImageUri(path); InputStream in = null; try { final int IMAGE_MAX_SIZE = 1200000; // 1.2MP in = mContentResolver.openInputStream(uri); // Decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, o); in.close(); int scale = 1; while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) { scale++; } Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth + ", orig-height: " + o.outHeight); Bitmap b = null; in = mContentResolver.openInputStream(uri); if (scale > 1) { scale--; // scale to max possible inSampleSize that still yields an image // larger than target o = new BitmapFactory.Options(); o.inSampleSize = scale; b = BitmapFactory.decodeStream(in, null, o); // resize to desired dimensions int height = b.getHeight(); int width = b.getWidth(); Log.d(TAG, "1th scale operation dimenions - width: " + width + ", height: " + height); double y = Math.sqrt(IMAGE_MAX_SIZE / (((double) width) / height)); double x = (y / height) * width; Bitmap scaledBitmap = Bitmap.createScaledBitmap(b, (int) x, (int) y, true); b.recycle(); b = scaledBitmap; System.gc(); } else { b = BitmapFactory.decodeStream(in); } in.close(); Log.d(TAG, "bitmap size - width: " +b.getWidth() + ", height: " + b.getHeight()); return b; } catch (IOException e) { Log.e(TAG, e.getMessage(),e); return null; } 

taken from here

PS: it is better to use ready-made libraries in which it has already been decided (listed in the comments)

  • And it’s better not to make a bike but to use ImageLoader - github.com/nostra13/Android-Universal-Image-Loader or Glide - anber
  • or Picasso - DeKaNszn
  • I would be careful not to say so categorically: when downloading images from the outside, you always need to scale them - Barmaley
  • @Barmaley even a single picture with a huge resolution can be dropped in OOM. It is better to consider the external source of images untrusted by default. - DeKaNszn

See also the chapter Loading Large Bitmaps Efficiently . It describes how to calculate the BitmapOptions.inSampleSize parameter to save memory when decoding large images if you do not want to use ready-made libraries.

With 50 images in the same TextView, you will most likely end up in memory anyway. For good, you need to unload from memory images that are not on the screen. You can also install android:largeHeap , so that the application has more memory available.

    And so, hello to everyone again. Again the same problem arose. I once used the usual WebView. but now you need to display everything nicely in TextView. And therefore. Having read all the comments, I understood how to get the pictures. But I can not understand how to break the incoming text into pieces, which would organize the ListView. After all, as I understand it, you should get a certain list of "a piece of text - a picture - a piece of text"