blob: 3002cd7684d97a17bcd32a4856dd76b70c722b95 [file] [log] [blame]
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.leakcanary;
import android.content.Context;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.widget.Toast;
import com.squareup.leakcanary.internal.FutureResult;
import java.io.File;
import static java.util.concurrent.TimeUnit.SECONDS;
public final class AndroidHeapDumper implements HeapDumper {
private final Context context;
private final LeakDirectoryProvider leakDirectoryProvider;
private final Handler mainHandler;
public AndroidHeapDumper(Context context, LeakDirectoryProvider leakDirectoryProvider) {
this.leakDirectoryProvider = leakDirectoryProvider;
this.context = context.getApplicationContext();
mainHandler = new Handler(Looper.getMainLooper());
}
@Override public File dumpHeap() {
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}
FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, SECONDS)) {
CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
return RETRY_LATER;
}
Toast toast = waitingForToast.get();
try {
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
return heapDumpFile;
} catch (Exception e) {
CanaryLog.d(e, "Could not dump heap");
// Abort heap dump
return RETRY_LATER;
}
}
private void showToast(final FutureResult<Toast> waitingForToast) {
mainHandler.post(new Runnable() {
@Override public void run() {
final Toast toast = new Toast(context);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
LayoutInflater inflater = LayoutInflater.from(context);
toast.setView(inflater.inflate(R.layout.leak_canary_heap_dump_toast, null));
toast.show();
// Waiting for Idle to make sure Toast gets rendered.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
waitingForToast.set(toast);
return false;
}
});
}
});
}
private void cancelToast(final Toast toast) {
mainHandler.post(new Runnable() {
@Override public void run() {
toast.cancel();
}
});
}
}