继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Android 进程绝杀技forceStop(二)

汪汪一只猫
关注TA
已关注
手记 426
粉丝 129
获赞 715

五. Service

5.1 bringDownDisabledPackageServicesLocked

[-> ActiveServices.java]

    //killProcess = true;  doit = true;
    boolean bringDownDisabledPackageServicesLocked(String packageName, Set<String> filterByClasses,
            int userId, boolean evenPersistent, boolean killProcess, boolean doit) {
        boolean didSomething = false;

        if (mTmpCollectionResults != null) {
            mTmpCollectionResults.clear();
        }

        if (userId == UserHandle.USER_ALL) {
            for (int i = mServiceMap.size() - 1; i >= 0; i--) {
                //从mServiceMap中查询到所有属于该包下的service [见流程5.2]
                didSomething |= collectPackageServicesLocked(packageName, filterByClasses,
                        evenPersistent, doit, killProcess, mServiceMap.valueAt(i).mServicesByName);
                if (!doit && didSomething) {
                    ...
                }
            }
        } else {
            ServiceMap smap = mServiceMap.get(userId);
            if (smap != null) {
                ArrayMap<ComponentName, ServiceRecord> items = smap.mServicesByName;
                //从mServiceMap中查询到所有属于该包下的service [见流程5.2]
                didSomething = collectPackageServicesLocked(packageName, filterByClasses,
                        evenPersistent, doit, killProcess, items);
            }
        }

        if (mTmpCollectionResults != null) {
            //结束掉所有收集到的Service [见流程5.3]
            for (int i = mTmpCollectionResults.size() - 1; i >= 0; i--) {
                bringDownServiceLocked(mTmpCollectionResults.get(i));
            }
            mTmpCollectionResults.clear();
        }
        return didSomething;
    }

5.2 collectPackageServicesLocked

[-> ActiveServices.java]

    //killProcess = true;  doit = true;
    private boolean collectPackageServicesLocked(String packageName, Set<String> filterByClasses,
            boolean evenPersistent, boolean doit, boolean killProcess,
            ArrayMap<ComponentName, ServiceRecord> services) {
        boolean didSomething = false;
        for (int i = services.size() - 1; i >= 0; i--) {
            ServiceRecord service = services.valueAt(i);
            final boolean sameComponent = packageName == null
                    || (service.packageName.equals(packageName)
                        && (filterByClasses == null
                            || filterByClasses.contains(service.name.getClassName())));
            if (sameComponent
                    && (service.app == null || evenPersistent || !service.app.persistent)) {
                if (!doit) {
                    ...
                }
                didSomething = true;

                if (service.app != null) {
                    service.app.removed = killProcess;
                    if (!service.app.persistent) {
                        service.app.services.remove(service);
                    }
                }
                service.app = null;
                service.isolatedProc = null;
                if (mTmpCollectionResults == null) {
                    mTmpCollectionResults = new ArrayList<>();
                }
                //将满足条件service放入mTmpCollectionResults
                mTmpCollectionResults.add(service);
            }
        }
        return didSomething;
    }

该方法的主要功能就是收集该满足条件service放入mTmpCollectionResults.

5.3 bringDownServiceLocked

