Android
进程保活主要包括两个方面:
Android
系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是重要性略低的进程,依此类推,回收系统资源。
重要性层次结构一共有5
级。前台进程 > 可见进程 > 服务进程 > 后台进程 > 空进程,其中前台进程最重要。
用户当前操作所必需的进程。如果一个进程满足以下任一条件,视为前台进程:
Activity
—已调用Activity.onResume()
方法;Service
绑定到用户正在交互的Activity
;Service
——服务已调用startForeground()
,普通服务改为前台服务;Service
(onCreate()
、onStart()
或onDestroy()
)onReceive()
方法的BroadcastReceiver
通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行,这一万不得已的情况下,系统才会终止它们。 此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。
没有任何前台组件,但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:
Activity
(已调用其onPause()
方法)。 例如,如果前台Activity
启动了一个对话框,允许在其后显示上一个Activity
,则有可能会发生这种情况;Activity
的Service
;可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。
正在运行已使用startService()
方法启动的服务且不属于上述两个更高类别进程的进程。
尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。
包含目前对用户不可见的Activity
的进程(已调用Activity.onStop()
方法)。
这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在LRU
(最近最少使用)列表中,以确保包含用户最近查看的Activity
的进程最后一个被终止。 如果某个 Activity
正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该Activity
时,Activity
会恢复其所有可见状态。
不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。
Android
进程回收策略(LowMemoryKiller
)进程的启动分冷启动和热启动,当用户退出某一个进程的时候,并不会真正的将进程退出,而是将这个进程放到后台,以便下次启动的时候可以马上启动起来,这个过程名为热启动,这也是Android
的设计理念之一。这个机制会带来一个问题,每个进程都有自己独立的内存地址空间,随着应用打开数量的增多,系统已使用的内存越来越大,就很有可能导致系统内存不足。为了解决这个问题,系统引入LowMemoryKiller
(简称lmk
)管理所有进程,根据一定策略来kill
某个进程并释放占用的内存,保证系统的正常运行。
所有应用进程都是从zygote
孵化出来的,记录在AMS
中mLruProcesses
列表中,由AMS
进行统一管理,AMS
中会根据进程的状态更新进程对应的oom_adj
值,这个值会通过文件传递到kernel
中去,kernel
有个低内存回收机制,在内存达到一定阀值时会触发清理oom_adj
值高的进程腾出更多的内存空间。
什么是oom_adj
?它是linux
内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。 对于oom_adj
的作用,只需要记住以下几点即可:
oom_adj
越大,表示此进程优先级越低,越容易被杀回收;越小,表示进程优先级越高,越不容易被杀回收APP
进程的oom_adj >= 0
,系统进程的oom_adj
才可能< 0
Activity
提权(降低oom_adj
)监控手机锁屏解锁事件,在屏幕锁屏时启动1
个像素透明的Activity
,在用户解锁时将Activity
销毁掉,从而达到提高进程优先级的作用,可以使进程的优先级在屏幕锁屏时间由4
提升为最高优先级1
。
代码实现:
public class KeepManager {
private static final KeepManager ourInstance = new KeepManager();
public static KeepManager getInstance() {
return ourInstance;
}
private KeepManager() {
}
private KeepReceiver keepReceiver;
private WeakReference<Activity> mKeepActivity;
/**
* 注册
* @param context
*/
public void registerKeepReceiver(Context context){
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
keepReceiver = new KeepReceiver();
context.registerReceiver(keepReceiver, filter);
}
/**
* 反注册
* @param context
*/
public void unRegisterKeepReceiver(Context context){
if (null != keepReceiver) {
context.unregisterReceiver(keepReceiver);
}
}
/**
* 启动1个像素的KeepActivity
* @param context
*/
public void startKeep(Context context) {
Intent intent = new Intent(context, KeepActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
/**
* 销毁1个像素的KeepActivity
*/
public void finishKeep() {
if (null != mKeepActivity) {
Activity activity = mKeepActivity.get();
if (null != activity) {
activity.finish();
}
mKeepActivity = null;
}
}
public void setKeepActivity(KeepActivity mKeepActivity) {
this.mKeepActivity = new WeakReference<Activity>(mKeepActivity);
}
}
public class KeepReceiver extends BroadcastReceiver {
private static final String TAG = "KeepReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.e(TAG, "receive:" + action);
if (TextUtils.equals(action, Intent.ACTION_SCREEN_OFF)) {
//灭屏 开启1px activity
KeepManager.getInstance().startKeep(context);
} else if (TextUtils.equals(action, Intent.ACTION_SCREEN_ON)) {
//亮屏 关闭
KeepManager.getInstance().finishKeep();
}
}
}
Service
提权(降低oom_adj
)创建一个前台服务用于提高APP
在按下Home
键之后的进程优先级,startForeground(ID,Notification)
方法可以使Service
成为前台Service
。 前台服务需要在通知栏显示一条通知:
API level < 18
:参数2
设置为newNotification()
,图标不会显示;API level >= 18 & API level < 26
:在需要提权的service A
启动一个InnerService
,两个服务都startForeground
,且绑定相同的id
,Stop
掉InnerService
,通知栏图标被移除;API level >= 26
:必须手动创建通知栏,无法移除通知栏图标,startForegroundService
代替了startService
;代码如下所示:
public class ForegroundService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("deamon", "deamon",
NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (manager == null)
return;
manager.createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, "deamon").setAutoCancel(true).setCategory(
Notification.CATEGORY_SERVICE).setOngoing(true).setPriority(
NotificationManager.IMPORTANCE_LOW).build();
startForeground(10, notification);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//如果 18 以上的设备 启动一个Service startForeground给相同的id
//然后结束那个Service
startForeground(10, new Notification());
startService(new Intent(this, InnnerService.class));
} else {
startForeground(10, new Notification());
}
}
public static class InnnerService extends Service {
@Override
public void onCreate() {
super.onCreate();
startForeground(10, new Notification());
stopSelf();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
}
在发生特定系统事件时,系统会发出广播,通过在AndroidManifest
中静态注册对应的广播监听器,即可在发生响应事件时拉活。但是从android 7.0
开始,对广播进行了限制。
广播限制
有多个APP
在用户设备上安装,只要开启其中一个就可以将其他的APP
也拉活。比如手机里装了QQ
、QQ
空间、兴趣部落等等,那么打开任意一个APP
后,其它的APP
也都会被唤醒。
Service
机制(Sticky
)拉活将Service
设置为START_STICKY
,利用系统机制在Service
挂掉后自动拉活。
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
后一定能重启。只要targetSdkVersion
不小于5
,就默认是START_STICKY
。但是某些ROM
系统不会拉活。并且经过测试,Service
第一次被异常杀死后很快被重启,第二次会比第一次慢,第三次又会比前一次慢,一旦在短时间内Service
被杀死4-5
次,则系统不再拉起。
手机系统设置里会有“帐户”一项功能,任何第三方APP
都可以通过此功能将数据在一定时间内同步到服务器中去。系统在将APP
帐户同步时,会将未启动的APP
进程拉活。
public class AuthenticationService extends Service {
private AccountAuthenticator accountAuthenticator;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return accountAuthenticator.getIBinder();
}
@Override
public void onCreate() {
super.onCreate();
accountAuthenticator = new AccountAuthenticator(this);
}
static class AccountAuthenticator extends AbstractAccountAuthenticator {
public AccountAuthenticator(Context context) {
super(context);
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
String[] features) throws NetworkErrorException {
return null;
}
}
}
在manifest
中配置Service
:
<service android:name=".account.AuthenticationService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
service>
在xml
中添加authenticator.xml
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.dn.daemon.account"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" />
public class AccountHelper {
//与authenticator.xml中accountType一致
private static final String ACCOUNT_TYPE = "com.dn.daemon.account";
private static final String CONTENT_AUTHORITY = "com.dn.daemon.provider";
public static void addAccount(Context context) {
AccountManager accountManager = (AccountManager) context.getSystemService(
Context.ACCOUNT_SERVICE);
Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
if (accounts.length > 0) {
//账户已存在
return;
}
Account account = new Account("hxl", ACCOUNT_TYPE);
accountManager.addAccountExplicitly(account, "dn123", new Bundle());//直接添加账户
}
}
......
创建一个Service
作为同步Service
,并且在onBind
返回AbstractThreadedSyncAdapter
的getSyncAdapterBinder
:
public class SyncService extends Service {
private static final String TAG = "SyncService";
private SyncAdapter syncAdapter;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return syncAdapter.getSyncAdapterBinder();
}
@Override
public void onCreate() {
super.onCreate();
syncAdapter = new SyncAdapter(this, true);
}
static class SyncAdapter extends AbstractThreadedSyncAdapter {
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
Log.e(TAG,"账户同步了!");
}
}
}
在manifest
中配置Service
:
<service android:name=".account.SyncService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
service>
在xml
中添加syncadapter.xml
:
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.dn.daemon.provider"
android:accountType="com.dn.daemon.account"
android:userVisible="false"
android:supportsUploading="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="true"
/>
ContentProvider
public class SyncProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) {
return 0;
}
}
在manifest
中配置ContentProvider
:
<provider
android:authorities="com.dn.daemon.provider"
android:name=".account.SyncProvider"
android:exported="false"
/>
为了达到进程保活的效果,可以开启自动同步。时间间隔虽然设置了1s
,但是Android
本身为了考虑同步所带来的消耗和减少唤醒设备的次数,1s
只是一个参考时间:
public class AccountHelper {
//与authenticator.xml中accountType一致
private static final String ACCOUNT_TYPE = "com.dn.daemon.account";
private static final String CONTENT_AUTHORITY = "com.dn.daemon.provider";
public static void addAccount(Context context) {
AccountManager accountManager = (AccountManager) context.getSystemService(
Context.ACCOUNT_SERVICE);
Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
if (accounts.length > 0) {
//账户已存在
return;
}
Account account = new Account("hxl", ACCOUNT_TYPE);
accountManager.addAccountExplicitly(account, "dn123", new Bundle());//直接添加账户
}
public static void autoSync() {
Account dongnao = new Account("hxl", ACCOUNT_TYPE);
//设置同步
ContentResolver.setIsSyncable(dongnao, CONTENT_AUTHORITY, 1);
//设置自动同步
ContentResolver.setSyncAutomatically(dongnao, CONTENT_AUTHORITY, true);
//设置同步周期
ContentResolver.addPeriodicSync(dongnao, CONTENT_AUTHORITY, new Bundle(), 1);
}
}
JobSchedule
拉活JobScheduler
允许在特定状态与特定时间间隔周期执行任务。可以利用它的这个特点完成保活的功能,效果即开启一个定时器,与普通定时器不同的是其调度由系统完成。
public class MyJobService extends JobService {
private static final String TAG = "MyJobService";
public static void startJob(Context context) {
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(10,
new ComponentName(context.getPackageName(),
MyJobService.class.getName())).setPersisted(true);
/**
* I was having this problem and after review some blogs and the official documentation,
* I realised that JobScheduler is having difference behavior on Android N(24 and 25).
* JobScheduler works with a minimum periodic of 15 mins.
*
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
//7.0以上延迟1s执行
builder.setMinimumLatency(1000);
}else{
//每隔1s执行一次job
builder.setPeriodic(1000);
}
jobScheduler.schedule(builder.build());
}
@Override
public boolean onStartJob(JobParameters jobParameters) {
Log.e(TAG,"开启job");
//7.0以上轮询
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
startJob(this);
}
return false;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
return false;
}
}
// 使用
MyJobService.startJob(this);
根据终端不同,在小米手机(包括MIUI)接入小米推送、华为手机接入华为推送。
两个进程共同运行,如果有其中一个进程被杀,那么另外一个进程就会将被杀的进程重新拉起。相对于其他方案成功率比较高。
LocalService
:
public class LocalService extends Service {
private MyBinder myBinder;
class MyBinder extends IMyAidlInterface.Stub{
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString) throws RemoteException {
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return myBinder;
}
@Override
public void onCreate() {
super.onCreate();
myBinder = new MyBinder();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("deamon", "deamon",
NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
if (manager == null)
return;
manager.createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this,"deamon")
.setAutoCancel(true)
.setCategory(Notification.CATEGORY_SERVICE).setOngoing(true)
.setPriority(NotificationManager.IMPORTANCE_LOW)
.build();
startForeground(10, notification);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//如果 18 以上的设备 启动一个Service startForeground给相同的id
//然后结束那个Service
startForeground(10, new Notification());
startService(new Intent(this, InnnerService.class));
} else {
startForeground(10, new Notification());
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
bindService(new Intent(this, RemoteService.class), new MyServiceConnection(),
BIND_AUTO_CREATE);
return super.onStartCommand(intent, flags, startId);
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//回调
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
//
startService(new Intent(LocalService.this, RemoteService.class));
bindService(new Intent(LocalService.this, RemoteService.class), new MyServiceConnection(),
BIND_AUTO_CREATE);
}
}
public static class InnnerService extends Service {
@Override
public void onCreate() {
super.onCreate();
startForeground(10, new Notification());
stopSelf();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
}
RemoteService
:
public class RemoteService extends Service {
private MyBinder myBinder;
class MyBinder extends IMyAidlInterface.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString) throws RemoteException {
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return myBinder;
}
@Override
public void onCreate() {
super.onCreate();
myBinder = new MyBinder();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("deamon", "deamon",
NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
if (manager == null)
return;
manager.createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this,"deamon")
.setAutoCancel(true)
.setCategory(Notification.CATEGORY_SERVICE)
.setOngoing(true)
.setPriority(NotificationManager.IMPORTANCE_LOW)
.build();
startForeground(10, notification);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//如果 18 以上的设备 启动一个Service startForeground给相同的id
//然后结束那个Service
startForeground(10, new Notification());
startService(new Intent(this, InnnerService.class));
} else {
startForeground(10, new Notification());
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
bindService(new Intent(this, LocalService.class), new MyServiceConnection(),
BIND_AUTO_CREATE);
return super.onStartCommand(intent, flags, startId);
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
startService(new Intent(RemoteService.this, LocalService.class));
bindService(new Intent(RemoteService.this, LocalService.class),
new MyServiceConnection(), BIND_AUTO_CREATE);
}
}
public static class InnnerService extends Service {
@Override
public void onCreate() {
super.onCreate();
startForeground(10, new Notification());
stopSelf();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
}
MyJobService
:
@SuppressLint("NewApi")
public class MyJobService extends JobService {
private static final String TAG = "MyJobService";
public static void startJob(Context context) {
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(10,
new ComponentName(context.getPackageName(),
MyJobService.class.getName())).setPersisted(true);
/**
* I was having this problem and after review some blogs and the official documentation,
* I realised that JobScheduler is having difference behavior on Android N(24 and 25).
* JobScheduler works with a minimum periodic of 15 mins.
*
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//7.0以上延迟1s执行
builder.setMinimumLatency(1000);
} else {
//每隔1s执行一次job
builder.setPeriodic(1000);
}
jobScheduler.schedule(builder.build());
}
@Override
public boolean onStartJob(JobParameters jobParameters) {
Log.e(TAG, "开启job");
//7.0以上轮询
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
startJob(this);
}
//判断服务是否在运行
boolean isLocalServiceRun = isServiceRunning(this, LocalService.class.getName());
boolean isRemoteServiceRun = isServiceRunning(this, RemoteService.class.getName());
if (!isLocalServiceRun || !isRemoteServiceRun) {
startService(new Intent(this, LocalService.class));
startService(new Intent(this, RemoteService.class));
}
return false;
}
private boolean isServiceRunning(Context context, String serviceName) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(10);
for (ActivityManager.RunningServiceInfo runningService : runningServices) {
if (TextUtils.equals(runningService.service.getClassName(), serviceName)) {
return true;
}
}
return false;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
return false;
}
}
manifest
设置不同进程:
<service android:name=".service.LocalService"
android:exported="true"
android:process=":local"/>
<service android:name=".service.LocalService$InnnerService"
android:exported="true"
android:process=":local"/>
<service android:name=".service.RemoteService"
android:exported="true"
android:process=":remote"/>
<service android:name=".service.RemoteService$InnnerService"
android:exported="true"
android:process=":remote"/>
<service android:name=".service.MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
启动进程:
startService(new Intent(this, LocalService.class));
startService(new Intent(this, RemoteService.class));
MyJobService.startJob(this);
https://blog.csdn.net/hxl517116279/article/details/90815307