一种提高Android应用进程存活率新方法(上)

基础知识

Android 进程优先级

1 进程优先级等级一般分法

  • Activte process
  • Visible Process
  • Service process
  • Background process
  • Empty process

2 Service技巧

  • onStartCommand返回START_STICKY
  • onDestroy中startself
  • Service后台变前置,setForground(true)
  • android:persistent = “true”

3 进程优先级号

ProcessList.java


  1. // Adjustment used in certain places where we don't know it yet. 
  2. // (Generally this is something that is going to be cached, but we 
  3. // don't know the exact value in the cached range to assign yet.) 
  4. static final int UNKNOWN_ADJ = 16; 
  5.   
  6. // This is a process only hosting activities that are not visible, 
  7. // so it can be killed without any disruption. 
  8. static final int CACHED_APP_MAX_ADJ = 15; 
  9. static final int CACHED_APP_MIN_ADJ = 9; 
  10.   
  11. // The B list of SERVICE_ADJ -- these are the old and decrepit 
  12. // services that aren't as shiny and interesting as the ones in the A list. 
  13. static final int SERVICE_B_ADJ = 8; 
  14.   
  15. // This is the process of the previous application that the user was in. 
  16. // This process is kept above other things, because it is very common to 
  17. // switch back to the previous app.  This is important both for recent 
  18. // task switch (toggling between the two top recent apps) as well as normal 
  19. // UI flow such as clicking on a URI in the e-mail app to view in the browser, 
  20. // and then pressing back to return to e-mail. 
  21. static final int PREVIOUS_APP_ADJ = 7; 
  22.   
  23. // This is a process holding the home application -- we want to try 
  24. // avoiding killing it, even if it would normally be in the background, 
  25. // because the user interacts with it so much. 
  26. static final int HOME_APP_ADJ = 6; 
  27.   
  28. // This is a process holding an application service -- killing it will not 
  29. // have much of an impact as far as the user is concerned. 
  30. static final int SERVICE_ADJ = 5; 
  31.   
  32. // This is a process with a heavy-weight application.  It is in the 
  33. // background, but we want to try to avoid killing it.  Value set in 
  34. // system/rootdir/init.rc on startup. 
  35. static final int HEAVY_WEIGHT_APP_ADJ = 4; 
  36.   
  37. // This is a process currently hosting a backup operation.  Killing it 
  38. // is not entirely fatal but is generally a bad idea. 
  39. static final int BACKUP_APP_ADJ = 3; 
  40.   
  41. // This is a process only hosting components that are perceptible to the 
  42. // user, and we really want to avoid killing them, but they are not 
  43. // immediately visible. An example is background music playback. 
  44. static final int PERCEPTIBLE_APP_ADJ = 2; 
  45.   
  46. // This is a process only hosting activities that are visible to the 
  47. // user, so we'd prefer they don't disappear. 
  48. static final int VISIBLE_APP_ADJ = 1; 
  49.   
  50. // This is the process running the current foreground app.  We'd really 
  51. // rather not kill it! 
  52. static final int FOREGROUND_APP_ADJ = 0; 
  53.   
  54. // This is a process that the system or a persistent process has bound to, 
  55. // and indicated it is important. 
  56. static final int PERSISTENT_SERVICE_ADJ = -11; 
  57.   
  58. // This is a system persistent process, such as telephony.  Definitely 
  59. // don't want to kill it, but doing so is not completely fatal. 
  60. static final int PERSISTENT_PROC_ADJ = -12; 
  61.   
  62. // The system process runs at the default adjustment. 
  63. static final int SYSTEM_ADJ = -16; 
  64.   
  65. // Special code for native processes that are not being managed by the system (so 
  66. // don't have an oom adj assigned by the system). 
  67. static final int NATIVE_ADJ = -17;  

Android Low Memory Killer

Android系统内存不足时,系统会杀掉一部分进程以释放空间,谁生谁死的这个生死大权就是由LMK所决定的,这就是Android系统中的Low Memory Killer,其基于Linux的OOM机制,其阈值定义如下面所示的lowmemorykiller文件中,当然也可以通过系统的init.rc实现自定义。

