关于Service总结知识


  • 前言,这些天一直在看一些以前学过的知识,突然发现好多以前的旧知识都忘记了,特此记录下来,此篇会一直更新下去.

StartService

intent = new Intent(this, MyService.class);
        startService(intent); //开启
@Override
    protected void onDestroy() {
        super.onDestroy();
        stopService(intent); //停止
    }
  • 特点: 开启服务后不依赖Activity,即使当前Activity销毁后,service依然在运行,除非调用stopservice或者stopselfe来停止服务

BindService

connection=new MyServiceConnection();
bindService(intent, connection, Service.BIND_AUTO_CREATE);
 private class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
 @Override
    protected void onDestroy() {
        super.onDestroy();
      if(connection!=null);
        unbindService(connection);
    }
  • 绑定服务后,如果当前的Activity销毁,服务也会被销毁,如果Activity销毁时必须要调用unBindService()开取消绑定,

一般进程保活套路

  当前业界的Android进程保活手段主要分为** 黑、白、灰 **三种,其大致的实现思路如下:

  • 黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)
  • 白色保活:启动前台Service
  • 灰色保活:利用系统的漏洞启动前台Service

进程划分(重要性从高到底)

前台进程

  正在使用的程序,一般系统不会杀死,除非用户强制停止或者系统内存不足等极端情况下会杀死

  1. 某个进程持有正在与用户交互的Activity并且该Activity正处于resume状态
  2. 某个进程持有一个Service,并且该Service正在与用户交互的Activity绑定
  3. 某个进程持有一个Service,并且该Service调用setForeground方法使自己位于前台
  4. 某个进程持有一个Service,并且该Service正在执行它的某个生命周期回调方法
  5. 某个进程持有一个broadcastReceiver,并且broadcastreceiver正在执行onReceiver方法
可见进程

用户正在使用,看得到,没有覆盖到整个屏幕,只有屏幕的一部分可见进程不包含任何前台进程,一般系统也是不会杀死

  1. 拥有不在前台,但仍对用户可见的Activity(一调用onPause)
  2. 拥有绑定到可见Activity的Service
服务进程
  1. 某个进程中运行着一个Service且改Service是通过StartService启动,与用户看见的界面没有直接关联
后台进程
  1. 用户按了"back"或者"Home"后,程序本身看不到了,但是其实还在运行的程序,不如Activity调用了Onpause方法
空进程
  1. 某个进程不包含任何活跃的组件时,该进程就会被设置为空进程,完全没用,杀了他只有好处没有坏处

进程保活方法

1. 开启一个1像素的Activity (前台进程保活)

//二个Activity
/****MainActivity**/
 LiveService.toLiveService(this); //开启一个service
/***********ServiceActivity*********/
public class ServiceActivity extends Activity {
  //打开自己的方法
    public static void actionStartActivity(Context context) {
        Intent intent = new Intent(context, ServiceActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service);
        Window window = getWindow();
        window.setGravity(Gravity.START | Gravity.TOP);
        WindowManager.LayoutParams params = window.getAttributes();
        params.height = 1;//设置当前的Activity为1像素
        params.width = 1;
        params.x = 0;//位置在左上角
        params.y = 0;
        window.setAttributes(params);
      //注册当前Activity用来开启或者关闭当前页面
        ScreenManager.getDefault(this).setActivity(this);
    }
}

//LiveService页面
public class LiveService extends Service {
  //开启service
    public static void toLiveService(Context context) {
        Intent intent = new Intent(context, LiveService.class);
        context.startService(intent);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        final ScreenManager aDefault = ScreenManager.getDefault(this);
      //注册广播监听锁屏或者解锁
        ScreenBroadcastListener listener = new ScreenBroadcastListener(this);
        listener.setListener(new ScreenBroadcastListener.BroadcastListener() {
            @Override
            public void screenOn() {
              //解锁
                aDefault.finshActivity();
            }

            @Override
            public void screenOff() {
              //锁屏
                aDefault.startActivity();
            }
        });
        return START_REDELIVER_INTENT;
    }
}

//开启广播页面
public class ScreenBroadcastListener {
    private BroadcastListener listener;
    private Context mContext;
    private ScreenBroadcastReceiver broadcastReceiver;

    public ScreenBroadcastListener(Context mContext) {
        this.mContext = mContext;
        broadcastReceiver = new ScreenBroadcastReceiver();
    }

    public void setListener(BroadcastListener listener) {
        this.listener = listener;
        registerListener();
    }

    private void registerListener() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        mContext.registerReceiver(broadcastReceiver, filter);
    }

    public interface BroadcastListener {
        void screenOn();

        void screenOff();
    }

    private class ScreenBroadcastReceiver extends BroadcastReceiver {
        private String action = null;

        @Override
        public void onReceive(Context context, Intent intent) {
            action = intent.getAction();
            if (Intent.ACTION_SCREEN_ON.equals(action)) {
                listener.screenOn();
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                listener.screenOff();
            }
        }
    }
}

//页面管理器
public class ScreenManager {
    private WeakReference<Activity> weakReference; //弱引用
    private Context mContext;
    private static ScreenManager screenManager;

