android源码解析(二十五)-->onLowMemory执行流程

上篇文章中我们分析了Activity的onSaveInstanceState方法执行时机,知道了Activity在一般情况下,若只是执行onPause方法则不会执行onSaveInstanceState方法,而一旦执行了onStop方法就会执行onSaveInstanceState方法,具体的信息,可以参见onSaveInstanceState方法执行时机:http://blog.csdn.net/qq_23547831/article/details/51464535 这篇文章中同样的我们分析一下Actvity(当然不只是Activity,同样包含Servier,ContentProvider,Application等)的另一个内部方法:onLowMemory。该方法主要用于当前系统可用内存比较低的时候回调使用。

这里简单介绍一下Android系统的内存分配机制。Android系统中一个个的App都是一个个不同的应用进程,拥有各自的JVM与运行时,每个App的进程可使用的内存大小都是固定的,当系统中App打开数量过多时,就会使Android系统的可用内存降低,对于当前正在使用的App而言,可能还需要继续申请系统内存,而我们的剩余系统内存已经不足以被当前App所申请了,这时候系统会自动的清理那些后台进程,进而释放出可用内存用于前台进程的使用,当然这里系统清理后台进程的算法不是我们讨论的重点。这里我们只是大概的分析Android系统回调Activity的onLowMemory方法的流程。

通过前面关于Activity的启动流程分析我们知道ActivityManagerService是整个Android系统的管理中枢,负责Activity,Servier等四大组件的启动与销毁等工作,同样的对于应用进程的管理工作也是在ActivityMaangerServier中完成的,我们知道android系统中有两个比较重要的进程Zygote进程和SystemServer进程,其中Zygote进程是整个Android系统的根进程,其他所有的进程都是通过Zygote进程fork出来的。而SystemServer进程则用于运行各种服务,为其他的应用进程提供各种功能接口等,在前面我们分析过SystemServer进程的启动流程(参考:http://blog.csdn.net/qq_23547831/article/details/51105171)其中在SystemServer的startBootService方法中我们调用了:

// Set up the Application instance for the system process and get started.
        mActivityManagerService.setSystemProcess();

方法,看其注释说明,说的是为System进程初始化Application实例,这里我们可以看一下该方法的具体实现:

public void setSystemProcess() {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
            ServiceManager.addService("dbinfo", new DbBinder(this));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(this));
            }
            ServiceManager.addService("permission", new PermissionController(this));
            ServiceManager.addService("processinfo", new ProcessInfoService(this));

            ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS);
            mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());

            synchronized (this) {
                ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
                app.persistent = true;
                app.pid = MY_PID;
                app.maxAdj = ProcessList.SYSTEM_ADJ;
                app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
                synchronized (mPidsSelfLocked) {
                    mPidsSelfLocked.put(app.pid, app);
                }
                updateLruProcessLocked(app, false, null);
                updateOomAdjLocked();
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(
                    "Unable to find android system package", e);
        }
    }

这里简单介绍一下ServierManager是一个管理服务的服务,而其addServier方法就是注册各种服务(服务注册到JNI层,具体的关于是如何注册到JNI层的这里暂不做过多的解释)。可以发现在方法体中我们注册了名称为:memInfo的服务MemBinder,MemBinder是一个Binder类型的服务,主要用于检测系统内存情况,这里可以看一下其具体的实现逻辑:

static class MemBinder extends Binder {
        ActivityManagerService mActivityManagerService;
        MemBinder(ActivityManagerService activityManagerService) {
            mActivityManagerService = activityManagerService;
        }

        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
                    != PackageManager.PERMISSION_GRANTED) {
                pw.println("Permission Denial: can‘t dump meminfo from from pid="
                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                        + " without permission " + android.Manifest.permission.DUMP);
                return;
            }

            mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, "  ", args, false, null);
        }
    }

查看源码,我们可以发现MemBinder类继承于Binder类也就是说其实一个Binder类型的服务,并且有一个成员方法dump,该方法主要用于执行shell命令,当系统可用内存比较低的时候就会执行了该方法,然后回调到ActivityManagerService中的killAllBackground方法,下面我们重点看一下killAllBackground方法的具体实现:

@Override
    public void killAllBackgroundProcesses() {
        ...
           doLowMemReportIfNeededLocked(null);
        ...
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

可以看到这个方法体中会执行doLowMemReportIfNeededLocked方法,该方法是做什么的呢?我们继续看一下doLowMemReportIfNeededLoced方法的实现:

final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) {
        ...
        scheduleAppGcsLocked();
        ...
    }

好吧,在这个方法中我们又调用了scheduleAppGcsLocked方法,这样我们就继续看一下scheduleAppGcsLocked方法的实现逻辑:

