1: 開啟一個像素的Activity
系統一般是不會殺死前臺進程的。所以要使得進程常駐,我們只需要在鎖屏的時候在本進程開啟一個Activity,
為了欺騙用戶,讓這個Activity的大小是1像素,并且透明無切換動畫,
在開屏幕的時候,把這個Activity關閉掉,所以這個就需要監聽系統鎖屏廣播.
//權限
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT"/>
//注冊
<service android:name=".ui.service.MainService"
android:process="1000"/>
<receiver android:name=".ui.broadcastReceiver.ScreenBroadcastListener$ScreenBroadcastReceiver">
<intent-filter android:priority="90000">
<action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter>
</receiver>
//1px Activity
public class SinglePixelActivity extends Activity {
public static final String TAG = SinglePixelActivity.class.getSimpleName();
public static void actionToSinglePixelActivity(Context context) {
Intent intent = new Intent(context, SinglePixelActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(R.layout.activity_singlepixel);
Window window = getWindow();
//放在左上角
window.setGravity(Gravity.START | Gravity.TOP);
WindowManager.LayoutParams attributes = window.getAttributes();
//寬高設計為1個像素
attributes.width = 1;
attributes.height = 1;
//起始坐標
attributes.x = 0;
attributes.y = 0;
window.setAttributes(attributes);
ScreenManager.getInstance(this).setActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
在屏幕關閉的時候把SinglePixelActivity啟動起來,
在開屏的時候把SinglePixelActivity 關閉掉,
所以要監聽系統鎖屏廣播,以接口的形式通知MainActivity啟動或者關閉SinglePixActivity。
public class ScreenBroadcastListener {
private Context mContext;
private ScreenBroadcastReceiver mScreenReceiver;
private ScreenStateListener mListener;
public ScreenBroadcastListener(Context context) {
mContext = context.getApplicationContext();
mScreenReceiver = new ScreenBroadcastReceiver();
}
interface ScreenStateListener {
void onScreenOn();
void onScreenOff();
}
/**
* screen狀態廣播接收者
*/
private class ScreenBroadcastReceiver extends BroadcastReceiver {
private String action = null;
@Override
public void onReceive(Context context, Intent intent) {
action = intent.getAction();
if (Intent.ACTION_SCREEN_ON.equals(action)) { // 開屏
mListener.onScreenOn();
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 鎖屏
mListener.onScreenOff();
}
}
}
public void registerListener(ScreenStateListener listener) {
mListener = listener;
registerListener();
}
private void registerListener() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mScreenReceiver, filter);
}
}
//1px Activity管理類
public class ScreenManager {
private Context mContext;
private WeakReference<Activity> mActivityWref;
public static ScreenManager gDefualt;
public static ScreenManager getInstance(Context context) {
if (gDefualt == null) {
gDefualt = new ScreenManager(context.getApplicationContext());
}
return gDefualt;
}
private ScreenManager(Context context) {
this.mContext = context;
}
public void setActivity(Activity pActivity) {
mActivityWref = new WeakReference<Activity>(pActivity);
}
public void startActivity() {
SinglePixelActivity.actionToSinglePixelActivity(mContext);
}
public void finishActivity() {
//結束掉SinglePixelActivity
if (mActivityWref != null) {
Activity activity = mActivityWref.get();
if (activity != null) {
activity.finish();
}
}
}
}
現在MainActivity改成如下
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ScreenManager screenManager = ScreenManager.getInstance(MainActivity.this);
ScreenBroadcastListener listener = new ScreenBroadcastListener(this);
listener.registerListener(new ScreenBroadcastListener.ScreenStateListener() {
@Override
public void onScreenOn() {
screenManager.finishActivity();
}
@Override
public void onScreenOff() {
screenManager.startActivity();
}
});
}
}
初步的思路是先判斷app進程是否存在,如果存在的話,
就利用startActivities啟動MainActivity和DetailActivity。
為什么還要啟動MainActivity而不直接只啟動DetailActivity?
因為有如下情況,進程中的所有Activity都已經退出了,但進程還沒有被系統回收,
這時判斷進程是否存在返回true,然后只啟動DetailActivity的話,按Back鍵任務棧就直接到底,返回桌面了。
而我們要的效果是按Back鍵返回上一級Activity,也就是MainActivity。
如果app進程已經退出,不存在了,此時就用一個Intent啟動應用,
該Intent中包含一個Bundle, Bundle中存有啟動DetailActivity所需的參數,
這個Intent傳入SplashActivity后,再由SplashActivity傳給MainActivity,在MainActivity中加入判斷,
如果有該參數,則表示應用是從通知欄啟動的,要進行跳轉到DetailActivity的操作,否則就是常規啟動。
(缺點: 如果沒root 在部分機器上會造成廣播監聽不到)
2
系統自帶的,onStartCommand方法必須具有一個整形的返回值,
這個整形的返回值用來告訴系統在服務啟動完畢后,
如果被Kill,系統將如何操作,這種方案雖然可以,
但是在某些情況or某些定制ROM上可能失效,
認為可以多做一種保保守方案。
START_STICKY
如果系統在onStartCommand返回后被銷毀,系統將會重新創建服務并依次調用onCreate和onStartCommand(
注意:根據測試Android2.3.3以下版本只會調用onCreate根本不會調用onStartCommand,
Android4.0可以辦到),這種相當于服務又重新啟動恢復到之前的狀態了)。
3
JobSheduler
JobSheduler是作為進程死后復活的一種手段,
native進程方式最大缺點是費電,
Native 進程費電的原因是感知主進程是否存活有兩種實現方式,在 Native 進程中通過死循環或定時器,輪訓判斷主進程是否存活,當主進程不存活時進行拉活。
其次5.0以上系統不支持。
但是JobSheduler可以替代在Android5.0以上native進程方式,這種方式即使用戶強制關閉,也能被拉起來,親測可行。
4 進程相互喚醒
顧名思義,就是指的不同進程,
不同app之間互相喚醒,如你手機里裝了支付寶、淘寶、天貓、UC等阿里系的app,
那么你打開任意一個阿里系的app后,有可能就順便把其他阿里系的app給喚醒了。
注:服務保活的前提是:要盡可能減少內存和電量的消耗。
Android 7.0版本以上,目前沒有什么真正意義上的保活。