亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android通過(guò)JNI實(shí)現(xiàn)守護(hù)進(jìn)程

 更新時(shí)間:2016年09月13日 16:29:23   作者:LeBron_Six  
這篇文章主要為大家詳細(xì)介紹了Android通過(guò)JNI實(shí)現(xiàn)守護(hù)進(jìn)程的相關(guān)資料,感興趣的小伙伴們可以參考一下

開(kāi)發(fā)一個(gè)需要常住后臺(tái)的App其實(shí)是一件非常頭疼的事情,不僅要應(yīng)對(duì)國(guó)內(nèi)各大廠(chǎng)商的ROM,還需要應(yīng)對(duì)各類(lèi)的安全管家...雖然不斷的研究各式各樣的方法,但是效果并不好,比如任務(wù)管理器把App干掉,服務(wù)就起不來(lái)了...

網(wǎng)上搜尋一番后,主要的方法有以下幾種方法,但都是治標(biāo)不治本:

1、提高Service的優(yōu)先級(jí):這個(gè),也只能說(shuō)在系統(tǒng)內(nèi)存不足需要回收資源的時(shí)候,優(yōu)先級(jí)較高,不容易被回收,然并卵...

2、提高Service所在進(jìn)程的優(yōu)先級(jí):效果不是很明顯

3、在onDestroy方法里重啟service:這個(gè)倒還算挺有效的一個(gè)方法,但是,直接干掉進(jìn)程的時(shí)候,onDestroy方法都進(jìn)不來(lái),更別想重啟了

4、broadcast廣播:和第3種一樣,沒(méi)進(jìn)入onDestroy,就不知道什么時(shí)候發(fā)廣播了,另外,在Android4.4以上,程序完全退出后,就不好接收廣播了,需要在發(fā)廣播的地方特定處理

5、放到System/app底下作為系統(tǒng)應(yīng)用:這個(gè)也就是平時(shí)玩玩,沒(méi)多大的實(shí)際意義。

6、Service的onStartCommand方法,返回START_STICKY,這個(gè)也主要是針對(duì)系統(tǒng)資源不足而導(dǎo)致的服務(wù)被關(guān)閉,還是有一定的道理的。

應(yīng)對(duì)的方法是有,實(shí)現(xiàn)起來(lái)都比較繁瑣。如果你自己可以定制ROM,那就有很多種辦法了,比如把你的應(yīng)用加入白名單,或是多安裝一個(gè)沒(méi)有圖標(biāo)的app作為守護(hù)進(jìn)程...但是,哪能什么都是定制的,對(duì)于安卓開(kāi)發(fā)者來(lái)說(shuō),這個(gè)難題必須攻破~

那么,有沒(méi)有辦法在一個(gè)APP里面,開(kāi)啟一個(gè)子線(xiàn)程,在主線(xiàn)程被干掉了之后,子線(xiàn)程通過(guò)監(jiān)聽(tīng)、輪詢(xún)等方式去判斷服務(wù)是否存在,不存在的話(huà)則開(kāi)啟服務(wù)。答案自然是肯定的,通過(guò)JNI的方式(NDK編程),fork()出一個(gè)子線(xiàn)程作為守護(hù)進(jìn)程,輪詢(xún)監(jiān)聽(tīng)服務(wù)狀態(tài)。守護(hù)進(jìn)程(Daemon)是運(yùn)行在后臺(tái)的一種特殊進(jìn)程。它獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。而守護(hù)進(jìn)程的會(huì)話(huà)組和當(dāng)前目錄,文件描述符都是獨(dú)立的。后臺(tái)運(yùn)行只是終端進(jìn)行了一次fork,讓程序在后臺(tái)執(zhí)行,這些都沒(méi)有改變。

那么我們先來(lái)看看Android4.4的源碼,ActivityManagerService(源碼/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)是如何關(guān)閉在應(yīng)用退出后清理內(nèi)存的:

Process.killProcessQuiet(pid); 

應(yīng)用退出后,ActivityManagerService就把主進(jìn)程給殺死了,但是,在Android5.0中,ActivityManagerService卻是這樣處理的:

Process.killProcessQuiet(app.pid); 
Process.killProcessGroup(app.info.uid, app.pid); 

