jQuery鼠标指针特效

关于进程的死亡处理流程

AMS 如何知道app死亡

参考目录:
进程管理之进程的创建
Android系统中的进程管理:内存的回收
AMS-kill Launcher进程的代码流程

在任何时候,应用进程都可能死亡,例如被OOM Killer或者LowMemoryKiller杀死,自身crash死亡又或者被用户手动杀死。
无论哪种情况,作为应用进程的管理者ActivityManagerService都需要知道。

在应用进程死亡之后,ActivityManagerService需要执行如下工作:

  • 执行清理工作 ActivityManagerService内部的ProcessRecord以及可能存在的四大组件的相关结构需要全部清理干净
  • 重新计算进程的优先级 上文已经提到过,进程的优先级是有关联性的,有其中一个进程死亡了,可能会连到影响到其他进程的优先级需要调整。

ActivityManagerService是利用Binder提供的死亡通知机制来进行进程的死亡处理的.

简单来说,死亡通知机制就提供了进程间的一种死亡监听的能力:当目标进程死亡的时候,监听回调会执行。
ActivityManagerService中的AppDeathRecipient监听了应用进程的死亡消息,该类代码如下:

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    private final class AppDeathRecipient implements IBinder.DeathRecipient {
        final ProcessRecord mApp;
        final int mPid;
        final IApplicationThread mAppThread;

        AppDeathRecipient(ProcessRecord app, int pid,
                IApplicationThread thread) {
            if (true) Slog.v(
                TAG, "New death recipient " + this
                 + " for thread " + thread.asBinder());
            mApp = app;
            mPid = pid;
            mAppThread = thread;
        }

        @Override
        public void binderDied() {
            if (true) Slog.v(
                TAG, "Death received in " + this
                + " for thread " + mAppThread.asBinder());
            synchronized(ActivityManagerService.this) {
                appDiedLocked(mApp, mPid, mAppThread, true, null);
            }
        }
    }

每一个应用进程在启动之后,都会attach到ActivityManagerService上通知它自己的进程已经启动完成了。
这时ActivityManagerService便会为其创建一个死亡通知的监听器。
在这之后如果进程死亡了,ActivityManagerService便会收到通知。

//attch 贴上,附上
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
        
        ...    
            
        final String processName = app.processName;
        try {
            AppDeathRecipient adr = new AppDeathRecipient(
                    app, pid, thread);
            thread.asBinder().linkToDeath(adr, 0);
            app.deathRecipient = adr;
        } catch (RemoteException e) {
            app.resetPackageList(mProcessStats);
            mProcessList.startProcessLocked(app,
                    new HostingRecord("link fail", processName),
                    ZYGOTE_POLICY_FLAG_EMPTY);
            return false;
        }
        ...
    
}

进程死亡之后的处理工作是appDiedLocked这个方法中处理的,可以参考[AMS-kill Launcher进程的代码流程]分析。

思路:利用Log.d(TAG,Log.getStackTraceString(new Throwable()))打印堆栈.

实际案例

问题: 类似于广告机,主要用于长时间使用/展示客户的某一个app.
其中有些客户遇到app自己crash或者ANR的情况,客户的app没办法重新启动展示。

如果一个一个去解决客户app的问题,那个花费的时间成本过于庞大。

思路一:写个Service,开机后启动,过5~10 min 检测客户app进程是否存活,如果死亡,就重新启动

思路二:利用AMS的AppDeathRecipient[app死亡通知器],在这里做逻辑处理,如同Launcher3和SystemUI,它俩被kill调,就会重新启动

+++ b/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3941,6 +3941,21 @@ public class ActivityManagerService extends IActivityManager.Stub

final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
            boolean fromBinderDied, String reason) {
      ...
         if (!hasMemcg()) {
             FrameworkStatsLog.write(FrameworkStatsLog.APP_DIED, SystemClock.elapsedRealtime());
         }
+        //add text start
+        if (app.killed && app.processName.equals("com.xxx")) {
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    Log.d(TAG, "xxxxxxxxxxxxx restart ....");
+                    Intent intent = mContext.getPackageManager().getLaunchIntentForPackage("com.xxx");
+                    if (intent != null) {
+                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        mContext.startActivity(intent);
+                    }
+                }
+            }, 3000 * 10);
+        }
+        //add text end
     }

某种情况不会走AppDeathRecipient**

systemui被杀后重启,从启动进程到attachApplicationLocked超过10s  执行processStartTimedOutLocked 导致再次被杀,由于没有执行到attach,不会死亡回调不再重启

03-12 17:33:11.113265  1235  1405 I am_kill : [0,4610,com.android.systemui,-800,Too many Binders sent to SYSTEM]
03-12 17:33:11.357911  1235  1392 I wm_set_keyguard_shown: [1,0,0,setKeyguardShown]