    public static ScreenManager getDefault(Context context) {
        if (screenManager == null) {
            screenManager = new ScreenManager(context.getApplicationContext());
        }
        return screenManager;
    }

    public ScreenManager(Context mContext) {
        this.mContext = mContext;
    }

    public void setActivity(Activity activity) {
        weakReference = new WeakReference<Activity>(activity);
    }

    public void startActivity() {
        ServiceActivity.actionStartActivity(mContext);
    }

    public void finshActivity() {
        if (weakReference != null) {
            Activity activity = weakReference.get();
            if (activity != null)
                activity.finish();
        }
    }
}
//查看当前程序的进程 
adb shell  //进入设备 exit退出
ps|grep 完整包名  //查看当前程序的进程ID

u0_a79     2997    254      835760 83964    ep_poll f1e34bb9 S com.sincerity.interviewdemo
进程用户   进程ID   进程父ID   进程虚拟内存 实际驻留内存              进程名称
 
cat /proc/进程ID/oom_adj  //查看进程的优先级
adj级别 解释
UNKNOWN_ADJ 16 预留的最低级别,一般对于缓存的进程才有可能设置成这个级别
CACHED_APP_MAX_ADJ 15 缓存进程,空进程,在内存不足的情况下就会优先被kill
CACHED_APP_MIN_ADJ 9 缓存进程,也就是空进程
SERVICE_B_ADJ 8 不活跃的进程
PREVIOUS_APP_ADJ 7 切换进程
HOME_APP_ADJ 6 与Home交互的进程
SERVICE_ADJ 5 有Service的进程
VY_WEIGHT_APP_ADJ 4 高权重进程
BACKUP_APP_ADJ 3 正在备份的进程
PERCEPTIBLE_APP_ADJ 2 可感知的进程,比如那种播放音乐
VISIBLE_APP_ADJ 1 可见进程
FOREGROUND_APP_ADJ 0 前台进程
PERSISTENT_SERVICE_ADJ -11 重要进程
PERSISTENT_PROC_ADJ -12 核心进程
SYSTEM_ADJ -16 系统进程
NATIVE_ADJ -17 系统起的Native进程

不同设备的adj不同 oom_adj越大,占用物理内存越多会被最先kill掉

2. 前台服务保活(服务保活)

public class KeepLiveService extends Service {
    public static final int NOTIFICATION_ID = 0x11;

    public KeepLiveService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            startForeground(NOTIFICATION_ID, new Notification());
        } else {
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(NOTIFICATION_ID, builder.build());
            startService(new Intent(this, InnerService.class));
        }
    }

    public static class InnerService extends Service {

        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }

        @Override
        public void onCreate() {
            super.onCreate();
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(NOTIFICATION_ID, builder.build());
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopForeground(true);
                    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    manager.cancel(NOTIFICATION_ID);
                    stopSelf();
                }
            }, 200);
        }
    }
}

3. 相互唤醒

4. josSchudler

  • JobSheduler是作为进程死后复活的一种手段,native进程方式最大缺点是费电, Native 进程费电的原因是感知主进程是否存活有两种实现方式,在 Native 进程中通过死循环或定时器,轮训判断主进程是否存活,当主进程不存活时进行拉活。其次5.0以上系统不支持。 但是JobSheduler可以替代在Android5.0以上native进程方式,这种方式即使用户强制关闭,也能被拉起来
public class MyJobService extends JobService {
    @Override
    public void onCreate() {
        super.onCreate();
        startScheduler();
    }

    private void startScheduler() {
        JobInfo.Builder builder = new JobInfo.Builder(1, new ComponentName(getPackageName(), MyJobService.class.getName()));
        builder.setPeriodic(5);
        builder.setPersisted(true);
        JobScheduler scheduler = (JobScheduler) this.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        scheduler.schedule(builder.build());
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        return false;
    }

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

5. 粘性服务和系统捆绑服务

  • onStartCommand方法必须具有一个整形的返回值,这个整形的返回值用来告诉系统在服务启动完毕后,如果被Kill,系统将如何操作,这种方案虽然可以,但是在某些情况or某些定制ROM上可能失效,可以多做一种保保守方案
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_REDELIVER_INTENT;
}
  • START_STICKY :如果系统在onStartCommand返回后被销毁,系统将会重新创建服务并依次调用onCreate和onStartCommand这种相当于服务又重新启动恢复到之前的状态了

  • START_NOT_STICKY:如果系统在onStartCommand返回后被销毁,如果返回该值,则在执行完onStartCommand方法后如果Service被杀掉系统将不会重启该服务。

  • START_REDELIVER_INTENT START_STICKY的兼容版本,不同的是其不保证服务被杀后一定能重启。

  • 这里说的系统服务很好理解,比如NotificationListenerService,NotificationListenerService就是一个监听通知的服务,只要手机收到了通知,NotificationListenerService都能监听到,即时用户把进程杀死,也能重启.

    public class LiveService extends NotificationListenerService {
    
        public LiveService() {
              //自己的服务去做点事情...
        }
    
        @Override
        public void onNotificationPosted(StatusBarNotification sbn) {
        }
    
        @Override
        public void onNotificationRemoved(StatusBarNotification sbn) {
        }
    }
    //记得添加权限
    <service
       android:name=".LiveService"
        android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
           <intent-filter>
                    <action android:name="android.service.notification.NotificationListenerService" />
                </intent-filter>
            </service>
    

