精华内容
下载资源
问答
  • Android 内存被异常回收后直接重启APP

    千次阅读 2018-09-21 15:09:59
    其实很简单,当内存被回收后,我们可以直接重启APP,重新走一遍APP启动的流程,这样数据就会重新加载一遍,避免了数据为空的问题。 首先,我们先定义一个AppStatusManager管理生命周期类 /** * APP状态跟踪器常量码...

    做Android开发的小朋友一定会遇到程序退到后台,遇到内存被回收,导致数据为空,这时候启动APP就会出现空指针问题。

    那么,我们该怎么解决这个问题呢?其实很简单,当内存被回收后,我们可以直接重启APP,重新走一遍APP启动的流程,这样数据就会重新加载一遍,避免了数据为空的问题。

    首先,我们先定义一个AppStatusManager管理生命周期类

    /**
     * APP状态跟踪器常量码
     */
    public class AppStatusConstant {
        public static final int STATUS_FORCE_KILLED = -1; //应用放在后台被强杀了
        public static final int STATUS_KICK_OUT = 1;//TOKEN失效或者被踢下线
        public static final int STATUS_NORMAL = 2;  //APP正常态
        public static final int STATUS_LOGOUT = 3;//用户注销登录
        public static final int STATUS_OFFLINE = 4;//未登录状态
        public static final int STATUS_ONLINE = 5;//登录状态
    
        //intent到MainActivity 区分跳转目的
        public static final String KEY_HOME_ACTION = "key_home_action";//返回到主页面
    
        public static final int ACTION_BACK_TO_HOME = 11; //默认值
        public static final int ACTION_RESTART_APP = 12;//重启APP
        public static final int ACTION_KICK_OUT = 13;//被踢出
        public static final int ACTION_LOGOUT = 14;//退出登录
    }
    
    public class AppStatusManager implements Application.ActivityLifecycleCallbacks {
    
        public int mAppStatus = AppStatusConstant.STATUS_FORCE_KILLED;
        public static AppStatusManager mAppStatusManager;
        private Application application;
        private boolean isForground;//是否前台
        private int activeCount;//Activity运行个数
    
    
        private AppStatusManager(Application application) {
            this.application = application;
            application.registerActivityLifecycleCallbacks(this);
        }
    
        public static void init(Application application) {
            if (mAppStatusManager == null) {
                mAppStatusManager = new AppStatusManager(application);
            }
        }
    
        public static AppStatusManager getInstance() {
            return mAppStatusManager;
        }
    
        /**
         * 获取APP状态
         */
        public int getAppStatus() {
            return mAppStatus;
        }
    
        /**
         * 设置APP状态
         */
        public void setAppStatus(int appStatus) {
            this.mAppStatus = appStatus;
        }
    
        /**
         * 是否前台显示
         */
        public boolean isForground() {
            return isForground;
        }
    
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }
    
        @Override
        public void onActivityStarted(Activity activity) {
            activeCount++;
        }
    
        @Override
        public void onActivityResumed(Activity activity) {
            isForground = true;
        }
    
        @Override
        public void onActivityPaused(Activity activity) {
        }
    
        @Override
        public void onActivityStopped(Activity activity) {
            activeCount--;
            if (activeCount == 0) {
                isForground = false;
            }
        }
    
        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }
    
        @Override
        public void onActivityDestroyed(Activity activity) {
        }
    }
    
    public class BaseApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            AppStatusManager.init(this);
        }
    }
    

    然后定义个TrackerActivity基类,监听内存回收问题

    
    public abstract class TrackerActivity extends AppCompatActivity {
        private String TAG = "TAG";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            switch (AppStatusManager.getInstance().getAppStatus()) {
                case AppStatusConstant.STATUS_FORCE_KILLED:
                    Log.e(TAG, "STATUS_FORCE_KILLED");
                    /*处理APP被强杀*/
                    protectApp();
                    break;
                case AppStatusConstant.STATUS_KICK_OUT:
                    /*处理APP被退出登录*/
                    Log.e(TAG, "STATUS_KICK_OUT");
                    break;
                case AppStatusConstant.STATUS_NORMAL:
                    /*APP正常状态*/
                    Log.e(TAG, "STATUS_NORMAL");
                    setUpContentView();
                    setUpData();
                    break;
            }
        }
        
        /**
         * 设置布局
         */
        protected abstract void setUpContentView();
    
        /**
         * 设置数据
         */
        protected abstract void setUpData();
    
        /**
         * 处理APP被强杀
         */
        protected void protectApp() {
            /*跳转主界面处理*/
            Intent intent = new Intent(this, MainActivity.class);
            intent.putExtra(AppStatusConstant.KEY_HOME_ACTION, AppStatusConstant.ACTION_RESTART_APP);
            startActivity(intent);
        }
    }
    

    接下来,我们只用在实现SplashActivity MainActivity SecondActivity ,就行了

    SplashActivity

    
    public class SplashActivity extends TrackerActivity {
        private MyHandler mHandler = new MyHandler(this);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            //设置默认状态
            AppStatusManager.getInstance().setAppStatus(AppStatusConstant.STATUS_NORMAL);
            super.onCreate(savedInstanceState);
        }
    
        @Override
        protected void setUpContentView() {
            setContentView(R.layout.activity_splash);
        }
    
        @Override
        protected void setUpData() {
            if (mHandler != null) {
                mHandler.sendEmptyMessageDelayed(0, 2000);
            }
        }
    
        private static class MyHandler extends Handler {
            private WeakReference<Context> reference;
    
            public MyHandler(Context context) {
                reference = new WeakReference<>(context);
            }
    
            @Override
            public void handleMessage(Message msg) {
                SplashActivity activity = (SplashActivity) reference.get();
                if (activity != null) {
                    activity.startActivity(new Intent(activity, MainActivity.class));
                    activity.finish();
                }
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mHandler != null) {
                mHandler.removeCallbacksAndMessages(null);
                mHandler = null;
            }
        }
    }
    

    MainActivity

    public class MainActivity extends TrackerActivity {
    
        @Override
        protected void setUpContentView() {
            setContentView(R.layout.activity_main);
        }
    
        @Override
        protected void setUpData() {
        }
    
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            int action = intent.getIntExtra(AppStatusConstant.KEY_HOME_ACTION, AppStatusConstant.ACTION_BACK_TO_HOME);
            switch (action) {
                case AppStatusConstant.ACTION_RESTART_APP:
                    protectApp();
                    break;
                case AppStatusConstant.ACTION_BACK_TO_HOME:
                    break;
                default:
                    break;
            }
        }
    
        @Override
        protected void protectApp() {
            /*跳转引导页,重启APP*/
            startActivity(new Intent(this, SplashActivity.class));
            finish();
        }
    
        public void toSecondActivity(View view) {
            startActivity(new Intent(this, SecondActivity.class));
        }
    }
    

    SecondActivity

    
    public class SecondActivity extends TrackerActivity {
        
        @Override
        protected void setUpContentView() {
            setContentView(R.layout.activity_second);
        }
    
        @Override
        protected void setUpData() {
        }
    
        public void toThirdActivity(View view) {
            startActivity(new Intent(this, ThirdActivity.class));
        }
    
    

    大功告成!!! 让我们看看具体显示流程吧

    在这里插入图片描述

    流程: SplashActivity -> MainActivity -> SecondActivity -> ThirdActivity -> APP被强杀,内存被回收,APP就会重启,重走流程 -> SplashActivity -> MainActivity

    github

    展开全文
  • APP的回收和重启机制

    2020-12-05 17:24:32
    前言 我们在打开APP以后长期锁屏或者将APP放置后台,然后再点击... 启动app进程的时候是怎么检测到app之前被干掉,从而重建APP 系统是如何保存现场的,保存在哪里 系统是如何恢复现场的,从哪里恢复 不过对于首个

    前言

    我们在打开APP以后长期锁屏或者将APP放置后台,然后再点击APP图标或者任务栏点击指定的APP,重新打开APP时,会出现一段时间的白屏,那么这个时候其实APP曾经就被回收了,然后随着我们重新打开而重启了整个进程。

    在讲这个机制之前我们先思考几个问题,然后再带着这几个问题再去深入源码:

    1. app是如何被干掉
    2. app被干掉以后,做了哪些处理
    3. 启动app进程的时候是怎么检测到app之前被干掉,从而重建APP
    4. 系统是如何保存现场的,保存在哪里
    5. 系统是如何恢复现场的,从哪里恢复

    不过对于首个问题,我们这片文章不做说明,因为这涉及到linux kernel层杀死进程的机制,这一套机制花费的篇幅会比较长,以后有机会单独开篇章讲。这里只需要记住一旦linux决定要杀掉执行进程的时候会向AMS发送一个通知.

    那么下面开始第二个问题:app被干掉以后,做了哪些处理
    我们知道ActivityThread的main方法是一个APP进程的入口,在main方法中会做一件非常重要的事(关乎到APP被回收和重启)。

    ActivityThread.main()->会new出ActivityThread对象->ActivityThread 对象调用attach方法->获取AMS->调用AMS.attachApplication->调用AMS.attachApplicationLocked->执行thread.asBinder().linkToDeath(adr, 0);

    private final class AppDeathRecipient implements IBinder.DeathRecipient {
            final ProcessRecord mApp;
            final int mPid;
            final IApplicationThread mAppThread;
    
            AppDeathRecipient(ProcessRecord app, int pid,
                    IApplicationThread thread) {
                if (DEBUG_ALL) Slog.v(
                    TAG, "New death recipient " + this
                     + " for thread " + thread.asBinder());
                mApp = app;
                mPid = pid;
                mAppThread = thread;
            }
    
            @Override
            public void binderDied() {
                if (DEBUG_ALL) Slog.v(
                    TAG, "Death received in " + this
                    + " for thread " + mAppThread.asBinder());
                synchronized(ActivityManagerService.this) {
                    appDiedLocked(mApp, mPid, mAppThread, true);
                }
            }
        }

    AppDeathRecipient就是监听APP被干掉的工具,当APP被干掉的时候,AppDeathRecipient的binderDied方法触发,appDiedLocked就是APP被杀掉以后通知的入口。那么下面我们来看下当APP被杀掉以后,系统做了什么处理:

    appDiedLocked

    那么我们就从这个方法开始说起:

        @GuardedBy("this")
        final void appDiedLocked(ProcessRecord app) {
           appDiedLocked(app, app.pid, app.thread, false);
        }
    
        @GuardedBy("this")
        final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
                boolean fromBinderDied) {
            // First check if this ProcessRecord is actually active for the pid.
            synchronized (mPidsSelfLocked) {
                ProcessRecord curProc = mPidsSelfLocked.get(pid);
                if (curProc != app) {
                    Slog.w(TAG, "Spurious death for " + app + ", curProc for " + pid + ": " + curProc);
                    return;
                }
            }
    
            BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
            synchronized (stats) {
                stats.noteProcessDiedLocked(app.info.uid, pid);
            }
    
            if (!app.killed) {
                if (!fromBinderDied) {
                    killProcessQuiet(pid);
                }
                ProcessList.killProcessGroup(app.uid, pid);
                app.killed = true;
            }
    
            // Clean up already done if the process has been re-started.
            if (app.pid == pid && app.thread != null &&
                    app.thread.asBinder() == thread.asBinder()) {
                boolean doLowMem = app.getActiveInstrumentation() == null;
                boolean doOomAdj = doLowMem;
                if (!app.killedByAm) {
                    reportUidInfoMessageLocked(TAG,
                            "Process " + app.processName + " (pid " + pid + ") has died: "
                                    + ProcessList.makeOomAdjString(app.setAdj, true) + " "
                                    + ProcessList.makeProcStateString(app.setProcState), app.info.uid);
                    mAllowLowerMemLevel = true;
                } else {
                    // Note that we always want to do oom adj to update our state with the
                    // new number of procs.
                    mAllowLowerMemLevel = false;
                    doLowMem = false;
                }
                EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName,
                        app.setAdj, app.setProcState);
                if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                    "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder());
                handleAppDiedLocked(app, false, true);
    
                if (doOomAdj) {
                    updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
                }
                if (doLowMem) {
                    doLowMemReportIfNeededLocked(app);
                }
            } else if (app.pid != pid) {
                // A new process has already been started.
                reportUidInfoMessageLocked(TAG,
                        "Process " + app.processName + " (pid " + pid
                                + ") has died and restarted (pid " + app.pid + ").", app.info.uid);
                EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName);
            } else if (DEBUG_PROCESSES) {
                Slog.d(TAG_PROCESSES, "Received spurious death notification for thread "
                        + thread.asBinder());
            }
    
            // On the device which doesn't have Cgroup, log LmkStateChanged which is used as a signal
            // for pulling memory stats of other running processes when this process died.
            if (!hasMemcg()) {
                StatsLog.write(StatsLog.APP_DIED, SystemClock.elapsedRealtime());
            }
        }
    

    这里首先调用了handleAppDiedLocked方法,那么我们看下handleAppDiedLocked做了什么操作:

       /**
         * Main function for removing an existing process from the activity manager
         * as a result of that process going away.  Clears out all connections
         * to the process.
         */
        @GuardedBy("this")
        final void handleAppDiedLocked(ProcessRecord app,
                boolean restarting, boolean allowRestart) {
            int pid = app.pid;
            boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
                    false /*replacingPid*/);
            if (!kept && !restarting) {
                removeLruProcessLocked(app);
                if (pid > 0) {
                    ProcessList.remove(pid);
                }
            }
    
            if (mProfileData.getProfileProc() == app) {
                clearProfilerLocked();
            }
    
            mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> {
                Slog.w(TAG, "Crash of app " + app.processName
                        + " running instrumentation " + app.getActiveInstrumentation().mClass);
                Bundle info = new Bundle();
                info.putString("shortMsg", "Process crashed.");
                finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
            });
        }
    

    首先调用cleanUpApplicationRecordLocked方法,这个方法主要是将弹出的crash窗口,ANR窗口以及debug提示窗口关闭,清理providers,receivers,service相关的信息,以及在清理过程中判断当前进程是否要重启。而返回的kept如果为true,那么代表当前APP正在重启,false就可以直接干掉APP。
    接下来会调用mAtmInternal.handleAppDied方法。mAtmInternal是一个ActivityTaskManagerInternal抽象类对象,这里是LocalService实现类,这个是ATMS的内部类这个方法将是我们APP被进程干掉以后的主要处理逻辑,那么我们继续往下走:

    LocalService.java

           @HotPath(caller = HotPath.PROCESS_CHANGE)
            @Override
            public void handleAppDied(WindowProcessController wpc, boolean restarting,
                    Runnable finishInstrumentationCallback) {
                synchronized (mGlobalLockWithoutBoost) {
                    // Remove this application's activities from active lists.
                    boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc);
    
                    wpc.clearRecentTasks();
                    wpc.clearActivities();
    
                    if (wpc.isInstrumenting()) {
                        finishInstrumentationCallback.run();
                    }
    
                    if (!restarting && hasVisibleActivities) {
                        mWindowManager.deferSurfaceLayout();
                        try {
                            if (!mRootActivityContainer.resumeFocusedStacksTopActivities()) {
                                // If there was nothing to resume, and we are not already restarting
                                // this process, but there is a visible activity that is hosted by the
                                // process...then make sure all visible activities are running, taking
                                // care of restarting this process.
                                mRootActivityContainer.ensureActivitiesVisible(null, 0,
                                        !PRESERVE_WINDOWS);
                            }
                        } finally {
                            mWindowManager.continueSurfaceLayout();
                        }
                    }
                }
            }
    

    看第一行代码的注释,意思就是说从当前活动的列表中移除应用所有的activity,所以这就是那个核心方法:

    RootActivityContainer.java

        boolean handleAppDied(WindowProcessController app) {
            boolean hasVisibleActivities = false;
            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                    final ActivityStack stack = display.getChildAt(stackNdx);
                    hasVisibleActivities |= stack.handleAppDiedLocked(app);
                }
            }
            return hasVisibleActivities;
        }
    

    最终调用了ActivityStack的handleAppDiedLocked方法

    ActivityStack.java

        /**
         * Reset local parameters because an app's activity died.
         * @param app The app of the activity that died.
         * @return result from removeHistoryRecordsForAppLocked.
         */
        boolean handleAppDiedLocked(WindowProcessController app) {
            if (mPausingActivity != null && mPausingActivity.app == app) {
                if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
                        "App died while pausing: " + mPausingActivity);
                mPausingActivity = null;
            }
            if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
                mLastPausedActivity = null;
                mLastNoHistoryActivity = null;
            }
    
            return removeHistoryRecordsForAppLocked(app);
        }
    
    
        private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
            removeHistoryRecordsForAppLocked(mLRUActivities, app, "mLRUActivities");
            removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
                    "mStoppingActivities");
            removeHistoryRecordsForAppLocked(mStackSupervisor.mGoingToSleepActivities, app,
                    "mGoingToSleepActivities");
            removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
                    "mFinishingActivities");
    
            final boolean isProcessRemoved = app.isRemoved();
            if (isProcessRemoved) {
                // The package of the died process should be force-stopped, so make its activities as
                // finishing to prevent the process from being started again if the next top (or being
                // visible) activity also resides in the same process.
                app.makeFinishingForProcessRemoved();
            }
    
            boolean hasVisibleActivities = false;
    
            // Clean out the history list.
            int i = numActivities();
            if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                    "Removing app " + app + " from history with " + i + " entries");
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
                mTmpActivities.clear();
                mTmpActivities.addAll(activities);
    
                while (!mTmpActivities.isEmpty()) {
                    final int targetIndex = mTmpActivities.size() - 1;
                    final ActivityRecord r = mTmpActivities.remove(targetIndex);
                    if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                            "Record #" + targetIndex + " " + r + ": app=" + r.app);
    
                    if (r.app == app) {
                        if (r.visible) {
                            hasVisibleActivities = true;
                        }
                        final boolean remove;
                        if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
                                || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
                                && r.launchCount < 3 && !r.finishing) {
                            // If the process crashed during a resize, always try to relaunch it, unless
                            // it has failed more than twice. Skip activities that's already finishing
                            // cleanly by itself.
                            remove = false;
                        } else if ((!r.haveState && !r.stateNotNeeded
                                && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
                            // Don't currently have state for the activity, or
                            // it is finishing -- always remove it.
                            remove = true;
                        } else if (!r.visible && r.launchCount > 2 &&
                                r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
                            // We have launched this activity too many times since it was
                            // able to run, so give up and remove it.
                            // (Note if the activity is visible, we don't remove the record.
                            // We leave the dead window on the screen but the process will
                            // not be restarted unless user explicitly tap on it.)
                            remove = true;
                        } else {
                            // The process may be gone, but the activity lives on!
                            remove = false;
                        }
                        if (remove) {
                            if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
                                    "Removing activity " + r + " from stack at " + i
                                    + ": haveState=" + r.haveState
                                    + " stateNotNeeded=" + r.stateNotNeeded
                                    + " finishing=" + r.finishing
                                    + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
                            if (!r.finishing || isProcessRemoved) {
                                Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
                                EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
                                        r.mUserId, System.identityHashCode(r),
                                        r.getTaskRecord().taskId, r.shortComponentName,
                                        "proc died without state saved");
                            }
                        } else {
                            // We have the current state for this activity, so
                            // it can be restarted later when needed.
                            if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
                            if (DEBUG_APP) Slog.v(TAG_APP,
                                    "Clearing app during removeHistory for activity " + r);
                            r.app = null;
                            // Set nowVisible to previous visible state. If the app was visible while
                            // it died, we leave the dead window on screen so it's basically visible.
                            // This is needed when user later tap on the dead window, we need to stop
                            // other apps when user transfers focus to the restarted activity.
                            r.nowVisible = r.visible;
                            if (!r.haveState) {
                                if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE,
                                        "App died, clearing saved state of " + r);
                                r.icicle = null;
                            }
                        }
                        cleanUpActivityLocked(r, true, true);
                        if (remove) {
                            removeActivityFromHistoryLocked(r, "appDied");
                        }
                    }
                }
            }
    
            return hasVisibleActivities;
        }
    

    这个方法主要将r.app设置为null。那么这个有什么作用呢?后面在做解释,到这里APP被杀死的之后的处理都做完了,那么我们接下来就要看如何重启了。

    这里做个总结:

    1. 清掉对应的BroadcastReceiver,Service以及Provider相关的信息
    2. 关闭所有的系统弹出的弹窗(ANR弹窗,crash弹窗以及Debug弹窗)
    3. 尝试重启APP进程
    4. 然后并不会移除对应的ActivityRecord对象,并且将ActivityRecord的WindowProcessController对象置空。

    App被回收时的处理.jpg

    第三个问题:启动app进程的时候是怎么检测到app之前被干掉,从而重建APP
    我们重新打开一个APP有两种选择:一种是点击Launcher界面的app图标启动,另一种就是打开任务页面,点击对应要重启的app。这里我们就以第一种举例吧,当点击APP图标的时候,APP是怎么启动的呢?这个时候我们先进入LauncherActivity看下吧。

            @Override
        protected void onCreate(Bundle icicle) {
            super.onCreate(icicle);
            
            mPackageManager = getPackageManager();
    
            if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
                requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
                setProgressBarIndeterminateVisibility(true);
            }
            onSetContentView();
    
            mIconResizer = new IconResizer();
            
            mIntent = new Intent(getTargetIntent());
            mIntent.setComponent(null);
            mAdapter = new ActivityAdapter(mIconResizer);
    
            setListAdapter(mAdapter);
            getListView().setTextFilterEnabled(true);
    
            updateAlertTitle();
            updateButtonText();
    
            if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
                setProgressBarIndeterminateVisibility(false);
            }
        }
        /**
         * Adapter which shows the set of activities that can be performed for a given intent.
         */
        private class ActivityAdapter extends BaseAdapter implements Filterable {
        }
    

    LauncherActivity代码量不多,一眼就看到了这个Adapter,看注释知道这个应该就是桌面列表的适配器了,在onCreate的地方进行初始化,然后桌面其实就是一个ListView。接下来看下每个APP图标的点击事件:

        @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
            Intent intent = intentForPosition(position);
            startActivity(intent);
        }
    

    LauncherActivity由于继承了ListActivity,所以它本身实现了一个onListItemClick方法,这个方法就是item点击的方法(即每个图标点击以后的处理方法),是个比较简单的启动Activity的流程。这里没啥特殊的地方。接下来就简要过一遍Activity的启动流程了。

    • Activity#startActivity
    • Activity#startActivityForResult
    • Instrumentation#execStartActivity()
    • ActivityManagerService#startActivity()
    • ActivityTaskManagerService#startActivity()
    • ActivityTaskManagerService#startActivityAsUser()
    • ActivityStarter#execute()
    • ActivityStarter#startActivityMayWait()
    • ActivityStarter#startActivity()
    • ActivityStarter#startActivityUnchecked()
    • RootActivityContainer#startActivityLocked()
    • ActivityStack#resumeTopActivityUncheckedLocked()
    • ActivityStack#resumeTopActivityInnerLocked()

    上面我们重点关注下最后一步的操作:

    
        @GuardedBy("mService")
        private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
            。。。代码省略。。。
            // Find the next top-most activity to resume in this stack that is not finishing and is
            // focusable. If it is not focusable, we will fall into the case below to resume the
            // top activity in the next focusable task.
            ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
    
            final boolean hasRunningActivity = next != null;
    
            // TODO: Maybe this entire condition can get removed?
            if (hasRunningActivity && !isAttached()) {
                return false;
            }
    
            。。。代码省略。。。
            
    
            if (next.attachedToProcess()) {
                。。。代码省略。。。
            } else {
                // Whoops, need to restart this activity!
                if (!next.hasBeenLaunched) {
                    next.hasBeenLaunched = true;
                } else {
                    if (SHOW_APP_STARTING_PREVIEW) {
                        next.showStartingWindow(null /* prev */, false /* newTask */,
                                false /* taskSwich */);
                    }
                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
                }
                if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
                mStackSupervisor.startSpecificActivityLocked(next, true, true);
            }
            return true;
        }
    
    

     

    final class ActivityRecord extends ConfigurationContainer {
        boolean hasProcess() {
            return app != null;
        }
    
        boolean attachedToProcess() {
            return hasProcess() && app.hasThread();
        }
    }
    

    next是ActivityRecord对象,next就是接下来将要启动的页面,判断attachedToProcess()为false的时候即app==null的情况下,会调用mStackSupervisor.startSpecificActivityLocked,而startSpecificActivityLocked就会进行Activity的重建。所以联系到之前收到进程被回收通知时的处理,可以得出一个结论就是对于非主动结束Activity生命周期的,AMS是不会清理掉对应的ActivityRecord的app对象的。换句话说app是否为null,是AMS决定是否重启Activity的依据。

       void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
            // 当APP被回收了,进程被杀掉以后,这个wpc必然是为空的
            final WindowProcessController wpc =
                    mService.getProcessController(r.processName, r.info.applicationInfo.uid);
    
            boolean knownToBeDead = false;
            //所以当APP回收的时候不会执行realStartActivityLocked方法
            if (wpc != null && wpc.hasThread()) {
                try {
                    realStartActivityLocked(r, wpc, andResume, checkConfig);
                    return;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting activity "
                            + r.intent.getComponent().flattenToShortString(), e);
                }
    
                // If a dead object exception was thrown -- fall through to
                // restart the application.
                knownToBeDead = true;
            }
    
            // Suppress transition until the new activity becomes ready, otherwise the keyguard can
            // appear for a short amount of time before the new process with the new activity had the
            // ability to set its showWhenLocked flags.
            if (getKeyguardController().isKeyguardLocked()) {
                r.notifyUnknownVisibilityLaunched();
            }
    
            try {
                if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
                    Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
                            + r.processName);
                }
                //发送一个Message启动APP进程的message(这里涉及到了启动APP进程的流程,以后有时间单独开一篇讲)
                final Message msg = PooledLambda.obtainMessage(
                        ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
                        r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
                mService.mH.sendMessage(msg);
            } finally {
                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
            }
        }
    

    至此APP重启流程也是告一段落。

    主要是在resumeTopActivityInnerLocked的时候通过判断对应ActivityRecord的WindowProcessController对象来判断是否需要重启进程

    App重建.jpg


    第四个问题:APP是如何保存现场,保存在哪里
    这里再不看源码的情况下,大胆猜想一下会是怎么做的呢?APP进程最后一旦被杀掉,那么所有的资源都会被释放掉,所以现场肯定不能保存在客户端,那么唯一的可能就是在服务端了。既然这里涉及到了服务端和客户端之间的数据交互。所以在回答这个问题之前,先来看下服务端和客户端之间到底是如何进行数据交互的。

    由于篇幅有限,Activity的主要启动流程这里不做详细介绍。我们直接从ActivityStarter的startActivity方法:

        private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
                String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
                String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
                SafeActivityOptions options,
                boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
                TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
                PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
            ...代码省略...
    
            ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                    callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                    resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                    mSupervisor, checkedOptions, sourceRecord);
            ...代码省略...
    
            final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                    true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
            return res;
        }
    

    关注重点,在这个方法里面new出了一个ActivityRecord对象(ActivityRecord对象就是服务端保存Activity的所有信息)。在ActivityRecord里面初始化了一个Token,这个Token对于每一个ActivityRecord都是唯一的,后面所有地方都会通过这个token来进行校验。
    接着经过一系列的调用到ActivityStackSupervisor的realStartActivityLocked方法

        boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
                boolean andResume, boolean checkConfig) throws RemoteException {
    
            ...代码省略...
    
            try {
                
                ...代码省略...
                try {
                    ...代码省略...
    
    
                    // Create activity launch transaction.
                    final ClientTransaction clientTransaction = ClientTransaction.obtain(
                            proc.getThread(), r.appToken);
    
                    final DisplayContent dc = r.getDisplay().mDisplayContent;
                    clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                            System.identityHashCode(r), r.info,
                            // TODO: Have this take the merged configuration instead of separate global
                            // and override configs.
                            mergedConfiguration.getGlobalConfiguration(),
                            mergedConfiguration.getOverrideConfiguration(), r.compat,
                            r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                            r.icicle, r.persistentState, results, newIntents,
                            dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                                    r.assistToken));
    
                    // Set desired final state.
                    final ActivityLifecycleItem lifecycleItem;
                    if (andResume) {
                        lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                    } else {
                        lifecycleItem = PauseActivityItem.obtain();
                    }
                    clientTransaction.setLifecycleStateRequest(lifecycleItem);
    
                    // Schedule transaction.
                    mService.getLifecycleManager().scheduleTransaction(clientTransaction);
                    ...代码省略...
    
                } catch (RemoteException e) {
                    if (r.launchFailed) {
                        // This is the second time we failed -- finish activity and give up.
                        Slog.e(TAG, "Second failure launching "
                                + r.intent.getComponent().flattenToShortString() + ", giving up", e);
                        proc.appDied();
                        stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                                "2nd-crash", false);
                        return false;
                    }
    
                    // This is the first time we failed -- restart process and
                    // retry.
                    r.launchFailed = true;
                    proc.removeActivity(r);
                    throw e;
                }
            } finally {
                endDeferResume();
            }
    
            ...代码省略...
    
            return true;
        }
    

    这里面的结构在Android28的时候做了比较大的变动,具体在Android9.0 Activity启动原理差异解析这篇文章有详细说到
    新增了LaunchActivityItem,ResumeActivityItem, PauseActivityItem, StopActivityItem, DestroyActivityItem这五个类来实现Activity的五个生命周期。这几个类都是实现了ActivityLifecycleItem接口,有TransactionExecutor统一调度。 本文不做过多详解,这里直接总结下:

    1. 创建activity启动的事务,并且添加到clientTransaction对象的列表当中去,这个列表之后执行生命周期的时候会用到,需要记下。
    2. 设置最终期望到达的状态(我这里主要探讨的是activity启动时的事务,所以这里期望状态必然是resume状态)到事务当中去
    3. 执行事务(主要就是执行Activity的生命周期)

    在创建事务的时候会将ActivityRecord的token传入,执行事务的时候会通过Binder机制调回到了客户端这边,然后执行LaunchActivityItem的时候execute方法的时候初始化ActivityClientRecord,并将Token传入到ActivityClientRecord中,而ActivityClientRecord便是客户端用于保存Activity数据的对象,与服务器进行数据交互主要就是通过Token来实现。


    了解完客户端与服务端之间的数据通信之后,我们再来看下系统是系统是根据什么恢复现场的吧。这里就不得不提到两个方法:onSaveInstance,onRestoreInstance,一个是保存数据的方法,一个是加载数据的方法。但是有很多人都陷入了一个误区以为onSaveInstance是在Activity被回收的时候才会调用。其实不然,我们可以看下onSaveInstance和onRestoreInstance 的调用时机:

    onSaveInstance

    我们直接看代码了解下它是什么时候调用的:

        /**
         * The hook for {@link ActivityThread} to save the state of this activity.
         *
         * Calls {@link #onSaveInstanceState(android.os.Bundle)}
         * and {@link #saveManagedDialogs(android.os.Bundle)}.
         *
         * @param outState The bundle to save the state to.
         */
        final void performSaveInstanceState(@NonNull Bundle outState) {
            dispatchActivityPreSaveInstanceState(outState);
            onSaveInstanceState(outState);
            saveManagedDialogs(outState);
            mActivityTransitionState.saveState(outState);
            storeHasCurrentPermissionRequest(outState);
            if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
            dispatchActivityPostSaveInstanceState(outState);
        }
    

    发现是在Activity的performSaveInstanceState中调用,我们继续找调用的目标:

    public class Instrumentation {
        /**
         * Perform calling of an activity's {@link Activity#onSaveInstanceState}
         * method.  The default implementation simply calls through to that method.
         *  @param activity The activity being saved.
         * @param outState The bundle to pass to the call.
         * @param outPersistentState The persistent bundle to pass to the call.
         */
        public void callActivityOnSaveInstanceState(@NonNull Activity activity,
                @NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
            activity.performSaveInstanceState(outState, outPersistentState);
        }
    }
    

    是由Instrumentation 调用,看到这个类相信应该比较熟悉了。基本上ActivityThread要操作Activity都是通过这个类执行的,那么callActivityOnSaveInstanceState自然应该也是ActivityThread里面调用的,那么我们继续跟下去看下:

       /**
         * Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates
         * the client record's state.
         * All calls to stop an activity must be done through this method to make sure that
         * {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
         */
        private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
            // Before P onSaveInstanceState was called before onStop, starting with P it's
            // called after. Before Honeycomb state was always saved before onPause.
            //saveState值只有在Activity要被销毁的时候才会设置为false,其他情况下都是true
            //如果Activity只是看不见了,那么r.activity.mFinished必然为false
            //而r.state每次在onResume的时候就会置为空, 所以onStop的时候r.state == null必然为true
            //r.isPreHoneycomb()指的是版本号要HONEYCOMB之后才会置true(版本在HONEYCOMB之前onSaveInstance是在onPause之前调用的)
            final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                    && !r.isPreHoneycomb();
            final boolean isPreP = r.isPreP();
            if (shouldSaveState && isPreP) {
                callActivityOnSaveInstanceState(r);
            }
    
            try {
                r.activity.performStop(r.mPreserveWindow, reason);
            } catch (SuperNotCalledException e) {
                throw e;
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException(
                            "Unable to stop activity "
                                    + r.intent.getComponent().toShortString()
                                    + ": " + e.toString(), e);
                }
            }
            r.setState(ON_STOP);
    
            if (shouldSaveState && !isPreP) {
                callActivityOnSaveInstanceState(r);
            }
        }
    
        private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
            r.state = new Bundle();
            r.state.setAllowFds(false);
            if (r.isPersistable()) {
                r.persistentState = new PersistableBundle();
                mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                        r.persistentState);
            } else {
                mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
            }
        }
    

    从这段代码可以看出onSaveInstanceState与onStop(r.activity.performStop最终会执行onStop方法)的执行顺序与android版本有关,在Android P 之前onSaveInstance是在onStop之前调用,在Android P 之后onSaveInstance就是在onStop之后调用。但是总的来说onSaveInstance就是在Activity已经"看不见"了以后就会执行,并非Activity被回收的时候才执行。而onStop方法其实是在StopActivityItem的execute方法里面执行的,然后调用handleStopActivity方法:

        @Override
        public void handleStopActivity(IBinder token, boolean show, int configChanges,
                PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
            final ActivityClientRecord r = mActivities.get(token);
            r.activity.mConfigChangeFlags |= configChanges;
    
            final StopInfo stopInfo = new StopInfo();
            //callActivityOnStop方法就在这里面调用
            performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
                    reason);
    
            if (localLOGV) Slog.v(
                TAG, "Finishing stop of " + r + ": show=" + show
                + " win=" + r.window);
    
            updateVisibility(r, show);
    
            // Make sure any pending writes are now committed.
            if (!r.isPreHoneycomb()) {
                QueuedWork.waitToFinish();
            }
    
            stopInfo.setActivity(r);
            stopInfo.setState(r.state);
            stopInfo.setPersistentState(r.persistentState);
            pendingActions.setStopInfo(stopInfo);
            mSomeActivitiesChanged = true;
        }
    

    上文提到的callActivityOnStop就在performStopActivityInner里面调用。
    然后初始化一个StopInfo对象,这是一个PendingTransactionActions的内部类,实现了Runnable接口(后续会调用该对象的run方法的)。将saveInstance的state保存进去,然后存进了PendingTransactionActions对象中。这里的操作告一段落。然后我们回到TransactionExecutor里面看下调用的地方:

        /** Cycle through all states requested by callbacks and execute them at proper times. */
        @VisibleForTesting
        public void executeCallbacks(ClientTransaction transaction) {
            final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
            ...代码省略...
    
            final int size = callbacks.size();
            for (int i = 0; i < size; ++i) {
                ...代码省略...
    
                item.execute(mTransactionHandler, token, mPendingActions);
                item.postExecute(mTransactionHandler, token, mPendingActions);
             
    
               ...代码省略...
            }
        }
    

    这个方法的核心代码就是这两句,item.execute就会执行对应ActivityLifeCycleItem的execute方法。最后会执行Activity的生命周期.上文我们执行完stop方法之后,接下来应该就是执行postExecute方法了:

        @Override
        public void postExecute(ClientTransactionHandler client, IBinder token,
                PendingTransactionActions pendingActions) {
            client.reportStop(pendingActions);
        }
    

    代码很少,会执行client的reportStop方法,client是ClientTransactionHandler对象,也就是我们的ActivityThread(继承了ClientTransactionHandler)

        @Override
        public void reportStop(PendingTransactionActions pendingActions) {
            mH.post(pendingActions.getStopInfo());
        }
    

    mH是Handler对象,post的是一个Runnable对象,还记得刚才说的StopInfo么?没错就是现在post出去的那一个。我们继续看下StopInfo的run方法:

                // Tell activity manager we have been stopped.
                try {
                    if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
                    // TODO(lifecycler): Use interface callback instead of AMS.
                    ActivityTaskManager.getService().activityStopped(
                            mActivity.token, mState, mPersistentState, mDescription);
                } catch (RemoteException ex) {
                    // Dump statistics about bundle to help developers debug
                    final LogWriter writer = new LogWriter(Log.WARN, TAG);
                    final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
                    pw.println("Bundle stats:");
                    Bundle.dumpStats(pw, mState);
                    pw.println("PersistableBundle stats:");
                    Bundle.dumpStats(pw, mPersistentState);
    
                    if (ex instanceof TransactionTooLargeException
                            && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
                        Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
                        return;
                    }
                    throw ex.rethrowFromSystemServer();
                }
    

    这里会获取ATMS对象,执行activityStopped方法。那么ATMS又是什么?其实是Android29以后新增的一个管理Activity生命周期的服务端的类,就是从AMS里面抽取出来的。此处将token传了进入,我们也可以看到之前saveInstance之后的mState也传了进去。后面大致也能猜测到: 1.服务端通过Token找到对应的ActivityRecord对象. 2.更新ActivityRecord的Bundle对象。那么我们看下代码来验证下猜想吧:

        @Override
        public final void activityStopped(IBinder token, Bundle icicle,
                PersistableBundle persistentState, CharSequence description) {
            ...代码省略...
            final ActivityRecord r;
            synchronized (mGlobalLock) {
                r = ActivityRecord.isInStackLocked(token);
                if (r != null) {
                    if (r.attachedToProcess()
                            && r.isState(ActivityStack.ActivityState.RESTARTING_PROCESS)) {
                        // The activity was requested to restart from
                        // {@link #restartActivityProcessIfVisible}.
                        restartingName = r.app.mName;
                        restartingUid = r.app.mUid;
                    }
                    r.activityStoppedLocked(icicle, persistentState, description);
                }
            }
           ...代码省略...
        }
    

    r = ActivityRecord.isInStackLocked(token);这段代码印证了猜想1,确实是通过token找到对应的ActivityRecord。那么继续往下看activityStoppedLocked的处理逻辑:

        final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
                CharSequence description) {
            ...代码省略...
    
            if (newIcicle != null) {
                // If icicle is null, this is happening due to a timeout, so we haven't really saved
                // the state.
                icicle = newIcicle;
                haveState = true;
                launchCount = 0;
                updateTaskDescription(description);
            }
            ...代码省略...
        }
    

    这里就印证了猜想2,将客户端的Bundle对象保存到了服务端ActivityRecord里面。这样保存现场的操作也就完成了。

     

    保存现场数据.jpg

    接下来就是如何恢复现场了:

    onRestoreInstance

    而Activity每次可见的时候onRestoreInstance并非每次都会调用,那么什么时候调用呢,继续往下看源码:

        /**
         * The hook for {@link ActivityThread} to restore the state of this activity.
         *
         * Calls {@link #onSaveInstanceState(android.os.Bundle)} and
         * {@link #restoreManagedDialogs(android.os.Bundle)}.
         *
         * @param savedInstanceState contains the saved state
         */
        final void performRestoreInstanceState(@NonNull Bundle savedInstanceState) {
            onRestoreInstanceState(savedInstanceState);
            restoreManagedDialogs(savedInstanceState);
        }
    

    调用逻辑应该是类似的,我们可以直接进入ActivityThread查看调用方:

        @Override
        public void handleStartActivity(ActivityClientRecord r,
                PendingTransactionActions pendingActions) {
            final Activity activity = r.activity;
            if (r.activity == null) {
                // TODO(lifecycler): What do we do in this case?
                return;
            }
            if (!r.stopped) {
                throw new IllegalStateException("Can't start activity that is not stopped.");
            }
            if (r.activity.mFinished) {
                // TODO(lifecycler): How can this happen?
                return;
            }
    
            // Start
            activity.performStart("handleStartActivity");
            r.setState(ON_START);
    
            if (pendingActions == null) {
                // No more work to do.
                return;
            }
    
            // Restore instance state
            if (pendingActions.shouldRestoreInstanceState()) {
                if (r.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
    
            // Call postOnCreate()
            if (pendingActions.shouldCallOnPostCreate()) {
                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state,
                            r.persistentState);
                } else {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString()
                                    + " did not call through to super.onPostCreate()");
                }
            }
        }
    

    可以看到只有pendingActions.shouldRestoreInstanceState()为true的时候并且state不为null的时候才会调用callActivityOnRestoreInstanceState,而pendingActions.shouldRestoreInstanceState()为true的前提是Activity执行了onCreate的时候才会设置为true,而state只有在之前有过保存的时候才会不为null。因此onRestoreInstance只有在Activity新建的时候才会调用,而onSaveInstance是在Activity"看不见了"以后就会调用。所以这两个方法并不会成对调用。

    那么什么情况下state不为null呢?

    1. 在Activity退回到后台时,执行了onSaveInstance,这个时候state对象就初始化,
    2. 将state传给服务端以后,当Activity被系统杀死时,服务端会保留对应的ActivityRecord对象,该对象保存着state数据。
    3. APP重启的时候,会对应的ActiviyRecord会将state传回给客户端,让客户端恢复现场。

    题外话

    1. 背景
      很多同学都喜欢用静态变量保存全局公用的参数,这么做就可以不用再通过Intent一层一层传值,逻辑上面会简单不少。但是APP也会因为静态变量导致空指针crash。
    2. 原因
      为何?虽然静态变量拥有超长的生命周期,他可以跟整个进程共存亡,理论上来说静态变量是不会出现空指针的情况。但是一旦APP由于系统内存紧张被干掉以后,静态变量也就会存着被回收的可能,而此时一旦APP被重启以后必然需要恢复现场,这个时候就会出现静态变量空指针的异常。
    3. 解决方案
      那么怎么解决上面的问题呢?两种方案:1.去掉静态变量的使用,通过intent传值。因为intent跟内部的state一样都会在服务端保存一份,即便APP进程被杀掉以后也不会被回收掉。2.在onSaveInstance的时候将静态变量也保存起来,恢复现场的时候在给静态变量重新赋值。




    转自:https://www.jianshu.com/p/41abffdfaf77

     

    展开全文
  • 华为mt7 无论怎么样他都是重启 用as看了错误: 01-20 14:26:28.916 16803-16803/? E/MtaSDK: java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{...
  • 使用一个插件即可, 插件下载地址 ...总体步骤就是,下载插件,导入插件,重启studio,然后用数据线连接手机和电脑,再点击这个按钮就行了 前提是电脑和手机要在同一个网络, 我是用电脑开的WiFi,再用手机连上

    使用一个插件即可,

    插件下载地址

    https://plugins.jetbrains.com/plugin/7983

    或者https://github.com/pedrovgs/AndroidWiFiADB


    总体步骤就是,下载插件,导入插件,重启studio,然后用数据线连接手机和电脑,再点击这个按钮就行了

    前提是电脑和手机要在同一个网络,

    我是用电脑开的WiFi,再用手机连上这个WiFi


    下载完之后,

    file–>setting–>plugins–>Install pluginfrom disk……
    这里写图片描述

    这里写图片描述

    会提示重启一下studio,然后就可以看到下面这个小按钮了

    这里写图片描述

    然后先用数据线把电脑和手机连接一下,再点击一下,上面的那个图标

    会出现这样的提示

    这就意味着连接成功,可以拔掉数据线了,

    然后再点运行按钮就可以看到你的手机了

    展开全文
  • 当您登录 Mac 或打开某个 App 时,App 及其窗口可能会自动重新打开。如果想忽略这一步,可以采取macw小编分享的以下办法。 使用“退出登录”或“重新启动”对话框中的设置 退出登录或重新启动 Mac 时,在系统提示时...

    当您登录 Mac 或打开某个 App 时,App 及其窗口可能会自动重新打开。如果想忽略这一步,可以采取macw小编分享的以下办法。

    使用“退出登录”或“重新启动”对话框中的设置
    

    退出登录或重新启动 Mac 时,在系统提示时请取消选择“再次登录时重新打开窗口”。
    在这里插入图片描述
    或者以安全模式启动,然后正常重新启动。此操作不会更改设置,但是 Mac 不会记住上次退出登录或重新启动时打开的窗口。

    如果取消选择此设置,则 App 会继续自动打开窗口:

    • 该 App 可能是一个登录项。您可以在“用户与群组”偏好设置中添加或删除登录项。
    • 该 App 可能安装了自带的启动软件。这通常是由 App 内的设置控制的。检查 App 的偏好设置或文档,或者联系 App 开发者。

    使用“通用”偏好设置

    为防止 App 记住已打开的窗口,请选取苹果菜单  >“系统偏好设置”,然后点按“通用”并确保选择“退出 App 时关闭窗口”。
    在这里插入图片描述
    或者,您也可以在打开 App 时按住 Shift 键。此操作不会更改设置,但是 App 不会记住上次退出该 App 时打开的窗口。

    如果以上步骤不适用于某个 App,则该 App 可能自带重新打开窗口的设置。检查 App 的偏好设置或文档,或者联系 App 开发者。

    文章转载自:https://www.macw.com/news/1191.html

    展开全文
  • <div><p>打包的时候提示: warning: Ignoring InnerClasses attribute for an anonymous inner class ...这个warning问题应该怎么解决.</p><p>该提问来源于开源项目:xu-li/cordova-plugin-wechat</p></div>
  • Android studio 如何修改APP图标和名字这里有: https://blog.csdn.net/sinat_41924085/article/details/80514151 怎么改图标都不生效?? 软件安装到手机上面之后重启手机,重启重启重启!!!!!!!!!然后...
  • C:\Users\wx\AppData\Local\Microsoft\WindowsApps;;. 二月 28, 2020 12:09:59 下午 org.apache.coyote.AbstractProtocol init INFO: Initializing ProtocolHandler ["http-bio-8080"] 二月 28, 2020 12:09:59 ...
  • Android Service重启

    千次阅读 2016-04-29 17:27:35
    防止自己的app被其他的应用程序或用户手动杀死  目前有这样的解决方案。... 现在问题又来了,自己的应用怎么知道什么时候被杀死了,杀死之后又怎么重启呢?答案是,无法知道,自己也无法重启。幸好andr
  •  关于ADB server didn't ACK 的问题,在网上查了很多,重启eclipse,设置环境变量等等。。。最后我重启电脑了。还是没有用,后来在网上一篇博客中查到:5037端口被占用的话,也会出现如下提示:ADB server didn't ...
  • 关于android service 重启

    2014-11-24 14:51:53
     现在有这样的需求,防止自己的app被其他的应用程序(比如qq手机管家)杀死,该怎么实现呢。我们知道app都是运行在进程中的,android是怎样管理这些进程的呢。要想app不被杀死,只要做到进程不被结束就解决了问题。...
  • 有时候IP被限制了,怎么换IP访问,重启路由器可以换IP吗?一般家庭的基于PPPOE拨号方式上网的,使用的是动态IP,可以更换IP,下面让咱们一起去看看如何重启路由器: 1.断电源重启 如果是忘记了路由器管理密码或者是...
  • Android服务重启

    千次阅读 2014-04-21 21:39:13
    现在有这样的需求,防止自己的app被其他的应用程序(比如qq手机管家)杀死,该怎么实现呢。我们知道app都是运行在进程中的,android是怎样管理这些进程的呢。要想app不被杀死,只要做到进程不被结束就解决了问题。...
  • 今天试了spring-boot-devtool 环境是 mvn + intelliJ IDEA 然后发现怎么也无法重启,通过查看其原理: spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是自动应用代码更改到最新的App上面去...
  • 有时候IP被限制了,怎么换IP访问,重启路由器可以换IP吗?一般家庭的基于PPPOE拨号方式上网的,使用的是动态IP,可以更换IP,下面一起去看看如何重启路由器: 1、断电源重启 如果是忘记了路由器管理密码或者是不想...
  • 在集成了统计SDK(友盟统计,百度统计等)之后,有一个非常有利于测试的功能:...然而在公司android开发中不集成这些SDK,那应该怎么实现这样的功能呢?下面让我们来看下如何使用UncaughtExceptionHandler来捕获异常。
  • APP应用环境切换

    2020-05-19 16:08:20
    在开发APP过程中(这里只讨论android应用)常常会遇到4套环境["开发环境","测试环境","与生产... 环境切换后要进行数据重置,一般是采用APP重启. 相关细节优化,避免OOM等异常 以上是初步想法,那么怎么具体实现,利用a...
  • 2当代码报错的时候,会立即退出, 只能手动重新运行 python app.py 那么django, flask的代码编辑后自动重启的功能是非常实用的. 那么怎么做呢? 其实特别简单 用flask作者自己的库,一行搞定.舒心 ...
  • 现在,我们可以使用nodemon这个工具,它的作用,是监听代码文件的变动,当代码改变之后,自动重启。 二、怎么使用 1、下载安装(我这里使用npm) npm install -g nodemon 2、新建一个文件app.js,并编写下面...
  • 做手机整机测试的,肯定有开关机的需求,关机,几分钟后再开机(一直循环操作测试,就是不能重启);这个需求在关机后就没有...废话不多说,来看看脚本怎么写  # -*- coding:UTF-8 -*- import os import time time.sl
  • 遇到这种问题, 本来是想使用 es6的, 结果 nodemon 出现以下问题, 服务启动后, 监听 app.js 文件修改, 重启的售后报错, 说端口号已被占用, 意思就是没有重启成功, 而是在原来的基础上又执行了一次, 这种问题怎么解决...
  • 有大概50%概率打开app程序会一直陷入白屏,照算法来看估计是死循环,怎么设置超时 重启,比如:如果白屏(死循环)时间超过10秒什么的,就自动重启,重新计算,而不必 退出重进,直到计算结果正确、显示正常为止,请...
  • uni-app 反向代理

    千次阅读 2019-09-30 14:45:37
    我问,怎么做的? 他是baseUrl 为真实ip地址,并没有使用反向代理模式。 反向代理 看uni-app官网没有专门的介绍,但是这里有运行环境判断 如果是针对所有平台配置,可以在VUE-config.js中配置。 这里不就是vue...
  • 工厂后台设置 appid、secret、微信支付商户号和秘钥时候的 token 怎么填? 不要填! 不要填! 不要填! 重要的事情说三遍,这个小程序用不到,是给服务号使用的,所以大家空着不要填 微信支付时候,提示 50000 ...
  • 就在调试fiddler的那天,我误打误撞的发现app请求的地址怎么是我本地的呢 因为当时fiddler配置后没生效(需要重启) , 我就一直划着app才发现的 首先需要手机,电脑同一 wifi 然后给wifi设置手动代理 服务器是电脑...
  • 通常我称系统为了维持当前app运行稳定而进行内存清场动作导致后台app被强制清理的情况成为内存重启。 那么内存重启会导致的一个问题就是app被杀掉之后对应的静态变量也会被同时清理掉。那么怎么解决这个问题呢。 ...
  • 然后看着教程都对不上,求助怎么重新创建APP工程,怎么启动ADV!!!! ![图片说明](https://img-ask.csdn.net/upload/201701/12/1484198170_645884.jpg) ![图片说明]...
  • mac上App变成了大问号 今天打开电脑把之前安装的没到或者使用频率太小的的app...重启后再将app拖入到废纸篓中 【我觉得应该是第一次姿势不对导致的】 你可以先创建一个文件,将文件直接拖入废纸篓先练习下姿势 ...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 186
精华内容 74
关键字:

怎么重启app