就差了一句話(huà),卻差別很大。Android5.0在應(yīng)用退出后,ActivityManagerService不僅把主進(jìn)程給殺死,另外把主進(jìn)程所屬的進(jìn)程組一并殺死,這樣一來(lái),由于子進(jìn)程和主進(jìn)程在同一進(jìn)程組,子進(jìn)程在做的事情,也就停止了...要不怎么說(shuō)Android5.0在安全方面做了很多更新呢...

那么,有沒(méi)有辦法讓子進(jìn)程脫離出來(lái),不要受到主進(jìn)程的影響,當(dāng)然也是可以的。那么,在C/C++層是如何實(shí)現(xiàn)的呢?先上關(guān)鍵代碼:

/**
 * srvname 進(jìn)程名
 * sd 之前創(chuàng)建子進(jìn)程的pid寫(xiě)入的文件路徑
 */
int start(int argc, char* srvname, char* sd) {
 pthread_t id;
 int ret;
 struct rlimit r;

 int pid = fork();
 LOGI("fork pid: %d", pid);
 if (pid < 0) {
 LOGI("first fork() error pid %d,so exit", pid);
 exit(0);
 } else if (pid != 0) {
 LOGI("first fork(): I'am father pid=%d", getpid());
 //exit(0);
 } else { // 第一個(gè)子進(jìn)程
 LOGI("first fork(): I'am child pid=%d", getpid());
 setsid();
 LOGI("first fork(): setsid=%d", setsid());
 umask(0); //為文件賦予更多的權(quán)限,因?yàn)槔^承來(lái)的文件可能某些權(quán)限被屏蔽

 int pid = fork();
 if (pid == 0) { // 第二個(gè)子進(jìn)程
 // 這里實(shí)際上為了防止重復(fù)開(kāi)啟線(xiàn)程,應(yīng)該要有相應(yīng)處理

 LOGI("I'am child-child pid=%d", getpid());
 chdir("/"); //<span style="font-family: Arial, Helvetica, sans-serif;">修改進(jìn)程工作目錄為根目錄,chdir(“/”)</span>
 //關(guān)閉不需要的從父進(jìn)程繼承過(guò)來(lái)的文件描述符。
 if (r.rlim_max == RLIM_INFINITY) {
 r.rlim_max = 1024;
 }
 int i;
 for (i = 0; i < r.rlim_max; i++) {
 close(i);
 }

 umask(0);
 ret = pthread_create(&id, NULL, (void *) thread, srvname); // 開(kāi)啟線(xiàn)程,輪詢(xún)?nèi)ケO(jiān)聽(tīng)啟動(dòng)服務(wù)
 if (ret != 0) {
 printf("Create pthread error!\n");
 exit(1);
 }
 int stdfd = open ("/dev/null", O_RDWR);
 dup2(stdfd, STDOUT_FILENO);
 dup2(stdfd, STDERR_FILENO);
 } else {
 exit(0);
 }
 }
 return 0;
}

/**
 * 啟動(dòng)Service
 */
void Java_com_yyh_fork_NativeRuntime_startService(JNIEnv* env, jobject thiz,
 jstring cchrptr_ProcessName, jstring sdpath) {
 char * rtn = jstringTostring(env, cchrptr_ProcessName); // 得到進(jìn)程名稱(chēng)
 char * sd = jstringTostring(env, sdpath);
 LOGI("Java_com_yyh_fork_NativeRuntime_startService run....ProcessName:%s", rtn);
 a = rtn;
 start(1, rtn, sd);
}

這里有幾個(gè)重點(diǎn)需要理解一下:

1、為什么要fork兩次?第一次fork的作用是為后面setsid服務(wù)。setsid的調(diào)用者不能是進(jìn)程組組長(zhǎng)(group leader),而第一次調(diào)用的時(shí)候父進(jìn)程是進(jìn)程組組長(zhǎng)。第二次調(diào)用后,把前面一次fork出來(lái)的子進(jìn)程退出,這樣第二次fork出來(lái)的子進(jìn)程,就和他們脫離了關(guān)系。

2、setsid()作用是什么?setsid() 使得第二個(gè)子進(jìn)程是會(huì)話(huà)組長(zhǎng)(sid==pid),也是進(jìn)程組組長(zhǎng)(pgid == pid),并且脫離了原來(lái)控制終端。故不管控制終端怎么操作,新的進(jìn)程正常情況下不會(huì)收到他發(fā)出來(lái)的這些信號(hào)。

