我开发了一个应用程序,它使用Android的Dream Service作为各种屏幕保护程序 - 它显示图像的幻灯片。这些图像以二进制格式存储在数据库中并被解码。我意识到这不是最好的方法,但考虑到这个应用程序的特定结构和目的,它是最现实的。此外,该类不会不断地访问数据库,也不会连续解码图像 - 它会在启动时执行此操作,然后关闭资源。
话虽如此,在屏幕保护程序运行一段时间之后,我偶尔会收到“应用程序已停止工作”消息,我认为该消息与内存不足错误有关。我发现这有点奇怪,因为据我所知,位图只被解码一次 - 当服务附加到窗口时。我不明白为什么当唯一的重复动作将位图加载到ImageView容器时会出现内存问题,当然不是我认为需要大量资源的东西。我查看了我的代码,但无法找到问题。
我做错了什么;如何阻止这些错误发生?
public class screenSaver extends DreamService {
XmlPullParser parser;
String storeImages = "";
// creates messages
public Bitmap drawText(Context c, int resource, String text) {
Resources resources = c.getResources();
Bitmap bitmap = BitmapFactory.decodeResource(resources, resource);
android.graphics.Bitmap.Config config = bitmap.getConfig();
if (config == null) {
config = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(config, true);
Canvas canvas = new Canvas(bitmap);
TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
float scale = resources.getDisplayMetrics().density;
paint.setColor(Color.BLACK);
paint.setTextSize(48 * scale);
int textWidth = canvas.getWidth() - (int) (16 * scale);
StaticLayout textLayout = new StaticLayout(text, paint, textWidth, Layout.Alignment.ALIGN_CENTER, 1f, 0f, false);
int textHeight = textLayout.getHeight();
float x = (bitmap.getWidth() - textWidth) / 2;
float y = (bitmap.getHeight() - textHeight) / 2;
canvas.save();
canvas.translate(x, y);
textLayout.draw(canvas);
canvas.restore();
return bitmap;
}
ArrayList<Bitmap> imageList = new ArrayList<Bitmap>();
int slideCounter = 0;
ImageView slide;
Cursor images;
Cursor corpImages;
final Handler handler = new Handler(Looper.getMainLooper());
private int counter = 0;
private Runnable runnable = new Runnable() {
@Override
public void run() {
slide.setImageBitmap(imageList.get(counter));
if (counter == (imageList.size() - 1)) {
counter = 0;
} else {
counter++;
}
}
};
public screenSaver() {
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
setInteractive(false);
setFullscreen(true);
setContentView(R.layout.screen_saver);
databaseHelper dbHelper = new databaseHelper(this);
Intent testIntent = new Intent(this, lockActivity.class);
testIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(testIntent); // unpin screen so screen saver can load
SQLiteDatabase db = dbHelper.getReadableDatabase();
SharedPreferences preferences = getSharedPreferences("config", MODE_PRIVATE);
final String store = preferences.getString("store", "");
String managerMessageText = "";
String mainMessageText = "";
String districtMessageText = "";
try {
FileInputStream input = new FileInputStream(new File(this.getFilesDir(), "stores.xml"));
parser = Xml.newPullParser();
parser.setInput(input, null);
// begin search for correct 'store' tag
boolean elementsRemain = true;
while (elementsRemain) {
parser.next();
int event = parser.getEventType();
switch (event) {
case XmlPullParser.START_TAG:
String name = parser.getName();
if (name.equals("store")) {
Log.i("Screen Saver", "entering if store");
String number = parser.getAttributeValue(null, "number");
if (number.equals(store)) {
// located corresponding store, beginning parsing to find associate images and messages
boolean withinStore = true;
while (withinStore) {
parser.next();
if (parser.getEventType() == XmlPullParser.START_TAG) {
String tag = parser.getName();
if (tag.equals("images")) {
parser.nextTag();
while (parser.getEventType() == XmlPullParser.START_TAG && parser.getName().equals("image")) {
if (parser.getAttributeValue(null, "id") != null && (!parser.getAttributeValue(null, "id").equals(""))) {
storeImages += parser.getAttributeValue(null, "id") + ",";
}
parser.nextTag();
if (parser.getEventType() == XmlPullParser.END_TAG) {
parser.nextTag();
}
}
}
parser.next();
if (parser.getEventType() == XmlPullParser.TEXT) {
switch (tag) {
case "message":
managerMessageText += parser.getText();
break;
case "district":
districtMessageText += parser.getText();
break;
case "corporate":
mainMessageText += parser.getText();
break;
default:
break;
}
}
} else if (parser.getEventType() == XmlPullParser.END_TAG && parser.getName().equals("store")) {
withinStore = false;
}
}
parser.next();
}
} else {
}
break;
case XmlPullParser.END_DOCUMENT:
elementsRemain = false;
break;
}
}
} catch (Exception e) {
Log.e("Error reading XML ", " " + e.getMessage());
}
/* LTO images
try {
File managerFile = new File(this.getFilesDir(), store + ".txt");
File universalFile = new File(this.getFilesDir(), "universal.txt");
File districtFile = new File(this.getFilesDir(), "district.txt");
BufferedReader reader = new BufferedReader(new FileReader(managerFile));
managerMessageText = reader.readLine();
reader = new BufferedReader(new FileReader(universalFile));
mainMessageText = reader.readLine();
reader = new BufferedReader(new FileReader(districtFile));
districtMessageText = reader.readLine();
} catch (Exception e) {
Log.e("Error opening file: ", e.getMessage());
}*/
/* images = db.rawQuery("SELECT " + databaseHelper.IMAGE + " FROM " + databaseHelper.TABLE_NAME + " where " + databaseHelper.LTO + " = 1", null);
images.moveToFirst();
while(!images.isAfterLast()) {
imageList.add(BitmapFactory.decodeByteArray(images.getBlob(images.getColumnIndex(databaseHelper.IMAGE)), 0, images.getBlob(images.getColumnIndex(databaseHelper.IMAGE)).length ));
images.moveToNext();
}
images.close(); */
if (storeImages.length() > 1) {
storeImages = storeImages.substring(0, storeImages.length() - 1); // remove trailing comma
}
// get all images that are associated with store
corpImages = db.rawQuery("SELECT " + databaseHelper.SLIDE_IMAGE + " FROM " + databaseHelper.SLIDE_TABLE + " WHERE " + databaseHelper.SLIDE_ID + " IN (" + storeImages + ")", null);
corpImages.moveToFirst();
while (!corpImages.isAfterLast()) {
imageList.add(BitmapFactory.decodeByteArray(corpImages.getBlob(corpImages.getColumnIndex(databaseHelper.SLIDE_IMAGE)), 0, corpImages.getBlob(corpImages.getColumnIndex(databaseHelper.SLIDE_IMAGE)).length));
corpImages.moveToNext();
}
corpImages.close();
db.close();
// begin drawing message bitmaps
if (managerMessageText != "") {
imageList.add(drawText(this, R.drawable.message_background, "Manager Message: \n" + managerMessageText));
}
if (mainMessageText != "") {
imageList.add(drawText(this, R.drawable.message_background, "Corporate Message: \n" + mainMessageText));
}
if (districtMessageText != "") {
imageList.add(drawText(this, R.drawable.message_background, "District Manager Message: \n" + districtMessageText));
}
slide = (ImageView) findViewById(R.id.slider);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
updateGUI();
}
}, 0, 8000);
}
;
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
// unpin screen so it can update
Intent testIntent = new Intent(this, lockActivity.class);
testIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(testIntent); // unpin screen so it can update
}
private void updateGUI() {
if (reminder.running || hourlyReminder.running) {
this.finish();
} else {
handler.post(runnable);
}
}
}
非常感谢任何指导。
答案 0 :(得分:0)
使用decodeResource()方法直接尝试为构造的位图&amp;分配内存。可以导致OutOfMemory。有效解码位图有几种选择。
将BitmapFactory.Options的inJustDecodeBounds设置为true可避免在解码步骤中进行内存分配。看来你没有使用这个选项。
当您只需显示缩小版/缩小版时,无需将完整图像/位图加载到内存中。您可以通过设置BitmapFactory.Options的inSampleSize来控制它。您似乎也没有使用此选项。
尝试使用:
options.inJustDecodeBounds = true;
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
等。解码位图以有效处理内存时的选项。
您可以在此处找到完整的教程:https://developer.android.com/training/displaying-bitmaps/load-bitmap.html