Android app主动杀死进程并重启

Posted herozqtc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android app主动杀死进程并重启相关的知识,希望对你有一定的参考价值。

最近出现一个bug,这个bug还是很常见的很容易实现,在设计代码的时候还是需要注意的,就是在app授权location定位权限之后,将app放在后台,在设置里对app的权限关闭操作,再从后台打开app,app会crash。

这里的问题原因就是当settings里关闭了权限之后,安卓系统会主动关闭app进程,使得重新打开app时,之前保存的app的activity页面是被异常关闭的,会触发 onCreate方法的参数savedInstanceState: Bundle不为空


同时之前内存中的累的对象不会保证数据的完整和正确被索引到,在我的例子里就是cache的对象被销毁了,重新打开app没有经过正常的初始化流程,导致crash。

搜集了资料发现,即使是微信也不可避免系统异常杀死当前process的行为,只能使用重启的操作重启app来规范这样的现象。

下面是总结的几个常用的app重启的方案:

    /**
     * 使用 AlarmManager 来帮助重启
     *
     * @param context
     * @param cls
     */
    public static void restartByAlarm(Context context, Class<?> cls)
    
        Intent mStartActivity = new Intent(context, cls);
        int mPendingIntentId = 123456;
        PendingIntent pIntent = PendingIntent.getActivity(context, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 500, pIntent);

        System.exit(0);
    
	
	 /**
     * 使用 killProcess 杀死自身,系统会恢复应用
     *
     * @param context
     * @param cls
     */
    public static void restartByKillProcess(Context context, Class<?> cls)
    
        Intent intent = new Intent(context, cls);
        intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
        android.os.Process.killProcess(android.os.Process.myPid());
    

    /**
     * 通过清栈触发应用重启。但不会重启 application ,与应用相关的静态变量也会更重启前一样。
     *
     * @param context
     */
    public static void restartByClearTop(Context context)
    
        Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        context.startActivity(intent);
    

    /**
     * 利用系统重启api触发应用重启
     *
     * @param context
     */
    public static void restartBySystemApi(Context context)
    
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        manager.restartPackage(context.getPackageName());
    

    /**
     * 通过 Intent.makeRestartActivityTask 来触发应用重启,跟 restartByClearTop 类似。
     * 但不会重启 application ,与应用相关的静态变量也会更重启前一样。
     *
     * @param context
     */
    public static void restartByCompatApi(Context context, Class<?> cls)
    
        Intent intent = new Intent(context, cls);
        Intent restartIntent = Intent.makeRestartActivityTask(intent.getComponent());
        context.startActivity(restartIntent);
        System.exit(0);
    

    /**
     * 5.1 版本以后可以借助 JobScheduler 来重启应用
     *
     * @param context
     */
    public static void restartByJobScheduler(Context context, Class<?> cls)
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
        
            int delayTimeMin = 1000;
            int delayTimeMax = 2000;

            MyJobSchedulerService.setMainIntent(new Intent(context, cls));

            JobInfo.Builder jobInfoBuild = new JobInfo.Builder(0, new ComponentName(context, MyJobSchedulerService.class));
            jobInfoBuild.setMinimumLatency(delayTimeMin);
            jobInfoBuild.setOverrideDeadline(delayTimeMax);
            JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
            jobScheduler.schedule(jobInfoBuild.build());

            System.exit(0);
        
    

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    static class MyJobSchedulerService extends JobService
    
        private static Intent mIntent;

        public static void setMainIntent(Intent intent)
        
            mIntent = intent;
        

        @Override
        public boolean onStartJob(JobParameters params)
        
            startActivity(mIntent);
            jobFinished(params, false);
            return false;
        

        @Override
        public boolean onStopJob(JobParameters params)
        
            return false;
        
    

但是在使用第二种方案的时候,发现在加了判断savedInstanceState不为空的时候,重启之后savedInstanceState还是不为空,进入死循环。是因为单纯的重启app还是会保留上一次的页面的参数,就是之前系统异常杀死的时候的参数
    

解决办法就是在重启之前调用一次finish()方法,只有这样,下次启动页面savedInstanceState的值就重置为null了。
    

最终kotlin的写法是:

if (savedInstanceState != null) 
   finish()
   val intent = Intent(this, MainActivity::class.java)
   intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
   startActivity(intent)
   exitProcess(0)

但是这样还是有个问题,就是点击后台app重启,现象是会先杀掉,再重新打开app,会有个弹出的动画,给人一种启动两次的样子。


        问题保留,以后深究。

systemd `systemctl stop` 主动杀死子进程

【中文标题】systemd `systemctl stop` 主动杀死子进程【英文标题】:systemd `systemctl stop` aggressively kills subprocesses 【发布时间】:2017-04-15 08:23:21 【问题描述】:

我有一个类似守护进程的进程,它启动两个子进程(其中一个子进程启动约 10 个其他子进程)。当我systemctl stop 我的进程时,子子进程似乎被systemctl '积极地'杀死 - 这并没有给我的进程清理的机会。

我如何让systemctl stop 退出积极的杀戮,从而让我的进程安排有序的清理?

我试过timeoutSec=30 无济于事。

【问题讨论】:

您查看过man systemd.servicesman systemd.kill 中的文档吗? 【参考方案1】:

KillMode= [1] 默认为control-group。这意味着您服务的每个进程都会被 SIGTERM 杀死。

你有两个选择:

在每个进程中处理 SIGTERM 并在 TimeoutStopSec 内关闭(默认为 90 秒 [2]) 如果你真的想从你的主进程中委托关闭,设置KillMode=mixed。 SIGTERM 将仅发送到主进程。然后在TimeoutStopSec 内再次关机。如果您没有在TimeoutStopSec 内关闭,systemd 会将SIGKILL 发送到您的所有进程。

注意:我建议在选项 2 中使用 KillMode=mixed 而不是 KillMode=process,因为后者只会将最终的 SIGKILL 发送到您的主进程,这意味着您的子进程不会被杀死锁起来了。

[1]https://www.freedesktop.org/software/systemd/man/systemd.kill.html#KillMode=

[2]https://www.freedesktop.org/software/systemd/man/systemd-system.conf.html#DefaultTimeoutStartSec=

【讨论】:

【参考方案2】:

一个迟到的(可能的)答案,但是当我用谷歌搜索了几周的类似问题时,什么也没找到,我想我添加了我的解决方案。 我的错误是我以 root 身份运行 systemd 单元并(使用 sudo)切换到 startscript(继承自 SysVinit 脚本)中的“正确”用户。 这将启动 user.slice 中的进程,该进程在关机时被无情地杀死。当我将单元文件更改为以正确的用户 (USER=myuser) 身份运行并从启动脚本中删除 sudo 时,进程在 system.slice 中启动并在关机时得到正确处理。

【讨论】:

以上是关于Android app主动杀死进程并重启的主要内容,如果未能解决你的问题,请参考以下文章

使用极光/友盟推送,APP进程杀死后为啥收不到推送

我的 Android 小部件被杀死,“不再需要 bellander.andro ....”

systemd `systemctl stop` 主动杀死子进程

Android进程保活-自“裁”或者耍流氓

Android进程经常产生logcat“已死亡”消息。

Android P 性能优化:创建APP进程白名单,杀死白名单之外的进程