3、umask(0)的作用:由于子進(jìn)程從父進(jìn)程繼承下來(lái)的一些東西,可能并未把權(quán)限繼承下來(lái),所以要賦予他更高的權(quán)限,便于子進(jìn)程操作。

4、chdir ("/");作用:進(jìn)程活動(dòng)時(shí),其工作目錄所在的文件系統(tǒng)不能卸下,一般需要將工作目錄改變到根目錄。

5、進(jìn)程從創(chuàng)建它的父進(jìn)程那里繼承了打開(kāi)的文件描述符。如不關(guān)閉,將會(huì)浪費(fèi)系統(tǒng)資源,造成進(jìn)程所在的文件系統(tǒng)無(wú)法卸下以及引起無(wú)法預(yù)料的錯(cuò)誤。所以在最后,記得關(guān)閉掉從父進(jìn)程繼承過(guò)來(lái)的文件描述符。

然后,在上面的代碼中開(kāi)啟線(xiàn)程后做的事,就是循環(huán)去startService(),代碼如下:

void thread(char* srvname) {
 while(1){
 check_and_restart_service(srvname); // 應(yīng)該要去判斷service狀態(tài),這里一直restart 是不足之處
 sleep(4);
 }
}

/**
 * 檢測(cè)服務(wù),如果不存在服務(wù)則啟動(dòng).
 * 通過(guò)am命令啟動(dòng)一個(gè)laucher服務(wù),由laucher服務(wù)負(fù)責(zé)進(jìn)行主服務(wù)的檢測(cè),laucher服務(wù)在檢測(cè)后自動(dòng)退出
 */
void check_and_restart_service(char* service) {
 LOGI("當(dāng)前所在的進(jìn)程pid=",getpid());
 char cmdline[200];
 sprintf(cmdline, "am startservice --user 0 -n %s", service);
 char tmp[200];
 sprintf(tmp, "cmd=%s", cmdline);
 ExecuteCommandWithPopen(cmdline, tmp, 200);
 LOGI( tmp, LOG);
}  

/**
 * 執(zhí)行命令
 */
void ExecuteCommandWithPopen(char* command, char* out_result,
 int resultBufferSize) {
 FILE * fp;
 out_result[resultBufferSize - 1] = '\0';
 fp = popen(command, "r");
 if (fp) {
 fgets(out_result, resultBufferSize - 1, fp);
 out_result[resultBufferSize - 1] = '\0';
 pclose(fp);
 } else {
 LOGI("popen null,so exit");
 exit(0);
 }
}

這兩個(gè)啟動(dòng)服務(wù)的函數(shù),里面就涉及到一些Android和linux的命令了,這里我就不細(xì)說(shuō)了。特別是am,挺強(qiáng)大的功能的,不僅可以開(kāi)啟服務(wù),也可以開(kāi)啟廣播等等...然后調(diào)用ndk-build命令進(jìn)行編譯,生成so庫(kù),ndk不會(huì)的,自行百度咯~

C/C++端關(guān)鍵的部分主要是以上這些,自然而然,Java端還得配合執(zhí)行。

首先來(lái)看一下C/C++代碼編譯完的so庫(kù)的加載類(lèi),以及native的調(diào)用:

package com.yyh.fork;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;

public class NativeRuntime {

 private static NativeRuntime theInstance = null;

 private NativeRuntime() {

 }
 
 public static NativeRuntime getInstance() {
 if (theInstance == null)
 theInstance = new NativeRuntime();
 return theInstance;
 }

