我的应用程序运行良好,直到我通过退出并启动应用程序多次,只要初始化过程尚未完成,在安装后首次启动时中断初始化过程。处理逻辑和AsyncTask可以很好地处理这个问题,所以我不会有任何不一致之处,但是我在堆中遇到问题。它越来越多,而我这样干扰退出并在应用程序设置时启动,这将导致OutOfMemory错误。我已经发现通过分析MAT堆的泄漏,但我仍然有另一个泄漏,我还无法分离。
有关背景信息:我将应用程序上下文,列表和时间戳存储在静态类中,以便能够从应用程序的任何位置的类访问它,而无需通过构造函数使用繁琐的传递引用。 无论如何,这个静态类(ApplicationContext)一定有什么问题,因为它会由于区域列表而导致内存泄漏。区域对象被处理GeoJSON数据。这是这个类的样子:需要帮助,以了解我的Android应用程序中的内存泄漏
public class ApplicationContext extends Application {
private static Context context;
private static String timestamp;
private static List<Zone> zones = new ArrayList<Zone>();
public void onCreate() {
super.onCreate();
ApplicationContext.context = getApplicationContext();
}
public static Context getAppContext() {
return ApplicationContext.context;
}
public static List<Zone> getZones() {
return zones;
}
public static void setData(String timestamp, List<Zone> zones) {
ApplicationContext.timestamp = timestamp;
ApplicationContext.zones = zones;
}
public static String getTimestamp() {
return timestamp;
}
}
我已经尝试过存储区域这样
ApplicationContext.zones = new ArrayList(zones);
,但它没有任何效果。我已经尝试将zones属性放入另一个静态类中,因为ApplicationContext在所有其他类(由于AndroidManifest中的条目)之前加载,这可能会导致此类行为,但这不是问题。
setData在我的“ProcessController”中被调用两次。一旦在doUpdateFromStorage中,并且一次在doUpdateFromUrl(String)中。这个类看起来是这样的:
public final class ProcessController {
private HttpClient httpClient = new HttpClient();
public final InitializationResult initializeData() {
String urlTimestamp;
try {
urlTimestamp = getTimestampDataFromUrl();
if (isModelEmpty()) {
if (storageFilesExist()) {
try {
String localTimestamp = getLocalTimestamp();
if (isStorageDataUpToDate(localTimestamp, urlTimestamp)) {
return doDataUpdateFromStorage();
}
else {
return doDataUpdateFromUrl(urlTimestamp);
}
}
catch (IOException e) {
return new InitializationResult(false, Errors.cannotReadTimestampFile());
}
}
else {
try {
createNewFiles();
return doDataUpdateFromUrl(urlTimestamp);
}
catch (IOException e) {
return new InitializationResult(false, Errors.fileCreationFailed());
}
}
}
else {
if (isApplicationContextDataUpToDate(urlTimestamp)) {
return new InitializationResult(true, "");
}
else {
return doDataUpdateFromUrl(urlTimestamp);
}
}
}
catch (IOException e1) {
return new InitializationResult(false, Errors.noTimestampConnection());
}
}
private String getTimestampDataFromUrl() throws IOException {
if (ProcessNotification.isCancelled()) {
throw new InterruptedIOException();
}
return httpClient.getDataFromUrl(FileType.TIMESTAMP);
}
private String getJsonDataFromUrl() throws IOException {
if (ProcessNotification.isCancelled()) {
throw new InterruptedIOException();
}
return httpClient.getDataFromUrl(FileType.JSONDATA);
}
private String getLocalTimestamp() throws IOException {
if (ProcessNotification.isCancelled()) {
throw new InterruptedIOException();
}
return PersistenceManager.getFileData(FileType.TIMESTAMP);
}
private List<Zone> getLocalJsonData() throws IOException, ParseException {
if (ProcessNotification.isCancelled()) {
throw new InterruptedIOException();
}
return JsonStringParser.parse(PersistenceManager.getFileData(FileType.JSONDATA));
}
private InitializationResult doDataUpdateFromStorage() throws InterruptedIOException {
if (ProcessNotification.isCancelled()) {
throw new InterruptedIOException();
}
try {
ApplicationContext.setData(getLocalTimestamp(), getLocalJsonData());
return new InitializationResult(true, "");
}
catch (IOException e) {
return new InitializationResult(false, Errors.cannotReadJsonFile());
}
catch (ParseException e) {
return new InitializationResult(false, Errors.parseError());
}
}
private InitializationResult doDataUpdateFromUrl(String urlTimestamp) throws InterruptedIOException {
if (ProcessNotification.isCancelled()) {
throw new InterruptedIOException();
}
String jsonData;
List<Zone> zones;
try {
jsonData = getJsonDataFromUrl();
zones = JsonStringParser.parse(jsonData);
try {
PersistenceManager.persist(jsonData, FileType.JSONDATA);
PersistenceManager.persist(urlTimestamp, FileType.TIMESTAMP);
ApplicationContext.setData(urlTimestamp, zones);
return new InitializationResult(true, "");
}
catch (IOException e) {
return new InitializationResult(false, Errors.filePersistError());
}
}
catch (IOException e) {
return new InitializationResult(false, Errors.noJsonConnection());
}
catch (ParseException e) {
return new InitializationResult(false, Errors.parseError());
}
}
private boolean isModelEmpty() {
if (ApplicationContext.getZones() == null || ApplicationContext.getZones().isEmpty()) {
return true;
}
return false;
}
private boolean isApplicationContextDataUpToDate(String urlTimestamp) {
if (ApplicationContext.getTimestamp() == null) {
return false;
}
String localTimestamp = ApplicationContext.getTimestamp();
if (!localTimestamp.equals(urlTimestamp)) {
return false;
}
return true;
}
private boolean isStorageDataUpToDate(String localTimestamp, String urlTimestamp) {
if (localTimestamp.equals(urlTimestamp)) {
return true;
}
return false;
}
private boolean storageFilesExist() {
return PersistenceManager.filesExist();
}
private void createNewFiles() throws IOException {
PersistenceManager.createNewFiles();
}
}
也许是另一种有用的信息,这ProcessController是我的MainActivity的的AsyncTask在应用程序设置调用:
public class InitializationTask extends AsyncTask<Void, Void, InitializationResult> {
private ProcessController processController = new ProcessController();
private ProgressDialog progressDialog;
private MainActivity mainActivity;
private final String TAG = this.getClass().getSimpleName();
public InitializationTask(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
ProcessNotification.setCancelled(false);
progressDialog = new ProgressDialog(mainActivity);
progressDialog.setMessage("Processing.\nPlease wait...");
progressDialog.setIndeterminate(true); //means that the "loading amount" is not measured.
progressDialog.setCancelable(true);
progressDialog.show();
};
@Override
protected InitializationResult doInBackground(Void... params) {
return processController.initializeData();
}
@Override
protected void onPostExecute(InitializationResult result) {
super.onPostExecute(result);
progressDialog.dismiss();
if (result.isValid()) {
mainActivity.finalizeSetup();
}
else {
AlertDialog.Builder dialog = new AlertDialog.Builder(mainActivity);
dialog.setTitle("Error on initialization");
dialog.setMessage(result.getReason());
dialog.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
mainActivity.finish();
}
});
dialog.show();
}
processController = null;
}
@Override
protected void onCancelled() {
super.onCancelled();
Log.i(TAG, "onCancelled executed");
Log.i(TAG, "set CancelNotification status to cancelled.");
ProcessNotification.setCancelled(true);
progressDialog.dismiss();
try {
Log.i(TAG, "clearing files");
PersistenceManager.clearFiles();
Log.i(TAG, "files cleared");
}
catch (IOException e) {
Log.e(TAG, "not able to clear files.");
}
processController = null;
mainActivity.finish();
}
}
这里是JSONParser的身体。 (更新:我设置的方法没有静态,但问题仍然存在。)我省略了对象的创建从JSON对象,因为我不认为这是错误:
public class JsonStringParser {
private static String TAG = JsonStringParser.class.getSimpleName();
public static synchronized List<Zone> parse(String jsonString) throws ParseException, InterruptedIOException {
JSONParser jsonParser = new JSONParser();
Log.i(TAG, "start parsing JSON String with length " + ((jsonString != null) ? jsonString.length() : "null"));
List<Zone> zones = new ArrayList<Zone>();
//does a lot of JSON parsing here
Log.i(TAG, "finished parsing JSON String");
jsonParser = null;
return zones;
}
}
这里是堆转储这说明了问题:
这是详细信息列表,显示这问题有事情做与数组列表。
任何想法,这里有什么错?顺便说一句:我不知道什么是其他泄漏,因为没有详细信息。
也许重要:该图显示了我不重新启动和停止应用程序时的状态。这是一个干净的开始图。但是当我多次启动和停止时,由于空间不足可能会导致问题。
下面是一个真正的崩溃图。我开始和停止,同时初始化多次应用:
[更新]
我通过存储在Android方面到我的ApplicationContext类,使PersistenceManager的非静态范围缩小一点。问题没有改变,所以我绝对相信这与我在全球存储Android上下文无关。它仍然是上图中的“问题可疑1”。所以我必须对这个巨大的列表做些什么,但是什么?我已经试图序列化它,但是非列化这个列表需要比20secs更长的时间,所以这不是一个选项。
现在我尝试了一些不同的东西。我踢出了整个ApplicationContext,所以我不再有任何静态引用。我试图在MainActivity中保存Zone对象的ArrayList。尽管我重构了至少需要让应用程序运行的部分,但是我甚至没有将Array或Activity传递给所需的所有类,但仍然存在相同的问题,所以我的猜测是区域对象本身在某种程度上是问题。或者我无法正确读取堆转储。看到下面的新图。这是一个简单的应用程序开始没有干扰的结果。
[更新]
我得出的结论是没有内存泄漏,因为“内存在一个实例积累”听起来不像泄漏。问题在于,一次又一次的启动和停止会启动新的AsyncTasks,如同在一张图上看到的,所以解决方案是不启动新的AsyncTask。我在SO上找到了一个可能的解决方案,但它对我来说还不适用。
“通过构造函数传递引用”是帮助避免这样的问题。老实说,以这种方式使用静态内存肯定是一种创建内存泄漏的方法,特别是对于上下文的静态引用。你需要显示你调用setData的地方以及你传递给它的内容。这可能会帮助您解决内存泄漏问题。 – Emile
@Emile好吧,我添加了额外的信息。 – Bevor
它可以是'私人MainActivity mainActivity;'由你的'AsyncTask'发生内存泄漏吗? 'AsyncTask'结束后,你是否试图将它设置为null? –