6. 双进程守护

  • 双进程守护的思想就是,两个进程共同运行,如果有其中一个进程被杀,那么另一个进程就会将被杀的进程重新拉起,相互保护,在一定的意义上,维持进程的不断运行。
      双进程守护的两个进程,一个进程用于我们所需的后台操作,且叫它本地进程,另一个进程只负责监听着本地进程的状态,在本地进程被杀的时候拉起,于此同时本地进程也在监听着这个进程,准备在它被杀时拉起,我们将这个进程称为远端进程。
      由于在 Android 中,两个进程之间无法直接交互,所以我们这里还要用到 AIDL (Android interface definition Language ),进行两个进程间的交互。

    /**
     * Created by Sincerity on 2019/3/27.
     * 描述:双进程守护 ,service保活手段
     */
    public class LocalService extends Service {
        private myServiceName serviceName;
    
        @Override
        public IBinder onBind(Intent intent) {
            serviceName = new myServiceName();
            return serviceName;
        }
    
        public LocalService() {
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Toast.makeText(this,  "LocalService 已经启动", Toast.LENGTH_LONG).show();
            startService(new Intent(LocalService.this, RemoteService.class));
            bindService(new Intent(LocalService.this, RemoteService.class), connection, Service.BIND_IMPORTANT);
            return START_STICKY;
        }
    
        ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
                try {
                    Log.i("LocalService", "connected with " + myAidlInterface.getServiceName());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Toast.makeText(LocalService.this, "链接断开,重新启动 RemoteService", Toast.LENGTH_SHORT).show();
                startService(new Intent(LocalService.this, RemoteService.class));
                bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
            }
        };
    
        public class myServiceName extends IMyAidlInterface.Stub {
    
            @Override
            public String getServiceName() throws RemoteException {
                return LocalService.class.getName();
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            unbindService(connection);
        }
    }
    
    /**
     * Created by Sincerity on 2019/3/27.
     * 描述:描述:双进程守护 ,service保活手段
     */
    public class RemoteService extends Service {
        private myService myService;
    
        @Override
        public IBinder onBind(Intent intent) {
            myService = new myService();
            return myService;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Toast.makeText(this,"RemoteService 启动",Toast.LENGTH_LONG).show();
            bindService(new Intent(this, LocalService.class), connection
                    , Service.BIND_IMPORTANT);
            return START_STICKY;
        }
    
        ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
                try {
                    Log.i("LocalService", "connected with " + myAidlInterface.getServiceName());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Toast.makeText(RemoteService.this, "链接断开,重新启动 LocalService", Toast.LENGTH_LONG).show();
                startService(new Intent(RemoteService.this, LocalService.class));
                bindService(new Intent(RemoteService.this, LocalService.class), connection, Context.BIND_IMPORTANT);
            }
        };
    
        public RemoteService() {
        }
    
        private class myService extends IMyAidlInterface.Stub {
    
            @Override
            public String getServiceName() throws RemoteException {
                return RemoteService.class.getName();
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            unbindService(connection);
        }
    }
    
    //测试
    @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_startService:
                    startService(new Intent(this, LocalService.class));
                    break;
                case R.id.btn_stopService:
                    stopService(new Intent(this, LocalService.class));
                    break;
                case R.id.btn_stop2Service:
                    stopService(new Intent(this, RemoteService.class));
                    break;
                case R.id.btn_checkedService:
                    Toast.makeText(this, 
                            "服务1的状态" + getServiceIsLife(LocalService.class.getName()) +
                            "服务2的状态" + getServiceIsLife(RemoteService.class.getName()), Toast.LENGTH_SHORT).show();
                    break;
            }
        }
     /**
         * 
         * @param ServiceName service.class.getname来获取
         * @return 是否存活
         */
     boolean getServiceIsLife(String ServiceName) {
            if (TextUtils.isEmpty(ServiceName)) {
                return false;
            }
            ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
            ArrayList<ActivityManager.RunningServiceInfo> infoArrayList = (ArrayList<ActivityManager.RunningServiceInfo>) manager.getRunningServices(Integer.MAX_VALUE);
            for (int i = 0; i < infoArrayList.size(); i++) {
                if (infoArrayList.get(i).service.getClassName().equals(ServiceName)) {
                    return true;
                }
            }
            return false;
        }
    

总结

  • **:有些手机厂商把这些知名的app放入了自己的白名单中,保证了进程不死来提高用户体验(如微信、QQ都在VIVO的白名单中)。如果从白名单中移除,他们终究还是和普通app一样躲避不了被杀的命运,为了尽量避免被杀,还是老老实实去做好优化工作吧.

  • 所以,进程保活的根本方案终究还是回到了性能优化上

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容