序言
最近項目要實現這樣一個效果:運行后,要有一個service始終保持在后台運行,不管用戶作出什么操作,都要保證service不被kill。參考了現今各種定制版的系統和安全廠商牛虻軟件,如何能保證自己的Service不被殺死呢?其實除了常規的手段,我們可以參考一下微信和360,設置-程序-正在運行,可以看到微信是同時開啟了兩個進程和服務:
Service簡介
Service是在一段不定的時間運行在后台,不和用戶交互應用組件。每個Service必須在manifest中 通過<service>來聲明。可以通過contect.startservice和contect.bindserverice來啟動。和其他的應用組件一樣,運行在進程的主線程中。這就是說如果service需要很多耗時或者阻塞的操作,需要在其子線程中實現(或者用系統提供的IntentService,它繼承了Service,它處理數據是用自身新開的線程)。【當然你也可以在新的線程中startService,這樣Service就不是在MainThread了】
本地服務 Local Service 用於應用程序內部
它可以啟動並運行,直至有人停止了它或它自己停止。在這種方式下,它以調用Context.startService()啟動,而以調用Context.stopService()結束。它可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。不論調用了多少次startService()方法,你只需要調用一次stopService()來停止服務。
【用於實現應用程序自己的一些耗時任務,比如查詢升級信息,並不占用應用程序比如Activity所屬線程,而是單開線程后台執行,這樣用戶體驗比較好】
遠程服務 Remote Service 用於Android系統內部的應用程序之間
它可以通過自己定義並暴露出來的接口進行程序操作。客戶端建立一個到服務對象的連接,並通過那個連接來調用服務。連接以調用Context.bindService()方法建立,以調用 Context.unbindService()關閉。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。
【可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可】
1,Service的生命周期
2,Service運行方式
以startService()啟動服務,系統將通過傳入的Intent在底層搜索相關符合Intent里面信息的service。如果服務沒有啟動則先運行onCreate,然后運行onStartCommand (可在里面處理啟動時傳過來的Intent和其他參數),直到明顯調用stopService或者stopSelf才將停止Service。無論運行startService多少次,只要調用一次stopService或者stopSelf,Service都會停止。使用stopSelf(int)方法可以保證在處理好intent后再停止。onStartCommand ,在2.0后被引入用於service的啟動函數,2.0之前為public void onStart(Intent intent, int startId) 。
以bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止。onBind()只有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法並不會導致該方法被多次調用。采用Context.bindService()方法啟動服務時只能調用onUnbind()方法解除調用者與服務解除,服務結束時會調用onDestroy()方法。
3,擁有service的進程具有較高的優先級
官方文檔告訴我們,Android系統會盡量保持擁有service的進程運行,只要在該service已經被啟動(start)或者客戶端連接(bindService)到它。當內存不足時,需要保持,擁有service的進程具有較高的優先級。
1. 如果service正在調用onCreate,onStartCommand或者onDestory方法,那么用於當前service的進程則變為前台進程以避免被killed。
2. 如果當前service已經被啟動(start),擁有它的進程則比那些用戶可見的進程優先級低一些,但是比那些不可見的進程更重要,這就意味着service一般不會被killed.
3. 如果客戶端已經連接到service (bindService),那么擁有Service的進程則擁有最高的優先級,可以認為service是可見的。
4. 如果service可以使用startForeground(int, Notification)方法來將service設置為前台狀態,那么系統就認為是對用戶可見的,並不會在內存不足時killed。
5. 如果有其他的應用組件作為Service,Activity等運行在相同的進程中,那么將會增加該進程的重要性。
實現前台:
Service如果要防止盡可能不被系統殺掉,需要設置為在前台運行。
1.MainActivity.java
1 package com.example.helloteacher; 2 3 import com.example.teacherService.HelloteacherService; 4 import com.example.teacherService.HelloteacherService2; 5 6 import android.app.Activity; 7 import android.content.Intent; 8 import android.os.Bundle; 9 import android.util.Log; 10 import android.view.View; 11 import android.widget.Button; 12 import android.widget.TextView; 13 14 public class MainActivity extends Activity { 15 private TextView fuction; 16 private Button start; 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.activity_main); 21 //初始化控件 22 init(); 23 //創建進程 24 inProcess(); 25 } 26 ///初始化控件函數 27 private void init() { 28 start=(Button)findViewById(R.id.button1); 29 fuction=(TextView)findViewById(R.id.tv_bottom_funtion); 30 start.setOnClickListener(new StartOnClickListener()); 31 fuction.setOnClickListener(new FuctionOnClickListener()); 32 } 33 //功能按鈕監聽實現函數 34 private final class FuctionOnClickListener implements View.OnClickListener{ 35 36 @Override 37 public void onClick(View v) { 38 // TODO Auto-generated method stub 39 Intent intent = new Intent(MainActivity.this, FuctionActivity.class); 40 startActivity(intent); 41 } 42 } 43 //進程函數 44 private void inProcess(){ 45 Intent intent =new Intent(MainActivity.this, HelloteacherService.class); 46 startService(intent); 47 Intent intent2 =new Intent(MainActivity.this, HelloteacherService2.class); 48 startService(intent2); 49 } 50 private final class StartOnClickListener implements View.OnClickListener{ 51 52 @Override 53 public void onClick(View v) { 54 55 } 56 } 57 58 59 }
2.HelloteacherService.java
1 package com.example.teacherService; 2 3 import com.example.Receiver.Alarmreceiver; 4 import com.example.helloteacher.R; 5 6 import android.app.AlarmManager; 7 import android.app.Notification; 8 import android.app.PendingIntent; 9 import android.app.Service; 10 import android.content.Intent; 11 import android.os.IBinder; 12 import android.os.SystemClock; 13 import android.util.Log; 14 15 public class HelloteacherService extends Service { 16 HelloteacherService2 hs2; 17 private String TAG="HelloteacherService"; 18 @Override 19 public void onCreate() { 20 super.onCreate(); 21 Log.i(TAG, "-->>onCreate"); 22 } 23 24 @Override 25 public int onStartCommand(Intent intent, int flags, int startId) { 26 Log.i(TAG, "-->>onStartCommand-->>"+startId); 27 flags = START_STICKY; 28 29 //啟用前台服務,主要是startForeground() 30 Notification notification = new Notification(R.drawable.ic_launcher, "用電腦時間過長了!白痴!" 31 , System.currentTimeMillis()); 32 notification.setLatestEventInfo(this, "快去休息!!!", 33 "一定保護眼睛,不然遺傳給孩子,老婆跟別人跑啊。", null); 34 //設置通知默認效果 35 notification.flags = Notification.FLAG_SHOW_LIGHTS; 36 startForeground(1, notification); 37 38 AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE); 39 //讀者可以修改此處的Minutes從而改變提醒間隔時間 40 //此處是設置每隔55分鍾啟動一次 41 //這是55分鍾的毫秒數 42 int Minutes = 55 * 60 * 1000; 43 //SystemClock.elapsedRealtime()表示1970年1月1日0點至今所經歷的時間 44 long triggerAtTime = SystemClock.elapsedRealtime() + Minutes; 45 //此處設置開啟AlarmReceiver這個Service 46 Intent i = new Intent(this, Alarmreceiver.class); 47 PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0); 48 //ELAPSED_REALTIME_WAKEUP表示讓定時任務的出發時間從系統開機算起,並且會喚醒CPU。 49 manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi); 50 51 return super.onStartCommand(intent, flags, startId); 52 } 53 //監聽服務2實現函數 54 55 56 @Override 57 public IBinder onBind(Intent arg0) { 58 Log.i(TAG, "-->>onBind"); 59 // TODO Auto-generated method stub 60 return null; 61 } 62 @Override 63 public void onDestroy() { 64 Log.i(TAG, "-->>onDestroy"); 65 super.onDestroy(); 66 67 } 68 }
3.BootReceiver.java
1 package com.example.Receiver; 2 3 import android.app.AlarmManager; 4 import android.app.PendingIntent; 5 import android.content.BroadcastReceiver; 6 import android.content.Context; 7 import android.content.Intent; 8 import android.os.SystemClock; 9 10 public class BootReceiver extends BroadcastReceiver { 11 12 /*要接收的intent源*/ 13 static final String ACTION = "android.intent.action.BOOT_COMPLETED"; 14 15 public void onReceive(Context context, Intent mintent) 16 { 17 if (Intent.ACTION_BOOT_COMPLETED.equals(mintent.getAction())) { 18 // 啟動完成 19 Intent intent = new Intent(context, Alarmreceiver.class); 20 intent.setAction("arui.alarm.action"); 21 PendingIntent sender = PendingIntent.getBroadcast(context, 0, 22 intent, 0); 23 long firstime = SystemClock.elapsedRealtime(); 24 AlarmManager am = (AlarmManager) context 25 .getSystemService(Context.ALARM_SERVICE); 26 27 // 10秒一個周期,不停的發送廣播 28 am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstime, 29 10 * 1000, sender); 30 } 31 } 32 33 34 }
4.Alarmreceiver.java
1 package com.example.Receiver; 2 3 import com.example.teacherService.HelloteacherService; 4 5 import android.content.BroadcastReceiver; 6 import android.content.Context; 7 import android.content.Intent; 8 9 public class Alarmreceiver extends BroadcastReceiver { 10 11 @Override 12 public void onReceive(Context context, Intent intent) { 13 // TODO Auto-generated method stub 14 if (intent.getAction().equals("arui.alarm.action")) { 15 Intent i = new Intent(); 16 i.setClass(context, HelloteacherService.class); 17 // 啟動service 18 // 多次調用startService並不會啟動多個service 而是會多次調用onStart 19 context.startService(i); 20 } 21 } 22 23 }
5.配置文件
AndroidManifest.xml
下面是添加在配置文件中的部分代碼:
1 <activity android:name="com.example.helloteacher.FuctionActivity" /> 2 <service android:name="com.example.teacherService.HelloteacherService" android:process=":HelloteacherService"/> 3 <service android:name="com.example.teacherService.HelloteacherService2"/> 4 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission> 5 <receiver android:name="com.example.Receiver.BootReceiver" > 6 <intent-filter> 7 <action android:name="android.intent.action.BOOT_COMPLETED" /> 8 </intent-filter> 9 </receiver> 10 <receiver android:name="com.example.Receiver.Alarmreceiver" > 11 <intent-filter> 12 <action android:name="arui.alarm.action" /> 13 </intent-filter> 14 </receiver>
通過以上的配置,就可以實現Service的前台運行!下面附上效果圖: