1:low memory killer
系統(tǒng)出于性能和體驗(yàn)上的考慮,APP退到后臺后并不會真正的kill、掉進(jìn)程,而是將其緩存起來。
打開的應(yīng)用越多,緩存的應(yīng)用也就越多,在系統(tǒng)進(jìn)程不足的情況下,系統(tǒng)根據(jù)自己的一套進(jìn)程回收機(jī)制,來判斷kill掉哪些進(jìn)程,以騰出進(jìn)程給需要的app,這套進(jìn)程回收機(jī)制叫做low memory killer。
2:內(nèi)存不足
內(nèi)存閥值,每個手機(jī)都不一樣,當(dāng)可用內(nèi)存小于該值得時候,Android就會殺死對應(yīng)優(yōu)先級得進(jìn)程。
3:進(jìn)程的優(yōu)先級
進(jìn)程的優(yōu)先級通過oom_adj來判斷,oom_adj取值如下:
| adj級別 | 值 | 說明 |
|---|---|---|
| UNKNOWN_ADJ | 16 | 預(yù)留的最低級別,一般對于緩存的進(jìn)程才有可能設(shè)置成這個級別 |
| CACHED_APP_MAX_ADJ | 15 | 緩存進(jìn)程,空進(jìn)程,在內(nèi)存不足的情況下就會優(yōu)先被kill |
| CACHED_APP_MIN_ADJ | 9 | 緩存進(jìn)程,也就是空進(jìn)程 |
| SERVICE_B_ADJ | 8 | 不活躍的進(jìn)程 |
| PREVIOUS_APP_ADJ | 7 | 切換進(jìn)程 |
| HOME_APP_ADJ | 6 | 與Home交互的進(jìn)程 |
| SERVICE_ADJ | 5 | 有Service的進(jìn)程 |
| HEAVY_WEIGHT_APP_ADJ | 4 | 高權(quán)重進(jìn)程 |
| BACKUP_APP_ADJ | 3 | 正在備份的進(jìn)程 |
| PERCEPTIBLE_APP_ADJ | 2 | 可感知的進(jìn)程,比如那種播放音樂 |
| VISIBLE_APP_ADJ | 1 | 可見進(jìn)程 |
| FOREGROUND_APP_ADJ | 0 | 前臺進(jìn)程 |
| PERSISTENT_SERVICE_ADJ | -11 | 重要進(jìn)程 |
| PERSISTENT_PROC_ADJ | -12 | 核心進(jìn)程 |
| SYSTEM_ADJ | -16 | 系統(tǒng)進(jìn)程 |
| NATIVE_ADJ | -17 | 系統(tǒng)起的Native進(jìn)程 |
0-3是比較安全的oom_adj一般不會被系統(tǒng)殺死的,所以我們只要保證自己的app oom_adj在0-3之間就可以了。
可以通過adb命令:cat /proc /4181/oom_adj來查看自己app的oom_adj的值
4181是進(jìn)程號
保活
方案1:activity1像素?;?/h4>
原理:手機(jī)關(guān)閉屏幕的時候,偷偷創(chuàng)建一個activity,讓應(yīng)用成為前臺進(jìn)程,打開屏幕時關(guān)閉activity,這樣用戶就不會發(fā)現(xiàn)什么異常,我們知道前臺應(yīng)用的oom_adj為0是不會被殺死的,這樣就達(dá)到看保活的目的。
//設(shè)置1像素
Window window = getWindow();
window.setGravity(Gravity.LEFT | Gravity.TOP);
WindowManager.LayoutParams params = window.getAttributes();
params.x = 0;
params.y = 0;
params.height = 1;
params.width = 1;
window.setAttributes(params);
缺點(diǎn):activity不夠干凈,只有在息屏的時候才生效,存在局限性比較大,而且谷歌原生的系統(tǒng)息屏的時候不會清理進(jìn)程,但是現(xiàn)在很多廠商會在息屏的時候清理內(nèi)存,所以本方案的可行性不高,可以作為了解。
方案2:前臺service保活
?;钤恚簡右粋€前臺服務(wù),從而拉高整個應(yīng)用的優(yōu)先級。
因?yàn)橐坏┩ㄖ挥脩舾傻裟敲丛摫;罘桨妇筒缓糜昧?,所以通知圖標(biāo)存在與否是該方案是否可行的關(guān)鍵。
但是該方案是谷歌官方承認(rèn)的?;罘桨?,所以可行性還是很高的。
需要適配
API<18通知圖標(biāo)不會顯示
//activity中
startservice(new Intent( packagecontext: this, Foregroundservice. class)):
//service中
startforeground(1, new Notification());
API>=18&&API<26可以啟動雙服務(wù),綁定同樣的D,然后stop
這個方法的原理是8.0系統(tǒng)之前會根據(jù)服務(wù)的id來判斷通知,那么第二個id設(shè)置跟第一個相同,然后自殺,系統(tǒng)就會誤認(rèn)為此通知已死就不會通知了,那么通知欄上面就不會顯示
//activity中
startservice(new Intent( packagecontext: this, Foregroundservice. class)):
//service中
startforeground(1, new Notification());
public static class Innerservice extends Service {
@Nullable
@Override
public Ibinder onbind(Intent intent){
return null;
}
@Override
public void oncreate() {
super.oncreate
startforeground(1, new Notification())
stopself();
}
API>=26后暫時沒有方式能夠隱藏
8.0之后不可以創(chuàng)建同樣的id的通知,所以此隱藏通知的方法就不好用了,當(dāng)然了,通知顯示與否不是該方案成功的評判標(biāo)準(zhǔn),所以說還是可以用的。
拉活
1:廣播拉活
1.1:系統(tǒng)廣播拉活
在發(fā)生特定系統(tǒng)事件時,系統(tǒng)會發(fā)出廣播,通過在 Androidmanifest中靜
態(tài)注冊對應(yīng)的廣播監(jiān)聽器,即可在發(fā)生響應(yīng)事件時拉活
但是從 android7.0開始,對廣播進(jìn)行了限制,而且在8.0更加嚴(yán)格該方法就不適用了。
1.2:全家桶拉活
有多個app在用戶設(shè)備上安裝,只要開啟其中一個就可以將其他的app也拉
活。比如手機(jī)里裝了手Q、QQ空間、興趣部落等等,那么打開任意一個app后,其
他的app也都會被喚醒
2:賬戶(account)同步拉活
系統(tǒng)每隔一段時間會進(jìn)行賬戶同步,當(dāng)系統(tǒng)去賬戶同步的時候(不一定多長時間,跟系統(tǒng)有關(guān)),我們就去拉活app,這個方案是非常穩(wěn)定的,當(dāng)然了國內(nèi)的系統(tǒng)都是定制的,所以還是需要一定的適配的。
優(yōu)點(diǎn):系統(tǒng)喚醒,比較穩(wěn)定
缺點(diǎn):時間不能把控
2.1注冊一個service,添加一個系統(tǒng)可識別的action
<service android: name- account. Authenticationservice
<intent-filter>
<action android: name-androld. accounts. Accountauthenticator
</intent-filter
<meta-data
android: name="android, accounts, Accountauthenticato
android: resource="@xml/account authenticator /
</service>
2.2service中通過binder把AccountAuthenticator 對象告訴系統(tǒng)
private AccountAuthenticator accountAuthenticator;
@override
public IBinder on Bind(Intent intent) {
return account Authenticator.getIBinder() ;
@override
public void onCreate() {
super.onCreate() ;
accountAuthenticator=new AccountAuthenticator(context:this) ;
2.3:同步賬戶:
public class Syncservice extends service{
private SyncAdapter mSyncAdapter;
private static final String TAG="SyncService";
@override
public IBinder onBind(Intent intent) {
return mSyncAdapter.getSyncAdapterBinder() ;
@override
public void onCreate() {
super.onCreate() ;
mSyncAdapter=new SyncAdapter(getApplicationContext() , true) ;
public static class SyncAdapter extends AbstractThreadedSyncAdapter{
public SyncAdapter(Context context, boolean auto Initialize) {
super(context, autoInitialize) ;
@override
public void onPerformSync(Accounta ccount, Bundle extras, String authority, ContentProvider clientprovider, SyncResult syncResult{
Log.e(TAG, “同步賬戶") ;
//與互聯(lián)網(wǎng)或者本地?cái)?shù)據(jù)庫同步賬戶
}
2.4:添加權(quán)限
<uses-permission
android:name="android.permission.AUTHENTICATE_ACCOUNTS"
android:maxsdkversion="22"/>
<uses-permission
android:name="android.permission, GET_ACCOUNTS"
android:maxsdkversion="22"/>
<uses-permissionandroid:name="android.permission.MRITE_SYNC_SETTINGS"/>
3:JobScheduler拉活
JobScheduler允許在特定狀態(tài)與特定時間間隔周期執(zhí)行任務(wù)??梢岳?br>
用它的這個特點(diǎn)完成保活的功能,效果即開啟一個定時器,與普通定時器不
同的是其調(diào)度由系統(tǒng)完成。
同樣在某些ROM可能并不能達(dá)到需要的效果
@Requires Api(api=Build.VERSION.CODES.LOLLIPOP
public class MyJob service extends Jobservice{
public static void startJob(Context context) {
JobScheduler jobscheduler=(Jobscheduler) context.getSystemservice(Context.JOB_SCHEDULER_SERVICE) ;
JobInfo.Builder builder=new JobInfo.Builder(8, new ComponentName(context.get PackageName() ,
HyJob service.class.getName() ) ) .setPersisted(true) ;
//小于7.0
builder.setPeriodic(1000) ;//6.0以后最小是5s
//大于7.0
builder. set MinimumLatency(1000) ;//6.0以后最小是5s
job scheduler.schedule(builder.build() ) ;
}
@override
public boolean onStartJob(JobParameters params) {
//大于7.0
startJob(this);
return false;
}
@override
public boolean onStopJob(Jobparameters params) {
return false;
}
}