/**
     * Schedule the execution of all pending app GCs.
     */
    final void scheduleAppGcsLocked() {
        mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG);

        if (mProcessesToGc.size() > 0) {
            // Schedule a GC for the time to the next process.
            ProcessRecord proc = mProcessesToGc.get(0);
            Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);

            long when = proc.lastRequestedGc + GC_MIN_INTERVAL;
            long now = SystemClock.uptimeMillis();
            if (when < (now+GC_TIMEOUT)) {
                when = now + GC_TIMEOUT;
            }
            mHandler.sendMessageAtTime(msg, when);
        }
    }

可以发现这里执行的逻辑就是通过mHandler发送一个msg.what为GC_BACKGROUND_PROCESSES_MSG的异步消息,这样消息体最终会被mHandler的handleMessage方法所执行,继续看一下mHandler的handleMessage方法的执行逻辑:

case GC_BACKGROUND_PROCESSES_MSG: {
                synchronized (ActivityManagerService.this) {
                    performAppGcsIfAppropriateLocked();
                }
            } break;

在mHandler的handleMessage方法中,首先会判断msg的what是否为GC_BACKGROUND_PROCESSES_MSG,然后会执行performAppGcsIfAppropriateLocked方法,这样我们继续看一下performAppGcsIfAppropriateLocked方法的实现:

/**
     * If all looks good, perform GCs on all processes waiting for them.
     */
    final void performAppGcsIfAppropriateLocked() {
        if (canGcNowLocked()) {
            performAppGcsLocked();
            return;
        }
        // Still not idle, wait some more.
        scheduleAppGcsLocked();
    }

可以发现这里首先判断是否能够执行gc操作,若不能继续执行上面的scheduleAppGcsLocked方法,然后继续执行发送异步消息的逻辑,直到变量canGcNowLocked为true,并执行performAppGcsLocked方法,然后return掉,这样我们继续跟踪代码,看一下performAppGcsLocked方法的执行逻辑:

/**
     * Perform GCs on all processes that are waiting for it, but only
     * if things are idle.
     */
    final void performAppGcsLocked() {
        final int N = mProcessesToGc.size();
        if (N <= 0) {
            return;
        }
        if (canGcNowLocked()) {
            while (mProcessesToGc.size() > 0) {
                ProcessRecord proc = mProcessesToGc.remove(0);
                if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
                    if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
                            <= SystemClock.uptimeMillis()) {
                        // To avoid spamming the system, we will GC processes one
                        // at a time, waiting a few seconds between each.
                        performAppGcLocked(proc);
                        scheduleAppGcsLocked();
                        return;
                    } else {
                        // It hasn‘t been long enough since we last GCed this
                        // process...  put it in the list to wait for its time.
                        addProcessToGcListLocked(proc);
                        break;
                    }
                }
            }

            scheduleAppGcsLocked();
        }
    }

可以发现该方法经过一系列的逻辑判断之后会执行performAppGcLocked方法,我们继续看一下该方法的实现:

/**
     * Ask a given process to GC right now.
     */
    final void performAppGcLocked(ProcessRecord app) {
        try {
            app.lastRequestedGc = SystemClock.uptimeMillis();
            if (app.thread != null) {
                if (app.reportLowMemory) {
                    app.reportLowMemory = false;
                    app.thread.scheduleLowMemory();
                } else {
                    app.thread.processInBackground();
                }
            }
        } catch (Exception e) {
            // whatever.
        }
    }

可以发现最终执行的是app.thread.scheduleLowMemory方法,而这里的app.thread是ActivityThread.ApplicationThread对象,所以这里最终是通过Binder进程间通讯,执行的是ActivityThread.ApplicationThread的scheduleLowMemory方法,好吧让我们看一下ActivityThread.ApplicationThread的scheduleLowMemory

方法的实现逻辑…

@Override
        public void scheduleLowMemory() {
            sendMessage(H.LOW_MEMORY, null);
        }

在ActivityThread中的scheduleLowMemory方法中并没有执行额外逻辑,而是直接调用了sendMessage方法,继续跟踪方法的执行:

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

可以发现在sendMessage方法中最终通过一个Handler类型的mH成员变量发送一个异步消息,这样异步消息最终会被mH的handleMessage方法执行。。。。,经过查看源代码我们知道在mH的handleMessage方法中最终调用的是handleLowMemory方法:

final void handleLowMemory() {
        ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);

        final int N = callbacks.size();
        for (int i=0; i<N; i++) {
            callbacks.get(i).onLowMemory();
        }

        // Ask SQLite to free up as much memory as it can, mostly from its page caches.
        if (Process.myUid() != Process.SYSTEM_UID) {
            int sqliteReleased = SQLiteDatabase.releaseMemory();
            EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased);
        }

        // Ask graphics to free up as much as possible (font/image caches)
        Canvas.freeCaches();

        // Ask text layout engine to free also as much as possible
        Canvas.freeTextLayoutCaches();

        BinderInternal.forceGc("mem");
    }