 /**
 * RunExecutable 啟動(dòng)一個(gè)可自行的lib*.so文件
 * @date 2016-1-18 下午8:22:28
 * @param pacaageName
 * @param filename
 * @param alias 別名
 * @param args 參數(shù)
 * @return
 */
 public String RunExecutable(String pacaageName, String filename, String alias, String args) {
 String path = "/data/data/" + pacaageName;
 String cmd1 = path + "/lib/" + filename;
 String cmd2 = path + "/" + alias;
 String cmd2_a1 = path + "/" + alias + " " + args;
 String cmd3 = "chmod 777 " + cmd2;
 String cmd4 = "dd if=" + cmd1 + " of=" + cmd2;
 StringBuffer sb_result = new StringBuffer();

 if (!new File("/data/data/" + alias).exists()) {
 RunLocalUserCommand(pacaageName, cmd4, sb_result); // 拷貝lib/libtest.so到上一層目錄,同時(shí)命名為test.
 sb_result.append(";");
 }
 RunLocalUserCommand(pacaageName, cmd3, sb_result); // 改變test的屬性,讓其變?yōu)榭蓤?zhí)行
 sb_result.append(";");
 RunLocalUserCommand(pacaageName, cmd2_a1, sb_result); // 執(zhí)行test程序.
 sb_result.append(";");
 return sb_result.toString();
 }

 /**
 * 執(zhí)行本地用戶(hù)命令
 * @date 2016-1-18 下午8:23:01
 * @param pacaageName
 * @param command
 * @param sb_out_Result
 * @return
 */
 public boolean RunLocalUserCommand(String pacaageName, String command, StringBuffer sb_out_Result) {
 Process process = null;
 try {
 process = Runtime.getRuntime().exec("sh"); // 獲得shell進(jìn)程
 DataInputStream inputStream = new DataInputStream(process.getInputStream());
 DataOutputStream outputStream = new DataOutputStream(process.getOutputStream());
 outputStream.writeBytes("cd /data/data/" + pacaageName + "\n"); // 保證在command在自己的數(shù)據(jù)目錄里執(zhí)行,才有權(quán)限寫(xiě)文件到當(dāng)前目錄
 outputStream.writeBytes(command + " &\n"); // 讓程序在后臺(tái)運(yùn)行,前臺(tái)馬上返回
 outputStream.writeBytes("exit\n");
 outputStream.flush();
 process.waitFor();
 byte[] buffer = new byte[inputStream.available()];
 inputStream.read(buffer);
 String s = new String(buffer);
 if (sb_out_Result != null)
 sb_out_Result.append("CMD Result:\n" + s);
 } catch (Exception e) {
 if (sb_out_Result != null)
 sb_out_Result.append("Exception:" + e.getMessage());
 return false;
 }
 return true;
 }

 public native void startActivity(String compname);

 public native String stringFromJNI();

 public native void startService(String srvname, String sdpath);

 public native int findProcess(String packname);

 public native int stopService();

 static {
 try {
 System.loadLibrary("helper"); // 加載so庫(kù)
 } catch (Exception e) {
 e.printStackTrace();
 }
 }

}

然后,我們?cè)谑盏介_(kāi)機(jī)廣播后,啟動(dòng)該服務(wù)。

package com.yyh.activity;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.yyh.fork.NativeRuntime;
import com.yyh.utils.FileUtils;
public class PhoneStatReceiver extends BroadcastReceiver {

 private String TAG = "tag";

 @Override
 public void onReceive(Context context, Intent intent) {
 if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
 Log.i(TAG, "手機(jī)開(kāi)機(jī)了~~");
 NativeRuntime.getInstance().startService(context.getPackageName() + "/com.yyh.service.HostMonitor", FileUtils.createRootPath());
 } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
 }
 }

 
}

Service服務(wù)里面,就可以做該做的事情。

package com.yyh.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class HostMonitor extends Service {

 @Override
 public void onCreate() {
 super.onCreate();
 Log.i("daemon_java", "HostMonitor: onCreate! I can not be Killed!");
 }

 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
 Log.i("daemon_java", "HostMonitor: onStartCommand! I can not be Killed!");
 return super.onStartCommand(intent, flags, startId);
 }

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

當(dāng)然,也不要忘記在Manifest.xml文件配置receiver和service:

<receiver
   android:name="com.yyh.activity.PhoneStatReceiver"
   android:enabled="true"
   android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
   <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED" />
    <action android:name="android.intent.action.USER_PRESENT" />
   </intent-filter>
  </receiver>
  
  <service android:name="com.yyh.service.HostMonitor"
    android:enabled="true"
    android:exported="true">
   </service>

