最近项目用到Service常驻后台,研究了一下发现手Q和微信都是使用了双进程来保证一键清理后自动复活,copy网上双进程Service的例子,再结合onTrimMemory(),基本实现一键清理后自动复活。
使用双进程Service,关键是在AndroidManifest.xml里面定义Service时加入Android:process=":service1":
1 <service android:enabled="true" android:name="com.service.demo.Service1" android:process=":service1"></service> 2 <service android:enabled="true" android:name="com.service.demo.Service2" android:process=":service2"></service>
双进程Service可以让2个进程互相保护,其中一个Service被清理后,另外没被清理的进程可以立即重启进程。
--------以下onTrimMemory的解释引用于网络
onTrimMemory()是Android 4.0之后提供的API,系统会根据不同的内存状态来回调。根据不同的内存状态,来响应不同的内存释放策略。OnTrimMemory的参数是一个int数值,代表不同的内存状态:
TRIM_MEMORY_COMPLETE:内存不足,并且该进程在后台进程列表最后一个,马上就要被清理
TRIM_MEMORY_MODERATE:内存不足,并且该进程在后台进程列表的中部。
TRIM_MEMORY_BACKGROUND:内存不足,并且该进程是后台进程。
TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的UI已经不可见了。
以上4个是4.0增加
TRIM_MEMORY_RUNNING_CRITICAL:内存不足(后台进程不足3个),并且该进程优先级比较高,需要清理内存
TRIM_MEMORY_RUNNING_LOW:内存不足(后台进程不足5个),并且该进程优先级比较高,需要清理内存
TRIM_MEMORY_RUNNING_MODERATE:内存不足(后台进程超过5个),并且该进程优先级比较高,需要清理内存
以上3个是4.1增加。
本文的例子源码可以到这里下载http://pan.baidu.com/s/1qW3KvtM
以下是本文运行DEMO的结果:开启服务后双进程Service分别启动(Toast显示出来),然后使用“一键加速”来清理内存,双进程Service被逐一清理(触发Service的onTrimMemory()),但后面又分别重新启动了。
代码分析 :
AndroidManifest.xml
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 package="com.example.servicetest2" 3 android:versionCode="1" 4 android:versionName="1.0" > 5 6 <uses-sdk 7 android:minSdkVersion="14" 8 android:targetSdkVersion="19" /> 9 10 <application 11 android:allowBackup="true" 12 android:icon="@drawable/ic_launcher" 13 android:label="@string/app_name" 14 android:theme="@style/AppTheme" > 15 16 <activity android:name="com.service.demo.Main"> 17 <intent-filter > 18 <action android:name="android.intent.action.MAIN"/> 19 <category android:name="android.intent.category.LAUNCHER"/> 20 </intent-filter> 21 </activity> 22 <!-- 关键代码 --> 23 <service android:enabled="true" android:name="com.service.demo.Service1" android:process=":service1"></service> 24 <service android:enabled="true" android:name="com.service.demo.Service2" android:process=":service2"></service> 25 26 </application> 27 28 </manifest>
Service1.java
1 package com.service.demo; 2 3 import java.util.List; 4 5 import android.app.ActivityManager; 6 import android.app.ActivityManager.RunningAppProcessInfo; 7 import android.app.Service; 8 import android.app.ActivityManager.RunningServiceInfo; 9 import android.content.ComponentCallbacks; 10 import android.content.ComponentName; 11 import android.content.Context; 12 import android.content.Intent; 13 import android.content.ServiceConnection; 14 import android.content.res.Configuration; 15 import android.os.Handler; 16 import android.os.IBinder; 17 import android.os.RemoteException; 18 import android.util.Log; 19 import android.widget.Toast; 20 21 /** 22 * 23 * @author hellogv 24 * 25 */ 26 public class Service1 extends Service { 27 28 private String TAG = getClass().getName(); 29 // 用于判断进程是否运行 30 private String Process_Name = "com.example.servicetest2:service2"; 31 32 /** 33 *启动Service2 34 */ 35 private StrongService startS2 = new StrongService.Stub() { 36 @Override 37 public void stopService() throws RemoteException { 38 Intent i = new Intent(getBaseContext(), Service2.class); 39 getBaseContext().stopService(i); 40 } 41 42 @Override 43 public void startService() throws RemoteException { 44 Intent i = new Intent(getBaseContext(), Service2.class); 45 getBaseContext().startService(i); 46 } 47 }; 48 49 @Override 50 public void onTrimMemory(int level){ 51 Toast.makeText(getBaseContext(), "Service1 onTrimMemory..."+level, Toast.LENGTH_SHORT) 52 .show(); 53 54 keepService2();//保持Service2一直运行 55 56 } 57 58 @Override 59 public void onCreate() { 60 Toast.makeText(Service1.this, "Service1 onCreate...", Toast.LENGTH_SHORT) 61 .show(); 62 keepService2(); 63 } 64 65 /** 66 * 判断Service2是否还在运行,如果不是则启动Service2 67 */ 68 private void keepService2(){ 69 boolean isRun = Utils.isProessRunning(Service1.this, Process_Name); 70 if (isRun == false) { 71 try { 72 Toast.makeText(getBaseContext(), "重新启动 Service2", Toast.LENGTH_SHORT).show(); 73 startS2.startService(); 74 } catch (RemoteException e) { 75 e.printStackTrace(); 76 } 77 } 78 } 79 80 @Override 81 public int onStartCommand(Intent intent, int flags, int startId) { 82 return START_STICKY; 83 } 84 85 @Override 86 public IBinder onBind(Intent intent) { 87 return (IBinder) startS2; 88 } 89 }
Service2.java
1 package com.service.demo; 2 3 import java.util.List; 4 5 import android.app.ActivityManager; 6 import android.app.ActivityManager.RunningAppProcessInfo; 7 import android.app.ActivityManager.RunningServiceInfo; 8 import android.app.Application; 9 import android.app.Service; 10 import android.content.Context; 11 import android.content.Intent; 12 import android.os.Handler; 13 import android.os.IBinder; 14 import android.os.RemoteException; 15 import android.util.Log; 16 import android.widget.Toast; 17 /** 18 * 19 * @author 掌缘生灭 20 * 21 */ 22 public class Service2 extends Service { 23 private String TAG = getClass().getName(); 24 private String Process_Name = "com.example.servicetest2:service1"; 25 26 /** 27 *启动Service1 28 */ 29 private StrongService startS1 = new StrongService.Stub() { 30 31 @Override 32 public void stopService() throws RemoteException { 33 Intent i = new Intent(getBaseContext(), Service1.class); 34 getBaseContext().stopService(i); 35 } 36 37 @Override 38 public void startService() throws RemoteException { 39 Intent i = new Intent(getBaseContext(), Service1.class); 40 getBaseContext().startService(i); 41 42 } 43 }; 44 45 @Override 46 public void onTrimMemory(int level){ 47 Toast.makeText(getBaseContext(), "Service2 onTrimMemory..."+level, Toast.LENGTH_SHORT) 48 .show(); 49 keepService1(); 50 } 51 52 public void onCreate() { 53 Toast.makeText(Service2.this, "Service2 onCreate...", Toast.LENGTH_SHORT).show(); 54 keepService1(); 55 } 56 57 /** 58 * 判断Service1是否还在运行,如果不是则启动Service1 59 */ 60 private void keepService1(){ 61 boolean isRun = Utils.isProessRunning(Service2.this, Process_Name); 62 if (isRun == false) { 63 try { 64 Toast.makeText(getBaseContext(), "重新启动 Service1", Toast.LENGTH_SHORT).show(); 65 startS1.startService(); 66 } catch (RemoteException e) { 67 e.printStackTrace(); 68 } 69 } 70 } 71 @Override 72 public int onStartCommand(Intent intent, int flags, int startId) { 73 return START_STICKY; 74 } 75 76 @Override 77 public IBinder onBind(Intent intent) { 78 return (IBinder) startS1; 79 } 80 81 }
Utils.java
package com.service.demo; import java.util.List; import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.content.Context; public class Utils { /** * 判断进程是否运行 * @return */ public static boolean isProessRunning(Context context, String proessName) { boolean isRunning = false; ActivityManager am = (ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE); List<RunningAppProcessInfo> lists = am.getRunningAppProcesses(); for (RunningAppProcessInfo info : lists) { if (info.processName.equals(proessName)) { isRunning = true; } } return isRunning; } }
StrongService.aidl
1 package com.service.demo; 2 interface StrongService{ 3 void startService(); 4 void stopService(); 5 }