• 首先我们要找到整个内存不足然后回调回收activity的入口,在哪里呢?在ActivityThread里,通过之前的源码阅读我们发现,当一个新的app启动的时候,系统将从Zygote进程fork一个子进程出来,当然我们知道App不一定只存在一个进程,只要定义了Activity进程要游离,我们也可以将其游离,但本质上都是由核心Android 进程 Zygote 进程去启动,但app进程启动之后,ActivityThread将被初始化,代码如下:
//路径:android/app/AppThread.java public static void main(String[] args) {        SamplingProfilerIntegration.start();        // CloseGuard defaults to true and can be quite spammy.  We        // disable it here, but selectively enable it later (via        // StrictMode) on debug builds, but using DropBox, not logs.        CloseGuard.setEnabled(false);        Environment.initForCurrentUser();        // Set the reporter for event logging in libcore        EventLogger.setReporter(new EventLoggingReporter());        Security.addProvider(new AndroidKeyStoreProvider());        // Make sure TrustedCertificateStore looks in the right place for CA certificates        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());        TrustedCertificateStore.setDefaultUserDirectory(configDir);        Process.setArgV0("");        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }
  • 可以看到这个方法将AppThread初始化,形成一个贯穿整个app的主线程,那么初始化完成将会调用attach()方法,这个方法里有什么 内容呢?来看代码:
/**AppThread attch 方法入口*/private void attach(boolean system) {        sCurrentActivityThread = this;        mSystemThread = system;        if (!system) {            ViewRootImpl.addFirstDrawHandler(new Runnable() {                @Override                public void run() {                    ensureJitEnabled();                }            });            android.ddm.DdmHandleAppName.setAppName("",                                                    UserHandle.myUserId());            RuntimeInit.setApplicationObject(mAppThread.asBinder());            final IActivityManager mgr = ActivityManagerNative.getDefault();            try {                mgr.attachApplication(mAppThread);            } catch (RemoteException ex) {                // Ignore            }            // Watch for getting close to heap limit.            /**             这里添加Gc的监听,如果超过虚拟机分配最大内存的 3/4,那么触发mgr.releaseSomeActivities            */            BinderInternal.addGcWatcher(new Runnable() {                @Override public void run() {                    if (!mSomeActivitiesChanged) {                        return;                    }                    Runtime runtime = Runtime.getRuntime();                    long dalvikMax = runtime.maxMemory();                    long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();                    if (dalvikUsed > ((3*dalvikMax)/4)) {                        if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)                                + " total=" + (runtime.totalMemory()/1024)                                + " used=" + (dalvikUsed/1024));                        mSomeActivitiesChanged = false;                        try {                            mgr.releaseSomeActivities(mAppThread);                        } catch (RemoteException e) {                        }                    }                }            });        } else {            // Don't set application object here -- if the system crashes,            // we can't display an alert, we just want to die die die.            android.ddm.DdmHandleAppName.setAppName("system_process",                    UserHandle.myUserId());            try {                mInstrumentation = new Instrumentation();                ContextImpl context = ContextImpl.createAppContext(                        this, getSystemContext().mPackageInfo);                mInitialApplication = context.mPackageInfo.makeApplication(true, null);                mInitialApplication.onCreate();            } catch (Exception e) {                throw new RuntimeException(                        "Unable to instantiate Application():" + e.toString(), e);            }        }        // add dropbox logging to libcore        DropBox.setReporter(new DropBoxReporter());        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {            @Override            public void onConfigurationChanged(Configuration newConfig) {                synchronized (mResourcesManager) {                    // We need to apply this change to the resources                    // immediately, because upon returning the view                    // hierarchy will be informed about it.                    if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {                        // This actually changed the resources!  Tell                        // everyone about it.                        if (mPendingConfiguration == null ||                                mPendingConfiguration.isOtherSeqNewer(newConfig)) {                            mPendingConfiguration = newConfig;                            sendMessage(H.CONFIGURATION_CHANGED, newConfig);                        }                    }                }            }            @Override            public void onLowMemory() {            }            @Override            public void onTrimMemory(int level) {            }        });    }
  • oK,我们很明显的看到了一个 BinderInternal.addGcWatcher()这个方法,我们可以看字面意思就知道,这个是一个Gc 回收的监听器,作用是在AppThread 作用域下的GcRoots Gc开始的时候,我们去监听这个GC回收,具体如何做到呢?我们量看一下GcWatcher 的实现思路:
//路径 BinderInternal.java -> GcWatcher static final class GcWatcher {        @Override        protected void finalize() throws Throwable {            handleGc();            sLastGcTime = SystemClock.uptimeMillis();            synchronized (sGcWatchers) {                sTmpWatchers = sGcWatchers.toArray(sTmpWatchers);            }            for (int i=0; i(new GcWatcher());        }    }
  • 哈哈,看到没,这里重写了finallize()方法,从JVM原理我们就知道,第一次进行GC回收的时候,GC线程将会回调finallize()方法,注意注意!!!敲黑板!这里有个sGcWatcher, 这玩意儿是干嘛的?来来来,我们看看sGcWatcher 的定义,
//路径 BinderInternal.javastatic WeakReference sGcWatcher            = new WeakReference(new GcWatcher());    static ArrayList sGcWatchers = new ArrayList<>();    static Runnable[] sTmpWatchers = new Runnable[1];    static long sLastGcTime;
  • Ok,我们看到了这是一个静态弱引用GcWatcher, 目的是啥?因为因为,如果静态强引用,这个引用就存在静态引用方法区,这时这个强引用GC线程无法回收!!所以这里当然要使用弱引用,当Gc 回收之后触发弱引用的scGcWather 的finallize()方法,但是此时只会触发一次啊!当然没关系,因为在方法最后我们又重新开辟了一个新的弱引用对象,所以,这个引用逃逸了,逃过一劫。原来如此
  • OK,既然都讲到这里了,我们就展开讲一下,为何这里一定会保证gc回收呢?那我们需要看一下BinderInternal 里的内容了,接下来看这一段BinderInternal里的方法:
//路径 BinderInternal.javapublic static void forceGc(String reason) {        EventLog.writeEvent(2741, reason);        Runtime.getRuntime().gc();    }
  • Ok, 我们看到这个方法一定会去触发运行时GC,那这个方法在何处调用呢?我们倒着走回去看看:
//路径:ActivityThread.javavoid doGcIfNeeded() {        mGcIdlerScheduled = false;        final long now = SystemClock.uptimeMillis();        //Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime()        //        + "m now=" + now);        if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {            //Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");            BinderInternal.forceGc("bg");        }    }//路径:ActivityThread.javafinal void handleLowMemory() {        ArrayList callbacks = collectComponentCallbacks(true, null);        final int N = callbacks.size();        for (int i=0; i
  • 分析源码发现,这里有两条线,一条是 doGcIfNeeded()方法,一条是handleLowMemory(),我们之后再来分析第二条线,这条线是系统检测内存不足的情况出发的,现在先分析第一条线,doGcIfNeeded()方法,我们继续往上走,看看,是谁来调用了我们的doGcIfNeeded()方法:
    //路径 : ActivityThread.java -> GcIdler    final class GcIdler implements MessageQueue.IdleHandler {        @Override        public final boolean queueIdle() {            doGcIfNeeded();            return false;        }    }
  • OK, 原来是您啊,GcIdler, 我们 GcIdler 是一个扩展了IdleHandler的类,我们翻一下这个类的注释描述会发现有这么一段:/**
  • Callback interface for discovering when a thread is going to block
  • waiting for more messages. */ public static interface IdleHandler { /**
    • Called when the message queue has run out of messages and will now
    • wait for more. Return true to keep your idle handler active, false
    • to have it removed. This may be called if there are still messages
    • pending in the queue, but they are all scheduled to be dispatched
    • after the current time. */ boolean queueIdle(); } 大概意思就说这个接口是在Messagequeue队列里的所有message都已经处理完了之后想等待更多的handler,如果queueIdle()返回true,这个handler就会一直保持存活,false执行完就丢弃,如果有持续的消息从IdleHandler进入,那么将在队列里等待。 - 这个GcIdler 事在ActivityThread 里实例化的,终于又回到了ActivityThread,我们来看看,这个方法是哪里调用的:
    //路径: ActivityThread.java   void scheduleGcIdler() {        if (!mGcIdlerScheduled) {            mGcIdlerScheduled = true;            Looper.myQueue().addIdleHandler(mGcIdler);        }        mH.removeMessages(H.GC_WHEN_IDLE);    }
  • Ok,经过上面的分析我们就明白了, Looper.myQueue() ,也就是主线程里的handler线程队列内容全部处理 结束,这个GcIdler 的 queueIdle() 就会被触发,那么GC就会被触发。已经到这,大概我们就明白GC调用的时机,我们接下来继续往上走,看看,何时我们会把这个GcIdler 加入执行队列,一直往上走我们来到了ActivityManagerService, 看里面的这段代码:
//路径ActivityManagerService.java    /**     * 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();        }    }
  • 上面的代码,我们看这一段, if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory),这个从字面理解就是,如果目前的oom_adj 比 ProcessList.PERCEPTIBLE_APP_ADJ 级别要高,或者进程在低内存环境下运行,就会触发这个方法,我们来看看oom_adj 是如何定义的:
//路径:ProcessList.java   // The minimum time we allow between crashes, for us to consider this    // application to be bad and stop and its services and reject broadcasts.    static final int MIN_CRASH_INTERVAL = 60*1000;    // OOM adjustments for processes in various states:    // Adjustment used in certain places where we don't know it yet.    // (Generally this is something that is going to be cached, but we    // don't know the exact value in the cached range to assign yet.)    static final int UNKNOWN_ADJ = 16;    // This is a process only hosting activities that are not visible,    // so it can be killed without any disruption.    static final int CACHED_APP_MAX_ADJ = 15;    static final int CACHED_APP_MIN_ADJ = 9;    // The B list of SERVICE_ADJ -- these are the old and decrepit    // services that aren't as shiny and interesting as the ones in the A list.    static final int SERVICE_B_ADJ = 8;    // This is the process of the previous application that the user was in.    // This process is kept above other things, because it is very common to    // switch back to the previous app.  This is important both for recent    // task switch (toggling between the two top recent apps) as well as normal    // UI flow such as clicking on a URI in the e-mail app to view in the browser,    // and then pressing back to return to e-mail.    static final int PREVIOUS_APP_ADJ = 7;    // This is a process holding the home application -- we want to try    // avoiding killing it, even if it would normally be in the background,    // because the user interacts with it so much.    static final int HOME_APP_ADJ = 6;    // This is a process holding an application service -- killing it will not    // have much of an impact as far as the user is concerned.    static final int SERVICE_ADJ = 5;    // This is a process with a heavy-weight application.  It is in the    // background, but we want to try to avoid killing it.  Value set in    // system/rootdir/init.rc on startup.    static final int HEAVY_WEIGHT_APP_ADJ = 4;    // This is a process currently hosting a backup operation.  Killing it    // is not entirely fatal but is generally a bad idea.    static final int BACKUP_APP_ADJ = 3;    // This is a process only hosting components that are perceptible to the    // user, and we really want to avoid killing them, but they are not    // immediately visible. An example is background music playback.    static final int PERCEPTIBLE_APP_ADJ = 2;    // This is a process only hosting activities that are visible to the    // user, so we'd prefer they don't disappear.    static final int VISIBLE_APP_ADJ = 1;    // This is the process running the current foreground app.  We'd really    // rather not kill it!    static final int FOREGROUND_APP_ADJ = 0;    // This is a process that the system or a persistent process has bound to,    // and indicated it is important.    static final int PERSISTENT_SERVICE_ADJ = -11;    // This is a system persistent process, such as telephony.  Definitely    // don't want to kill it, but doing so is not completely fatal.    static final int PERSISTENT_PROC_ADJ = -12;    // The system process runs at the default adjustment.    static final int SYSTEM_ADJ = -16;    // Special code for native processes that are not being managed by the system (so    // don't have an oom adj assigned by the system).    static final int NATIVE_ADJ = -17;    // Memory pages are 4K.    static final int PAGE_SIZE = 4*1024;    // The minimum number of cached apps we want to be able to keep around,    // without empty apps being able to push them out of memory.    static final int MIN_CACHED_APPS = 2;
  • 以上可以得出结论,就是如果此时App正在前台显示运行,并且不是低内存状态,那么进程全局的GC就不会被触发!
    好了,到这里我们大概已经知道这个Activity的触发时机了,接下来,我们来分析第二条线,就是ActivityThread.java 里的 final void handleLowMemory() 方法,这个又是什么时候会调用呢?我们也从这条线往上走,我们发现一样也是走到了上面ActivityManagerService 里的 performAppGcsLocked()方法,把代码复制下来我们继续分析!:
 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();        }    }
  • 看到了吗? 实际上触发GC的方法在 performAppGcLocked(proc) 这个方法里,下面scheduleAppGcsLocked() 这个方法是用来通知Activity, Service, Application onLowMemory() 回调的,那么接下来我们来看看 performAppGcLocked(proc) 方法里有什么玄机:
   /**     * 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.        }    }
  • 我去,这么一看,没什么玄机啊?这个代码我们发现就是说,如果是低内存情况下执行scheduleLowMemory()方法,最终由 handleLowMemory() 实现GC回收;如果说不是的话,那么只有一种情况就是App是在后台运行的情况会进行GC回收,也就是没有前台展示界面的情况,这种情况走 processInBackground() 方法,最终由 doGcIfNeeded() 方法去实现回收。
    到这,我们就整明白了App回收是何时触发的,那么接下来我们来看看,Activity会被释放部分Activity这种情况,前面已经分析过了是在ActivityThread attach() 方法里进行的回调,我们再回到attach() 方法,继续看以下代码:
//路径:ActivityThread.java BinderInternal.addGcWatcher(new Runnable() {                @Override public void run() {                    if (!mSomeActivitiesChanged) {                        return;                    }                    Runtime runtime = Runtime.getRuntime();                    long dalvikMax = runtime.maxMemory();                    long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();                    if (dalvikUsed > ((3*dalvikMax)/4)) {                        if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)                                + " total=" + (runtime.totalMemory()/1024)                                + " used=" + (dalvikUsed/1024));                        mSomeActivitiesChanged = false;                        try {                            mgr.releaseSomeActivities(mAppThread);                        } catch (RemoteException e) {                        }                    }                }            });
  • 以上代码我们可以得出结论:Activity如果要被回收,那么要在虚拟机内存使用超过系统分配的最大内存的 3 / 4, 这时将会触发一次大规模GC 释放Activity(这个不管是app是在后台运行还是app低内存状态下运行都是如此),其余情况只有Activity 在onStop() 之后才会触发。接下来看看mgr.releaseSomeActivities(mAppThread);这个方法,我们先看下mgr是个什么鬼,翻翻之前代码,你看到了这句代码final IActivityManager mgr = ActivityManagerNative.getDefault(); 哦吼,你发现了, 这玩意儿其实就是ActivityManagerService, 那么接下来我们回到AMS中,看看,这个releaseSomeActivities()函数:
//ActivityManagerService.java    @Override    public void releaseSomeActivities(IApplicationThread appInt) {        synchronized(this) {            final long origId = Binder.clearCallingIdentity();            try {                ProcessRecord app = getRecordForAppLocked(appInt);                mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");            } finally {                Binder.restoreCallingIdentity(origId);            }        }    }
  • 以上代码发现,实际上走到了StackSupervisor 里的 releaseSomeActivitiesLocked 方法,StackSupervisor 是什么?可以理解为activity任务栈的管理中心,系统所有应用的activity任务都在此管理:
    void releaseSomeActivitiesLocked(ProcessRecord app, String reason) {        // Examine all activities currently running in the process.        TaskRecord firstTask = null;        // Tasks is non-null only if two or more tasks are found.        ArraySet tasks = null;        if (DEBUG_RELEASE) Slog.d(TAG, "Trying to release some activities in " + app);        for (int i=0; i();                        tasks.add(firstTask);                    }                    tasks.add(r.task);                }            }        }        if (tasks == null) {            if (DEBUG_RELEASE) Slog.d(TAG, "Didn't find two or more tasks to release");            return;        }        // If we have activities in multiple tasks that are in a position to be destroyed,        // let's iterate through the tasks and release the oldest one.        final int numDisplays = mActivityDisplays.size();        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {            final ArrayList stacks = mActivityDisplays.valueAt(displayNdx).mStacks;            // Step through all stacks starting from behind, to hit the oldest things first.            for (int stackNdx = 0; stackNdx < stacks.size(); stackNdx++) {                final ActivityStack stack = stacks.get(stackNdx);                // Try to release activities in this stack; if we manage to, we are done.                if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {                    return;                }            }        }    }
if (r.finishing || r.state == ActivityState.DESTROYING || r.state == ActivityState.DESTROYED) { if (DEBUG_RELEASE) Slog.d(TAG, "Abort release; already destroying: " + r); return; } 和 if (r.visible || !r.stopped || !r.haveState || r.state == ActivityState.RESUMED || r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED || r.state == ActivityState.STOPPING)
  • 从以上的这几个条件判断可以发现,只有在activity 执行到onStop并且没有被finish 或者destroy 的情况下,才会进行Activity的回收。至此之后就会执行到Activity 的 performDestroy方法进行ondestroy,然后就等待GC回收的处理了。
    好了,到此我们已经知道Activity在低内存或者后台运行的时候何时回收,回收哪几种activity,怎么样回收了,我们学习了这个流程之后,将会使我们更好地去实现自己的Activity,实现更加健壮的代码

更多相关文章

  1. Android adb功能使用方法
  2. Android 学习日记(二)android studio运行github代码
  3. Android Apk反编译得到Java源代码
  4. Android 4.4 全套源码及子模块源码的下载方法
  5. Android 通过java代码实现EditText输入限制
  6. android解析XML文件的三方法之SAX
  7. 傻瓜式建立数据库,高效数据库操作代码的编写--android
  8. 15个开发者最亲睐的Android代码编辑器

随机推荐

  1. 下载Android(安卓)SDK tools完成Android(
  2. 关于服务端设置了IPV6时,Android请求网络
  3. Android(安卓)模拟器 HAXM硬件加速安装
  4. android 自定义权限问题
  5. 深入理解 Android(安卓)系统升级
  6. 抽离Android原生控件的方法
  7. 怎样查看apk须要支持的Android版本号
  8. Android下Service入门
  9. Android常见错误处理
  10. Android官方DataBinding(三):RecyclerView