run起來(lái),在程序應(yīng)用里面,結(jié)束掉這個(gè)進(jìn)程,不一會(huì)了,又自動(dòng)起來(lái)了~~~~完美~~~~跟流氓軟件一個(gè)樣,沒(méi)錯(cuò),就是這么賤,就是這么霸道!!

這邊是運(yùn)行在谷歌的原生系統(tǒng)上,Android版本為5.0...總結(jié)一下就是:服務(wù)常駐要應(yīng)對(duì)的不是各種難的技術(shù),而是各大ROM。QQ為什么不會(huì)被殺死,是因?yàn)閲?guó)內(nèi)各大ROM不想讓他死...

本文主要提供的是一個(gè)思路,實(shí)現(xiàn)還有諸多不足之處,菜鳥(niǎo)之作,不喜勿噴。

最后附上本例的源代碼:Android 通過(guò)JNI實(shí)現(xiàn)雙守護(hù)進(jìn)程

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 談?wù)凴xJava2中的異常及處理方法

    談?wù)凴xJava2中的異常及處理方法

    這篇文章主要給大家介紹了關(guān)于RxJava2中異常及處理方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用RxJava2具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Android 使用FragmentTabhost代替Tabhost

    Android 使用FragmentTabhost代替Tabhost

    這篇文章主要介紹了Android 使用FragmentTabhost代替Tabhost的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Android實(shí)現(xiàn)多點(diǎn)觸控功能

    Android實(shí)現(xiàn)多點(diǎn)觸控功能

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)多點(diǎn)觸控功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Android開(kāi)發(fā)實(shí)現(xiàn)按鈕點(diǎn)擊切換背景并修改文字顏色的方法

    Android開(kāi)發(fā)實(shí)現(xiàn)按鈕點(diǎn)擊切換背景并修改文字顏色的方法

    這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)按鈕點(diǎn)擊切換背景并修改文字顏色的方法,涉及Android界面布局與相關(guān)屬性設(shè)置技巧,需要的朋友可以參考下
    2018-01-01
  • 淺談android獲取存儲(chǔ)目錄(路徑)的幾種方式和注意事項(xiàng)

    淺談android獲取存儲(chǔ)目錄(路徑)的幾種方式和注意事項(xiàng)

    今天小編就為大家分享一篇淺談android獲取存儲(chǔ)目錄(路徑)的幾種方式和注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • Android Jetpack- Paging的使用詳解

    Android Jetpack- Paging的使用詳解

    這篇文章主要介紹了Android Jetpack- Paging的使用詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Android普通對(duì)話(huà)框用法實(shí)例分析

    Android普通對(duì)話(huà)框用法實(shí)例分析

    這篇文章主要介紹了Android普通對(duì)話(huà)框用法,以實(shí)例形式較為詳細(xì)的分析了Android對(duì)話(huà)框的創(chuàng)建技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • 解析Android ANR問(wèn)題

    解析Android ANR問(wèn)題

    ANR 的全稱(chēng)是 Application No Responding,即應(yīng)用程序無(wú)響應(yīng),具體是一些特定的 Message (Key Dispatch、Broadcast、Service) 在應(yīng)用的UI線(xiàn)程(主線(xiàn)程)沒(méi)有在規(guī)定的時(shí)間內(nèi)處理完,進(jìn)而觸發(fā) ANR 異常
    2021-01-01
  • Android編程程序?qū)崿F(xiàn)一鍵鎖屏的方法講解

    Android編程程序?qū)崿F(xiàn)一鍵鎖屏的方法講解

    今天小編就為大家分享一篇關(guān)于Android編程程序?qū)崿F(xiàn)一鍵鎖屏的方法講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • 實(shí)例講解Android中的AutoCompleteTextView自動(dòng)補(bǔ)全組件

    實(shí)例講解Android中的AutoCompleteTextView自動(dòng)補(bǔ)全組件

    AutoCompleteTextView組件被用在輸入框中能實(shí)現(xiàn)輸入內(nèi)容自動(dòng)補(bǔ)全的功能,類(lèi)似于大家平時(shí)用Google時(shí)的輸入聯(lián)想,這里我們來(lái)用實(shí)例講解Android中的AutoCompleteTextView自動(dòng)補(bǔ)全組件,特別是實(shí)現(xiàn)郵箱地址補(bǔ)全的例子,非常實(shí)用
    2016-05-05

最新評(píng)論