03-12 17:33:21.186292  1235  1405 W ActivityManager: Process ProcessRecord{b9dfae1 29414:com.android.systemui/u0a161} failed to attach
03-12 17:33:21.187587  1235  1405 W ActivityManager: Forcing bringing down service: ServiceRecord{bcb0cfb u0 com.android.systemui/.ImageWallpaper}
03-12 17:33:21.187749  1235  1405 W ActivityManager: Forcing bringing down service: ServiceRecord{bc9e032 u0 com.android.systemui/.dump.SystemUIAuxiliaryDumpService}
03-12 17:33:21.187809  1235  1405 W ActivityManager: Forcing bringing down service: ServiceRecord

//操作者主动移除app 后台日志,这个情况需要另外处理
ActivityManager system_server I  Killing 18419:com.android.settings/1000 (adj 1001): remove task

思考Launcher3/SystemUI 为什么被kill后重新启动?

  • Launcher
    当Launcher在栈顶时[用户可见,可互动],kill它之后会被重新启动--[AMS-kill Launcher进程的代码流程 -- Launcher在栈顶的情况]

如果它不在栈顶,kill 它,他不会被启动。但是如果其它app都退栈了,需要Launcher在栈顶时,它会被重新启动。

  • SystemUI

ActivityManager: Start proc 2988:com.android.systemui/u0a129 for restart com.android.systemui 谁打印的?

./frameworks/base/services/core/java/com/android/server/am/ProcessList.java

ActivityManagerService mService = null;

ProcessList::startProcessLocked() -->ProcessList::handleProcessStartedLocked(){
    ...
    checkSlow(app.startTime, "startProcess: building log message");
        StringBuilder buf = mStringBuilder;
        buf.setLength(0);
        buf.append("Start proc ");
        buf.append(pid);
        buf.append(':');
        buf.append(app.processName);
        buf.append('/');
        UserHandle.formatUid(buf, app.startUid);
        if (app.isolatedEntryPoint != null) {
            buf.append(" [");
            buf.append(app.isolatedEntryPoint);
            buf.append("]");
        }
        buf.append(" for ");
        buf.append(app.hostingRecord.getType());
        if (app.hostingRecord.getName() != null) {
            buf.append(" ");
            buf.append(app.hostingRecord.getName());
        }
        mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
    ...
}

//应用启动日志,for 后面接的原因,这个时被广播启动的
ActivityManager: Start proc 982:com.android.music/u0a29 for broadcast com.android.music/.MediaButtonIntentReceiver


//而kill systemui,它是被它自己重启了
ActivityManager: Start proc 2988:com.android.systemui/u0a129 for restart com.android.systemui

ActivityManager: Start proc表示应用进程第一次启动
ActivityTaskManager: Start u0 表示启动已存在堆栈中的应用

find ./frameworks/base/services/core/java/com/android/server -name '*.java' | xargs grep 'Start '

正在重启SystemUIService,它带动了systemui

ActivityManager         system_process   V  Death received in com.android.server.am.ActivityManagerService$AppDeathRecipient@fb6af5a for thread android.os.BinderProxy@3d2458b
ActivityManager         system_process   V  Death received in com.android.server.am.ActivityManagerService$AppDeathRecipient@9f93b04 for thread android.os.BinderProxy@83188ed
ActivityManager         system_process   I  Process com.android.systemui (pid 4101) has died: pers PER 
ActivityManager         system_process   W  Scheduling restart of crashed service com.android.systemui/.SystemUIService in 0ms for persistent
ActivityManager         system_process   W  Scheduling restart of crashed service com.android.systemui/.ImageWallpaper in 0ms for persistent
ActivityManager         system_process   W  Scheduling restart of crashed service com.android.systemui/.dump.SystemUIAuxiliaryDumpService in 0ms for persistent
ActivityManager         system_process   W  Scheduling restart of crashed service com.android.systemui/.keyguard.KeyguardService in 0ms for persistent
ActivityManager         system_process   W  Re-adding persistent process ProcessRecord{e65ac47 4101:com.android.systemui/u0a129}
ActivityManager         system_process   I  Killing 5302:com.android.packageinstaller/u0a76 (adj 985): empty #17
ActivityManager         system_process   D  java.lang.Throwable
                                                at com.android.server.am.ProcessList.handleProcessStartedLocked(ProcessList.java:2501)

从日志上看,...in 0ms for persistent,和清单文件中的persistent属性有关系.

  • 在系统刚起来的时候,该App也会被启动起来

  • 该App被强制杀掉后,系统会重启该App。这种情况只针对系统内置的App,第三方安装的App不会被重启.

/frameworks/base//services/core/java/com/android/server/am/ActivityManagerService.java

