Android进程与进程保活(涉及Notification)

Android进程与进程保活(涉及Notification)

由于内存限制,android系统会在内存不足时回收进程,
* 一、进程回收顺序:*
优先级:前台进程>可视进程>服务进程>后台进程>内容供应根节点>空进程
oom_adj越大 越可能被回收
系统进程 <0 前台进程0 可见进程1
进程级别参考:https://juejin.im/entry/58acf391ac502e007e9a0a11


1、Foreground process 前台进程

下面几种情况属于前台进程:
(1)Activity正在与用户进程交互(Activity的onResume已经被调用)
(2)与正在和用户交互的Activity绑定的Service
(3)Service运行在前台——Service中调用了startForeground函数
(4)Service正在执行生命周期回调函数(onCreate,onStart,onDestory)
(5)BroadcastReceiver正在执行onReceive方法

2、Visible process 可视进程

下面几种情况属于可视进程:
(1)Activity没有运行在前台,但是用户仍然可见(它的onPause方法被调用),例如:当前台Activity启动了一个Dialog,这样Dialog运行在前台,Activity仍然可见,属于可视进程。
(2)与一个可视的Activity绑定的服务所在的进程

3、Service process 服务进程
运行服务的进程被startService()启动,并且没有进入上面1中(3)、(4)这两种情况。例如,音乐播放、网络下载数据

4、Background process 后台进程
当Activity不可见的时候,它的进程属于后台进程(Activity的onStop方法被调用)

5、Empty process 空进程
没有包含活动应用组件的进程为空进程,也就是进程的应用组件已经运行完毕。


查看某个包的进程
Terminal 或者 cmd 进入 adb shell 模式
ps|grep 包名
第一个参数:u0_a124 当前用户
第二个参数:进程ID
第三个参数: 进程的父进程ID
第四个参数:进程的虚拟内存大小
第五个参数:实际内存大小
最后一个参数:进程名

查看某个进程的进程优先级(oom_adj)
cat /proc/进程id/oom_adj
(注意 cat后要加空格)
permission denied
要root

检查Service是否开启在前台
1 用 cat /proc/进程名/包名
查看开启前后的进程优先级变化 优先级降低了并且为0即为前台进程
2 或者
dumpsys activity services PackageName

查看 services 找到相应的services名
发现 isForeground=true 即为前台进程


进程保活常见方式

一、白色手段
开启前台Service,会在通知栏显示
通过notification方式 如音乐播放

如果希望从前台移除这个服务,只需要调用stopForeground(),一般情况我们只需要在onStartCommand里面调用 startForeground,然后再onDestroy里面调用stopForeground即可。

public class WhiteService extends Service {
    private static final String TAG = WhiteService.class.getSimpleName();
    private static final int NOTIFICATION_FLAG =0X11;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        // 在Android进行通知处理,首先需要重系统哪里获得通知管理器NotificationManager,它是一个系统Service。
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        // 设置点击通知跳转的Intent
        Intent nfIntent = new Intent(this, MainActivity.class);
        // 设置 延迟Intent 
        // 最后一个参数可以为PendingIntent.FLAG_CANCEL_CURRENT 或者 PendingIntent.FLAG_UPDATE_CURRENT
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, nfIntent, 0);

        //构建一个Notification构造器
        Notification.Builder builder = new Notification.Builder(this.getApplicationContext());

        builder.setContentIntent(pendingIntent)   // 设置点击跳转界面
                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
                        R.mipmap.fight_total_money2x)) // 设置下拉列表中的图标(大图标)
                .setTicker("您有一个notification")// statusBar上的提示
                .setContentTitle("这是标题") // 设置下拉列表里的标题
                .setSmallIcon(R.mipmap.fight_total_order2x) // 设置状态栏内的小图标24X24
                .setContentText("这是内容") // 设置详细内容
                .setContentIntent(pendingIntent) // 设置点击跳转的界面
                .setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
                .setDefaults(Notification.DEFAULT_VIBRATE) //默认震动方式
                .setPriority(Notification.PRIORITY_HIGH)   //优先级高

        Notification notification = builder.build(); // 获取构建好的Notification

        notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
        notification.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明当通知被用户点击时,通知将被清除。
        notification.flags |= FLAG_ONGOING_EVENT; //将此通知放到通知栏的"Ongoing"即"正在运行"组中
        notification.flags |= FLAG_NO_CLEAR; //表明在点击了通知栏中的"清除通知"后,此通知不清除,常与FLAG_ONGOING_EVENT一起使用


        manager.notify(NOTIFICATION_FLAG, notification);
        // 启动前台服务
        // 参数一:唯一的通知标识;参数二:通知消息。
        startForeground(NOTIFICATION_FLAG, notification);// 开始前台服务

        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 停止前台服务--参数:表示是否移除之前的通知
        stopForeground(true);
        Log.d(TAG, "onDestroy");
    }

}

