Android效能優化篇之服務優化

weixin_33785972發表於2018-06-14
5748654-9eba8e6c1bf81091.jpg
image

引言

1. Android效能優化篇之記憶體優化--記憶體洩漏

2.Android效能優化篇之記憶體優化--記憶體優化分析工具

3.Android效能優化篇之UI渲染效能優化

4.Android效能優化篇之計算效能優化

5.Android效能優化篇之電量優化(1)——電量消耗分析

6.Android效能優化篇之電量優化(2)

7.Android效能優化篇之網路優化

8.Android效能優化篇之Bitmap優化

9.Android效能優化篇之圖片壓縮優化

10.Android效能優化篇之多執行緒併發優化

11.Android效能優化篇之資料傳輸效率優化

12.Android效能優化篇之程式啟動時間效能優化

13.Android效能優化篇之安裝包效能優化

14.Android效能優化篇之服務優化

介紹

service:是一個後臺服務,專門用來處理常駐後臺的工作的元件。
服務的優化 主要體驗在兩個方面:一·服務的保活,二·服務後臺對於執行任務的集中管理.

下面我們主要對服務的保活方面進行講解,至於任務集中管理執行,在電量優化中已經講過,這裡就不再累贅。

開始先來說下我們實現的方式:
1.提高程式優先順序
2.java層雙程式守護
3.1個畫素的Activity保活
4.JobScheduler輪詢
5.native層雙程式守護

今天我們只寫前三種方式,第四種native層雙程式守護將在NDK章節講解。

我們先來看下程式的優先順序 :
1. 前臺程式
  • Activity已呼叫onResume()方法
  • Service服務已呼叫startForeground()
  • 生命週期回撥的Service (onCreate() 、onStart()或onDestroy())
  • 正執行其onReceive()方法的BroadcastReceiver
2. 可見程式
  • 不在前臺、但仍對使用者可見的Activity(比如呼叫其onPause()方法)
  • 繫結到可見(或前臺)Activity 的Service
3. 服務程式
  • startService()方法啟動的服務,且不屬於上面兩類
4. 後臺程式
  • 對使用者不可見的 Activity 的程式已呼叫 Activity 的onStop()方法
5. 空程式
  • 不含任何活動應用元件的程式
下面一起來看看程式在什麼時候會被殺死:
1.應用記憶體不足,回收程式

提高程式優先順序,減少程式oom_adj值,如啟動程式的setForeground()提高程式優先順序

當應用程式退到後臺時,釋放佔用的資源,因為當oom_adj相同時,優先釋放記憶體消耗大的程式

一直在後臺執行的程式一定要輕

2.系統第三方清理軟體,殺死程式

使用aidl,實現雙程式守護
白名單

3.各大rom廠商在應用退出時,會清理殺死程式

使用NDK,輪詢檢視指定程式是否被殺死,如果殺死fork程式,啟動
使用JobScheduler,輪詢檢視指定程式是否被殺死,如果殺死,啟動

雙程式守護(基於java層)

這裡我們將用到aidl,有不瞭解的同學可以自己去了解下,我們先來上程式碼:

1.編寫aidl介面
    interface ProcessConnect {

    }

介面裡面什麼都沒有,這個只是用來監聽是否斷開連線,如果斷開,就程式碼啟動服務。

2.工作服務
public class MessageService extends Service {

    private String TAG = "MessageService";

    private int ID=0X00022;
    private static Context mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    Log.e(TAG, "MessageService====>print");

                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //assert uri
        String path = "file:///android_asset/xiaoxin.wav";
        Notification.Builder builder = new Notification.Builder(mContext);
        Notification notification = builder
                .setContentText("messageservice")
                .setSmallIcon(R.drawable.ting)
                .setSound(Uri.parse(path))
                .build();

        startForeground(ID,notification);


        bindService(new Intent(MessageService.this,GuardService.class),mServiceConnection,BIND_WAIVE_PRIORITY);

        return START_STICKY;
    }
    public ServiceConnection  mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "MessageService====>onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

            startService(new Intent(MessageService.this,GuardService.class));
            bindService(new Intent(MessageService.this,GuardService.class),mServiceConnection,BIND_WAIVE_PRIORITY);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new ProcessConnect.Stub() {

        };
    }
}
3.守護服務
    public class GuardService extends Service {
        private Context mContext;
        private int ID=0X00021;
        @Override
        public void onCreate() {
            super.onCreate();
            mContext = this;
    
        }
    
            @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
                //assert uri
                String path = "file:///android_asset/xiaoxin.wav";
                Notification.Builder builder = new Notification.Builder(mContext);
                Notification notification = builder
                        .setContentText("GuardService")
                        .setSmallIcon(R.drawable.ting)
                        .setSound(Uri.parse(path))
                        .build();
    
                startForeground(ID,notification);
    
                bindService(new Intent(GuardService.this,MessageService.class),mServiceConnection,BIND_WAIVE_PRIORITY);
    
                return START_STICKY;
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new ProcessConnect.Stub(){
    
            };
        }
    
        public ServiceConnection  mServiceConnection = new ServiceConnection() {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("GuardService", "GuardService====>onServiceConnected");
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
                startService(new Intent(GuardService.this,MessageService.class));
                bindService(new Intent(GuardService.this,MessageService.class),mServiceConnection,BIND_WAIVE_PRIORITY);
            }
        };
    
    }

