Android應(yīng)用進(jìn)程?;?/p>
- Android應(yīng)用進(jìn)程保活方法介紹
在Android應(yīng)用程序中,為了保證應(yīng)用的正常運(yùn)行和穩(wěn)定性,有時(shí)需要對應(yīng)用進(jìn)程進(jìn)行保活。以下是一些實(shí)現(xiàn)進(jìn)程?;畹姆椒ǎ?/p>
- 使用前臺服務(wù)(Foreground Service):將服務(wù)調(diào)用startForeground()方法,并傳入一個(gè)通知對象,將該服務(wù)置于前臺運(yùn)行狀態(tài)。這樣可以使得該服務(wù)的優(yōu)先級更高,從而減少被系統(tǒng)殺死的概率。
- 使用JobScheduler:使用setPeriodic()方法可以讓應(yīng)用程序周期性地執(zhí)行任務(wù),從而避免長時(shí)間占用CPU資源,setPersisted(true)方法則表示當(dāng)設(shè)備重啟后,該任務(wù)仍然需要繼續(xù)執(zhí)行。
- 使用AlarmManager:使用這個(gè)API可以讓應(yīng)用程序在指定的時(shí)間間隔內(nèi)執(zhí)行任務(wù)。例如,可以設(shè)置一個(gè)鬧鐘,每隔一段時(shí)間喚醒應(yīng)用程序并執(zhí)行一些操作。
- 使用守護(hù)進(jìn)程:啟動一個(gè)后臺守護(hù)進(jìn)程,監(jiān)控應(yīng)用程序的狀態(tài)并在應(yīng)用程序被殺死時(shí)重新啟動它,使用守護(hù)進(jìn)程需要申請額外的權(quán)限。
- 使用雙進(jìn)程保活:啟動兩個(gè)相互綁定的進(jìn)程,在其中一個(gè)進(jìn)程被殺死時(shí),另一個(gè)進(jìn)程可以重新啟動它。
- 使用WorkManger: 這是目前比較新的?;顧C(jī)制,用于取代JobScheduler。
需要注意的是,為了避免濫用和浪費(fèi)系統(tǒng)資源,Android系統(tǒng)不斷升級后,已經(jīng)嚴(yán)格限制應(yīng)用程序使用過多的后臺資源和?;顧C(jī)制。
- JobScheduler用法簡介
JobScheduler是系統(tǒng)服務(wù),由系統(tǒng)負(fù)責(zé)調(diào)度第三方應(yīng)用注冊的JobScheduler,定時(shí)完成指定任務(wù)。
在應(yīng)用中創(chuàng)建一個(gè) JobService服務(wù),JobService需要 API Level 21以上才可以使用,該服務(wù)注冊時(shí)必須聲明 android.permission.BIND_JOB_SERVICE權(quán)限:
通常使用JobScheduler需要以下幾個(gè)步驟:
1、獲取 JobScheduler對象:通過Binder機(jī)制獲取該JobScheduler系統(tǒng)服務(wù);
//創(chuàng)建 JobScheduler JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
2、指定 JobScheduler任務(wù)信息 JobInfo:綁定任務(wù) ID,指定任務(wù)的運(yùn)行組件,也就是之前創(chuàng)建并注冊的 JobService, 最后要設(shè)置該任務(wù)在重啟后也要執(zhí)行;
//第一個(gè)參數(shù)指定任務(wù) ID //第二個(gè)參數(shù)指定任務(wù)在哪個(gè)組件中執(zhí)行 // setPersisted方法需要 android.permission.RECEIVE_BOOT_COMPLETED權(quán)限 // setPersisted方法作用是設(shè)備重啟后 ,依然執(zhí)行 JobScheduler定時(shí)任務(wù) JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(10, new ComponentName(context.getPackageName(), KeepAliveJobService.class.getName())) .setPersisted(true);
3、設(shè)置時(shí)間信息:7.0以下的系統(tǒng)可以設(shè)置間隔, 7.0以上的版本需要設(shè)置延遲執(zhí)行,否則無法啟動;
// 7.0 以下的版本,可以每隔 5000毫秒執(zhí)行一次任務(wù) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){ ?? jobInfoBuilder.setPeriodic(5_000); }else{ ?? // 7.0?以上的版本 ,?設(shè)置延遲 5?秒執(zhí)行 ?? //?該時(shí)間不能小于 JobInfo.getMinLatencyMillis?方法獲取的最小值 ?? jobInfoBuilder.setMinimumLatency(5_000); }
4、開啟定時(shí)任務(wù);
//開啟定時(shí)任務(wù) jobScheduler.schedule(jobInfoBuilder.build());
5、7.0以上的特殊處理,由于在7.0以上的系統(tǒng)中設(shè)置了延遲執(zhí)行,需要在 JobService的 onStartJob方法中再次開啟一次 JobScheduler任務(wù)執(zhí)行,也就是重復(fù)上述1 ~ 4執(zhí)行, 這樣就實(shí)現(xiàn)了周期性執(zhí)行的目的;
public class KeepAliveJobService extends JobService { @Override public boolean onStartJob(JobParameters params) { Log.i("KeepAliveJobService", "JobService onStartJob開啟"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ //如果當(dāng)前設(shè)備大于 7.0 ,延遲 5秒 ,再次執(zhí)行一次 startJob(this); } return false; } }
3.WorkManager用法簡介
WorkManager是適合用于持久性工作的推薦解決方案,它可處理三種類型的持久性工作:
- 立即執(zhí)行:必須立即開始且很快就完成的任務(wù),可以加急。
- 長時(shí)間運(yùn)行:運(yùn)行時(shí)間可能較長(有可能超過 10分鐘)的任務(wù)。
- 可延期執(zhí)行:延期開始并且可以定期運(yùn)行的預(yù)定任務(wù)。
通常使用WorkManager需要以下幾個(gè)步驟:
- 將依賴項(xiàng)添加到應(yīng)用的build.gradle文件中;
- 定義工作:工作使用 Worker類定義,doWork()方法在 WorkManager提供的后臺線程上異步運(yùn)行。如需為 WorkManager創(chuàng)建一些要運(yùn)行的工作,則需擴(kuò)展 Worker類并替換 doWork()方法;
public class XxxWorker extends Worker { publicXxxWorker( @NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @Override public Result doWork() { // Do the work here xxxxx(); // Indicate whether the work finished successfully with the Result return Result.success(); } }
3.創(chuàng)建 WorkRequest:定義工作后,必須使用 WorkManager服務(wù)進(jìn)行調(diào)度該工作才能運(yùn)行;
WorkRequest xxxWorkRequest = new OneTimeWorkRequest.Builder(XxxWorker.class) .build();
4.將 WorkRequest提交給系統(tǒng):需要使用enqueue()方法將WorkRequest提交到WorkManager;
WorkManager .getInstance(myContext) .enqueue(uploadWorkRequest);
在定義工作時(shí)要考慮要考慮下面常見的需求:
- 調(diào)度一次性工作還是重復(fù)性工作;
- 工作約束條件是怎樣的,例如要求連接到 Wi-Fi網(wǎng)絡(luò)或正在充電;
- 確保至少延遲一定時(shí)間再執(zhí)行工作;
- 設(shè)置重試和退避策略;
- 輸入數(shù)據(jù)如何傳遞給工作等等。
4.雙進(jìn)程?;?/strong>
雙進(jìn)程?;畹姆绞骄褪窃谶\(yùn)行了一個(gè)主進(jìn)程之外,還運(yùn)行了一個(gè) “本地前臺進(jìn)程”,并綁定“遠(yuǎn)程前臺進(jìn)程”,“遠(yuǎn)程前臺進(jìn)程”與“本地前臺進(jìn)程”實(shí)現(xiàn)了相同的功能,代碼基本一致,這兩個(gè)進(jìn)程都是前臺進(jìn)程,都進(jìn)行了提權(quán),并且互相綁定,當(dāng)監(jiān)聽到綁定的另外一個(gè)進(jìn)程突然斷開連接,則本進(jìn)程再次開啟前臺進(jìn)程提權(quán),并且重新綁定對方進(jìn)程,以達(dá)到拉活對方進(jìn)程的目的。
雙進(jìn)程?;畹膶?shí)現(xiàn)步驟如下:
- 定義 AIDL接口 IMyAidlInterface,每個(gè)服務(wù)中都需要定義繼承 IMyAidlInterface.Stub的 Binder類,作為進(jìn)程間通信的橋梁(這是個(gè)默認(rèn)的 AIDL接口 ),監(jiān)聽進(jìn)程的連接斷開;
// Declare any non-default types here with import statements interface IMyAidlInterface { /** * 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); }
2.實(shí)現(xiàn)一個(gè)判定服務(wù)運(yùn)行工具:
import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.text.TextUtils; import org.w3c.dom.Text; import java.util.List; public class ServiceUtils { /** *判定 Service是否在運(yùn)行 * @param context * @return */ public static boolean isServiceRunning(Context context, String serviceName){ if(TextUtils.isEmpty(serviceName)) return false; ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); //最多獲取 200個(gè)正在運(yùn)行的 Service List infos = activityManager.getRunningServices(200); //遍歷當(dāng)前運(yùn)行的 Service信息,如果找到相同名稱的服務(wù) ,說明某進(jìn)程正在運(yùn)行 for (ActivityManager.RunningServiceInfo info: infos){ if (TextUtils.equals(info.service.getClassName(), serviceName)){ return true; } } return false; } }
3.定義一個(gè)用于本地與遠(yuǎn)程連接的類:
class Connection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { //服務(wù)綁定成功時(shí)回調(diào) } @Override public void onServiceDisconnected(ComponentName name) { //再次啟動前臺進(jìn)程 startService(); //綁定另外一個(gè)遠(yuǎn)程進(jìn)程 bindService(); } }
4..定義一個(gè)本地前臺服務(wù)類:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Color; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import androidx.core.app.NotificationCompat; import static androidx.core.app.NotificationCompat.PRIORITY_MIN; /** *本地前臺服務(wù) */ public class LocalForegroundService extends Service { /** *遠(yuǎn)程調(diào)用 Binder對象 */ private MyBinder myBinder; /** *連接對象 */ private Connection connection; /** * AIDL遠(yuǎn)程調(diào)用接口 *其它進(jìn)程調(diào)與該 RemoteForegroundService服務(wù)進(jìn)程通信時(shí) ,可以通過 onBind方法獲取該 myBinder成員 *通過調(diào)用該成員的 basicTypes方法 ,可以與該進(jìn)程進(jìn)行數(shù)據(jù)傳遞 */ class MyBinder extends IMyAidlInterface.Stub { @Override public void basicTypes( int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { //通信內(nèi)容 } } @Override public IBinder onBind(Intent intent) { return myBinder; } @Override public void onCreate() { super.onCreate(); //創(chuàng)建 Binder對象 myBinder = new MyBinder(); //啟動前臺進(jìn)程 startService(); } private void startService(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ // startForeground(); //創(chuàng)建通知通道 NotificationChannel channel = new NotificationChannel("service", "service", NotificationManager.IMPORTANCE_NONE); channel.setLightColor(Color.BLUE); channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //正式創(chuàng)建 service.createNotificationChannel(channel); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "service"); Notification notification = builder.setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); //開啟前臺進(jìn)程 , API 26以上無法關(guān)閉通知欄 startForeground(10, notification); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){ startForeground(10, new Notification()); // API 18 ~ 25以上的設(shè)備 ,啟動相同 id的前臺服務(wù) ,并關(guān)閉 ,可以關(guān)閉通知 startService(new Intent(this, CancelNotificationService.class)); } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){ ?????????? //?將該服務(wù)轉(zhuǎn)為前臺服務(wù) ?????????? //?需要設(shè)置 ID?和?通知 ?????????? //?設(shè)置 ID?為 0 ,?就不顯示已通知了 ,?但是 oom_adj?值會變成后臺進(jìn)程 11 ?????????? //?設(shè)置 ID?為 1 ,?會在通知欄顯示該前臺服務(wù) ?????????? // 8.0?以上該用法報(bào)錯(cuò) ?????????? startForeground(10, new Notification()); ?????? } ?? } ?? /** ??? *?綁定?另外一個(gè)?服務(wù) ??? * LocalForegroundService?與 RemoteForegroundService?兩個(gè)服務(wù)互相綁定 ??? */ ?? private void bindService(){ ?????? //?綁定另外一個(gè)?服務(wù) ?????? // LocalForegroundService?與 RemoteForegroundService?兩個(gè)服務(wù)互相綁定 ?????? //?創(chuàng)建連接對象 ?????? connection = new Connection(); ?????? //?創(chuàng)建本地前臺進(jìn)程組件意圖 ?????? Intent bindIntent = new Intent(this, RemoteForegroundService.class); ?????? //?綁定進(jìn)程操作 ?????? bindService(bindIntent, connection, BIND_AUTO_CREATE); ?? } ?? @Override ?? public int onStartCommand(Intent intent, int flags, int startId) { ?????? //?綁定另外一個(gè)服務(wù) ?????? bindService(); ?????? return super.onStartCommand(intent, flags, startId); ?? } }
5.定義一個(gè)遠(yuǎn)程前臺服務(wù)類:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Color; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import androidx.core.app.NotificationCompat; import static androidx.core.app.NotificationCompat.PRIORITY_MIN; /** *遠(yuǎn)程前臺服務(wù) */ public class RemoteForegroundService extends Service { /** *遠(yuǎn)程調(diào)用 Binder對象 */ private MyBinder myBinder; /** *連接對象 */ private Connection connection; /** * AIDL遠(yuǎn)程調(diào)用接口 *其它進(jìn)程調(diào)與該 RemoteForegroundService服務(wù)進(jìn)程通信時(shí) ,可以通過 onBind方法獲取該 myBinder成員 *通過調(diào)用該成員的 basicTypes方法 ,可以與該進(jìn)程進(jìn)行數(shù)據(jù)傳遞 */ class MyBinder extends IMyAidlInterface.Stub { @Override public void basicTypes( int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { //通信內(nèi)容 } } @Override public IBinder onBind(Intent intent) { return myBinder; } @Override public void onCreate() { super.onCreate(); //創(chuàng)建 Binder對象 myBinder = new MyBinder(); //啟動前臺進(jìn)程 startService(); } private void startService(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ // startForeground(); //創(chuàng)建通知通道 NotificationChannel channel = new NotificationChannel("service", "service", NotificationManager.IMPORTANCE_NONE); channel.setLightColor(Color.BLUE); channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //正式創(chuàng)建 service.createNotificationChannel(channel); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "service"); Notification notification = builder.setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); //開啟前臺進(jìn)程 , API 26以上無法關(guān)閉通知欄 startForeground(10, notification); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){ startForeground(10, new Notification()); // API 18 ~ 25以上的設(shè)備 ,啟動相同 id的前臺服務(wù) ,并關(guān)閉 ,可以關(guān)閉通知 startService(new Intent(this, CancelNotificationService.class)); } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){ ?????????? //?將該服務(wù)轉(zhuǎn)為前臺服務(wù) ?????????? //?需要設(shè)置 ID?和?通知 ?????????? //?設(shè)置 ID?為 0 ,?就不顯示已通知了 ,?但是 oom_adj?值會變成后臺進(jìn)程 11 ?????????? //?設(shè)置 ID?為 1 ,?會在通知欄顯示該前臺服務(wù) ?????????? // 8.0?以上該用法報(bào)錯(cuò) ?????????? startForeground(10, new Notification()); ?????? } ?? } ?? /** ??? *?綁定?另外一個(gè)?服務(wù) ??? * LocalForegroundService?與 RemoteForegroundService?兩個(gè)服務(wù)互相綁定 ??? */ ?? private void bindService(){ ?????? //?綁定?另外一個(gè)?服務(wù) ?????? // LocalForegroundService?與 RemoteForegroundService?兩個(gè)服務(wù)互相綁定 ?????? //?創(chuàng)建連接對象 ?????? connection = new Connection(); ?????? //?創(chuàng)建本地前臺進(jìn)程組件意圖 ?????? Intent bindIntent = new Intent(this, LocalForegroundService.class); ?????? //?綁定進(jìn)程操作 ?????? bindService(bindIntent, connection, BIND_AUTO_CREATE); ?? } ?? @Override ?? public int onStartCommand(Intent intent, int flags, int startId) { ?????? //?綁定另外一個(gè)服務(wù) ?????? bindService(); ?????? return super.onStartCommand(intent, flags, startId); ?? } }
6.啟動兩個(gè)服務(wù):
import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; 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, LocalForegroundService.class)); startService(new Intent(this, RemoteForegroundService.class)); } }
5.雙進(jìn)程?;?JobScheduler整合方案
這種方案是在 JobService的onStartJob方法中判定“雙進(jìn)程?;睢敝械碾p進(jìn)程是否掛了,如果這兩個(gè)進(jìn)程掛了,就重新將掛掉的進(jìn)程重啟。
這里給出一個(gè)雙進(jìn)程保活+JobScheduler整合方案中JobScheduler部分的示意代碼,而雙進(jìn)程?;畈糠直3植蛔儭?/p>
public class KeepAliveJobService extends JobService { @Override public boolean onStartJob(JobParameters params) { Log.i("KeepAliveJobService", "JobService onStartJob開啟"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ //如果當(dāng)前設(shè)備大于 7.0 ,延遲 5秒 ,再次執(zhí)行一次 startJob(this); } //判定本地前臺進(jìn)程是否正在運(yùn)行 boolean isLocalServiceRunning = ServiceUtils.isServiceRunning(this, LocalForegroundService.class.getName()); if (!isLocalServiceRunning){ startService(new Intent(this, LocalForegroundService.class)); } //判定遠(yuǎn)程前臺進(jìn)程是否正在運(yùn)行 boolean isRemoteServiceRunning = ServiceUtils.isServiceRunning(this, RemoteForegroundService.class.getName()); if (!isRemoteServiceRunning){ startService(new Intent(this, RemoteForegroundService.class)); } return false; } @Override public boolean onStopJob(JobParameters params) { Log.i("KeepAliveJobService", "JobService onStopJob關(guān)閉"); return false; } public static void startJob(Context context){ //創(chuàng)建 JobScheduler JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); //第一個(gè)參數(shù)指定任務(wù) ID //第二個(gè)參數(shù)指定任務(wù)在哪個(gè)組件中執(zhí)行 // setPersisted方法需要 android.permission.RECEIVE_BOOT_COMPLETED權(quán)限 // setPersisted方法作用是設(shè)備重啟后 ,依然執(zhí)行 JobScheduler定時(shí)任務(wù) JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(10, new ComponentName(context.getPackageName(), KeepAliveJobService.class.getName())) .setPersisted(true); // 7.0以下的版本,可以每隔 5000毫秒執(zhí)行一次任務(wù) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){ ?????????? jobInfoBuilder.setPeriodic(5_000); ?????? }else{ ?????????? // 7.0?以上的版本 ,?設(shè)置延遲 5?秒執(zhí)行 ?????????? //?該時(shí)間不能小于 JobInfo.getMinLatencyMillis?方法獲取的最小值 ????????? jobInfoBuilder.setMinimumLatency(5_000); ?????? } ?????? //?開啟定時(shí)任務(wù) ????? jobScheduler.schedule(jobInfoBuilder.build()); ?? } }
6.參考文獻(xiàn)
1、【Android進(jìn)程保活】應(yīng)用進(jìn)程拉活 (雙進(jìn)程守護(hù) + JobScheduler?;?:
https://hanshuliang.blog.csdn.net/article/details/115607584
2、【Android進(jìn)程?;睢繎?yīng)用進(jìn)程拉活 (雙進(jìn)程守護(hù)?;?):
https://hanshuliang.blog.csdn.net/article/details/115604667
3、【Android進(jìn)程?;睢繎?yīng)用進(jìn)程拉活 ( JobScheduler拉活):
https://hanshuliang.blog.csdn.net/article/details/115584240
4、Android實(shí)現(xiàn)進(jìn)程?;畹乃悸罚?/p>
https://blog.csdn.net/gs12software/article/details/130502312
5、WorkManager使用入門:
https://developer.android.google.cn/develop/background-work/background-tasks/persistent/getting-started
-
Android
+關(guān)注
關(guān)注
12文章
3915瀏覽量
127019 -
應(yīng)用
+關(guān)注
關(guān)注
2文章
438瀏覽量
34119 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
201瀏覽量
13937
發(fā)布評論請先 登錄
相關(guān)推薦
評論