Android进程保活:如何让app一直运行
1)为什么需要进行进程保活呢?需求是什么?2)进程分类3)进程的优先级4)如何提高进程优先级5)如何进行进程保活。
目录
1)为什么需要进行进程保活呢?需求是什么?
2)进程分类
3)进程的优先级
4)如何提高进程优先级
5)如何进行进程保活
一、为什么需要进行进程保活呢?需求是什么?
比如:
- app在运行过程中,可能会因为各种原因导致闪退,报错或者被杀死等,尤其是一些自动售卖机,安装了android系统,如果这个时候app闪退了,那么就无法售卖了,我们需要能让程序运行起来。
- 如果用中有后台服务需要一直运行,例如音乐播放器、即时通讯等。
- 应用可能需要接收推送消息并及时展示给用户。为了实这个功能,需要保证应用进程直处于活跃状态,以便能够及时处理和显示推送消息。
- 有些应用需要执行定时任务,例如定时更新数据、定时发送统计信息等。通过保持进程活跃,可以确保这些任务按时执行。
- 某些应用需要在屏幕上显示悬浮窗口,例如悬浮球、悬浮菜单等
就需要通过进程保活来防止系统将其杀死,今天我们就来讲讲,进程保活。
二、进程分类
进程会被杀死,那么什么进程会被杀死呢?
1)前台进程(Foreground Process):前台进程是指当前正在与用户进行交互的app进程。例如,用户正在使用某个app或者app正在展示一个前台界面时;在前台运行的 Service 服务 , Service 调用了 startForeground,该app所在的进程就被认为是前台进程。前台进程拥有最高的优先级,不容易被系统杀死。
2)可见进程(Visible Process)可见进程是指虽然没有处于前台,但是对用户可见的app进程。例如,当一个Activity被另一个Activity或Dialog部分遮挡时,被遮挡的Activity所在的进程就被认为是可见进程。可见进程的优先级较高,相比后台进程更不容易被系统杀死。Activity 组件调用 onPause 生命周期函数。
3)服务进程(Service Process)调用 startService 方法启动的 Service 进程组件 , 就是服务进程 , 其没有与 Activity 组件绑定 , 因此该 Service 组件的优先级要降低一个等级 , 称为服务进程 ;
4)后台进程(Background Process):后台进程是指已经失去用户焦点且不可见的app进程。例如,当用户按Home键回到桌面时,app进入后台状态。后台进程的优先级较低,当系统内存不足时可能会被系统杀死以释放资源。
5)空进程(Empty Process):空进程是指没有任何活动组件(如Activity、Service等)的应用进程。这进程通常只是为了缓存应用的数据而保留,当需要重新启动应用时可以快速恢复。空进程的优先级最低,是系统内存不足时首先被杀死的对象。
三、那么,如何才能清楚的确认,究竟是那种进程呢?
通过oom_adj值.
oom_adj值是Linux内核为每个进程分配的,用于反映进程的优先级。在Android系统中,当内存不足需要杀死进程以回收内存时,系统会根据oom_adj值来决定是否回收该进程。oom_adj值越大,对应的进程优先级越低,越有可能被系统回收。
以下是一些oom_adj值对应的进程优先级(注意:这些值可能会因Android版本的不同而有所变化,以下信息基于参考文章提供的信息):
oom_adj值 | 进程优先级描述 |
---|---|
-16 | SYSTEM_ADJ:系统进程,具有最高优先级 |
0 | FOREGROUND_APP_ADJ:前台进程,用户当前正在交互的进程 |
1 | VISIBLE_APP_ADJ:可见进程,虽然不在前台但用户仍然可以看到(如锁屏界面上的应用) |
2 | PERCEPTIBLE_APP_ADJ:可感知进程,如后台播放音乐、铃声、震动等 |
3 | BACKUP_APP_ADJ:进入后台的进程,按下Menu键可查看 |
5 | SERVICE_ADJ:服务进程,持有应用服务的进程 |
如何查看oom_adj值
在 Android Studio 中 , 可以看到该运行的程序的进程号 PID 为 30575
进入 adb shell 命令行 , su 获取 root 权限 , 使用如下命令cat /proc/30575/oom_adj
, 查询指定 PID 的 oom_adj 值就可以看到,为0就是前台进程。
那么,内存不足,多少才算不足呀?
这些数字,其单位是 4 K B , 80640 , 乘以 4 K B \rm 4KB 4KB , 除以1024 , 最后得到 315MB
内存不足时杀进程的优先顺序 :
18432 : 内存小于 18432 × 4 K B \rm 18432 \times 4KB 18432×4KB 时 , 杀 " 前台进程 " ;
23040 : 内存小于 23040 × 4 K B \rm 23040 \times 4KB 23040×4KB 时 , 杀 " 可见进程 " ;
27648 : 内存小于 27648 × 4 K B \rm 27648 \times 4KB 27648×4KB 时 , 杀 " 服务进程进程 " ;
32256 : 内存小于 32256 × 4 K B \rm 32256 \times 4KB 32256×4KB 时 , 杀 " 后台进程 " ;
55296 : 内存小于 55296 × 4 K B \rm 55296 \times 4KB 55296×4KB 时 , 杀 " ContentProvider 进程 " ;
80640 : 内存小于 80640 × 4 K B \rm 80640 \times 4KB 80640×4KB 时 , 杀 " 空进程 " ;
四、如何提高进程优先级
了解了这么多,我们知道前台进程不容易被杀死,假如我们变成了其他进程,那么如何才能提高他的优先级呢?
4.1 前台 Service
(1)service
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
class MyForegroundService : Service() {
private val NOTIFICATION_ID = 1
private val CHANNEL_ID = "ForegroundServiceChannel"
override fun onCreate() {
super.onCreate()
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// 创建通知渠道(Android 8.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_LOW
)
notificationManager.createNotificationChannel(channel)
}
// 启动前台服务
startForeground(NOTIFICATION_ID, getNotification("服务正在运行..."))
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// 处理从Activity传递过来的数据(如果有的话)
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
private fun getNotification(contentText: String): Notification {
val builder = Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification) // 设置图标,确保你有这个资源
.setContentTitle("前台服务示例")
.setContentText(contentText)
.setPriority(Notification.PRIORITY_LOW)
// 可以在这里添加其他通知设置,如声音、振动等
return builder.build()
}
}
(2)注册服务
<application
...
>
...
<service
android:name=".MyForegroundService"
android:enabled="true"
android:exported="false" />
...
</application>
(3)activity
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 启动前台服务
val serviceIntent = Intent(this, MyForegroundService::class.java)
startService(serviceIntent)
}
// 其他方法...
}
好了,我们可以运行程序看看,比如按home键,然后再读取oom_adj值,看看有没有生效。
五、如何进行进程保活
通过上述信息,那么我们就可以知道,我们现在的进程究竟是什么?为我们接下来要进行进程保活做铺垫。进程保活是指在Android系统,通过一些手段和技术手段来确保应用进程持续运行的一种机制。
5.1 双进程保活
进程之间相互监听销毁的状态,从而重新启动对方。通过ServiceConnection 来进行监听。
(1)activity
startService(Intent(this, AService.java))
startService(Intent(this, BService.java))
(2)service
public class AService extends Service {
private Connection connection;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
// 启动前台进程
startService();
}
private void startService() {
// 创建通知通道
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);
// 正式创建
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();
startForeground(10, notification);
}
/**
* 绑定 另外一个 服务
*/
private void bindService() {
// 创建连接对象
connection = new Connection();
// 创建本地前台进程组件意图
Intent bindIntent = new Intent(this, BService.class);
// 绑定进程操作
bindService(bindIntent, connection, BIND_AUTO_CREATE);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 绑定另外一个服务
bindService();
return START_STICKY; // 尝试在系统杀死服务后重新创建它
}
class Connection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 服务绑定成功时回调
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 再次启动前台进程
startService();
// 绑定另外一个远程进程
bindService();
}
}
}
(3)service
public class BService extends Service {
private Connection connection;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
// 启动前台进程
startService();
}
private void startService(){
// 创建通知通道
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);
// 正式创建
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();
startForeground(10, notification);
}
/**
* 绑定 另外一个 服务
*/
private void bindService(){
// 创建连接对象
connection = new Connection();
// 创建本地前台进程组件意图
Intent bindIntent = new Intent(this, AService.class);
// 绑定进程操作
bindService(bindIntent, connection, BIND_AUTO_CREATE);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 绑定另外一个服务
bindService();
return START_STICKY; // 尝试在系统杀死服务后重新创建它
}
class Connection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 服务绑定成功时回调
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 再次启动前台进程
startService();
// 绑定另外一个远程进程
bindService();
}
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)