【Android】一种提高Android进程存活率新方法

【Android】一种提高Android进程存活率新方法

SkySeraph Jun. 19st 2016

Email:[email protected]

更多精彩请直接访问个人站点:www.skyseraph.com

一、基础知识

1.Android 进程优先级

1.1 进程优先级等级一般分法:
- Activte process
- Visible Process
- Service process
- Background process
- Empty process

1.2 进程优先级号

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;

2. 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.

3. 查看某个App的进程

步骤(手机与PC连接)
1. adb shell
2. ps | grep 进程名
3. cat /proc/pid/oom_adj //其中pid是上述grep得到的进程号

4. Android账号和同步机制

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

二、现有方法

1. 网络连接保活方法

a. GCM
b. 公共的第三方push通道(信鸽等)
c. 自身跟服务器通过轮询,或者长连接
具体实现请参考 微信架构师杨干荣的"微信Android客户端后台保活经验分享" (Ref-1).

2. 双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 }

3. 守护进程及时拉起

AlarmReceiver, ConnectReceiver,BootReceiver等

三、新方法(AccountSyncAdapter)

1. 思路:

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

2. 效果:

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

正常情况

采用AccountSyncAdapter方法后

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

3. 风险:

① SyncAdapter时间进度不高,往往会因为手机处于休眠状态,而时间往后调整,同步间隔最低为1分钟
② 可以被用户单独停止或者删除

4. 实现:核心代码如下

① 建立数据同步系统(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中声明

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

② 建立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);

③ 建立账号系统 (Account Authenticator)
通过建立Account账号,并关联SyncAdapter服务实现同步
- 创建Account服务

 1 public class XXAuthService extends Service {
 2     private XXAuthenticator mAuthenticator;
 3
 4     @Override
 5     public void onCreate() {
 6         mAuthenticator = new XXAuthenticator(this);
 7     }
 8
 9     private XXAuthenticator getAuthenticator() {
10         if (mAuthenticator == null)
11             mAuthenticator = new XXAuthenticator(this);
12         return mAuthenticator;
13     }
14
15     @Override
16     public IBinder onBind(Intent intent) {
17         return getAuthenticator().getIBinder();
18     }
19
20     class XXAuthenticator extends AbstractAccountAuthenticator {
21         private final Context context;
22         private AccountManager accountManager;
23         public XXAuthenticator(Context context) {
24             super(context);
25             this.context = context;
26             accountManager = AccountManager.get(context);
27         }
28
29         @Override
30         public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)
31                 throws NetworkErrorException {
32             // 添加账号 示例代码
33             final Bundle bundle = new Bundle();
34             final Intent intent = new Intent(context, AuthActivity.class);
35             intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
36             bundle.putParcelable(AccountManager.KEY_INTENT, intent);
37             return bundle;
38         }
39
40         @Override
41         public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
42                 throws NetworkErrorException {
43             // 认证 示例代码
44             String authToken = accountManager.peekAuthToken(account, getString(R.string.account_token_type));
45             //if not, might be expired, register again
46             if (TextUtils.isEmpty(authToken)) {
47                 final String password = accountManager.getPassword(account);
48                 if (password != null) {
49                     //get new token
50                     authToken = account.name + password;
51                 }
52             }
53             //without password, need to sign again
54             final Bundle bundle = new Bundle();
55             if (!TextUtils.isEmpty(authToken)) {
56                 bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
57                 bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
58                 bundle.putString(AccountManager.KEY_AUTHTOKEN, authToken);
59                 return bundle;
60             }
61
62             //no account data at all, need to do a sign
63             final Intent intent = new Intent(context, AuthActivity.class);
64             intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
65             intent.putExtra(AuthActivity.ARG_ACCOUNT_NAME, account.name);
66             bundle.putParcelable(AccountManager.KEY_INTENT, intent);
67             return bundle;
68         }
69
70         @Override
71         public String getAuthTokenLabel(String authTokenType) {
72 //            throw new UnsupportedOperationException();
73             return null;
74         }
75
76         @Override
77         public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
78             return null;
79         }
80
81         @Override
82         public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options)
83                 throws NetworkErrorException {
84             return null;
85         }
86
87         @Override
88         public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
89                 throws NetworkErrorException {
90             return null;
91         }
92
93         @Override
94         public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features)
95                 throws NetworkErrorException {
96             return null;
97         }
98     }
99 }

- 声明Account服务

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

其中authenticator为:

1 <?xml version="1.0" encoding="utf-8"?>
2 <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
3     android:accountType="@string/account_auth_type"
4     android:icon="@drawable/icon"
5     android:smallIcon="@drawable/icon"
6     android:label="@string/app_name"
7 />

- 使用Account服务
同SyncAdapter,通过AccountManager使用

  - 申请Token主要是通过 [AccountManager.getAuthToken]系列方法

  - 添加账号则通过 [AccountManager.addAccount]

  - 查看是否存在账号通过 [AccountManager.getAccountsByType]

Refs

1. [微信Android客户端后台保活经验分享]

2. [Android Low Memory Killer原理]

3. [stackOverflow 上介绍的双线程方法]