從上面兩個服務可以看到,每當一個服務結束,另一個服務就會啟動它,來實現程式不被關閉。

4.MainActivity開啟服務
    startService(new Intent(this,MessageService.class));
    startService(new Intent(this,GuardService.class));
5.配置
     <service android:name=".MessageService"></service>
    //新的程式中執行
    <service android:name=".GuardService" android:process=":guardservice"></service>

主要五步就搞定了,很簡單吧,但是不要高興的太早,因為這種雙程式守護的方法,只能對4.0以下有效,對於4.0以上機型,只能部分有用,這個問題最後再說,我們先來看下使用JobScheduler,輪詢啟動被殺死的程式。

1個畫素的Activity保活

啟動一個1個畫素的Activity,當使用者解鎖以後將這個Activity結束掉(順便同時把自己的核心服務再開啟一次)。被使用者發現了就不好了。
重點就是對螢幕進行監聽,下面我們來分析程式碼:

1個畫素的Activity實現:

    Window window = getWindow();
    window.setGravity(Gravity.LEFT|Gravity.TOP);
    LayoutParams params = window.getAttributes();
    params.height = 1;
    params.width = 1;
    params.x = 0;
    params.y = 0;
    window.setAttributes(params);

對螢幕進行監聽

    private void registerListener() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        mContext.registerReceiver(mScreenReceiver, filter);
    }

JobScheduler

在android開發中,會存在這麼些場景 : 你需要在稍後的某個時間點或者當滿足某個特定的條件時執行一個任務,例如當裝置接通電源介面卡或者連線到WIFI。幸運的是在API 21 ( Android 5.0,即Lollipop )中,google提供了一個新叫做JobScheduler API的元件來處理這樣的場景。

當一系列預置的條件被滿足時,JobScheduler API為你的應用執行一個操作。與AlarmManager不同的是這個執行時間是不確定的。除此之外,JobScheduler API允許同時執行多個任務。這允許你的應用執行某些指定的任務時不需要考慮時機控制引起的電池消耗。

下面我們就使用JobScheduler來啟動我們被殺死的服務:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class JobWakeUpService extends JobService {

    private JobScheduler service;
    private int JobId=100;
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        JobInfo info = new JobInfo.Builder(JobId,new ComponentName(this,JobWakeUpService.class))
                .setPeriodic(2000)
                .build();

        service = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);

        service.schedule(info);
        return START_STICKY;
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.e("JobWakeUpService", "JobWakeUpService====>print");
        //開始定時任務
        if(!isServiceWork(this,MessageService.class.getName())){
            //
            startService(new Intent(this,MessageService.class));
        }

        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        //停止
        service.cancel(JobId);
//        service.cancelAll();
        return false;
    }

    private boolean isServiceWork(Context context,String serviceName){
        ActivityManager am= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(100);
        if(runningServices == null){
            return false;
        }
        for (ActivityManager.RunningServiceInfo service : runningServices) {
            String className = service.service.getClassName();
            if(className.equals(serviceName)){
                return true;
            }
        }
        return false;

    }
}

我們看到這邊就是使用JobScheduler服務來進行迴圈呼叫我們的JobWakeUpService的onStartJob。

我們接下來看下配置:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>

    <service android:name=".JobWakeUpService"
        android:enabled="true"
        android:permission="android.permission.BIND_JOB_SERVICE"
        ></service>

呼叫:

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        startService(new Intent(this, JobWakeUpService.class));
    }

這個就能實現輪詢檢視指定程式是否被殺死,如果殺死,啟動的功能。

可能你們想問這種方式是否可以解決5.0以上程式不被殺死嗎?我只能遺憾的告訴你,不能,我在華為7.0上的測試,沒有能夠起來。

我們看了這麼多的方式,也不能解決程式不被殺死的情況,那有沒有更好的辦法呢?

native層雙程式守護

關於NDK來實現雙程式守護將在ndk文章中講解。

下載地址:NoDieService-Demo

相關文章