Android:作为字符串传输的位图导致内存不足错误

时间:2016-02-09 14:35:45

标签: java android memory bitmap out-of-memory

我正在开发一个Android项目,我在其中加载来自我们服务器的图像,同时加载有关餐馆的其他信息。问题是,它需要花费大量时间来加载,每当我尝试再次加载信息或将移动设备从横向移动到potrait或反之亦然时,我就会出现内存错误。

当我在网上查看时,用户说的问题是因为位图。在服务器端,我将PNG's转换为字符串并传输它们,然后将字符串转换为位图然后显示。为什么整个加载时间大约4-5秒并且崩溃。谁能帮我。

Android代码:

餐馆列表活动:

 public class getListOfRestaurantsForUser extends AsyncTask<Double,Void,ResponseEntity<RestRestaurant[]>>{

        RestaurantList restaurantList = null;

        getListOfRestaurantsForUser(RestaurantList restaurantList){
            this.restaurantList = restaurantList;
        }


        @Override
        protected ResponseEntity<RestRestaurant[]> 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<?> requestEntity = new HttpEntity<Object>(requestHeaders);
            restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            return restTemplate.exchange(restaurantListURL+longitude+"/"+latitude, HttpMethod.GET, requestEntity, RestRestaurant[].class);
        }

        @Override
        protected void onPostExecute(ResponseEntity<RestRestaurant[]> responseEntity){
            RestRestaurant[] restRestaurantList = responseEntity.getBody();
            Collections.addAll(restosAsList, restRestaurantList);

            ArrayList<HashMap<String, String>> restaurantsArrayHashList = new ArrayList<>();
            for(RestRestaurant restRestaurant : restosAsList){
                HashMap<String, String> restDisplay = new HashMap<>();
                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<?> 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();
                }
            });
        }
    }

RestaurantList适配器:

 @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<String, String> 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 < 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<String, String> 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 < 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;

    }

服务器端代码:

@Override
public List<Restaurant> getNearbyRestaurants(double longitude, double latitude) {
    BASE64Encoder base64Encoder = new BASE64Encoder();
    final int R = 6371; // Radius of the earth
    List<Restaurant> restaurantList = this.listRestaurants();
    List<Restaurant> nearbyRestaurantList = new ArrayList<>();
    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 < 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<Restaurant>() {
        @Override
        public int compare(Restaurant o1, Restaurant o2) {
            if(o1.getDistanceFromUser() > o2.getDistanceFromUser()){
                return 1;
            }
            if(o1.getDistanceFromUser() < o2.getDistanceFromUser()){
                return -1;
            }
            return 0;
        }
    });

   return nearbyRestaurantList;
}

错误日志:

 Caused by: java.lang.OutOfMemoryError: Failed to allocate a 13176356 byte allocation with 370616 free bytes and 361KB until OOM
                                                                                    at java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:83)
                                                                                    at java.lang.StringBuilder.<init>(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)

如果需要更多信息,请告知我们。谢谢。

更新

Image retrieval method :

 @RequestMapping(value = "/restaurantimage/{identifier}")
    public HttpEntity<byte[]> 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<>(image, headers);
            } catch (IOException ignored) {
            }
   return null;
}

2 个答案:

答案 0 :(得分:2)

我不知道服务器端的要求是什么,但对于移动应用程序,分配给应用程序的内存非常有限。你可能发送的位图必须是几千字节,当它被接收时,你将它存储在String或字节数组中,然后将它写在一个字符串中。 这会占用应用程序的大量内存,也会产生性能问题。

作为替代方法,您可以在json字符串中提供服务器中图像的url,然后将该url传递给某些Imagehelper / Lazyloading库(如Volley)的方法。

答案 1 :(得分:0)

我终于能够通过从Picaso加载图像并从服务器端发送图像的URL来解决问题。

代码:

Picasso.with(view.getContext())

Picasso.load(StaticRestTemplate.baseURL+"restaurantimage/"+restaurantList.get(RestaurantList.restoProfilePicture))
                .config(Bitmap.Config.RGB_565)
                .fit()
                .centerInside()
                .into(restoImage);