Android (Jelly Bean Only) - MapActivity 覆盖导致内存不足错误

标签 android memory overlay android-4.2-jelly-bean


更新(2.8.2013):感谢 Geobits 提供的位图类(class)!我不认为 PNG 是一种压缩格式(DOH 时刻之一):D

最后,尽管旧版本的 Android 和 Jelly Bean 之间仍然存在问题...这是我的 ICS 手机和 JellyBean 模拟器上堆的屏幕截图...ICS = <6MB,JB = >22MB 用于完全相同的应用程序,完全相同的图像!

(ICS 使用 <6MB 堆):
https://docs.google.com/file/d/0B7GW5mJ5fr3XSTFYWU54VjdUTEk/edit?usp=sharing

(JB 相同的应用程序 - 下面的应用程序 - 使用 >22MB):
https://docs.google.com/file/d/0B7GW5mJ5fr3XUGlCdHNMUGJJVFE/edit?usp=sharing

所以问题仍然存在……旧版本做了什么来防止 JB 没有的 OOM 情况?我如何在 Jelly Bean 中覆盖它而不将分辨率降低一吨(我必须降低到非常像素化的覆盖以避免 JB 中的 OOM)?


我在使用 MapActivity 和大型位图覆盖时遇到了 Jelly Bean 特定问题。在第一次运行 Jelly Bean 时查看堆占用了超过 22MB,而 ICS 占用了大约 6MB。在它增长后的每次运行中,根据您的位图的大小,它会给您一个 OOM 错误或只是有一个超大堆。

应用描述(这个示例应用的作用)

下面这不是我原来的应用程序...我用下面的代码制作了一个功能齐全的应用程序来复制问题并保持代码简单;这是一个具有父 Activity (Start.java) 和子 Activity (MainActivity.java) 的应用程序。 Start.java 只是一个带有按钮的屏幕,它将加载 MainActivity.java(我在时间上不遗余力;)。

MainActivity.java 将加载 map 并有一个大的叠加层。我的叠加层是来自 GoogleMaps 的纽约部分剪辑,我在 GIMP 中添加了一个过滤器,因此您可以分辨出它的不同之处。然后,此叠加层通过“topLeft”GeoPoint 和“bottomRight”GeoPoint“固定”到 map ,因此当您放大 map 时它会缩放。

位图大约有 7MB 大...所以 JellyBean 似乎加载了 3 倍(堆大约为 22MB)。观察堆,我看到它从 8MB 开始,然后迅速移动到 16MB 和 22MB。它也位于“res/drawable-nodpi”文件夹中。

我在以前的版本中看到了这个问题,“修复”是将位图放在“drawable-nodpi”中,这就是它存在的原因,但是对于 JellyBean,这个多重加载又回来了(旧问题链接:https://stackoverflow.com/questions/13906037/android-mapactivity-overlay-bitmap-not-relasing-memory-when-finish-executes )

我试过的东西 1) 尝试将位图放入所有可绘制文件夹(-ldpi、-mdpi、-hdpi、-xhdpi) 2) 我试过让叠加层更小……它确实远离 OOM 崩溃,但仍然加载 3 倍……而且由于我的其他更复杂的应用程序有其他更小的图像,这不是解决方案 3)我尝试创建一个“res/drawable”文件夹并将位图放入其中,它在第一次加载 MainActivity.java 时崩溃 - 似乎加载超过 3 倍) 4) 在 onDestroy() 方法中添加了一个 bitmap.recycle() - 这确实可以防止此示例应用出现 OOM 错误,但如前所述,我最需要修复的是占用 3x MB 的空间。

项目名称 - TestMapOverlayJB 完整代码 ZIP 存档(减去我的 MAP key - 您需要添加自己的) https://docs.google.com/file/d/0B7GW5mJ5fr3XY3h1MmFuREpoaDA/edit?usp=sharing

list

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testmapoverlayjb"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-permission android:name="android.permission.INTERNET"/>

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <uses-library android:name="com.google.android.maps"/>
        <activity
            android:name=".Start"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.NoTitleBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity" />
        <activity android:name=".MyBaseImageOverlay" />
    </application>
 </manifest>

开始.java:

package com.example.testmapoverlayjb;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class Start  extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.start);

    Button buttonMainMenuSettings = (Button) findViewById(R.id.buttonNY);
    buttonMainMenuSettings.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
         try {
                 Intent myIntent = new Intent(Start.this, MainActivity.class);
                 startActivity(myIntent);
         }
         catch (ActivityNotFoundException e) {
             Toast.makeText(getBaseContext(), "Activity Not Found", Toast.LENGTH_SHORT).show(); 
         }
    }});
    }
}