lowmemorykiller.c


  1. static uint32_t lowmem_debug_level = 1; 
  2. static int lowmem_adj[6] = { 
  3.     0, 
  4.     1, 
  5.     6, 
  6.     12, 
  7. }; 
  8. static int lowmem_adj_size = 4; 
  9. static int lowmem_minfree[6] = { 
  10.     3 * 512,    /* 6MB */ 
  11.     2 * 1024,   /* 8MB */ 
  12.     4 * 1024,   /* 16MB */ 
  13.     16 * 1024,  /* 64MB */ 
  14. }; 
  15. static int lowmem_minfree_size = 4;  

① 在Low Memory Killer中通过进程的oom_adj与占用内存的大小决定要杀死的进程,oom_adj值越小越不容易被杀死。其中,lowmem_minfree是杀进程的时机,谁被杀,则取决于lowmem_adj,具体值得含义参考上面 Android进程优先级 所述.

② 在init.rc中定义了init进程(系统进程)的oom_adj为-16,其不可能会被杀死(init的PID是1),而前台进程是0(这里的前台进程是指用户正在使用的Activity所在的进程),用户按Home键回到桌面时的优先级是6,普通的Service的进程是8.

init.rc


  1. # Set init and its forked children's oom_adj. 
  2.     write /proc/1/oom_adj -16  

关于Low Memory Killer的具体实现原理可参考Ref-2.

查看某个App的进程

步骤(手机与PC连接)

  1. adb shell
  2. ps | grep 进程名
  3. cat /proc/pid/oom_adj //其中pid是上述grep得到的进程号 

Linux AM命令

am命令:在Android系统中通过adb shell 启动某个Activity、Service、拨打电话、启动浏览器等操作Android的命令.其源码在Am.java中,在shell环境下执行am命令实际是启动一个线程执行Am.java中的主函数(main方法),am命令后跟的参数都会当做运行时参数传递到主函数中,主要实现在Am.java的run方法中。

拨打电话

命令:am start -a android.intent.action.CALL -d tel:电话号码

示例:am start -a android.intent.action.CALL -d tel:10086

打开一个网页

命令:am start -a android.intent.action.VIEW -d 网址

示例:am start -a android.intent.action.VIEW -d http://www.skyseraph.com

启动一个服务

命令:am startservice <服务名称>

示例:am startservice -n com.android.music/ com.android.music.MediaPlaybackService

NotificationListenerService

“A service that receives calls from the system when new notifications are posted or removed, or their ranking changed.” From Google

用来监听到通知的发送以及移除和排名位置变化,如果我们注册了这个服务,当系统任何一条通知到来或者被移除掉,我们都能通过这个service来监听到,甚至可以做一些管理工作。

Android账号和同步机制

属于Android中较偏冷的知识,具体参考 Ref 3 /4 /5

Android多进程

  • 实现:android:process
  • 好处:一个独立的进程可以充分利用自己的RAM预算,使其主进程拥有更多的空间处理资源。此外,操作系统对待运行在不同组件中的进程是不一样的。这意味着,当系统运行在低可用内存的条件时,并不是所有的进程都会被杀死
  • 大坑:每一个进程将有自己的Dalvik VM实例,意味着你不能通过这些实例共享数据,至少不是传统意义上的。例如,静态字段在每个进程都有自己的值,而不是你倾向于相信的只有一个值。
  • 更多详细请参考Ref 9

现有方法

网络连接保活方法

A. GCM

B. 公共的第三方push通道(信鸽等)

C. 自身跟服务器通过轮询,或者长连接

具体实现请参考 微信架构师杨干荣的”微信Android客户端后台保活经验分享” (Ref-1).

双service(通知栏) 提高进程优先级

思路:(API level > 18 )

  • 应用启动时启动一个假的Service(FakeService), startForeground(),传一个空的Notification
  • 启动真正的Service(AlwaysLiveService),startForeground(),注意必须相同Notification ID
  • FakeService stopForeground()

效果:通过adb查看,运行在后台的服务其进程号变成了1(优先级仅次于前台进程)

风险:Android系统前台service的一个漏洞,可能在6.0以上系统中修复

