前言
目前市場(chǎng)上主流的項(xiàng)目應(yīng)用app,在其進(jìn)程被殺掉之后,還是可以繼續(xù)運(yùn)行在后臺(tái)(?;睿?;比如,微信,淘寶,釘釘,QQ等。類(lèi)似耍流氓,保證應(yīng)用進(jìn)程不被殺死。當(dāng)然優(yōu)雅的說(shuō)法:常駐進(jìn)程。不過(guò)現(xiàn)在各個(gè)手機(jī)廠商都有白名單,將應(yīng)用加入到白名單,可100%解決進(jìn)程?;畹男枨?。

差強(qiáng)人意的方法
網(wǎng)上給一些常見(jiàn)的方法:
提高優(yōu)先級(jí)
這個(gè)辦法對(duì)普通應(yīng)用而言,
應(yīng)該只是降低了應(yīng)用被殺死的概率,但是如果真的被系統(tǒng)回收了,還是無(wú)法讓?xiě)?yīng)用自動(dòng)重新啟動(dòng)!讓service.onStartCommand返回START_STICKY,START_STICKY是service被kill掉后自動(dòng)重啟
?;?00%
通過(guò)實(shí)驗(yàn)發(fā)現(xiàn),如果在adb shell當(dāng)中kill掉進(jìn)程模擬應(yīng)用被意外殺死的情況(或者用360手機(jī)衛(wèi)士進(jìn)行清理操作),
如果服務(wù)的onStartCommand返回START_STICKY,
在進(jìn)程管理器中會(huì)發(fā)現(xiàn)過(guò)一小會(huì)后被殺死的進(jìn)程的確又會(huì)出現(xiàn)在任務(wù)管理器中,貌似這是一個(gè)可行的辦法。
但是如果在系統(tǒng)設(shè)置的App管理中選擇強(qiáng)行關(guān)閉應(yīng)用,這時(shí)候會(huì)發(fā)現(xiàn)即使onStartCommand返回了START_STICKY,應(yīng)用還是沒(méi)能重新啟動(dòng)起來(lái)!android:persistent="true"
網(wǎng)上還提出了設(shè)置這個(gè)屬性的辦法,通過(guò)實(shí)驗(yàn)發(fā)現(xiàn)即使設(shè)置了這個(gè)屬性,應(yīng)用程序被kill之后還是不能重新啟動(dòng)起來(lái)的!讓?xiě)?yīng)用成為系統(tǒng)應(yīng)用
實(shí)驗(yàn)發(fā)現(xiàn)即使成為系統(tǒng)應(yīng)用,被殺死之后也不能自動(dòng)重新啟動(dòng)。
但是如果對(duì)一個(gè)系統(tǒng)應(yīng)用設(shè)置了persistent="true",情況就不一樣了。
實(shí)驗(yàn)表明對(duì)一個(gè)設(shè)置了persistent屬性的系統(tǒng)應(yīng)用,即使kill掉會(huì)立刻重啟。
一個(gè)設(shè)置了persistent="true"的系統(tǒng)應(yīng)用,
android中具有core service優(yōu)先級(jí),這種優(yōu)先級(jí)的應(yīng)用對(duì)系統(tǒng)的low memory killer是免疫的!設(shè)置鬧鐘,定時(shí)喚醒
這個(gè)效果是百分百的,但是不符合實(shí)際業(yè)務(wù)場(chǎng)景。
應(yīng)用優(yōu)先級(jí)
Android中的進(jìn)程是托管的,當(dāng)系統(tǒng)進(jìn)程空間緊張的時(shí)候,會(huì)依照優(yōu)先級(jí)自動(dòng)進(jìn)行進(jìn)程的回收
Android將進(jìn)程分為5個(gè)等級(jí),它們按優(yōu)先級(jí)順序由高到低依次是:
- 空進(jìn)程 Empty process
- 可見(jiàn)進(jìn)程 Visible process
- 服務(wù)進(jìn)程 Service process
- 后臺(tái)進(jìn)程 Background process
- 前臺(tái)進(jìn)程 Foreground process
如何在程序殺死的清下重啟進(jìn)程-----SIGLE信號(hào)
- 思路
- 利用am命令,啟動(dòng)主進(jìn)程的一個(gè)service
- SIGLE信號(hào),通過(guò)SIGLE信號(hào)來(lái)判斷程序是否被殺死
在Linux系統(tǒng)下,如果使用sigaction將信號(hào)SIGCHLD的sa_flags中的SA_NOCLDSTOP選項(xiàng)打開(kāi),
當(dāng)子進(jìn)程停止(STOP作業(yè)控制)時(shí),
不產(chǎn)生此信號(hào)(即SIGCHLD)。不過(guò),當(dāng)子進(jìn)程終止時(shí),仍舊產(chǎn)生此信號(hào)(即SIGCHLD)。
僵尸
sigaction函數(shù):
函數(shù)功能是:檢查或修改與指定信號(hào)相關(guān)聯(lián)的處理動(dòng)作
sigaction(SIGCHLD, &sa, NULL);
wait()函數(shù)
函數(shù)功能是:父進(jìn)程一旦調(diào)用了wait就立即阻塞自己,由wait自動(dòng)分析是否當(dāng)前進(jìn)程的某個(gè)子進(jìn)程已經(jīng)退出,如果讓它找到了這樣一個(gè)已經(jīng)變成僵尸的子進(jìn)程,wait就會(huì)收集這個(gè)子進(jìn)程的信息,并把它徹底銷(xiāo)毀后返回;如果沒(méi)有找到這樣一個(gè)子進(jìn)程,wait就會(huì)一直阻塞在這里,直到有一個(gè)出現(xiàn)為止。
int status;
wait(&status);
查看Android進(jìn)程

