title: Android : Bitmaps transmitted as String causing out of memory errors
body: <p>I am working on an Android project in which I am loading images from our server while loading other information about restaurants. The problem is, it is taking a lot of time to load and whenever I try to load the information again or move the mobile from landscape to potrait or vice-versa, I get out of memory errors. </p>
  
  <p>As I checked on net, the problem users say is because of bitmaps. On the server-side, I am converting the <code>PNG's</code> to Strings and transmitting them, and the String is then converted to bitmap and then displayed. Why is the whole loading time around 4-5 seconds and crashes. Can anyone help me. </p>
  
  <p>Android code :</p>
  
  <p>RestaurantList Activity :</p>
  
  <pre><code> public class getListOfRestaurantsForUser extends AsyncTask&lt;Double,Void,ResponseEntity&lt;RestRestaurant[]&gt;&gt;{
  
          RestaurantList restaurantList = null;
  
          getListOfRestaurantsForUser(RestaurantList restaurantList){
              this.restaurantList = restaurantList;
          }
  
  
          @Override
          protected ResponseEntity&lt;RestRestaurant[]&gt; doInBackground(Double... doubles) {
              double longitude = doubles[0];
              double latitude = doubles[1];
              Log.d("Longitude",String.valueOf(longitude));
              final RestTemplate restTemplate = StaticRestTemplate.getRest();
              HttpHeaders requestHeaders = new HttpHeaders();
              requestHeaders.add("Cookie", "JSESSIONID=" + StaticRestTemplate.jsessionid);
              requestHeaders.setAccept(Collections.singletonList(new MediaType("application", "json")));
              HttpEntity&lt;?&gt; requestEntity = new HttpEntity&lt;Object&gt;(requestHeaders);
              restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
              return restTemplate.exchange(restaurantListURL+longitude+"/"+latitude, HttpMethod.GET, requestEntity, RestRestaurant[].class);
          }
  
          @Override
          protected void onPostExecute(ResponseEntity&lt;RestRestaurant[]&gt; responseEntity){
              RestRestaurant[] restRestaurantList = responseEntity.getBody();
              Collections.addAll(restosAsList, restRestaurantList);
  
              ArrayList&lt;HashMap&lt;String, String&gt;&gt; restaurantsArrayHashList = new ArrayList&lt;&gt;();
              for(RestRestaurant restRestaurant : restosAsList){
                  HashMap&lt;String, String&gt; restDisplay = new HashMap&lt;&gt;();
                  restDisplay.put(restaurantid, String.valueOf(restRestaurant.getRestaurantId()));
                  restDisplay.put(restaurantName, restRestaurant.getRestaurantName());
                  restDisplay.put(restaurantDistance, String.valueOf(restRestaurant.getDistanceFromUser()));
                  restDisplay.put(restaurantDetails,restRestaurant.getRestaurantDetails());
                  restDisplay.put(restoProfilePicture,restRestaurant.getProfilePicture());
                  restaurantsArrayHashList.add(restDisplay);
              }
  
              listView = (ListView) findViewById(R.id.restosList);
              restaurantListAdapter = new RestaurantListAdapter(restaurantList, restaurantsArrayHashList);
  
              listView.setAdapter(restaurantListAdapter);
  
              listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                  @Override
                  public void onItemClick(AdapterView&lt;?&gt; parent, View view,
                                          int position, long id) {
  
                      int restaurantId = restosAsList.get(position).getRestaurantId();
                      Intent intent = new Intent(RestaurantList.this, MenuCardList.class);
                      intent.putExtra("restaurantid", restaurantId);
                      startActivity(intent);
                      finish();
                  }
              });
          }
      }
  </code></pre>
  
  <p>RestaurantList adapter :</p>
  
  <pre><code> @Override
      public View getView(int position, View convertView, ViewGroup parent) {
          View view = convertView;
          if (convertView == null) {
              view = inflater.inflate(R.layout.individual_restaurant_detail, null);
              TextView restaurantName = (TextView) view.findViewById(R.id.resturantName);
  
              TextView distanceFromUser = (TextView) view.findViewById(R.id.distanceFromUser);
              TextView restaurantDetails = (TextView) view.findViewById(R.id.restaurantDetails);
              ImageView restoImage = (ImageView) view.findViewById(R.id.resturantImage);
              HashMap&lt;String, String&gt; restaurantList;
              restaurantList = data.get(position);
              restaurantName.setText(restaurantList.get(RestaurantList.restaurantName));
              restaurantName.setTypeface(Typeface.DEFAULT_BOLD);
              restaurantName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
              double distance = Double.valueOf(restaurantList.get(RestaurantList.restaurantDistance));
              if(distance &lt; 1000) {
                  distanceFromUser.setText("Entfernung " + restaurantList.get(RestaurantList.restaurantDistance)+"m");
              }else {
                  distanceFromUser.setText("Entfernung " + String.valueOf(distance/1000) +"km");
              }
              restaurantDetails.setText(restaurantList.get(RestaurantList.restaurantDetails));
              restoImage.setImageBitmap(convertByteArrayToBitmap(restaurantList.get(RestaurantList.restoProfilePicture)));
  
         }
          return view;
      } @Override
      public View getView(int position, View convertView, ViewGroup parent) {
          View view = convertView;
          if (convertView == null) {
              view = inflater.inflate(R.layout.individual_restaurant_detail, null);
              TextView restaurantName = (TextView) view.findViewById(R.id.resturantName);
  
              TextView distanceFromUser = (TextView) view.findViewById(R.id.distanceFromUser);
              TextView restaurantDetails = (TextView) view.findViewById(R.id.restaurantDetails);
              ImageView restoImage = (ImageView) view.findViewById(R.id.resturantImage);
              HashMap&lt;String, String&gt; restaurantList;
              restaurantList = data.get(position);
              restaurantName.setText(restaurantList.get(RestaurantList.restaurantName));
              restaurantName.setTypeface(Typeface.DEFAULT_BOLD);
              restaurantName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
              double distance = Double.valueOf(restaurantList.get(RestaurantList.restaurantDistance));
              if(distance &lt; 1000) {
                  distanceFromUser.setText("Entfernung " + restaurantList.get(RestaurantList.restaurantDistance)+"m");
              }else {
                  distanceFromUser.setText("Entfernung " + String.valueOf(distance/1000) +"km");
              }
              restaurantDetails.setText(restaurantList.get(RestaurantList.restaurantDetails));
              restoImage.setImageBitmap(convertByteArrayToBitmap(restaurantList.get(RestaurantList.restoProfilePicture)));
  
         }
          return view;
      }
  
    private Bitmap convertByteArrayToBitmap(String string){
          try {
              byte [] encodeByte= Base64.decode(string, Base64.DEFAULT);
              return BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);
          } catch (Exception ignored){}
          return null;
  
      }
  </code></pre>
  
  <p>Server side code :</p>
  
  <pre><code>@Override
  public List&lt;Restaurant&gt; getNearbyRestaurants(double longitude, double latitude) {
      BASE64Encoder base64Encoder = new BASE64Encoder();
      final int R = 6371; // Radius of the earth
      List&lt;Restaurant&gt; restaurantList = this.listRestaurants();
      List&lt;Restaurant&gt; nearbyRestaurantList = new ArrayList&lt;&gt;();
      for(Restaurant restaurant : restaurantList){
          Double latDistance = toRad(latitude-restaurant.getLatitude());
          Double lonDistance = toRad(longitude-restaurant.getLongitude());
          Double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) +
                  Math.cos(toRad(latitude)) * Math.cos(toRad(restaurant.getLatitude())) *
                          Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
          Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
          Double distance = R * c;
          restaurant.setDistanceFromUser(distance);
          if(distance &lt; 10){
              restaurant.setDistanceFromUser((restaurant.getDistanceFromUser()*1000));
              nearbyRestaurantList.add(restaurant);
          }
  
          String restaurantIdentifierImageString = this.restaurantImageService.getProfileIdentifierForRestaurant(restaurant.getRestaurantId());
  
          if(!(restaurantIdentifierImageString == null)){
  
             try {
                 try {
                    File imagePath = new File(restaurantImagePath + restaurantIdentifierImageString +".png");
                     BufferedImage bufferedImage = ImageIO.read(imagePath);
                     ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                     ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
                     restaurant.setProfilePicture( base64Encoder.encode(byteArrayOutputStream.toByteArray()));
  
                 } catch (Exception e) {
                     File imagePath = new File(defaultProfilePath);
                     BufferedImage bufferedImage = ImageIO.read(imagePath);
                     ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                     ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
                     restaurant.setProfilePicture(base64Encoder.encode(byteArrayOutputStream.toByteArray()));
                 }
             }catch (Exception e){
                 e.printStackTrace();
             }
  
          }
      }
  
      Collections.sort(nearbyRestaurantList, new Comparator&lt;Restaurant&gt;() {
          @Override
          public int compare(Restaurant o1, Restaurant o2) {
              if(o1.getDistanceFromUser() &gt; o2.getDistanceFromUser()){
                  return 1;
              }
              if(o1.getDistanceFromUser() &lt; o2.getDistanceFromUser()){
                  return -1;
              }
              return 0;
          }
      });
  
     return nearbyRestaurantList;
  }
  </code></pre>
  
  <p>Error log :</p>
  
  <pre><code> Caused by: java.lang.OutOfMemoryError: Failed to allocate a 13176356 byte allocation with 370616 free bytes and 361KB until OOM
                                                                                      at java.lang.AbstractStringBuilder.&lt;init&gt;(AbstractStringBuilder.java:83)
                                                                                      at java.lang.StringBuilder.&lt;init&gt;(StringBuilder.java:67)
                                                                                      at com.fasterxml.jackson.core.util.TextBuffer.contentsAsString(TextBuffer.java:346)
                                                                                      at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._finishAndReturnString(UTF8StreamJsonParser.java:2412)
                                                                                      at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.getText(UTF8StreamJsonParser.java:285)
                                                                                      at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:32)
  </code></pre>
  
  <p>If any more information is required, kindly let me know. Thank you.</p>
  
  <p><em>Update</em></p>
  
  <pre><code>Image retrieval method :
  
   @RequestMapping(value = "/restaurantimage/{identifier}")
      public HttpEntity&lt;byte[]&gt; getPhoto(@PathVariable String identifier) {
          boolean flag = false;
              try {
                  byte[] image;
                  try {
                      image = org.apache.commons.io.FileUtils.readFileToByteArray(new File(restaurantImagePath + identifier +".png"));
                  } catch (FileNotFoundException e) {
                      flag = true;
                      image = org.apache.commons.io.FileUtils.readFileToByteArray(new File(defaultProfilePath));
                      e.printStackTrace();
                  }
                  HttpHeaders headers = new HttpHeaders();
                  headers.setContentType(MediaType.IMAGE_PNG);
                  headers.setContentLength(image.length);
                  return new HttpEntity&lt;&gt;(image, headers);
              } catch (IOException ignored) {
              }
     return null;
  }
  </code></pre>