实现:核心代码如下

  • AlwaysLiveService 常驻内存服务

  1. @Override 
  2.    public int onStartCommand(Intent intent, int flags, int startId) { 
  3.        startForeground(R.id.notify, new Notification()); 
  4.        startService(new Intent(this, FakeService.class)); 
  5.        return super.onStartCommand(intent, flags, startId); 
  6.    }  
  • FakeService 临时服务

  1. public class FakeService extends Service { 
  2.     @Nullable 
  3.     @Override 
  4.     public IBinder onBind(Intent intent) { 
  5.         return null; 
  6.     } 
  7.   
  8.     @Override 
  9.     public int onStartCommand(Intent intent, int flags, int startId) { 
  10.         startForeground(R.id.notify, new Notification()); 
  11.         stopSelf(); 
  12.         return super.onStartCommand(intent, flags, startId); 
  13.     } 
  14.   
  15.     @Override 
  16.     public void onDestroy() { 
  17.         stopForeground(true); 
  18.         super.onDestroy(); 
  19.     } 
  20. }  

Service及时拉起

AlarmReceiver, ConnectReceiver,BootReceiver等

  • Service设置(见上面基础部分)
  • 通过监听系统广播,如开机,锁屏,亮屏等重新启动服务
  • 通过alarm定时器,启动服务

守护进程/进程互拉

在分析360手机助手app时,发现其拥有N多个进程,一个进程kill后会被其它未kill的进程拉起,这也是一种思路吧,虽然有点流氓~

守护进程一般有这样两种方式:

  • 多个java进程守护互拉
  • 底层C守护进程拉起App上层/java进程

Linux Am命令开启后台进程

一种底层实现让进程不被杀死的方法,在Android4.4以上可能有兼容性问题,具体参考Ref-7

NotificationListenerService通知

一种需要用户允许特定权限的系统拉起方式,4.3以上系统

前台浮窗

有朋友提出一种应用退出后启动一个不可交互的浮窗,个人觉得这种方法是无效的,读者有兴趣可以一试

新方法(AccountSync)

思路

利用Android系统提供的账号和同步机制实现

效果

  • 通过adb查看,运行在后台的服务其进程号变成了1(优先级仅次于前台进程),能提高进程优先级,对比如下图 

正常情况

采用AccountSyncAdapter方法后

  • 进程被系统kill后,可以由syn拉起

风险

  • SyncAdapter时间进度不高,往往会因为手机处于休眠状态,而时间往后调整,同步间隔最低为1分钟
  • 用户可以单独停止或者删除,有些手机账号默认是不同步的,需要手动开启

实现 (核心代码)

1 建立数据同步系统(ContentProvider)

通过一个ContentProvider用来作数据同步,由于并没有实际数据同步,所以此处就直接建立一个空的ContentProvider即可


  1. public class XXAccountProvider extends ContentProvider { 
  2.     public static final String AUTHORITY = "包名.provider"; 
  3.     public static final String CONTENT_URI_BASE = "content://" + AUTHORITY; 
  4.     public static final String TABLE_NAME = "data"; 
  5.     public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_BASE + "/" + TABLE_NAME); 
  6.   
  7.     @Override 
  8.     public boolean onCreate() { 
  9.         return true; 
  10.     } 
  11.   
  12.     @Nullable 
  13.     @Override 
  14.     public Cursor query(Uri uri, String[] projection, String selection, 
  15.                         String[] selectionArgs, String sortOrder) { 
  16.         return null; 
  17.     } 
  18.   
  19.     @Nullable 
  20.     @Override 
  21.     public String getType(Uri uri) { 
  22.         return new String(); 
  23.     } 
  24.   
  25.     @Nullable 
  26.     @Override 
  27.     public Uri insert(Uri uri, ContentValues values) { 
  28.         return null; 
  29.     } 
  30.   
  31.     @Override 
  32.     public int delete(Uri uri, String selection, String[] selectionArgs) { 
  33.         return 0; 
  34.     } 
  35.   
  36.     @Override 
  37.     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 
  38.         return 0; 
  39.     } 
  40. }  

然后再Manifest中声明


  1. <provider 
  2. android:name="**.XXAccountProvider" 
  3. android:authorities="@string/account_auth_provider" 
  4. android:exported="false" 
  5. android:syncable="true"/>  

2 建立Sync系统 (SyncAdapter)