[-> ActiveServices.java]

    private final void bringDownServiceLocked(ServiceRecord r) {
        for (int conni=r.connections.size()-1; conni>=0; conni--) {
            ArrayList<ConnectionRecord> c = r.connections.valueAt(conni);
            for (int i=0; i<c.size(); i++) {
                ConnectionRecord cr = c.get(i);
                cr.serviceDead = true;
                // 断开service的连接
                cr.conn.connected(r.name, null);
            }
        }

        if (r.app != null && r.app.thread != null) {
            for (int i=r.bindings.size()-1; i>=0; i--) {
                IntentBindRecord ibr = r.bindings.valueAt(i);

                if (ibr.hasBound) {
                    bumpServiceExecutingLocked(r, false, "bring down unbind");
                    mAm.updateOomAdjLocked(r.app);
                    ibr.hasBound = false;
                    //最终调用目标Service.onUnbind()方法
                    r.app.thread.scheduleUnbindService(r, ibr.intent.getIntent());
                }
            }
        }

        r.destroyTime = SystemClock.uptimeMillis();

        final ServiceMap smap = getServiceMap(r.userId);
        smap.mServicesByName.remove(r.name);
        smap.mServicesByIntent.remove(r.intent);
        r.totalRestartCount = 0;
        // [见流程5.4]
        unscheduleServiceRestartLocked(r, 0, true);

        //确保service不在pending队列
        for (int i=mPendingServices.size()-1; i>=0; i--) {
            if (mPendingServices.get(i) == r) {
                mPendingServices.remove(i);
            }
        }

        //取消service相关的通知
        r.cancelNotification();
        r.isForeground = false;
        r.foregroundId = 0;
        r.foregroundNoti = null;

        r.clearDeliveredStartsLocked();
        r.pendingStarts.clear();

        if (r.app != null) {
            synchronized (r.stats.getBatteryStats()) {
                r.stats.stopLaunchedLocked();
            }
            r.app.services.remove(r);
            if (r.app.thread != null) {
                updateServiceForegroundLocked(r.app, false);

                bumpServiceExecutingLocked(r, false, "destroy");
                mDestroyingServices.add(r);
                r.destroying = true;
                mAm.updateOomAdjLocked(r.app);
                // 最终调用目标service的onDestroy()
                r.app.thread.scheduleStopService(r);

            }
        }

        if (r.bindings.size() > 0) {
            r.bindings.clear();
        }

        if (r.restarter instanceof ServiceRestarter) {
           ((ServiceRestarter)r.restarter).setService(null);
        }

        int memFactor = mAm.mProcessStats.getMemFactorLocked();
        long now = SystemClock.uptimeMillis();
        if (r.tracker != null) {
            r.tracker.setStarted(false, memFactor, now);
            r.tracker.setBound(false, memFactor, now);
            if (r.executeNesting == 0) {
                r.tracker.clearCurrentOwner(r, false);
                r.tracker = null;
            }
        }

        smap.ensureNotStartingBackground(r);
    }

5.4 unscheduleServiceRestartLocked

    private final boolean unscheduleServiceRestartLocked(ServiceRecord r, int callingUid,
            boolean force) {
        if (!force && r.restartDelay == 0) {
            return false;
        }

        //将Services从重启列表移除,并重置重启计数器
        boolean removed = mRestartingServices.remove(r);
        if (removed || callingUid != r.appInfo.uid) {
            r.resetRestartCounter();
        }
        if (removed) {
            clearRestartingIfNeededLocked(r);
        }
        mAm.mHandler.removeCallbacks(r.restarter);
        return true;
    }

六. Provider

6.1 PM.collectPackageProvidersLocked

[-> ProviderMap.java]

    boolean collectPackageProvidersLocked(String packageName, Set<String> filterByClasses,
            boolean doit, boolean evenPersistent, int userId,
            ArrayList<ContentProviderRecord> result) {
        boolean didSomething = false;
        if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_OWNER) {
            // [见流程6.2]
            didSomething = collectPackageProvidersLocked(packageName, filterByClasses,
                    doit, evenPersistent, mSingletonByClass, result);
        }
        if (!doit && didSomething) {
            ... //不进入该分支
        }

        if (userId == UserHandle.USER_ALL) {
            for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
                if (collectPackageProvidersLocked(packageName, filterByClasses,
                        doit, evenPersistent, mProvidersByClassPerUser.valueAt(i), result)) {
                    ...
                    didSomething = true;
                }
            }
        } else {
            //从mProvidersByClassPerUser查询
            HashMap<ComponentName, ContentProviderRecord> items
                    = getProvidersByClass(userId);
            if (items != null) {
                didSomething |= collectPackageProvidersLocked(packageName, filterByClasses,
                        doit, evenPersistent, items, result);
            }
        }
        return didSomething;
    }
  • 当userId = UserHandle.USER_ALL时, 则会mSingletonByClassmProvidersByClassPerUser结构中查询所有属于该package的providers.

  • 当userId = UserHandle.USER_OWNER时,则会从mSingletonByClassmProvidersByClassPerUser中userId相等的 数据结构中查询所有属于该package的providers.

  • 当userId不属于上述两者之一时,则会从mProvidersByClassPerUser中userId相等的查询所有属于该package的providers.

6.2 PM.collectPackageProvidersLocked