uid Android用戶id 號(hào)
pid 當(dāng)前的進(jìn)程號(hào)
ppid 當(dāng)前進(jìn)程的父進(jìn)程號(hào)

開(kāi)始擼碼
由于上面講的內(nèi)容都是在c++實(shí)現(xiàn)的,所以搞個(gè)jni工程
- 創(chuàng)建native方法
public native void watcher(String userId, int processId);
- 主進(jìn)程創(chuàng)建一個(gè)service,用來(lái)在主進(jìn)程被殺的時(shí)候,通過(guò)am命令進(jìn)行重啟主進(jìn)程
public class KeepProcessService extends Service {
private static final String TAG = "BAO";
private int i = 0;
@Override
public void onCreate() {
super.onCreate();
ProcessWatcher watcher = new ProcessWatcher();
watcher.watcher(String.valueOf(Process.myUid()), Process.myPid());
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Log.i(TAG, "服務(wù)進(jìn)程,運(yùn)行中 i = "+i);
i++;
}
}, 0, 3000);
}
......省略其他代碼
}
- C++的實(shí)現(xiàn)
const char *_user_id;
int _process_id;
//子進(jìn)程變成僵尸進(jìn)程會(huì)調(diào)用這個(gè)方法
void sig_handler(int sino) {
int status;
//阻塞式函數(shù)
LOGE("等待死亡信號(hào)");
wait(&status);
LOGE("創(chuàng)建進(jìn)程");
create_child_process();
}
extern "C"
JNIEXPORT void JNICALL
Java_com_jason_signal_process_ProcessWatcher_watcher(JNIEnv *env, jobject thiz, jstring user_id, jint process_id) {
_process_id = process_id;
_user_id = env->GetStringUTFChars(user_id, NULL);
//為了防止子進(jìn)程被弄成僵尸進(jìn)程
struct sigaction sa;
sa.sa_flags=0;
sa.sa_handler = sig_handler;
sigaction(SIGCHLD, &sa, NULL);
create_child_process();
}
void create_child_process() {
//創(chuàng)建一個(gè)子進(jìn)程
pid_t pid = fork();
if(pid < 0) {
LOGE("創(chuàng)建子進(jìn)程失敗!");
} else if(pid > 0 && pid < getppid()) {
LOGE("這個(gè)是父進(jìn)程!");
} else {
LOGE("創(chuàng)建子進(jìn)程成功!");
LOGE("進(jìn)程PID是%d", getpid());
LOGE("進(jìn)程PPID是%d", getppid());
LOGE("創(chuàng)建的子進(jìn)程ID:%d", pid);
create_process_monitor();
}
}
void *thread_fun_signal(void *data) {
//ppid 表示的是父進(jìn)程號(hào) pid表示當(dāng)前進(jìn)程號(hào)
pid_t pid;
while((pid = getppid()) != 1) {
sleep(2);
LOGE("循環(huán) %d ",pid);
}
//當(dāng)子進(jìn)程的父進(jìn)程號(hào)等于1 ,表示主進(jìn)程被殺死了,子進(jìn)程被init進(jìn)程托管了
LOGE("重啟父進(jìn)程");
// 用am命令 啟動(dòng)KeepProcessService,來(lái)啟動(dòng)主進(jìn)程
execlp("am", "am", "startservice", "--user", _user_id,
"com.jason.signal.process/com.jason.signal.process.KeepProcessService", (char*)NULL);
}
//創(chuàng)建一個(gè)線程
void create_process_monitor() {
pthread_t pt_t;
pthread_create(&pt_t, NULL, thread_fun_signal, NULL);
}
以上就是利用Android的linux內(nèi)核的signal信號(hào)來(lái),重啟被殺掉的進(jìn)程。
如何在程序殺死的清下重啟進(jìn)程-----socket方式 進(jìn)程間通信
- 思路
- 創(chuàng)建一個(gè)子進(jìn)程作為socket的的服務(wù)端
- 將主進(jìn)程作為客戶端,通過(guò)socket進(jìn)行連接,當(dāng)主進(jìn)程被殺死之后,子進(jìn)程服務(wù)端會(huì)受到一個(gè)主進(jìn)程被殺的消息,這個(gè)時(shí)候通過(guò)am命令啟動(dòng)service重新啟動(dòng)主進(jìn)程。
- 介紹函數(shù)
- int socket()函數(shù)
int socket(int protofamily, int type, int protocol);//返回sockfd
socket函數(shù)對(duì)應(yīng)于普通文件的打開(kāi)操作。普通文件的打開(kāi)操作返回一個(gè)文件描述字,而socket()用于創(chuàng)建一個(gè)socket描述符(socket descriptor),它唯一標(biāo)識(shí)一個(gè)socket。這個(gè)socket描述字跟文件描述字一樣,后續(xù)的操作都有用到它,把它作為參數(shù),通過(guò)它來(lái)進(jìn)行一些讀寫(xiě)操作。
| 參數(shù) | 說(shuō)明 |
|---|---|
| protofamily | 即協(xié)議域,又稱為協(xié)議族(family)。常用的協(xié)議族有,AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或稱AF_UNIX,Unix域socket)、AF_ROUTE等等。協(xié)議族決定了socket的地址類(lèi)型 |
| type | 指定socket類(lèi)型, 常用的socket類(lèi)型SOCK_STREAM IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它們分別對(duì)應(yīng) 流協(xié)議,TCP傳輸協(xié)議、UDP傳輸協(xié)議、 STCP傳輸協(xié)議、TIPC傳輸協(xié)議 |
| protocol | socket支持哪些協(xié)議,https://www.cnblogs.com/liyuanhong/articles/10591069.html |
- bind()函數(shù)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
參數(shù)介紹:
| 參數(shù) | 說(shuō)明 |
|---|---|
| sockfd | socket描述字,它是通過(guò)socket()函數(shù)創(chuàng)建了,唯一標(biāo)識(shí)一個(gè)socket。bind()函數(shù)就是將給這個(gè)描述字綁定一個(gè)socket |
| addr | 一個(gè)const struct sockaddr *指針,指向要綁定給sockfd的協(xié)議地址。這個(gè)地址結(jié)構(gòu)根據(jù)地址創(chuàng)建socket時(shí)的地址協(xié)議族的不同而不同 |
| addrlen | 對(duì)應(yīng)的是地址的長(zhǎng)度 |

