“安卓手機爲什麼老是這麼卡”,
“安卓手機高配置的低能兒”
作爲一個安卓開發工程師,不知道聽到別人這麼說安卓的時候,你怎麼感想。
之前遇到個問題,app在後臺殺死後,用戶進行暖啓動,用戶會直接進入之前瀏覽過的頁面。這時公共bean的數據是沒有的,但是每次後臺請求數據時,是需要各種請求頭的,這時就會出錯。當時,只有一年工作經驗的我第一想到的就是保活,而且當時覺得保活很帥,我想實戰一下。但是,在和師傅商量後,否定了自己的想法。最後,我們在父類activity中每次判斷公共bean中的一個字段是否爲空實現的。後來,我想想其實保活對這個問題起不了多大作用,而且浪費系統資源。所以思路,交流,理解需求很重要。
爲什麼需要保活? 我們需要做一些實時的事情。比如:IM, 及時推送。
進程想要常駐後臺,一般兩類方法:保活和拉活。
保活: 白名單,1像素,前臺服務保活
拉活: 賬戶同步拉活,JobScheduler,雙進程,workManager
1像素:手機鎖屏後,在屏幕上開一個1像素的界面,將app置爲前臺進程。因爲國內手機廠商爲了息屏後省電,會將部分後臺進程殺死。
一像素Activity:
public class KeepAct extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//直接用windo設置該acitivty爲1像素
Window window = getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.y = 0;
params.x = 0;
params.width = 1;
params.height = 1;
window.setAttributes(params);
PowerManager powerManager = (PowerManager) this
.getSystemService(Context.POWER_SERVICE);
boolean ifOpen = powerManager.isScreenOn();
if (ifOpen)
finish();
else
KeepManager.getInstance().setmActivity(this);
}
@Override
protected void onResume() {
super.onResume();
Log.e("KeepAct", "onResume");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("KeepAct", "onDestroy");
}
}
1像素控制類:
public class KeepManager {
//惡漢單例
private static KeepManager keepManager = new KeepManager();
//全局---便於註銷
private KeepRevice keepRevice;
//弱引用KeepAct,釋放它
private WeakReference mActivity;
private KeepManager() {
}
public static KeepManager getInstance() {
return keepManager;
}
/**
* 註冊監聽 屏幕亮暗的廣播
*
* @param context
*/
public void register(Context context) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
keepRevice = new KeepRevice();
context.registerReceiver(keepRevice, intentFilter);
}
/**
* 註銷廣播
*
* @param context
*/
public void unregister(Context context) {
if (keepRevice != null)
context.unregisterReceiver(keepRevice);
}
/**
* 屏幕亮了
*/
public void screenOn() {
if (mActivity != null) {
Activity activity = (Activity) mActivity.get();
if (activity != null)
activity.finish();
mActivity = null;
}
}
/**
* 屏幕息屏
*/
public void screenOff(Context context) {
Intent intent = new Intent(context, KeepAct.class);
context.startActivity(intent);
}
public void setmActivity(Activity mActivity) {
this.mActivity = new WeakReference<Activity>(mActivity);
}
}
設置廣播監聽:
public class KeepRevice extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_SCREEN_OFF)) {
KeepManager.getInstance().screenOff(context);
} else {
KeepManager.getInstance().screenOn();
}
}
}
使用的時候,直接調用 KeepManager.getInstance().register(getApplication()); 不要忘記銷燬廣播。我們這裏需要注意的兩點:1.keepactivity 需要設置爲透明的,不然會閃爍一層黑色;2.keepactivity需要設置一個單獨的棧,否則會讓你的app莫名其妙開屏後跳出來,交互不好。
前臺服務保活:開啓一個前臺服務,提高應用的優先。
4.3之前,開啓一個服務設置爲前臺進程即可;
4.3開始,系統會通知用戶,有一個前臺進程服務,如微信視頻時候,通知欄中的通知。如果,不想讓用戶看到。8.0之前可以再啓動一個service,關閉通知消息,8.0之後則會強制顯示了。
如果通知不影響需求效果的話,這是一個不錯的保活方法。具體實現如下:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
//4.3以下,將service設置成前臺服務,並且不顯示通知消息
startForeground(SERVICE_ID, new Notification());
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
//4.3-->7.0,將service設置成前臺服務
startForeground(SERVICE_ID, new Notification());
//刪除通知消息
startService(new Intent(this, InnerService.class));
} else {
//8.0以上,通知欄消息需要設置channel
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel("channel", "xx", NotificationManager.IMPORTANCE_MIN);
if (manager != null) {
manager.createNotificationChannel(channel);
Notification notification=new NotificationCompat.Builder(this,"channel").build();
startForeground(SERVICE_ID,notification);
}
}
當然4.3到7.0的時候,可以自啓動一個service。關閉了通知,再自己銷燬。
public static class InnerService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
//讓服務變成前臺服務
startForeground(SERVICE_ID, new Notification());
//關閉自己
stopSelf();
}
}
上面說的是儘量不讓我們的app被系統殺死。如果app不幸已經殺死了,需要拉活app。
賬戶同步拉活:系統提供給app一段時間與服務器同步數據的工具,不同的手機時間段不同,一般情況是15分鐘。但是,不是每個app都會被同步,需要添加自己的app。在設置中的其他賬戶(oppo中這麼設置,別的手機不一樣)可以查看正在賬戶同步的app。
第一張和第三張是已經同步的app,第二張是添加了賬戶的app,還沒有同步。實測中小米和華爲8.0的手機表現可以,其他的手機大家測試一下。這種適配方式適合一般的推送的需求。
小米6x
09-12 17:02:17.944 21270-25407/com.example.onepoition E/onPerformSync: 同步賬戶09/12 16:32
09-12 17:02:17.944 21270-25407/com.example.onepoition E/onPerformSync: 同步賬戶09/12 16:47
09-12 17:02:17.944 21270-25407/com.example.onepoition E/onPerformSync: 同步賬戶09/12 17:02
09-12 17:16:59.653 21270-25762/com.example.onepoition E/onPerformSync: 同步賬戶09/12 17:16
華爲AL_20:
09-12 17:02:17.944 21270-25407/com.example.onepoition E/onPerformSync: 同步賬戶09/12 16:38
09-12 17:02:17.944 21270-25407/com.example.onepoition E/onPerformSync: 同步賬戶09/12 16:54
09-12 17:08:05.750 16524-20199/com.example.onepoition E/onPerformSync: 同步賬戶09/12 17:08
09-12 17:22:10.139 16524-21152/com.example.onepoition E/onPerformSync: 同步賬戶09/12 17:22
JobScheduler:android 5.0給的api,可以在滿足特定條件下實現app的後臺進程,和賬戶同步拉活的功能和實現差不多。但是,國內好多產商對它做了很多不友好的操作。
1. 在華爲和小米手機上,app手動殺死後無效了。同步賬戶可以仍同步;
2. 很多廠商需要設置app自啓動權限;
3. 有些手機一鍵清理,JobScheduler也會被清理。
雙進程:app開啓兩個進程,兩個進程各開一個服務,只要斷開連接,存活的進程把被殺死的進程拉活。雙進程拉活是一個比較老而且常用的方法。查看正在運行的app時,會發現有好多app是多進程機制。不過,現在很多多進程是因爲不同類型功能的分離。
總結:1. 首先考慮需求是否需要多進程,不要一直佔領後臺資源;
2. 一個時間段的保活可以使用前臺服務保護。比如:微信視頻時,通知欄會有微信使用前臺服務通知;
3. 不需要實時的推送可以使用賬戶同步 或者 JobScheduler,賬戶同步更優越些;
4. 如果公司有能力,白名單一波,爽歪歪!!!