通过实现SyncAdapter这个系统服务后, 利用系统的定时器对程序数据ContentProvider进行更新,具体步骤为:

  • 创建Sync服务

  1. public class XXSyncService extends Service { 
  2.     private static final Object sSyncAdapterLock = new Object(); 
  3.     private static XXSyncAdapter sSyncAdapter = null; 
  4.     @Override 
  5.     public void onCreate() { 
  6.         synchronized (sSyncAdapterLock) { 
  7.             if (sSyncAdapter == null) { 
  8.                 sSyncAdapter = new XXSyncAdapter(getApplicationContext(), true); 
  9.             } 
  10.         } 
  11.     } 
  12.   
  13.     @Override 
  14.     public IBinder onBind(Intent intent) { 
  15.         return sSyncAdapter.getSyncAdapterBinder(); 
  16.     } 
  17.   
  18.     static class XXSyncAdapter extends AbstractThreadedSyncAdapter { 
  19.         public XXSyncAdapter(Context context, boolean autoInitialize) { 
  20.             super(context, autoInitialize); 
  21.         } 
  22.         @Override 
  23.         public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { 
  24.             getContext().getContentResolver().notifyChange(XXAccountProvider.CONTENT_URI, null, false); 
  25.         } 
  26.     } 
  27. }  
  • 声明Sync服务

  1. <service 
  2. android:name="**.XXSyncService" 
  3. android:exported="true" 
  4. android:process=":core"> 
  5. <intent-filter> 
  6. <action 
  7. android:name="android.content.SyncAdapter"/> 
  8. </intent-filter> 
  9. <meta-data 
  10. android:name="android.content.SyncAdapter" 
  11. android:resource="@xml/sync_adapter"/> 
  12. </service>  

其中sync_adapter为:


  1. <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" 
  2.               android:accountType="@string/account_auth_type" 
  3.               android:allowParallelSyncs="false" 
  4.               android:contentAuthority="@string/account_auth_provide" 
  5.               android:isAlwaysSyncable="true" 
  6.               android:supportsUploading="false" 
  7.               android:userVisible="true"/>  

参数说明:

android:contentAuthority 指定要同步的ContentProvider在其AndroidManifest.xml文件中有个android:authorities属性。

android:accountType 表示进行同步的账号的类型。

android:userVisible 设置是否在“设置”中显示

android:supportsUploading 设置是否必须notifyChange通知才能同步

android:allowParallelSyncs 是否支持多账号同时同步

android:isAlwaysSyncable 设置所有账号的isSyncable为1

android:syncAdapterSettingsAction 指定一个可以设置同步的activity的Action。

  • 账户调用Sync服务

首先配置好Account(第三步),然后再通过ContentProvider实现

手动更新


  1. public void triggerRefresh() { 
  2. Bundle b = new Bundle(); 
  3. b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 
  4. b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 
  5. ContentResolver.requestSync( 
  6. account, 
  7. CONTENT_AUTHORITY, 
  8. b); 
  9. }  

添加账号


  1. Account account = AccountService.GetAccount(); 
  2. AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); 
  3. accountManager.addAccountExplicitly(...)  

同步周期设置


  1. ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1); 
  2. ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true); 
  3. ContentResolver.addPeriodicSync(account, CONTENT_AUTHORITY, new Bundle(), SYNC_FREQUENCY);  

3 建立账号系统 (Account Authenticator)

通过建立Account账号,并关联SyncAdapter服务实现同步

接下文

本文作者:佚名

来源:51CTO

时间: 2024-08-19 12:10:32

一种提高Android应用进程存活率新方法(上)的相关文章

一种提高Android应用进程存活率新方法(下)