開(kāi)始擼碼
- 創(chuàng)建native方法
public native void watch(String userId);
public native void connect();
- 同上方法創(chuàng)建service,在service的oncreate,進(jìn)行socket的創(chuàng)建和連接
watcher.watch(String.valueOf(Process.myUid()));
watcher.connect();
- C++的實(shí)現(xiàn):子進(jìn)程創(chuàng)建socket的服務(wù)單,主進(jìn)程進(jìn)行連接
int m_child;
const char *userId;
const char *PATH = "/data/data/com.jason.socket.process/my.sock";
extern "C"
JNIEXPORT void JNICALL
Java_com_jason_socket_process_Watcher_watch(JNIEnv *env, jobject thiz, jstring user_id) {
userId = env->GetStringUTFChars(user_id, NULL);
create_child_process();
}
void create_child_process() {
pid_t pid = fork();
if(pid < 0) {
} else if (pid > 0) {
} else {
do_child_work();
}
}
void do_child_work() {
//1 在子進(jìn)程建立socket服務(wù),作為服務(wù)端,等待父進(jìn)程連接
//2 讀取消息來(lái)自父進(jìn)程的消息:這邊唯一的消息是父進(jìn)程被殺掉
if(create_socket_server()) {
child_listen_msg();
}
}
int create_socket_server() {
//1 創(chuàng)建socket對(duì)象
int listenId = socket(AF_LOCAL, SOCK_STREAM, 0);
//2 斷開(kāi)之前的連接
unlink(PATH);
struct sockaddr_un addr;
//3 清空內(nèi)存
memset(&addr, 0, sizeof(sockaddr_un));
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path, PATH);
int connfd = 0;
LOGE("綁定端口號(hào)");
if(bind(listenId, (const sockaddr *) &addr, sizeof(addr))<0) {
LOGE("綁定錯(cuò)誤");
return 0;
}
//設(shè)置最大的連接數(shù)
listen(listenId, 5);
while (1) {
LOGE("子進(jìn)程循環(huán)等待連接 %d ",m_child);
// 不斷接受客戶端請(qǐng)求的數(shù)據(jù)
// 等待 客戶端連接 accept阻塞式函數(shù)
if ((connfd = accept(listenId, NULL, NULL)) < 0) {
if (errno == EINTR) {
continue;
} else{
LOGE("讀取錯(cuò)誤");
return 0;
}
}
//apk 進(jìn)程連接上了
m_child = connfd;
LOGE("apk 父進(jìn)程連接上了 %d ",m_child);
break;
}
LOGE("返回成功");
return 1;
}
void child_listen_msg() {
fd_set rfds;
while (1) {
// 清空端口號(hào)
FD_ZERO(&rfds);
// 設(shè)置新的端口號(hào)
FD_SET(m_child,&rfds);
// 設(shè)置超時(shí)時(shí)間
struct timeval timeout={3,0};
int r = select(m_child + 1, &rfds, NULL, NULL, &timeout);
LOGE("讀取消息前 %d ",r);
if (r > 0) {
char pkg[256] = {0};
// 確保讀到的內(nèi)容是制定的端口號(hào)
if (FD_ISSET(m_child, &rfds)) {
// 阻塞式函數(shù) 客戶端寫(xiě)到內(nèi)容
int result = read(m_child, pkg, sizeof(pkg));
// 讀到內(nèi)容的唯一方式 是客戶端斷開(kāi)
LOGE("重啟父進(jìn)程 %d ",result);
LOGE("讀到信息 %d userid %d ",result, userId);
execlp("am", "am", "startservice", "--user", userId,
"com.jason.socket.process/com.jason.socket.process.KeepProcessService", (char*)NULL);
break;
}
}
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_jason_socket_process_Watcher_connect(JNIEnv *env, jobject thiz) {
//主進(jìn)程socket連接父進(jìn)程
int sockfd;
struct sockaddr_un addr;
while (1) {
LOGE("客戶端 父進(jìn)程開(kāi)始連接");
sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sockfd < 0) {
return;
}
memset(&addr, 0, sizeof(sockaddr_un));
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path, PATH);
if (connect(sockfd, (const sockaddr *) &addr, sizeof(addr)) < 0) {
LOGE("連接失敗 休眠");
// 連接失敗
close(sockfd);
sleep(1);
// 再來(lái)繼續(xù)下一次嘗試
continue;
}
// 連接成功
m_parent = sockfd;
LOGE("連接成功 父進(jìn)程跳出循環(huán)");
break;
}
}
以上就是通過(guò)socket進(jìn)行進(jìn)程間通信,來(lái)實(shí)現(xiàn)進(jìn)程?;?。
結(jié)語(yǔ)
上面兩種進(jìn)程被殺重啟的方式,只能實(shí)現(xiàn)支持大部分的手機(jī),有部分廠商進(jìn)行底層修改。這兩種只是提供了兩種思路方案。