[-> ProviderMap.java]

    private boolean collectPackageProvidersLocked(String packageName,
            Set<String> filterByClasses, boolean doit, boolean evenPersistent,
            HashMap<ComponentName, ContentProviderRecord> providers,
            ArrayList<ContentProviderRecord> result) {
        boolean didSomething = false;
        for (ContentProviderRecord provider : providers.values()) {
            final boolean sameComponent = packageName == null
                    || (provider.info.packageName.equals(packageName)
                        && (filterByClasses == null
                            || filterByClasses.contains(provider.name.getClassName())));
            if (sameComponent
                    && (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
                if (!doit) {
                    ...
                }
                didSomething = true;
                result.add(provider);
            }
        }
        return didSomething;
    }

6.3 AMS.removeDyingProviderLocked

    private final boolean removeDyingProviderLocked(ProcessRecord proc,
             ContentProviderRecord cpr, boolean always) {
         final boolean inLaunching = mLaunchingProviders.contains(cpr);

         //唤醒cpr, 并从mProviderMap中移除provider相关信息
         if (!inLaunching || always) {
             synchronized (cpr) {
                 cpr.launchingApp = null;
                 cpr.notifyAll();
             }
             mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
             String names[] = cpr.info.authority.split(";");
             for (int j = 0; j < names.length; j++) {
                 mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
             }
         }

         for (int i = cpr.connections.size() - 1; i >= 0; i--) {
             ContentProviderConnection conn = cpr.connections.get(i);
             if (conn.waiting) {
                 //always = true,不进入该分支
                 if (inLaunching && !always) {
                     continue;
                 }
             }
             ProcessRecord capp = conn.client;
             conn.dead = true;
             if (conn.stableCount > 0) {
                 if (!capp.persistent && capp.thread != null
                         && capp.pid != 0
                         && capp.pid != MY_PID) {
                     //杀掉依赖该provider的client进程
                     capp.kill("depends on provider "
                             + cpr.name.flattenToShortString()
                             + " in dying proc " + (proc != null ? proc.processName : "??"), true);
                 }
             } else if (capp.thread != null && conn.provider.provider != null) {
                 capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
                 cpr.connections.remove(i);
                 if (conn.client.conProviders.remove(conn)) {
                     stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
                 }
             }
         }

         if (inLaunching && always) {
             mLaunchingProviders.remove(cpr);
         }
         return inLaunching;
     }

当其他app使用该provider, 且建立stable的连接, 那么对于非persistent进程,则会由于依赖该provider的缘故而被杀.

七. Broadcast

7.1 BQ.cleanupDisabledPackageReceiversLocked

[-> BroadcastQueue.java]

    boolean cleanupDisabledPackageReceiversLocked(
            String packageName, Set<String> filterByClasses, int userId, boolean doit) {
        boolean didSomething = false;
        for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
            // 【见流程7.2】
            didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
                    packageName, filterByClasses, userId, doit);
            if (!doit && didSomething) {
                return true;
            }
        }

        for (int i = mOrderedBroadcasts.size() - 1; i >= 0; i--) {
            didSomething |= mOrderedBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
                    packageName, filterByClasses, userId, doit);
            if (!doit && didSomething) {
                return true;
            }
        }

        return didSomething;
    }

该方法主要功能:

  • 清理并行广播队列mParallelBroadcasts;

  • 清理有序广播队列mOrderedBroadcasts

7.2 BR.cleanupDisabledPackageReceiversLocked

[-> BroadcastRecord.java]

    boolean cleanupDisabledPackageReceiversLocked(
           String packageName, Set<String> filterByClasses, int userId, boolean doit) {
       if ((userId != UserHandle.USER_ALL && this.userId != userId) || receivers == null) {
           return false;
       }

       boolean didSomething = false;
       Object o;
       for (int i = receivers.size() - 1; i >= 0; i--) {
           o = receivers.get(i);
           if (!(o instanceof ResolveInfo)) {
               continue;
           }
           ActivityInfo info = ((ResolveInfo)o).activityInfo;

           final boolean sameComponent = packageName == null
                   || (info.applicationInfo.packageName.equals(packageName)
                   && (filterByClasses == null || filterByClasses.contains(info.name)));
           if (sameComponent) {
               ...
               didSomething = true;
               //移除该广播receiver
               receivers.remove(i);
               if (i < nextReceiver) {
                   nextReceiver--;
               }
           }
       }
       nextReceiver = Math.min(nextReceiver, receivers.size());
       return didSomething;
    }

八. Alarm和Notification

在前面[小节2.2]介绍到处理完forceStopPackageLocked(),紧接着便是发送广播ACTION_PACKAGE_RESTARTED,经过Broadcast广播分发,最终调用到注册过该广播的接收者。

