Android中利用前台服务白色保活
Posted 周文凯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中利用前台服务白色保活相关的知识,希望对你有一定的参考价值。
进程优先级
进程
我们都知道,每一个APP进程(process)都拥运行在独立的虚拟机(virtual machine)中,这样就保证了每个APP的独立。在系统资源紧张的时候会选择一部分进程杀掉释放内存,那么系统是如何选取杀掉哪些留下哪些呢?这就涉及到进程的优先级。
优先级
为了对运行的进程进行统一管理,系统根据进程的不同状态进行了分离,分为如下五个状态:
前台进程 (Foreground process)
可与与用户交互的进程,满足以下的都是:
- 有可与用户交互的Activity(已调onResume());
- 有广播接收者(BroadcastReceiver)正在接收广播;
- 有服务(Service)正在执行它的回调方法(Service.onCreate(), Service.onStart(), or Service.onDestroy());
- 有Service调用了startForeground()方法使之位于前台运行。
前台进程系统是不会销毁的,除非极端情况。
可见进程 (Visible process)
- 有不在前台、但仍对用户可见的 Activity(已调用 onPause());
- 有绑定到可见(或前台)Activity 的 Service;
一般系统也不会回收可见进程的,除非要保持某个或多个前台进程存活而且资源吃紧的情况下。
服务进程 (Service process)
- 运行着一个通过startService()启动的Service;
在内存不够维持所有前台进程与可见进程运行时,服务进程会被销毁。
后台进程 (Background process)
- 用户返回到界面或切换到其他APP,看不到但是还在运行的程序。
可能随时被系统销毁,回收内存。
空进程 (Empty process)
- 没有任何活跃组件的进程;
最容易被销毁的进程
进程查看
获取进程pid
通过adb shell ps
查看进程
B0000AS212345L:~ zwenkai$ adb shell ps
USER PID PPID VSIZE RSS WCHAN PC NAME
root 1 0 932 688 ffffffff 00000000 S /init
root 2 0 0 0 ffffffff 00000000 S kthreadd
root 3 2 0 0 ffffffff 00000000 S ksoftirqd/0
root 6 2 0 0 ffffffff 00000000 S migration/0
... ...
由于进行信息太多,可以进行过滤下,比如我们只想查手百
的进程ID,尽管不知道它的包名是什么,但一定包含baidu
。ps|grep
B0000AS212345L:~ zwenkai$ adb shell ps | grep baidu
USER PID PPID VSIZE RSS WCHAN PC NAME
u0_a201 9610 165 1387724 143820 ffffffff 00000000 S com.baidu.searchbox
u0_a201 10452 165 1052784 45944 ffffffff 00000000 S com.baidu.searchbox:megappInstaller
USER | PID | PPID | VSIZE | RSS | WCHAN | PC | NAME |
---|---|---|---|---|---|---|---|
用户 | 进程ID | 父进程ID | 虚拟内存大小 | 实际内存大小 | 0:代表正在运行 | Program Counter | 进程名 |
可以看到它的进程ID为9610
。
查看进程优先级
通过上一步找到进程ID为9610
,在proc
目录下找到对应的进程ID文件夹,查看里面的oom_adj
就可以获取到进程的优先级了。
B0000AS212345L:~ zwenkai$ adb shell
shell@hwp7:/ $ cat proc/9610/oom_adj
0
可以看到值为0
,手百
目前在前台,把它后台之后再看一下。
shell@hwp7:/ $ cat proc/9610/oom_adj
6
那这里的0
和6
分别代表什么呢?具体定义可以在com.android.server.am.ProcessList
查看:
常量名称 | 级别(android M) | 级别(android N) | 描述 |
---|---|---|---|
NATIVE_ADJ | -17 | -1000 | native进程 |
SYSTEM_ADJ | -16 | -900 | 系统进程 |
PERSISTENT_PROC_ADJ | -12 | -800 | 系统persistent进程,如telephony |
PERSISTENT_SERVICE_ADJ | -11 | -700 | 绑定系统进程或persistent进程的服务 |
FOREGROUND_APP_ADJ | 0 | 0 | 前台进程,正在前台运行的APP |
VISIBLE_APP_ADJ | 1 | 100 | 可见进程 |
PERCEPTIBLE_APP_ADJ | 2 | 200 | 用户可感知进程,如音乐播放 |
BACKUP_APP_ADJ | 3 | 300 | 正在执行备份的进程 |
HEAVY_WEIGHT_APP_ADJ | 4 | 400 | 高权重进程 |
SERVICE_ADJ | 5 | 500 | 服务进程 |
HOME_APP_ADJ | 6 | 600 | 与Home交互的进程 |
PREVIOUS_APP_ADJ | 7 | 700 | 切换进程 |
CACHED_APP_MIN_ADJ | 9 | 900 | 缓存进程即空进程 |
CACHED_APP_MAX_ADJ | 15 | 906 | 缓存进程即空进程 |
UNKNOWN_ADJ | 16 | 1001 | 未知进程 |
其他APP进程分析
看下其他APP的进程是怎么搞的,这里挑几款一定有Server后台进程的。
网易云音乐
Service声明
<service
android:name="com.netease.play.player.service.VideoPlayService"
android:process=":play" />
进程ID
1|shell@hwp7:/ $ ps | grep netease
u0_a87 16029 165 1142056 104776 ffffffff 00000000 R com.netease.cloudmusic
u0_a87 16084 165 1072908 68608 ffffffff 00000000 S com.netease.cloudmusic:browser
u0_a87 16178 165 1058940 61900 ffffffff 00000000 S com.netease.cloudmusic:play
进程优先级
有三个进程,对应进程优先级:
状态 | 主进程 | play进程 | browser进程 |
---|---|---|---|
前台(未播放) | 0 | 0 | 7 |
后台(未播放) | 6 | 4 | 7 |
前台(播放) | 0 | 0 | 7 |
后台(播放) | 6 | 1 | 7 |
通过以上前后台变化验证了猜想,play进程
在播放音乐的时候尽管后台,它的进程优先级是1
,是不会被销毁的。
喜马拉雅
Service 声明
<service
android:name="com.ximalaya.ting.android.opensdk.player.service.XmPlayerService"
android:process=":player">
<intent-filter
android:priority="1000">
<action
android:name="com.ximalaya.ting.android.mainapp.player.service.XmPlayerService" />
</intent-filter>
</service>
进程ID
shell@hwp7:/ $ ps | grep ximalaya
u0_a92 16944 165 1245432 156184 ffffffff 00000000 S com.ximalaya.ting.android
u0_a92 16979 165 1029400 59772 ffffffff 00000000 S com.ximalaya.ting.android:player
进程优先级
有两个进程,对应进程优先级:
状态 | 主进程 | player进程 |
---|---|---|
前台(未播放) | 0 | 0 |
后台(未播放) | 1 | 1 |
前台(播放) | 0 | 0 |
后台(播放) | 1 | 1 |
player进程
后台播放时是符合预期的,发现它NB的是后台时主进程进程优先级竟然也是1
,这里怀疑它进行了黑操作。
创建前台服务
TestService
class TestService : Service()
private val NOTICE_ID = 100
override fun onBind(intent: Intent?): IBinder?
return null
override fun onCreate()
super.onCreate()
val builder = Notification.Builder(this)
builder.setSmallIcon(R.mipmap.ic_launcher)
builder.setContentTitle("TestService")
builder.setContentText("TestService is running...")
startForeground(NOTICE_ID, builder.notification)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int
// 如果Service被终止, 当资源允许情况下,重启service
return START_STICKY
override fun onDestroy()
super.onDestroy()
// 如果Service被杀死,干掉通知
val mManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mManager.cancel(NOTICE_ID)
// 重启
val intent = Intent(applicationContext, TestService::class.java)
startService(intent)
注册Service
<service android:name=".TestService"
android:process=":test"/>
开启服务
class MainActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startService(Intent(this, TestService::class.java))
查看进程ID
1|shell@hwp7:/ $ ps | grep kevin
u0_a93 24386 165 999248 44576 ffffffff 00000000 S com.kevin.foregroundservice
u0_a93 24406 165 976268 29064 ffffffff 00000000 S com.kevin.foregroundservice:test
进程优先级
有两个进程,对应进程优先级:
状态 | 主进程 | test进程 |
---|---|---|
前台 | 0 | 1 |
后台 | 6 | 1 |
和分析的两款APP还是有差距,就是前台的时候我们的test进程
优先级是1
而不是0
,其实通过bindService
的方式创建就可以啦。
参考
以上是关于Android中利用前台服务白色保活的主要内容,如果未能解决你的问题,请参考以下文章