4. [Write your own Android Sync Adapter]

5. [Write your own Android Authenticator]

6. Android developer
- [android.accounts]
- [AccountManager]
- [AbstractAccountAuthenticator]
- [AccountAuthenticatorActivity]
- [Creating a Sync Adapter]

========

By SkySeraph-2016  www.skyseraph.com

时间: 2024-08-02 23:15:39

【Android】一种提高Android进程存活率新方法的相关文章

[Android]GOF23种设计模式 &amp; Android中的设计模式

GOF23种设计模式 设计原则: 1. 单一职责原则(SRP):就一个类而言,应该仅有一个引起它变化的原因 2. 开放-封闭原则(OCP):软件实体(类.模块.函数等)应该可以扩展,但是不可修改.即对于扩展是开放的, 对于修改是封闭的. 3. 依赖倒转原则: A. 高层模块不应该依赖低层模块,两个都应该依赖抽象.B.抽象不应该依赖细节,细节应该依赖抽象.说白了,就是要针对接口编程,不要对实现编程. 4. 迪米特法则(LoD):如果两个类不必彼此直接通信,那么这两个类就不应该发生直接的相互作用.如

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

一.基础知识 1.Android 进程优先级 1.1 进程优先级等级一般分法:- Activte process- Visible Process- Service process- Background process- Empty process 1.2 进程优先级号 ProcessList.java // Adjustment used in certain places where we don't know it yet. // (Generally this is something

android 8种对话框(Dialog)使用方法汇总

本文为作者原创,转载请注明出处:http://www.cnblogs.com/gzdaijie/p/5222191.html 目录 1.写在前面2.代码示例2.1 普通Dialog(图1与图2)2.2 列表Dialog(图3)2.3 单选Dialog(图4)2.4 多选Dialog(图5)2.5 等待Dialog(图6)2.6 进度条Dialog(图7)2.7 编辑Dialog(图8)2.8 自定义Dialog(图9)3.复写回调函数 1.写在前面 Android提供了丰富的Dialog函数,本

Android 两种制作圆形/圆角图片的方法

前言: 目前网上有很多圆角图片的实例,Github上也有一些成熟的项目.之前做项目,为了稳定高效都是选用Github上的项目直接用.但这种结束也是Android开发必备技能 ,所以今天就来简单研究一下该技术,分享给大家. 预备知识: Xfermode介绍: 下面是Android ApiDemo里的"Xfermodes"实例,效果图. Xfermode有三个子类,结构如下: public class Xfermode extends Object java.lang.Object ? a

Android获取屏幕分辨率官方推荐的新方法

<pre name="code" class="java"> WindowManager wm = this.getWindowManager(); Point screenAttribute = new Point(); wm.getDefaultDisplay().getSize(screenAttribute); Toast.makeText(this, "Screen Width:" + screenAttribute.x +

重点:怎样正确的使用QThread类(注:包括推荐使用QThread线程的新方法QObject::moveToThread)

背景描述: 以前,继承 QThread 重新实现 run() 函数是使用 QThread唯一推荐的使用方法.这是相当直观和易于使用的.但是在工作线程中使用槽机制和Qt事件循环时,一些用户使用错了.Qt  核心开发人员Bradley T. Hughes, 推荐使用QObject::moveToThread 把它们移动到线程中.不幸的是, 以用户反对这样使用.Olivier Goffart, 前Qt  核心开发人之一, 告诉这些用户你们不这样做就错了.最终这俩种用法我们都在QThread的文档中发现

Android 子Activity组件在进程内的启动过程 &amp;&amp; 子Activity组件在新进程中的启动过程

1.子Activity组件在进程内的启动过程 在Android Activity组件的启动过程http://blog.csdn.net/jltxgcy/article/details/35984557一文中,我们已经详细分析了Activity的启动过程,对于子Activity组件在进程内的启动过程,我们只分析它们之间的不同. 主要是2处,1是不需要创建新的任务栈,2是不需要创建新进程和子线程. 第1点,体现在如下代码上: -/Android/frameworks/base/services/ja

Android 通过JNI实现守护进程,使得Service服务不被杀死

来自: http://finalshares.com/read-7306 转载请注明出处: http://blog.csdn.net/yyh352091626/article/details/50542554 开发一个需要常住后台的App其实是一件非常头疼的事情,不仅要应对国内各大厂商的ROM,还需要应对各类的安全管家...虽然不断的研究各式各样的方法,但是效果并不好,比如任务管理器把App干掉,服务就起不来了... 网上搜寻一番后,主要的方法有以下几种方法,但都是治标不治本: 1.提高Serv

android 三种网络通信接口及各个接口的代码示例

第一部分 Android网络基础 Android平台浏览器采用了WeBKit引擎,这款名为Chorme Lite的Web浏览器拥有强大扩展特性,每个开发者都以为编写自己的插件,使得浏览器的功能更加完善. 目前Android平台有3种网络接口. 第一种 java.net.*(标准java接口) 此接口提供与联网有关的类,包括流和数据包套接字.Internet协议.常见HTTP处理.如:创建URL以及URLConnection/HttpURLConnection对象.设置连接参数.连接服务器.向服务