8.1 Alarm清理

[-> AlarmManagerService.java]

    class UninstallReceiver extends BroadcastReceiver {
       public UninstallReceiver() {
           IntentFilter filter = new IntentFilter();
           filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
           //监听ACTION_PACKAGE_RESTARTED
           filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
           filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
           filter.addDataScheme("package");
           getContext().registerReceiver(this, filter);
           ...
       }

       @Override
       public void onReceive(Context context, Intent intent) {
           synchronized (mLock) {
               String action = intent.getAction();
               String pkgList[] = null;
               if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
                   ...
               } else {
                   ...
                   Uri data = intent.getData();
                   if (data != null) {
                       String pkg = data.getSchemeSpecificPart();
                       if (pkg != null) {
                           pkgList = new String[]{pkg};
                       }
                   }
               }
               if (pkgList != null && (pkgList.length > 0)) {
                   for (String pkg : pkgList) {
                       //移除alarm
                       removeLocked(pkg);
                       mPriorities.remove(pkg);
                       for (int i=mBroadcastStats.size()-1; i>=0; i--) {
                           ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);
                           if (uidStats.remove(pkg) != null) {
                               if (uidStats.size() <= 0) {
                                   mBroadcastStats.removeAt(i);
                               }
                           }
                       }
                   }
               }
           }
       }
   }

调用AlarmManagerService中的removeLocked()方法,从mAlarmBatchesmPendingWhileIdleAlarms队列中移除包所相关的alarm.

8.2 Notification清理

[-> NotificationManagerService.java]

    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            ...

            if (action.equals(Intent.ACTION_PACKAGE_ADDED)
                    || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
                    || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
                    || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
                    || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                        UserHandle.USER_ALL);
                String pkgList[] = null;
                boolean queryReplace = queryRemove &&
                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                    ...
                } else {
                    Uri uri = intent.getData();
                    ...
                    String pkgName = uri.getSchemeSpecificPart();
                    if (packageChanged) {
                        ...
                    }
                    pkgList = new String[]{pkgName};
                }

                if (pkgList != null && (pkgList.length > 0)) {
                    for (String pkgName : pkgList) {
                        if (cancelNotifications) {
                            //移除Notification
                            cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
                                    changeUserId, REASON_PACKAGE_CHANGED, null);
                        }
                    }
                }
                mListeners.onPackagesChanged(queryReplace, pkgList);
                mConditionProviders.onPackagesChanged(queryReplace, pkgList);
                mRankingHelper.onPackagesChanged(queryReplace, pkgList);
            }
        }
    };

调用NotificationManagerService.java
中的cancelAllNotificationsInt()方法,从mNotificationList队列中移除包所相关的Notification.

九. 级联诛杀

这里就跟大家分享一段经历吧,记得之前有BAT的某浏览器大厂(具体名称就匿了),浏览器会因为另一个app被杀而导致自己无辜被牵连所杀,并怀疑是ROM定制化导致的bug,于是发邮件向我厂请教缘由。

遇到这个问题,首先将两个app安装到Google原生系统,结果是依然会被级联诛杀,很显然可以排除厂商ROM定制的缘故,按常理说bug应该可以让app自行解决。出于好奇,帮他们进一步调查了下这个问题,发现并非无辜被杀,而是force-stop的级联诛杀所导致的。

简单来说就是App1调用了getClassLoader()来加载App2,那么App1所运行的进程便会在其pkgDeps队列中增加App2的包名,在前面[小节3.2]已经提到pkgDeps,杀进程的过程中会遍历该队列,当App2被forceStop所杀时,便是级联诛杀App1。App1既然会调用App2的ClassLoader来加载其方法,那么就建立了一定的联系,这是Google有意赋予forceStop这个强力杀的功能。

这个故事是想告诉大家在插件化或者反射的过程中要注意这种情况,防止不必要的误伤。接下来具体说说这个过程是如何建立依赖的。

9.1 CI.getClassLoader

[-> ContextImpl.java]

    public ClassLoader getClassLoader() {
        //【见小节9.2】
       return mPackageInfo != null ?
                 mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader();
     }

9.2 LA.getClassLoader

