Android ANR 원리 분석
메 인 스 레 드 에 시간 이 걸 리 면 렉 이 걸 리 고 렉 이 밸브 값 을 초과 하여 ANR 을 촉발 할 수 있 습 니 다.프로 세 스 가 시 작 될 때 Zygote 는 Activity Thread 의 main 방법 을 반사 적 으로 호출 하여 loop 순환 을 시작 합 니 다.ActivityThread(api29)
public static void main(String[] args) {
Looper.prepareMainLooper();
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper 의 loop 방법:
// 。
public static void loop() {
for (;;) {
// 1、
Message msg = queue.next(); // might block
...
// This must be in a local variable, in case a UI event sets the logger
// 2、
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
// 3、
msg.target.dispatchMessage(msg);
...
// 4、
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
}
}
loop 에 for 순환 이 존재 합 니 다.메 인 스 레 드 는 장시간 운행 할 수 있 습 니 다.메 인 스 레 드 에서 작업 을 수행 할 때 Handler post 하나의 작업 을 통 해 메시지 대기 열 로 이동 할 수 있 습 니 다.loop 순환 으로 msg 를 가 져 와 msg 의 target(Handler)에 맡 길 수 있 습 니 다.두 곳 이 걸 릴 수 있 습 니 다.
@UnsupportedAppUsage
Message next() {
for (;;) {
// 1、nextPollTimeoutMillis 0
nativePollOnce(ptr, nextPollTimeoutMillis);
// 2、 ,
if (msg != null && msg.target == null) {
// 3、 , ,
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 4、 ,
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 5、 , 1,nativePollOnce -1,
// No more messages.
nextPollTimeoutMillis = -1;
}
}
}
Linux IO 다 중 재 활용 방안 은 select,poll,epoll 이 있 습 니 다.그 중에서 epoll 성능 이 가장 좋 고 병발 량 을 지원 합 니 다.
1、 , , 。
2、 , IO 。
3、 IO , 。
동기 화 장벽 메시지Android App 은 동기 화 메시지 장벽,Message Queue(api 29)코드 를 직접 호출 할 수 없습니다.
@TestApi
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
...
}
시스템 의 높 은 우선 순 위 는 동기 화 장벽 메시지 에 사 용 됩 니 다.예 를 들 어 View 가 그 릴 때 ViewRootImpl 의 scheduleTraversals 방법 은 동기 화 장벽 메 시 지 를 삽입 하고 그 려 진 후에 동기 화 장벽 메 시 지 를 제거 합 니 다.ViewRootImpl api29
@UnsupportedAppUsage
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
View 의 그리 기 과정 이 메 인 스 레 드 의 다른 작업 에 영향 을 받 지 않도록 View 는 그리 기 전에 Message Queue 에 동기 화 장벽 메 시 지 를 삽입 한 다음 에 Vsync 신호 감청 을 등록 합 니 다.Choreographer$FrameDisplayEventReceiver 감청 은 vsync 신호 리 셋 을 받 습 니 다.
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
Message msg = Message.obtain(mHandler, this);
// 1、
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
// 2、doFrame
doFrame(mTimestampNanos, mFrame);
}
}
Vsync 신호 리 셋 을 받 았 습 니 다.주석 1 은 메 인 스 레 드 Message Queue post 에 비동기 메 시 지 를 보 내 주석 2 의 doFrame 이 우선 실 행 될 수 있 도록 합 니 다.doFrame 이 야 말로 View 가 진정 으로 그리 기 시작 한 곳 입 니 다.ViewRootIml 의 doTraversal,permTraversals 를 호출 하고 permTraversals 에 서 는 View 의 onMeasure,onLayout,onDraw 를 호출 합 니 다.
app 에서 동기 화 장벽 메 시 지 를 보 낼 수 없 지만 비동기 메 시 지 를 사용 하 는 것 은 허용 합 니 다.
비동기 메시지 SDK 에 서 는 App 이 Message Queue,Message 클래스 에 비동기 메 시 지 를 게시 할 수 없 도록 제한 합 니 다.
@UnsupportedAppUsage
/*package*/ int flags;
비동기 메 시 지 를 신중하게 사용 하고 잘못 사용 하면 메 인 스 레 드 가사 가 발생 할 수 있 습 니 다.Handler#dispatchMessage
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
정지 감시
카드 감시 방안 1 Looper\#loop
// 。
public static void loop() {
for (;;) {
// 1、
Message msg = queue.next(); // might block
...
// This must be in a local variable, in case a UI event sets the logger
// 2、
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
// 3、
msg.target.dispatchMessage(msg);
...
// 4、
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
}
}
설명 2 와 4 의 logging.println 은 api 가 인 터 페 이 스 를 제공 합 니 다.Handler 를 감청 하 는 데 시간 이 걸 립 니 다.Looper.getMainLooper().setMessageLogging(printer)을 통 해 메 시 지 를 받 는 전후 시간 입 니 다.카드 가 들 리 면 dispatchMessage 호출 이 끝 났 고 스 택 에는 카드 코드 가 포함 되 어 있 지 않 습 니 다.정기 적 으로 메 인 스 택 을 가 져 옵 니 다.시간 은 key 이 고 스 택 정 보 는 value 입 니 다.map 에 저장 하면 카드 가 발생 합 니 다.카드 시간 내 스 택 을 꺼 내 면 됩 니 다.오프라인 으로 사용 하기 좋 습 니 다.
온라인 카드 모니터링 에는 바이트 코드 파일 삽입 기술 이 필요 하 다.
Gradle Plugin+ASM 을 통 해 컴 파일 기간 은 모든 방법 시작 과 끝 위치 에 각각 한 줄 의 코드 를 삽입 하여 시간 을 집계 합 니 다.예 를 들 어 위 챗 매트릭스 가 사용 하 는 카드 톤 모니터링 방안.질문 주의:
ANR 원리
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
ANR 트리거 프로 세 스폭탄 을 묻다
배경 sevice 호출:Context.startService-->AMS.startService-->ActiveService.startService-->ActiveService.realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
// 1、 delay (SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
try {
// 2、 AMS
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
}
}
설명 1 내부 호출 scheduleServiceTimeoutLocked
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
// delay , 20s, 200s
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
설명 2 는 AMS 가 서 비 스 를 시작 하기 전에 설명 1 은 handler 지연 메 시 지 를 보 내 고 20s 내(프론트 서비스)가 처리 되 지 않 으 면 ActiveServices\#serviceTimeout 이 호출 됩 니 다.폭탄 을 해체 하 다
서 비 스 를 시작 하려 면 먼저 AMS 관 리 를 거 친 다음 에 AMS 가 서 비 스 를 실행 하 는 생명 주 기 를 알려 주 고 Activity Thread 의 handlerCreateService 방법 이 호출 되 었 습 니 다.
@UnsupportedAppUsage
private void handleCreateService(CreateServiceData data) {
try {
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
// 1、service onCreate
service.onCreate();
mServices.put(data.token, service);
try {
// 2、
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
주석 1,Service 의 onCreate 방법 은 주석 2 를 호출 하고 AMS 의 serviceDoneExecuting 방법 을 호출 합 니 다.최종 적 으로 ActiveServices.serviceDoneExecutingLocked 를 호출 합 니 다.
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
// delay
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
}
onCreate 호출 후 delay 메 시 지 를 제거 하고 폭탄 을 철거 합 니 다.폭탄 을 터 뜨리 고 Service 의 onCreate 가 10s 를 초과 한다 고 가정 하면 폭탄 이 터 집 니 다.즉,ActiveServices\#serviceTimeout 방법 이 호출 됩 니 다.api29
void serviceTimeout(ProcessRecord proc) {
if (anrMessage != null) {
proc.appNotResponding(null, null, null, null, false, anrMessage);
}
}
모든 ANR,최종 적 으로 ProcessRecord 를 호출 하 는 apNotResponsing 방법 을 가 져 옵 니 다.api29
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
String parentShortComponentName, WindowProcessController parentProcess,
boolean aboveSystem, String annotation) {
// 1、 event log
// Log the ANR to the event log.
EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
annotation);
// 2、 log、anr、cpu , StringBuilder 。
// Log the ANR to the main log.
StringBuilder info = new StringBuilder();
info.setLength(0);
info.append("ANR in ").append(processName);
if (activityShortComponentName != null) {
info.append(" (").append(activityShortComponentName).append(")");
}
info.append("
");
info.append("PID: ").append(pid).append("
");
if (annotation != null) {
info.append("Reason: ").append(annotation).append("
");
}
if (parentShortComponentName != null
&& parentShortComponentName.equals(activityShortComponentName)) {
info.append("Parent: ").append(parentShortComponentName).append("
");
}
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
// 3、dump , java native ,
// For background ANRs, don't pass the ProcessCpuTracker to
// avoid spending 1/2 second collecting stats to rank lastPids.
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
(isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids,
nativePids);
String cpuInfo = null;
// 4、 ANR
Slog.e(TAG, info.toString());
if (tracesFile == null) {
// 5、 tracesFile, SIGNAL_QUIT
// There is no trace file, so dump (only) the alleged culprit's threads to the log
Process.sendSignal(pid, Process.SIGNAL_QUIT);
}
// 6、 drapbox
mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null);
synchronized (mService) {
// 7、 ANR,
if (isSilentAnr() && !isDebugging()) {
kill("bg anr", true);
return;
}
// 8、
// Set the app's notResponding state, and look up the errorReportReceiver
makeAppNotRespondingLocked(activityShortComponentName,
annotation != null ? "ANR " + annotation : "ANR", info.toString());
// 9、 ANR dialog, handleShowAnrUi
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
mService.mUiHandler.sendMessage(msg);
}
}
ANR , --》
Service,onCreate Handler 10s ,Service onCreate , 。
Service onCreate 10s, , ANR, cpu、 , ANR dialog
시스템 의 data/anr/trace.txt 파일 을 캡 처 하지만,고 버 전 시스템 은 루트 권한 이 있어 야 이 디 렉 터 리 를 읽 을 수 있 습 니 다.ANRWatchDog github.com/SalomonBrys…
ANR 소스 라 이브 러 리 자동 검색
이상 은 안 드 로 이 드 ANR 원리 분석의 상세 한 내용 입 니 다.더 많은 안 드 로 이 드 ANR 원리 에 관 한 자 료 는 우리 의 다른 관련 글 을 주목 하 세 요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.