Мое приложение - это приложение для чата Wifi, с помощью которого вы можете общаться между двумя устройствами Android с помощью текстовых сообщений, снимков с камеры и отправлять их.Изображения сохраняются на SD-карту.
Раньше было выброшено OutOfMemoryError
после нескольких отправленных изображений, но я решил эту проблему, отправив
options.inPurgeable = true;
и
options.inInputShareable = true;
в метод BitmapFactory.decodeByteArray.Это делает пиксели «освобождаемыми», чтобы новые изображения могли использовать память.Таким образом, ошибка больше не сохраняется.
Но внутренняя память по-прежнему заполнена изображениями и появляется предупреждение «Недостаточно места: в памяти телефона недостаточно места».Приложение больше не падает, но на телефоне больше нет памяти после его завершения.Я должен вручную очистить данные приложения в меню «Настройки»> «Приложения»> «Управление приложениями».
Я пытался перерабатывать растровые изображения и даже пытался явно очистить кэш приложения, но, похоже, он не выполняет то, что я ожидал.
Эта функция получает изображение через TCP-сокет, записывает его на SD-карту и запускает мой пользовательский Activity PictureView:
public void receivePicture(String fileName) {
try {
int fileSize = inStream.readInt();
Log.d("","fileSize:"+fileSize);
byte[] tempArray = new byte[200];
byte[] pictureByteArray = new byte[fileSize];
path = Prefs.getPath(this) + "/" + fileName;
File pictureFile = new File(path);
try {
if( !pictureFile.exists() ) {
pictureFile.getParentFile().mkdirs();
pictureFile.createNewFile();
}
} catch (IOException e) { Log.d("", "Recievepic - Kunde inte skapa fil.", e); }
int lastRead = 0, totalRead = 0;
while(lastRead != -1) {
if(totalRead >= fileSize - 200) {
lastRead = inStream.read(tempArray, 0, fileSize - totalRead);
System.arraycopy(tempArray, 0, pictureByteArray, totalRead, lastRead);
totalRead += lastRead;
break;
}
lastRead = inStream.read(tempArray);
System.arraycopy(tempArray, 0, pictureByteArray, totalRead, lastRead);
totalRead += lastRead;
}
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(pictureFile));
bos.write(pictureByteArray, 0, totalRead);
bos.flush();
bos.close();
bos = null;
tempArray = null;
pictureByteArray = null;
setSentence("<"+fileName+">", READER);
Log.d("","path:"+path);
try {
startActivity(new Intent(this, PictureView.class).putExtra("path", path));
} catch(Exception e) { e.printStackTrace(); }
}
catch(IOException e) { Log.d("","IOException:"+e); }
catch(Exception e) { Log.d("","Exception:"+e); }
}
Вот PictureView
.Он создает байт [] из файла на SD-карте, декодирует массив в растровое изображение, сжимает растровое изображение и записывает его обратно на SD-карту.Наконец, в Progress.onDismiss
изображение устанавливается как изображение полноэкранного изображения. Просмотр:
public class PictureView extends Activity {
private String fileName;
private ProgressDialog progress;
public ImageView view;
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Log.d("","onCreate() PictureView");
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
view = new ImageView(this);
setContentView(view);
progress = ProgressDialog.show(this, "", "Laddar bild...");
progress.setOnDismissListener(new OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
File file_ = getFileStreamPath(fileName);
Log.d("","SETIMAGE");
Uri uri = Uri.parse(file_.toString());
view.setImageURI(uri);
}
});
new Thread() { public void run() {
String path = getIntent().getStringExtra("path");
Log.d("","path:"+path);
File pictureFile = new File(path);
if(!pictureFile.exists())
finish();
fileName = path.substring(path.lastIndexOf('/') + 1);
Log.d("","fileName:"+fileName);
byte[] pictureArray = new byte[(int)pictureFile.length()];
try {
DataInputStream dis = new DataInputStream( new BufferedInputStream(
new FileInputStream(pictureFile)) );
for(int i=0; i < pictureArray.length; i++)
pictureArray[i] = dis.readByte();
} catch(Exception e) { Log.d("",""+e); e.printStackTrace(); }
/**
* Passing these options to decodeByteArray makes the pixels deallocatable
* if the memory runs out.
*/
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inInputShareable = true;
Bitmap pictureBM =
BitmapFactory.decodeByteArray(pictureArray, 0, pictureArray.length, options);
OutputStream out = null;
try {
out = openFileOutput(fileName, MODE_PRIVATE);
/**
* COMPRESS !!!!!
**/
pictureBM.compress(CompressFormat.PNG, 100, out);
pictureBM = null;
progress.dismiss(); }
catch (IOException e) { Log.e("test", "Failed to write bitmap", e); }
finally {
if (out != null)
try { out.close(); out = null; }
catch (IOException e) { }
} }
}.start();
}
@Override
protected void onStop() {
super.onStop();
Log.d("","ONSTOP()");
Drawable oldDrawable = view.getDrawable();
if( oldDrawable != null) {
((BitmapDrawable)oldDrawable).getBitmap().recycle();
oldDrawable = null;
Log.d("","recycle");
}
Editor editor =
this.getSharedPreferences("clear_cache", Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
}
}
Когда пользователь нажимает клавишу возврата, изображение больше не должно быть доступно изнутри.приложение.Просто хранится на SD-карте.
В onStop()
Я перезаписываю старое растровое изображение и даже пытаюсь очистить данные приложения.Тем не менее появляется предупреждение «Низкое пространство».Как я могу быть уверен, что изображения больше не будут выделять память, когда они не нужны?
РЕДАКТИРОВАТЬ: Кажется, проблема заключается в методе сжатия.Если все после сжатия комментируется, проблема остается.Если я удаляю компресс, проблема исчезает.Сжатие, кажется, выделяет память, которая никогда не освобождается, и составляет 2-3 МБ на изображение.