什么是进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。
Android Service
Service是Android系统中的四大组件之一,它是一种长生命周期的,没有可视化界面,运行于后台的一种服务程序。
当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。 同时,Android会为每个应用程序分配一个单独的LINUX用户。Android会尽量保留一个正在运行进程,只在内存资源出现不足时,Android会尝试停止一些进程从而释放足够的资源给其他新的进程使用, 也能保证用户正在访问的当前进程有足够的资源去及时地响应用户的事件。
我们可以将一些组件运行在其他进程中,并且可以为任意的进程添加线程。组件运行在哪个进程中是在manifest文件里设置的,其中<Activity>,<Service>,<receiver>和<provider>都有一个process属性来指定该组件运行在哪个进程之中。我们可以设置这个属性,使得每个组件运行在它们自己的进程中,或是几个组件共同享用一个进程,或是不共同享用。<application>元素也有一个process属性,用来指定所有的组件的默认属性。
Service的主要作用:
- 后台运行
Service没有自己的界面,用户察觉不到,看似在后台运行。默认情况下Service是运行在主线程中的,为了不造成主线程阻塞,所以在Service中要处理耗时操作需要放到其他线程中去处理。 - 跨进程通信
由于进程与进程之间是独立分开的,一个进程不能访问另外一个进程的资源。Android提供了一种跨进程通信的方式AIDL。这种方式通过一个进程绑定另一个进程的Binder Service来实现。
进程优先级
Android系统会尽可能长的延续一个应用程序进程,但在内存过低的时候,仍然会不可避免需要移除旧的进程。为了决定哪些进程留下,哪些进程被杀死,系统根据在进程中在运行的组件及组件的状态,为每一个进程分配了一个优先级等级。优先级最低的进程首先被杀死。这个进程重要性的层次结构主要有五个等级:
- 前台进程
- 可见进程
- 服务进程
- 后台进程
- 空进程
前台进程
前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说, 在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。因为杀死前台进行需要用户交互,所以前台进程优先级是最高的。
前台进程常见情形:
- 进程持有一个正在与用户交互的Activity(这个Activity的onResume方法被调用)
-
进程持有一个Service,这个Service处于以下几种状态
- Service与用户正在交互的Activity绑定
- Service是在前台运行的(其调用了startForeground())
- Service正在执行其生命周期回调方法(onCreate(),onStart(),onDestroy())
- 进程持有一个BroadcastReceiver正在执行其onReceive方法
可见进程
如果一个进程不含有任何前台的组件,但仍然可被用户再屏幕上所见。可见进程也被认为很重要的,一般不会被销毁,除非是为了保证所有前台进程的运行而不得不杀死可见进程的时候。
可见进程常见情形:
进程持有一个Activity,这个Activity不在前台,但是仍然被用户可见(onPause)
进程持有一个Service,这个Service和一个可见的(或前台的)Activity绑定。
服务进程
如果一个进程中运行着一个Service,这个Service是通过startService()开启的,并且不属于上面两种较高优先级的情况,这个进程就是一个服务进程。
尽管服务进程没有和用户可以看到的东西绑定,但是它们一般在做的事情是用户关心的,比如后台播放音乐,后台下载数据等。所以系统会尽量维持它们的运行,除非系统内存不足以维持前台进程和可见进程的运行需要。
后台进程
如果进程不属于上面三种情况,但是进程持有一个用户不可见的activity(activity的onStop()被调用,但是onDestroy()没有调用的状态),就认为进程是一个后台进程。
后台进程不直接影响用户体验,系统会为了前台进程、可见进程、服务进程而任意杀死后台进程。
通常会有很多个后台进程存在,它们会被保存在一个LRU (least recently used)列表中,这样就可以确保用户最近使用的activity最后被销毁,即最先销毁时间最远的activity。
空进程
如果一个进程不包含任何活跃的应用组件,则认为是空进程。
例如:一个进程当中已经没有数据在运行了,但是内存当中还为这个应用驻留了一个进程空间。
保存这种进程的唯一理由是为了缓存的需要,为了加快下次要启动这个进程中的组件时的启动时间。系统为了平衡进程缓存和底层内核缓存的资源,经常会杀死空进程。
使用进程的优先级
Android 对进程的重要性评级的时候,选取它最高的级别。例如,如果一个进程含有一个service和一个可视activity,进程将被归入一个可视进程而不是service进程。
另外,当被某个进程被另外的一个进程依赖的时候,它的级别可能会增高。也就意味着一个为其他进程服务的进程永远会比那些被服务的进程重要级高。
因为服务进程比后台进程重要级高,因此一个要进行耗时工作的Activity最好启动一个Service来做这个工作,而不是在Activity中开启一个子线程――特别是这个操作需要的时间比Activity存在的时间还要长的时候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用Service可以让其进程至少获取到服务进程级别的重要级,而不用考虑Activity目前是什么状态。BroadcastReceiver做费时的工作的时候,也应该启用一个服务而不是开一个线程。
进程防杀
提高进程优先级
提升进程优先级至当前进程
之前小米工程师在微博上爆出QQ为了保活,采取在锁屏的时候启动一个1个像素的Activity,当用户解锁以后将这个Activity结束掉(顺便同时把自己的核心服务再开启一次)。这种做法很不容易被用户发现。
修改Manifest android:priority
在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = “1000”这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。
Service提升为前台进程
在onStartCommand里面调用 startForeground()方法把Service提升为前台进程级别,然后在onDestroy里面要记得调用stopForeground ()方法。
Service onStartCommand 返回 START_STICKY。
在Service的onStartCommand方法中手动返回START_STICKY,亲测当service因内存不足被kill,当内存又有的时候,service又被重新创建,比较不错,但是不能保证任何情况下都被重建,比如进程被干掉了。
补充说明:onStartCommand()方法,返回的是一个int整形。
这个整形可以有以下四个取值:
- START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
- START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务
- START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
- START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
在onDestroy方法里发广播重启service
service + broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service。(第三方应用或是在setting里-应用-强制停止时,APP进程就直接被干掉了,onDestroy方法都进不来,所以无法保证会执行)
监听系统广播判断Service状态
通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活,如果我们的Service不存活时就启动起来。
Application加上Persistent属性
看Android的文档知道,当进程长期不活动,或系统需要资源时,会自动清理门户,杀死一些Service,和不可见的Activity等所在的进程。但是如果某个进程不想被杀死(如数据缓存进程,或状态监控进程,或远程服务进程),应该怎么做,才能使进程不被杀死。
add android:persistent=”true” into the section in your AndroidManifest.xml
加上以上属性相当于将该进程设置为常驻内存进程。
切记,这个不可滥用,一般只适用于放在/system/app下的app,系统中用这个的service,app一多,整个系统可能就会崩溃。
比如系统phone中配置了android:persistent=”true”属性,并且Phone.apk是安装在/system/app/目录下的,所以在开机时会自动启动PhoneApp类。
手机厂商白名单
app运营商和某些手机厂商可能有合作关系,让自家的进程怎么杀也杀不死,让自家的应用卸载不了。
双进程守护
上述防进程被杀的方法,也并不能完全保证进程不被系统杀死。大多做法都能被用户在Setting设置里给停掉,或者被用户安装的清理软件给杀掉。
那么怎么才能让我们的进程被杀掉后再活过来呢?想必大家都听说过双进程守护的方式吧。具体实现原理是使用两个进程来相互监听存活状态,相互启动。为何要使用两个进程呢?
我们都知道在Android中让两个进程相互通信使用的是AIDL技术。所以我们需要用到Service的远程绑定,让每个进程中都启动一个Service,启动之后再与另一个进程的Service进行绑定,绑定成功之后监听对面Service的状态。如果其中一个Service被干掉了,另一个Service会再通过AIDL把被干掉的Service启动起来。
以下是具体实现的一个例子,在同一个程序内开启了两个服务,LocalService,RemoteService。这两个服务分别属于不同进程。
项目结构:
首先定义一个AIDL文件:
// IKeepLiveConnection.aidl
package com.congwiny.keepliveprocess;
// Declare any non-default types here with import statements
interface IKeepLiveConnection {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.congwiny.keepliveprocess">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".LocalService"/>
<service
android:name=".RemoteService"
android:process=":remote" />
</application>
</manifest>
MainActivity
package com.congwiny.keepliveprocess;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this,LocalService.class));
startService(new Intent(this,RemoteService.class));
}
}
LocalService
package com.congwiny.keepliveprocess;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class LocalService extends Service {
private static final String TAG = LocalService.class.getSimpleName();
private KeepLiveBinder binder;
private KeepLiveServiceConnection conn;
@Override
public void onCreate() {
super.onCreate();
binder = new KeepLiveBinder();
conn = new KeepLiveServiceConnection();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent intentNew = new Intent();
intentNew.setClass(this, RemoteService.class);
bindService(intentNew, conn, BIND_IMPORTANT);
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
private class KeepLiveServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "remote service onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "remote service onServiceDisconnected");
//重新绑定RemoteService
Intent intent = new Intent();
intent.setClass(LocalService.this, RemoteService.class);
startService(intent);
bindService(intent, conn, BIND_IMPORTANT);
}
}
@Override
public void onDestroy() {
super.onDestroy();
unbindService(conn);
}
private class KeepLiveBinder extends IKeepLiveConnection.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
}
RemoteService
package com.congwiny.keepliveprocess;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class RemoteService extends Service {
private static final String TAG = RemoteService.class.getSimpleName();
private KeepLiveBinder binder;
private KeepLiveServiceConnection conn;
@Override
public void onCreate() {
super.onCreate();
binder = new KeepLiveBinder();
conn = new KeepLiveServiceConnection();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent intentNew = new Intent();
intentNew.setClass(this, LocalService.class);
bindService(intentNew, conn, BIND_IMPORTANT);
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
private class KeepLiveServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "local service onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "local service onServiceDisconnected");
Intent intent = new Intent();
intent.setClass(RemoteService.this, LocalService.class);
//LocalService已经挂了,先必须先启动,后绑定
startService(intent);
bindService(intent, conn, BIND_IMPORTANT);
}
}
@Override
public void onDestroy() {
super.onDestroy();
//解绑,否则会出现 leaked ServiceConnection
unbindService(conn);
}
private class KeepLiveBinder extends IKeepLiveConnection.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
}
运行效果图:
JobScheduler
JobScheduler是Android5.0新增的API,JobScheduler API允许开发者在符合某些条件时创建执行在后台的任务。具体细节这里就不做描述。参考
在Android 5.0中使用JobScheduler
我们为什么要使用这个API呢?
当我们手动点击找到该应用信息页面,有个结束运行或者强行停止按钮,如果我们点击这个按钮后,我们的双进程守护也不管用了,我们的程序的进程会被强制杀死,再也启动不起来。
通过实际操作不同的手机,有的手机中运行的进程被杀死后,能在使用JobScheduler的情况下启动,而有些手机是不可以的。
代码实现:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.congwiny.keepliveprocess">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".LocalService"/>
<service
android:name=".RemoteService"
android:process=":remote" />
<service android:name=".JobSchedulerService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
</application>
</manifest>
package com.congwiny.keepliveprocess;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this,LocalService.class));
startService(new Intent(this,RemoteService.class));
startService(new Intent(this, JobSchedulerService.class));
}
}
package com.congwiny.keepliveprocess;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
@SuppressLint("NewApi")
public class JobSchedulerService extends JobService {
private static final String TAG = JobSchedulerService.class.getSimpleName();
private int kJobId = 0;
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
scheduleJob(getJobInfo());
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public boolean onStartJob(JobParameters params) {
Log.e(TAG, "onStartJob");
boolean isLocalServiceWork = isServiceWork(this, "com.congwiny.keepliveprocess.LocalService");
boolean isRemoteServiceWork = isServiceWork(this, "com.congwiny.keepliveprocess.RemoteService");
if (!isLocalServiceWork ||
!isRemoteServiceWork) {
this.startService(new Intent(this, LocalService.class));
this.startService(new Intent(this, RemoteService.class));
Toast.makeText(this, "job scheduler service start", Toast.LENGTH_SHORT).show();
}
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.e(TAG, "onStopJob");
Toast.makeText(this, "job scheduler service stop", Toast.LENGTH_SHORT).show();
scheduleJob(getJobInfo());
return true;
}
/**
* Send job to the JobScheduler.
*/
public void scheduleJob(JobInfo t) {
JobScheduler tm =
(JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.schedule(t);
}
public JobInfo getJobInfo() {
JobInfo.Builder builder = new JobInfo.Builder(kJobId++, new ComponentName(this, JobSchedulerService.class));
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
builder.setPersisted(true);
builder.setRequiresCharging(false);
builder.setRequiresDeviceIdle(false);
builder.setPeriodic(1000);//间隔时间--周期
return builder.build();
}
/**
* 判断某个服务是否正在运行的方法
*
* @return true代表正在运行,false代表服务没有正在运行
*/
public boolean isServiceWork(Context mContext, String serviceName) {
boolean isWork = false;
ActivityManager myAM = (ActivityManager) mContext
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningServiceInfo> myList = myAM.getRunningServices(100);
if (myList.size() <= 0) {
return false;
}
for (int i = 0; i < myList.size(); i++) {
String mName = myList.get(i).service.getClassName().toString();
if (mName.equals(serviceName)) {
isWork = true;
break;
}
}
return isWork;
}
}
扩展
Service和Thread的关系
Android中Service和Thread有什么区别,为什么有时候放着方便的Thread不用,而要使用Service呢?
要解答这个问题,首先我们要弄明白Service和Thread分别是怎么定义的:
Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
Service是Android的四大组件之一,被用来执行长时间的后台任务。默认情况下Service是运行在主线程中的。
二者的使用上的区别
在android中,Thread只是一个用来执行后台任务的工具类,它可以在Activity中被创建,也可以在Service中被创建。
Service组件主要有两个作用:后台运行和跨进程访问。service可以在android系统后台独立运行并且可以跨进程运行,线程是不可以。
如果需要执行复杂耗时的操作,必须在Service中再创建一个Thread来执行任务。Service的优先级高于后台挂起的Activity,当然也高于Activity所创建的Thread,因此,系统可能在内存不足的时候优先杀死后台的Activity或者Thread,而不会轻易杀死Service组件,即使被迫杀死Service,也会在资源可用时重启被杀死的Service。
Activity和Service是否处于同一进程
默认同一个应用程序的Activity和Service都属于同一个进程,Activity和Service都运行在主线程里。但如果在AndroidManifest.xml中给Service设置android:process="xxx",此Service就是属于"xxx"进程了。
总结
上述进程防杀给出一些思路,由于Android手机定制机型太多,有些底层已经被改,所以上述方法并不能保证在每个手机上都行的通,只是做个参考而已。进程防杀这个话题,引出的知识点还是很多,以前没有好好总结过,不成系统,现在总结出来,扩充自己的系统,以备自己以后复习使用。下面参考的一些文章有时间的话就学习下。