开启服务:

Intent intent = new Intent(MainActivity.this,WhiteService.class);
        startService(intent);// (服务与开启者无联系的启动形式  )

结束服务:

 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                stopService(intent);
            }
        });

更新通知:
重新创建notification.builder和notification
notificationManager调用 notify方法
其实就是再创建一个notification 只是标志和之前的那个一样 这样就会更新前面的通知了


二、灰色保活
也是开启前台Service,但是不会在通知栏显示
adb shell 模式下:
dumpsys activity services PackageName
若Service有 isForeground=true 而通知栏却没有显示 则是灰色保活方式

方式:
API < 18,启动前台Service时直接传入空的 new Notification();
API >= 18,在需要提优先级的service A启动一个InnerService,两个服务同时startForeground,且绑定同样的 ID。Stop 掉InnerService ,这样通知栏图标即被移除

public class GrayService extends Service {
    private static final String TAG = GrayService.class.getSimpleName();
    private final static int GRAY_SERVICE_ID = 0x12;

    public GrayService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //API 18以下,直接发送Notification并将其置为前台
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            startForeground(GRAY_SERVICE_ID, new Notification());
        } else {
            //API 18以上,发送Notification并将其置为前台后,启动InnerService
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.fight_total_money2x);
            Notification notification = builder.build(); // 获取构建好的Notification

            startForeground(GRAY_SERVICE_ID, notification);

            startService(new Intent(this, GrayInnerService.class));
        }
        Log.d(TAG, "GrayServiceOnCreate");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 停止前台服务--参数:表示是否移除之前的通知
        stopForeground(true);
        Log.d(TAG, "onDestroy");
    }
}
public class GrayInnerService extends Service {
    private static final String TAG = GrayInnerService.class.getSimpleName();
    public static final int GRAY_INNER_SERVICE_ID=0x12;
    public GrayInnerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //发送与GrayService中ID相同的Notification,然后将其取消并取消自己的前台显示
        Notification.Builder builder = new Notification.Builder(this);
        builder.setSmallIcon(R.mipmap.fight_total_order2x);
        Notification notification = builder.build(); // 获取构建好的Notification

        startForeground(GRAY_INNER_SERVICE_ID,notification);

        // 延迟0.1s 终止掉innerService 这样 通知栏图标会清除
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                stopForeground(true);
                NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                if (manager != null) {
                    manager.cancel(GRAY_INNER_SERVICE_ID);
                }else {
                    Log.e(TAG, "notification is null!");
                }
                stopSelf();
            }
        },100);
        Log.d(TAG, "GrayInnerServiceOnCreate");
    }
}

三、黑色手段
利用不同的app进程使用广播来进行相互唤醒
(1)开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒app
最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍视频),CONNECTIVITY_ACTION(网络切换)等三种广播
(2)接入第三方SDK也会唤醒相应的app进程,如微信sdk会唤醒微信,支付宝sdk会唤醒支付宝
(3)app互相唤醒


四、白名单方式
系统给app加入白名单,系统不会杀死白名单中的app


五、开启一个像素透明的Activity方式


进程被杀死唤醒
待填

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章