可以发现这里通过遍历ComponentCallbacks2并执行了其onLowMemory方法,那么这里的ComponentCallBacks2是什么呢?这里我们查看一下collectComponentCallbacks方法的实现逻辑。

ArrayList<ComponentCallbacks2> collectComponentCallbacks(
            boolean allActivities, Configuration newConfig) {
        ArrayList<ComponentCallbacks2> callbacks
                = new ArrayList<ComponentCallbacks2>();

        synchronized (mResourcesManager) {
            final int NAPP = mAllApplications.size();
            for (int i=0; i<NAPP; i++) {
                callbacks.add(mAllApplications.get(i));
            }
            final int NACT = mActivities.size();
            for (int i=0; i<NACT; i++) {
                ActivityClientRecord ar = mActivities.valueAt(i);
                Activity a = ar.activity;
                if (a != null) {
                    Configuration thisConfig = applyConfigCompatMainThread(
                            mCurDefaultDisplayDpi, newConfig,
                            ar.packageInfo.getCompatibilityInfo());
                    if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
                        // If the activity is currently resumed, its configuration
                        // needs to change right now.
                        callbacks.add(a);
                    } else if (thisConfig != null) {
                        // Otherwise, we will tell it about the change
                        // the next time it is resumed or shown.  Note that
                        // the activity manager may, before then, decide the
                        // activity needs to be destroyed to handle its new
                        // configuration.
                        if (DEBUG_CONFIGURATION) {
                            Slog.v(TAG, "Setting activity "
                                    + ar.activityInfo.name + " newConfig=" + thisConfig);
                        }
                        ar.newConfig = thisConfig;
                    }
                }
            }
            final int NSVC = mServices.size();
            for (int i=0; i<NSVC; i++) {
                callbacks.add(mServices.valueAt(i));
            }
        }
        synchronized (mProviderMap) {
            final int NPRV = mLocalProviders.size();
            for (int i=0; i<NPRV; i++) {
                callbacks.add(mLocalProviders.valueAt(i).mLocalProvider);
            }
        }

        return callbacks;
    }

可以发现该方法最终返回类型为ArrayList类型的callBacks而我们的callBacks中保存的是我们应用进程中的Activity,Service,Provider已经Application等。咦?Activity,Service,Provider,Application都是ComponentCallBacks2类型的么?我们看一看一下具体的定义:

Actvity的类定义:

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback

Service的类定义:

public abstract class Service extends ContextWrapper implements ComponentCallbacks2

ContentProvider的类定义:

public abstract class ContentProvider implements ComponentCallbacks2

Application的类定义:

public class Application extends ContextWrapper implements ComponentCallbacks2

可以发现其都是继承与ComponentCalbacks2,所以其都可以被当做是ComponentCallbacks2类型的变量。而同样是四大组件的BroadcastReceiver,我们可以下其类定义:

public abstract class BroadcastReceiver

可以看到其并未继承与ComponentCallbacks2,所以并未执行,所以通过这样的分析,我们知道了,最终应用程序中的Activity,Servier,ContentProvider,Application的onLowMemory方法会被执行。而由于我们是在系统内存紧张的时候会执行killAllBackground方法进而通过层层条用执行Activity、Service、ContentProvider、Application的onLowMemory方法,所以我们可以在这些组件的onLowMemory方法中执行了一些清理资源的操作,释放一些内存,尽量保证自身的应用进程不被杀死。

总结:

  • 系统在JNI层会时时检测内存变量,当内存过低时会通过kiilbackground的方法清理后台进程。
  • 经过层层的调用过程最终会执行Activity、Service、ContentProvider、Application的onLowMemory方法。
  • 可以在组件的onLowMemory方法中执行一些清理资源的操作,释放内存防止进程被杀死。
时间: 2024-08-23 23:27:18

android源码解析(二十五)-->onLowMemory执行流程的相关文章

jQuery 源码解析(二十五) DOM操作模块 html和text方法的区别

html和text都可以获取和修改DOM节点里的内容,方法如下: html(value)     ;获取匹配元素集合中的一个元素的innerHTML内容,或者设置每个元素的innerHTML内容,                ;value可选,可以是html代码或返回html代码的函数,如果没有参数则获取匹配元素集合中第一个元素的innerHTML内容 text(text)         ;获取匹配元素集合中所有元素合并后的文本内容,或者设置每个元素的文本内容,封装了createTextNo

