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

简介:

一种提高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. // userand 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方法后

采用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, nullfalse); 
  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
目录
相关文章
|
23天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
24天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
24天前
|
开发框架 安全 Android开发
探索安卓系统的新趋势:智能家居应用的蓬勃发展
随着智能家居概念的兴起,安卓系统在智能家居应用领域的应用日益广泛。本文将探讨安卓系统在智能家居应用开发方面的最新趋势和创新,以及其对用户生活的影响。
14 2
|
27天前
|
缓存 监控 Java
构建高效Android应用:从优化用户体验到提升性能
在竞争激烈的移动应用市场中,为用户提供流畅和高效的体验是至关重要的。本文深入探讨了如何通过多种技术手段来优化Android应用的性能,包括UI响应性、内存管理和多线程处理。同时,我们还将讨论如何利用最新的Android框架和工具来诊断和解决性能瓶颈。通过实例分析和最佳实践,读者将能够理解并实施必要的优化策略,以确保他们的应用在保持响应迅速的同时,还能够有效地利用系统资源。
|
25天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
3天前
|
移动开发 Java Android开发
构建高效Android应用:采用Kotlin协程优化网络请求
【4月更文挑战第24天】 在移动开发领域,尤其是对于Android平台而言,网络请求是一个不可或缺的功能。然而,随着用户对应用响应速度和稳定性要求的不断提高,传统的异步处理方式如回调地狱和RxJava已逐渐显示出局限性。本文将探讨如何利用Kotlin协程来简化异步代码,提升网络请求的效率和可读性。我们将深入分析协程的原理,并通过一个实际案例展示如何在Android应用中集成和优化网络请求。
|
3天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第24天】随着移动开发技术的不断演进,提升应用性能和用户体验已成为开发者的核心任务。在Android平台上,Kotlin语言凭借其简洁性和功能性成为主流选择之一。特别是Kotlin的协程功能,它为异步编程提供了一种轻量级的解决方案,使得处理并发任务更加高效和简洁。本文将深入探讨Kotlin协程在Android开发中的应用,通过实际案例分析协程如何优化应用性能,以及如何在项目中实现协程。
|
3天前
|
存储 缓存 安全
Android系统 应用存储路径与权限
Android系统 应用存储路径与权限
6 0
Android系统 应用存储路径与权限
|
3天前
|
存储 安全 Android开发
Android系统 自定义系统和应用权限
Android系统 自定义系统和应用权限
19 0
|
7天前
|
Python
多ip多进程代理的实现方法
多ip多进程代理的实现方法

相关实验场景

更多