public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
    ...
    synchronized (this) {
            // Only start up encryption-aware persistent apps; once user is
            // unlocked we'll come back around and start unaware apps
            t.traceBegin("startPersistentApps");
            startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
            t.traceEnd();

            // Start up initial activity.
    ...
}

    void startPersistentApps(int matchFlags) {
        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;

        synchronized (this) {
            try {
                final List<ApplicationInfo> apps = AppGlobals.getPackageManager()
                        .getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
                for (ApplicationInfo app : apps) {
                    if (!"android".equals(app.packageName)) {
                        addAppLocked(app, null, false, null /* ABI override */,
                                ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
                    }
                }
            } catch (RemoteException ex) {
            }
        }
        
 final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
            boolean disableHiddenApiChecks, boolean disableTestApiChecks,
            boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) {
                ...
            //如果是系统App,且persistent属性为true,则异常死亡后会重启,调整app的adj    
            if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
                app.setPersistent(true);
                app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
            }
            //如果App已启动,则不处理,否则调用startProcessLocked方法启动App
            //启动App是异步的,因此会将正在启动,但还没启动完成的App添加到mPersistentStartingProcesses列表中,当启动完成后再移除
            if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
                mPersistentStartingProcesses.add(app);
                mProcessList.startProcessLocked(app, new HostingRecord("added application",
                        customProcess != null ? customProcess : app.processName),
                        zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,
                        mountExtStorageFull, abiOverride);
            }
                ...
            
            }       

在App启动完成后,会在ActivityThread中调用ActivityManagerService的attachApplicationLocked()方法,
将该App从mPersistentStartingProcesses移除,
并注册一个死亡讣告监听器AppDeathRecipient,用于在App异常被杀后的处理工作.

流程如下:

AppDeathRecipient::binderDied() --> AMS::appDiedLocked()-->AMS::handleAppDiedLocked()-->AMS::cleanUpApplicationRecordLocked()

 final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
            boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
            
                ...
        // If this is a precede instance of another process instance
        allowRestart = true;
        synchronized (app) {
            if (app.mSuccessor != null) {
                // We don't allow restart with this ProcessRecord now,
                // because we have created a new one already.
                allowRestart = false;
                // If it's persistent, add the successor to mPersistentStartingProcesses
                if (app.isPersistent() && !app.removed) {
                    if (mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {
                        mPersistentStartingProcesses.add(app.mSuccessor);
                    }
                }
                // clean up the field so the successor's proc starter could proceed.
                app.mSuccessor.mPrecedence = null;
                app.mSuccessor = null;
                // Notify if anyone is waiting for it.
                app.notifyAll();
            }
        }            
       ...
        if (!app.isPersistent() || app.isolated) {
            if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                    "Removing non-persistent process during cleanup: " + app);
            if (!replacingPid) {
                mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
            }
            mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
        } else if (!app.removed) {
            // This app is persistent, so we need to keep its record around.
            //这个应用程序是持久的,所以我们需要保留它的记录。
            // If it is not already on the pending app list, add it there and start a new process for it.
            //如果它还不在待处理的应用程序列表中,请将其添加到那里并为其启动新流程。
            if (mPersistentStartingProcesses.indexOf(app) < 0) {
                mPersistentStartingProcesses.add(app);
                restart = true;
            }
        }       
}

关于进程保活
谈谈Android中的persistent属性

如果跟service 有关系的对应日志,请看下面:

07-17 09:52:57.674  1022  1037 I ActivityManager: Process com.shan.mvvm (pid 13678) has died: prcp SVC 
07-17 09:52:57.675  1022  1037 W ActivityManager: Scheduling restart of crashed service com.shan.mvvm/.MyService in 1000ms for start-requested

Scheduling restart of crashed service解决方案与源码分析

思路一:写个Service...

思路二:利用AMS的AppDeathRecipient...

ActivityManager  system_server  V  Death received in com.android.server.am.ActivityManagerService$AppDeathRecipient@cac334 for thread android.os.BinderProxy@c07255d


在收到app死亡通知的地方,搞个延时计划处理它...

  Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //do it
            }
        },3*1000);

PS:List的remove()方法陷阱+性能优化

for循环遍历list(错误)

//通过索引获取元素进行判断后删除
for(int i=0;i<list.size();i++){
    if(list.get(i).equals("del"))
        list.remove(i);
}

删除某个元素后,list的大小发生了变化,而索引也在变化,所以会导致在遍历的时候漏掉某些元素,甚至会数组越界。
比如当删除第1个元素,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。(
索引在增加,大小在减小。左增右减同时变化)

因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。

使用迭代器方法(正确,推荐)

//正确,并且推荐的方法
Iterator<Integer> itr = list.iterator();
while(itr.hasNext()) {
 if(itr.next()%2 ==0)
  itr.remove();
}

迭代器可以正常的循环及删除。但要注意的是,需要使用iterator的remove方法。
如果用list的remove方法同样会报ConcurrentModificationException错误。

使用for循环,倒序进行;(正确)

//正确
for(int i=list.size()-1;i>=0;i--) {
 if(list.get(i)%2==0) {
  list.remove(i);
 }
}

List的remove()方法陷阱+性能优化

posted @ 2025-03-07 14:47  僵小七  阅读(281)  评论(0)    收藏  举报