接上文 创建 Account服务 public class XXAuthService extends Service {      private XXAuthenticator mAuthenticator;         @Override      public void onCreate() {          mAuthenticator = new XXAuthenticator(this);      }         private XXAuthenticator get

4种提高多维数据分析的方法

[51CTO.com快译]联机分析处理(OLAP)需要有即时的响应,因此其性能是至关重要的.虽然其结构较为简单,但是在处理各种大的数据立方体(data cubes)时,会涉及到大量的计算. 常被称为OLAP(联机分析处理)的多维分析是一种交互式的数据分析过程,它包括:对于数据立方体(data cube)进行旋转(rotation).切片与切块(slice and dice).钻取(drill-down)等执行操作.其后端的计算结构较为简单,如下列SQL语句所示. SELECT D,..., SU

厌倦了编程书?来试试这3种提高编程技能的有趣方法吧

如果你曾经从书上学习编写代码,你就知道那有多乏味.为什么不试试一些激动人心的方法来使学习更有乐趣呢? fun1 下面介绍的这些网站每个都有自己独特的风格,但是它们都加入了游戏元素.这些稀奇古怪而有趣的游戏能够使你找回学习的乐趣. Code Combat 如果你正在学习JavaScript并且没有多少-或根本没有-编码经验,来试试这个免费的游戏.控制一个巫师和他的随从.关卡设置从简单的概念如预先编好的动作到带条件判断的行动到更高级的咒语例如计算. 学习过程是在一个可爱的魔幻RPG中进行,有骑士,食

提高Android Service 优先级的方法 .

  Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用.当系统觉得当前的资源非常有限的时候,为了保 证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存.这样就能保证真正对用户有用的程序仍然再运行.如果你的 Service 碰上了这种情况,多半会先被杀掉.但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级. 为什么是

【lombok】lombok---帮你简化生成必要但臃肿的java代码工具 【映射注解和lombok注解同时使用 以及 映射注解放在属性和get方法上的区别】

官方地址:https://projectlombok.org/ GitHub:https://github.com/rzwitserloot/lombok 指导说明文档:http://jnb.ociweb.com/jnb/jnbJan2010.html =============================================================================================================== 本来来说,lombok

[收藏]五种提高 SQL 性能的方法

性能 五种提高 SQL 性能的方法发布日期: 4/1/2004 | 更新日期: 4/1/2004Johnny Papa Data Points Archive 有时, 为了让应用程序运行得更快,所做的全部工作就是在这里或那里做一些很小调整.啊,但关键在于确定如何进行调整!迟早您会遇到这种情况:应用程序中的 SQL 查询不能按照您想要的方式进行响应.它要么不返回数据,要么耗费的时间长得出奇.如果它降低了报告或您的企业应用程序的速度,用户必须等待的时间过长,他们就会很不满意.就像您的父母不想听您解释

五种提高SQL性能的方法

性能 五种提高 SQL 性能的方法Johnny Papa 有时, 为了让应用程序运行得更快,所做的全部工作就是在这里或那里做一些很小调整.啊,但关键在于确定如何进行调整!迟早您会遇到这种情况:应用程序中的 SQL 查询不能按照您想要的方式进行响应.它要么不返回数据,要么耗费的时间长得出奇.如果它降低了报告或您的企业应用程序的速度,用户必须等待的时间过长,他们就会很不满意.就像您的父母不想听您解释为什么在深更半夜才回来一样,用户也不会听你解释为什么查询耗费这么长时间.("对不起,妈妈,我使用了太多

一种实现Win32消息处理处理函数的新方法 - 基于Thunk实现的类成员消息处理函数

Windows是一个消息驱动的操作系统,在系统中发生的所有消息均需要通过消息处理过程(或叫窗口过程)进行处理.由于C++给我们在程序设计中带来更多的灵活性(如继承.重载.多态等),所以我们都希望能够使用C++的类来封装Windows中的窗口过程函数,但是Windows规定了窗口过程函数必须定义为一个全局函数,也就是说需要使用面向过程的方法来实现,为了使用面向对象的技术来实现消息处理,我们必须另辟它径.目前我们在网络上见得比较多的方式是使用Thunk将即将传递给窗口过程的第一个参数(HWND hW

五种提高 SQL 性能的方法_MsSql

发布日期: 4/1/2004 | 更新日期: 4/1/2004 Johnny Papa Data Points Archive 有时, 为了让应用程序运行得更快,所做的全部工作就是在这里或那里做一些很小调整.啊,但关键在于确定如何进行调整!迟早您会遇到这种情况:应用程序中的 SQL 查询不能按照您想要的方式进行响应.它要么不返回数据,要么耗费的时间长得出奇.如果它降低了报告或您的企业应用程序的速度,用户必须等待的时间过长,他们就会很不满意.就像您的父母不想听您解释为什么在深更半夜才回来一样,用户