android源码解析(十九)--&gt;Dialog加载绘制流程

前面两篇文章,我们分析了Activity的布局文件加载.绘制流程,算是对整个Android系统中界面的显示流程有了一个大概的了解,其实Android系统中所有的显示控件(注意这里是控件,而不是组件)的加载绘制流程都是类似的,包括:Dialog的加载绘制流程,PopupWindow的加载绘制流程,Toast的显示原理等,上一篇文章中,我说在介绍了Activity界面的加载绘制流程之后,就会分析一下剩余几个控件的显示控制流程,这里我打算先分析一下Dialog的加载绘制流程. 可能有的同学问这里为什么

android源码解析(十六)--&gt;应用进程Context创建流程

今天讲讲应用进程Context的创建流程,相信大家平时在开发过程中经常会遇到对Context对象的使用,Application是Context,Activity是Context,Service也是Context,所以有一个经典的问题是一个App中一共有多少个Context? 这个问题的答案是Application + N个Activity + N个Service. 还有就是我们平时在使用Context过程中许多时候由于使用不当,可能会造成内存泄露的情况等等,这个也是需要我们注意的.这里有篇不错的

Redis源码解析(十五)--- aof-append only file解析

继续学习redis源码下的Data数据相关文件的代码分析,今天我看的是一个叫aof的文件,这个字母是append ONLY file的简称,意味只进行追加文件操作.这里的文件追加记录时为了记录数据操作的改变记录,用以异常情况的数据恢复的.类似于之前我说的redo,undo日志的作用.我们都知道,redis作为一个内存数据库,数据的每次操作改变是先放在内存中,等到内存数据满了,在刷新到磁盘文件中,达到持久化的目的.所以aof的操作模式,也是采用了这样的方式.这里引入了一个block块的概念,其实就

android源码解析(十八)--&gt;Activity布局绘制流程

这篇文章是承接上一篇文章(Android布局加载流程:http://blog.csdn.net/qq_23547831/article/details/51284556)来写的,大家都知道Activity在Android体系中扮演者一个界面展示的角色,通过上一篇文章的分析,我们知道Activity是通过Window来控制界面的展示的,一个Window对象就是一个窗口对象,而每个Activity中都有一个相应的Window对象,所以说一个Activity对象也就可以说是一个窗口对象,而Window

Spring 源码解析之HandlerAdapter源码解析(二)

Spring 源码解析之HandlerAdapter源码解析(二) 前言 看这篇之前需要有Spring 源码解析之HandlerMapping源码解析(一)这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了 解决上篇文章遗留的问题 getHandler(processedRequest) 这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMapping ge

Android源码解析——LruCache

Android源码解析--LruCache LRU 在读LruCache源码之前,我们先来了解一下这里的Lru是什么.LRU全称为Least Recently Used,即最近最少使用,是一种缓存置换算法.我们的缓存容量是有限的,它会面临一个问题:当有新的内容需要加入我们的缓存,但我们的缓存空闲的空间不足以放进新的内容时,如何舍弃原有的部分内容从而腾出空间用来放新的内容.解决这个问题的算法有多种,比如LRU,LFU,FIFO等.需要注意区分的是LRU和LFU.前者是最近最少使用,即淘汰最长时间未

android源码解析之(十五)--&gt;Activity销毁流程

继续我们的源码解析,上一篇文章我们介绍了Activity的启动流程,一个典型的场景就是Activity a 启动了一个Activity b,他们的生命周期回调方法是: onPause(a) –> onCreate(b) –> onStart(b) –> onResume(b) –> onStop(a) 而我们根据源码也验证了这样的生命周期调用序列,那么Activity的销毁流程呢?它的生命周期的调用顺序又是这样的呢? 这里我们我做一个简单的demo,让一个Activity a启动A

Android View体系(八)从源码解析View的layout和draw流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Scroller Android View体系(五)从源码解析View的事件分发机制 Android View体系(六)从源码解析Activity的构成 Android View体系(七)从源码解析View的measure流程 前言 上一篇文章我们讲了View的measure的流程,接下来我们

Android 源码系列之&lt;十四&gt;从源码的角度深入理解LeakCanary的内存泄露检测机制(下)

转载请注明出处:http://blog.csdn.net/llew2011/article/details/52958567 在上边文章Android 源码系列之<十三>从源码的角度深入理解LeakCanary的内存泄露检测机制(中)由于篇幅原因仅仅向小伙伴们讲述了在Android开发中如何使用LeakCanary来检测应用中出现的内存泄露,并简单的介绍了LeakCanary的相关配置信息.根据上篇文章的介绍我们知道LeakCanary为了不给APP进程造成影响所以新开启了一个进程,在新开启的