[-> LoadedApk.java]

    public ClassLoader getClassLoader() {
        synchronized (this) {
            if (mClassLoader != null) {
                return mClassLoader;
            }

            if (mPackageName.equals("android")) {
                if (mBaseClassLoader == null) {
                    mClassLoader = ClassLoader.getSystemClassLoader();
                } else {
                    mClassLoader = mBaseClassLoader;
                }
                return mClassLoader;
            }

            if (mRegisterPackage) {
                //经过Binder,最终调用到AMS.addPackageDependency 【见小节9.3】
                ActivityManagerNative.getDefault().addPackageDependency(mPackageName);
            }

            ...
            mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip,
                    mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                    libraryPermittedPath, mBaseClassLoader);

            return mClassLoader;
        }
    }

9.3 AMS.addPackageDependency

    public void addPackageDependency(String packageName) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            if (callingPid == Process.myPid()) {
                return;
            }
            ProcessRecord proc;
            synchronized (mPidsSelfLocked) {
                proc = mPidsSelfLocked.get(Binder.getCallingPid());
            }
            if (proc != null) {
                if (proc.pkgDeps == null) {
                    proc.pkgDeps = new ArraySet<String>(1);
                }
                //将该包名添加到pkgDeps
                proc.pkgDeps.add(packageName);
            }
        }
    }

调用ClassLoader来加载启动包名时,则会将该包名加入到进程的pkgDeps。

十. 总结

forceStop的功能如下:

Process: 调用AMS.killPackageProcessesLocked()清理该package所涉及的进程;

  1. Activity: 调用ASS.finishDisabledPackageActivitiesLocked()清理该package所涉及的Activity;

  2. Service: 调用AS.bringDownDisabledPackageServicesLocked()清理该package所涉及的Service;

  3. Provider: 调用AMS.removeDyingProviderLocked()清理该package所涉及的Provider;

  4. BroadcastRecevier: 调用BQ.cleanupDisabledPackageReceiversLocked()清理该package所涉及的广播

  5. 发送广播ACTION_PACKAGE_RESTARTED,用于停止已注册的alarm,notification.

persistent进程的特殊待遇:

  • 进程: AMS.killPackageProcessesLocked()不杀进程

  • Service: ActiveServices.collectPackageServicesLocked()不移除不清理service

- Provider: ProviderMap.collectPackageProvidersLocked()不收集不清理provider. 且不杀该provider所连接的client的persistent进程;

功能点归纳:

  1. force-stop并不会杀persistent进程;

  2. 当app被force-stop后,无法接收到任何普通广播,那么也就常见的监听手机网络状态的变化或者屏幕亮灭的广播来拉起进程肯定是不可行;

  3. 当app被force-stop后,那么alarm闹钟一并被清理,无法实现定时响起的功能;

  4. app被force-stop后,四大组件以及相关进程都被一一剪除清理,即便多进程架构的app也无法拉起自己;

  5. 级联诛杀:当app通过ClassLoader加载另一个app,则会在force-stop的过程中会被级联诛杀;

  6. 生死与共:当app与另个app使用了share uid,则会在force-stop的过程,任意一方被杀则另一方也被杀,建立起生死与共的强关系。

既然force-stop多次提到杀进程,那最后简单说两句关于保活:正确的保活姿态,应该是在用户需要时保证千万别被杀,用户不需要时别强保活,一切以用户为出发点。

  • 进程是否需要存活,系统上层有AMS来管理缓存进程和空进程,底层有LowMemoryKiller来根据系统可用内存的情况来管理进程是否存活,这样的策略是从系统整体性角度考虑,为了是给用户提供更好更流畅的用户体验。

  • 用户需要的时候千万别被杀:谨慎使用插件化和共享uid,除非愿意接受级联诛杀和生死与共的场景;还有就是提高自身app的稳定性,减少crash和anr的发生频率,这才是正道。

  • 用户不需要的时候别强保活:为了保活,多进程架构,利用各种小技巧来提升优先级等都是不可取的,一招force-stop足以干掉90%以上的保活策略,当然还有一些其他手段及漏洞来保活,系统层面往往还会采取一些特别的方法来禁止保活。博主曾经干过手机底层的性能与功耗优化工作,深知不少app的流氓行径,严重系统的流畅度与手机续航能力。

为了android有更好的用户体验,为了不影响手机系统性能,为了不降低手机续航能力,建议大家花更多时间精力在如何提高app的稳健性,如何优化app性能,共同打造Android的良好生态圈。

原文链接:http://www.apkbus.com/blog-864937-77292.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP