天天看點

【Android】鎖屏後應用保活、拉活、雙程序守護

最近在使用高德地圖實時擷取定位時遇到了個問題,鎖屏後一段時間(5~10分鐘左右)後程式會被系統殺死,為了保活,特研究了下程序保活機制。

0、基本操作和概念

針對root過的手機,可以通過下列指令檢視記憶體門檻值:

adb shell
su
cat /sys/module/lowmemorykiller/parameters/minfree
會出現6個數字,從小到大
           

某個數字 * 4KB / 1024KB = 某某MB;

這裡的4kb是Linux中一頁(one page)的大小;

得到的某某MB就是當系統記憶體小于某某M的時候,就殺死某些等級的程序。—俗稱記憶體門檻值

6個等級分别對應:

前台程序——可見程序——服務程序——背景程序——空程序

1、WakeLock 配合 PowerManager

從源頭上把控,不讓界面鎖屏。

https://blog.csdn.net/IO_Field/article/details/49591429

WakeLock mWakeLock;
     
        //申請裝置電源鎖
        public static void acquireWakeLock(Context context)
        {
            if (null == mWakeLock)
            {
                PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
                mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "WakeLock");
                if (null != mWakeLock)
                {
                    mWakeLock.acquire();
                }
            }
        }
        //釋放裝置電源鎖
        public static void releaseWakeLock()
        {
            if (null != mWakeLock)
            {
                mWakeLock.release();
                mWakeLock = null;
            }
           

此法于我無效,繼續探索。

2、背景播放無聲音樂

這個方法在持續進行位置擷取上使用過了,有效,但是仍不能保證我的主程序活着,主程序挂了,音樂播放自然也無效了。

3、1像素Activity保活

OnePixelReceiver.java

package com.jsc4.androidcppsafe.onePixelKeepLive;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * 監聽螢幕狀态的廣播
 */
public class OnePixelReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {    //螢幕關閉啟動1像素Activity
            Intent it = new Intent(context, OnePixelActivity.class);
            it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(it);
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {   //螢幕打開 結束1像素
            context.sendBroadcast(new Intent("finish"));
            Intent main = new Intent(Intent.ACTION_MAIN);
            main.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            main.addCategory(Intent.CATEGORY_HOME);
            context.startActivity(main);
        }
    }
}
           

OnePixelActivity.java

package com.jsc4.androidcppsafe.onePixelKeepLive;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.PowerManager;
import android.util.Log;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;

/**
 * Created by Administrator on 2017/7/10.
 */
public class OnePixelActivity extends Activity {

    private BroadcastReceiver endReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //設定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);

        //結束該頁面的廣播
        endReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                finish();
            }
        };
        registerReceiver(endReceiver, new IntentFilter("finish"));
        //檢查螢幕狀态
        checkScreen();
    }

    @Override
    protected void onResume() {
        super.onResume();
        checkScreen();
        Log.i("djtest", "OnePixelActivity: onResume");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("djtest", "OnePixelActivity: onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("djtest", "OnePixelActivity: onDestroy");
    }

    /**
     * 檢查螢幕狀态  isScreenOn為true  螢幕“亮”結束該Activity
     */
    private void checkScreen() {
        PowerManager pm = (PowerManager) OnePixelActivity.this.getSystemService(Context.POWER_SERVICE);
        boolean isScreenOn = pm.isScreenOn();
        if (isScreenOn) {
            finish();
        }
    }

}
           

在MainActivity.java的onCreate中:

//注冊監聽螢幕開啟和關閉的廣播
        OnePixelReceiver mOnepxReceiver = new OnePixelReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.intent.action.SCREEN_OFF");
        intentFilter.addAction("android.intent.action.SCREEN_ON");
        intentFilter.addAction("android.intent.action.USER_PRESENT");
        registerReceiver(mOnepxReceiver, intentFilter);
           

4、雙服務保活

參考:

https://github.com/linliangliang/TwoProcess

https://www.cnblogs.com/lulj/p/7161317.html

(1)在main檔案夾上右鍵,建立AIDL檔案IMyAidlInterface.aidl:

// IMyAidlInterface.aidl
package com.jsc4.androidcppsafe;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
//    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
//            double aDouble, String aString);

      String getServiceName();
}
           

(2)在java檔案夾下建立KeepServiceAlive檔案夾,下面建立3個檔案:

注意:必須點了運作工程,才能自動導入IMyAidlInterface類。

LocalService.java

package com.jsc4.androidcppsafe.KeepServiceAlive;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.jsc4.df.IMyAidlInterface;

import java.util.Timer;
import java.util.TimerTask;


/**
 * Created by lenovo on 2019/8/5.
 */

public class LocalService extends Service {

    private final static String TAG = LocalService.class.getName();
    private static int count = 0;
    private static Timer timer = null;
    private MyBinder mBinder;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            try {
                Log.i("LocalService", "connected with " + iMyAidlInterface.getServiceName());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (Constans.INSTANCE.isOpenServiceDefend == 1) {
                Log.i(TAG, "連結斷開,重新啟動 RemoteService......");
                startService(new Intent(LocalService.this, RemoteService.class));
                bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
            }

        }
    };

    public LocalService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();

        if (timer == null) {
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Log.i(TAG, "--" + count++);
                }
            }, 0, 1000);
        }


    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Constans.INSTANCE.isOpenServiceDefend == 1) {
            Log.i(TAG, "本地服務啟動......");
            startService(new Intent(LocalService.this, RemoteService.class));

            bindService(new Intent(LocalService.this, RemoteService.class), connection, Context.BIND_IMPORTANT);
        }
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        mBinder = new MyBinder();
        return mBinder;
    }

    private class MyBinder extends IMyAidlInterface.Stub {
        @Override
        public String getServiceName() throws RemoteException {
            return LocalService.class.getName();
        }

    }

    @Override
    public void onDestroy() {
        // TODO 自動生成的方法存根
        super.onDestroy();
        try {
            if (timer != null) {
                timer.cancel();
                timer=null;
            }
            unbindService(connection);
        } catch (Exception e) {
            System.exit(0);
        }

    }
}
           

RemoteService.java

package com.jsc4.androidcppsafe.KeepServiceAlive;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

import com.jsc4.df.IMyAidlInterface;

import java.util.Timer;
import java.util.TimerTask;


/**
 * Created by lenovo on 2019/8/5.
 */

public class RemoteService extends Service {
    private MyBinder mBinder;
    private static Timer timer = null;
    private static int count = 0;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            try {
                Log.i("RemoteService", "connected with " + iMyAidlInterface.getServiceName());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (Constans.INSTANCE.isOpenServiceDefend == 1) {
                Toast.makeText(RemoteService.this, "連結斷開,重新啟動 LocalService", Toast.LENGTH_LONG).show();
                startService(new Intent(RemoteService.this, LocalService.class));
                bindService(new Intent(RemoteService.this, LocalService.class), connection, Context.BIND_IMPORTANT);
            }
        }
    };

    public RemoteService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (timer == null) {
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Log.i("RemoteService", "==" + count++);
                }
            }, 0, 2000);
        }

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Constans.INSTANCE.isOpenServiceDefend == 1) {
            Log.i("RemoteService", "RemoteService 啟動...");
            bindService(new Intent(this, LocalService.class), connection, Context.BIND_IMPORTANT);
        }
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        mBinder = new MyBinder();
        return mBinder;
    }

    private class MyBinder extends IMyAidlInterface.Stub {

        @Override
        public String getServiceName() throws RemoteException {
            return RemoteService.class.getName();
        }
    }

    @Override
    public void onDestroy() {
        // TODO 自動生成的方法存根
        super.onDestroy();
        try {
            if (timer != null) {
                timer.cancel();
                timer=null;
            }
            unbindService(connection);
        } catch (Exception e) {
            System.exit(0);
        }
    }
}
           

Constans.java

package com.jsc4.androidcppsafe.KeepServiceAlive;

/**
 * Created by lenovo on 2019/8/5.
 */

public enum Constans {
    //枚舉類型實作單例
    INSTANCE;
    public static int openServiceDefend = 1;
    public static int stopServiceDefend = 0;
    public static int isOpenServiceDefend = 1;//是否開啟程序守護,預設開啟

    public static void setIsOpenServiceDefend(int isOpenServiceDefend) {
        Constans.isOpenServiceDefend = isOpenServiceDefend;
    }
}
           

(3)在AndroidMenifest.xml中添加:

<service
            android:name=".KeepServiceAlive.LocalService"
            android:enabled="true"
            android:exported="true"/>

        <service
            android:name=".KeepServiceAlive.RemoteService"
            android:enabled="true"
            android:exported="true"
            android:process=":RemoteProcess"/>
           

(4)在MainActivity.java中:

//********************************************************************************************************************************************
// 雙程序保活機制 相關函數
//********************************************************************************************************************************************
    private void startBackService() {

        startService(new Intent(this, LocalService.class));
    }

    private void stopBackService() {
        stopService(new Intent(this, LocalService.class));
    }

    private void stopRemoteService() {
        stopService(new Intent(this, RemoteService.class));
    }

    private void endAllService() {
        Constans.INSTANCE.isOpenServiceDefend = Constans.INSTANCE.stopServiceDefend;//結束程序守護
        stopRemoteService();
        stopBackService();
    }
           
在oncreate裡,添加:
// 啟動雙程序保活機制
startBackService();

在ondestroy裡,添加:
endAllService();// 結束雙程序守護
           

5、前台服務保活

這是我重點想說的功能,該功能親測有效。

注意:API>26後無法隐藏通知。

ForegroundService.java

package com.jsc4.androidcppsafe.ForegroundServiceKeepLive;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import javax.security.auth.login.LoginException;

public class ForegroundService extends Service {

    private static final int SERVICE_ID = 1;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("djtest", "ForegroundService:  前台服務建立了");

        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){ // 4.3以下
            startForeground(SERVICE_ID, new Notification());
        }else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O){ // 4.3-7.0
            startForeground(SERVICE_ID, new Notification());
            startService(new Intent(this, InnerService.class));
        }else{ // 8.0及以上
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel("channel", "andy", NotificationManager.IMPORTANCE_HIGH);
            manager.createNotificationChannel(channel);
            Notification notification = new NotificationCompat.Builder(this, "channel").build();
            startForeground(SERVICE_ID, notification);
        }
    }

    public static class InnerService extends Service{

        @Override
        public void onCreate() {
            super.onCreate();
            Log.i("djtest", "ForegroundService: InnerService服務建立了");

            startForeground(SERVICE_ID, new Notification());
            stopSelf();
        }

        @Nullable
        @Override
        public IBinder onBind(Intent intent) {

            return null;
        }
    }
}
           

AndroidMenifest.xml

<service android:name=".ForegroundServiceKeepLive.ForegroundService"/>
        <service android:name=".ForegroundServiceKeepLive.ForegroundService$InnerService"/>
           

MainActivity.java的oncreate中:

6、自啟動管理

這是純粹手機上設定。

将啟動管理中的你的APP選擇為手動控制。