主要 Activity .java:

package com.example.testmapoverlayjb;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import android.os.Bundle;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;

public class MainActivity extends MapActivity {
    Bitmap bitmapMapOverlay;
    MyBaseImageOverlay baseLargeOverlay;
    MapView mapView;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mapView = (MapView) findViewById(R.id.mapview);
        mapView.setBuiltInZoomControls(true);


        GeoPoint topLeft = new GeoPoint((int)40723714, (int)-74029412);
        GeoPoint bottomRight = new GeoPoint((int)40704522, (int)-73976011);
        bitmapMapOverlay = BitmapFactory.decodeResource(getResources(), R.drawable.ny_base1);
        baseLargeOverlay = new MyBaseImageOverlay(topLeft, bottomRight, bitmapMapOverlay);
        mapView.getOverlays().add(baseLargeOverlay);
        MapController mapController = mapView.getController();
    mapController.setZoom(16);
        mapController.setCenter(new GeoPoint((int)40715029,(int)-74001975));
    mapView.setSatellite(true);
    mapView.setStreetView(false);
        mapView.postInvalidate();
    }

    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }

    @Override
    protected void onDestroy() {
        bitmapMapOverlay.recycle();
        baseLargeOverlay = null;
        mapView = null;
        super.onDestroy();
    }

}

MyBaseImageOverlay.java:

package com.example.testmapoverlayjb;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;

public class MyBaseImageOverlay extends Overlay {
    private Bitmap baseBitmap;
    GeoPoint topLeft;
    GeoPoint bottomRight;

    public MyBaseImageOverlay(GeoPoint topL, GeoPoint bottomR, Bitmap bmp) {
        baseBitmap = bmp;
        topLeft = topL;
        bottomRight = bottomR;
    }

    @Override
    public void draw(Canvas canvas, MapView mapView, boolean shadow) {
    if(shadow) {
           return;
        }
        super.draw(canvas, mapView, shadow);
        // convert bitmap's bounding box into pixels 
        Point top_left = new Point(); 
        mapView.getProjection().toPixels(topLeft, top_left); 
        Point bottom_right = new Point(); 
        mapView.getProjection().toPixels(bottomRight, bottom_right); 
        // Prepare two rectangles (pixels) 
        Rect src = new Rect( 0,0,baseBitmap.getWidth() - 1, baseBitmap.getHeight() - 1 ); 
        Rect dst = new Rect( top_left.x, top_left.y, bottom_right.x,bottom_right.y ); 

        // draw bitmap 
        canvas.drawBitmap(baseBitmap, src, dst, null); 
    }
}

开始.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<Button
    android:id="@+id/buttonNY"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="20dp"
    android:text="NY" />

</RelativeLayout>

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.maps.MapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mapview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true"
    android:apiKey="YOUR_MAP_KEY_HERE"
/>

叠加位图: https://docs.google.com/file/d/0B7GW5mJ5fr3XZFdWYnVBUW45bWM/edit?usp=sharing

最佳答案

问题是,您的位图不是 ~7MB 大,而是您的 PNG 有多大。它是压缩的(除非您使用的是 16bpp 色彩空间)。你的图像是 2800x1324,所以假设一个正常的 ARGB_8888 格式,它应该占用 ~15MB 未压缩。 7 用于加载 PNG,加上 15 用于未压缩的位图...达到 22。因此,在将图像解压缩到内存中的过程中,它将使用所有这些。

您提到了您尝试过的几项消除 OOM 错误的方法(较小的位图、回收),但没有解决“3x”问题。您可以尝试使用 Bitmap.Config.RGB_565 加载图像。这应该以牺牲质量为代价节省一些内存(总共应该只使用大约 15MB),但除此之外你不能做很多除了你已经尝试过的事情。

关于Android (Jelly Bean Only) - MapActivity 覆盖导致内存不足错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14761769/

相关文章:

android - 我在改造方面遇到问题

android - 为应用程序实现不同的 Material 主题。基于 Material Design 的猫头鹰应用

android - 如何更新 ListView 中不可见项目的某些 View ? [安卓]

android - 带有 colspan 和 rowSpan 的 GridView

Opencv如何在视频上叠加文字

c# - 当复选框控件的 Visible 变为 true 时,comctl32.dll 使用过多的内存

python - 该类的两个不同实例在内存中具有相同的列表地址

ios - CIContext、iOS 9 和内存问题

html - CSS:使 div 在另一个 div 上滑动(即,使用 div 作为背景)

iOS - 当我将方法移动到另一个类时,MKMapView 覆盖不会呈现