保存到桌面加入收藏设为首页
IOS开发
当前位置:首页 > IOS开发

Android进程保活全攻略(上)-安度博客

时间:2019-02-04 14:57:13   作者:   来源:   阅读:93   评论:0
内容摘要:对于每个公司的APP来说,当然都希望自己APP的进程尽量的不被杀死,于是乎,就有了一些列进程保活的方法出现,网上也有很多关于这类的文章,但网上很多资料往往只告诉了思路,并未将实现代码展示,本次我的博客将分为上下两篇,阐述关于进程保活的所有方法,以及实现的方式,若有错漏之处,大家可......
  • 对于每个公司的APP来说,当然都希望自己APP的进程尽量的不被杀死,于是乎,就有了一些列进程保活的方法出现,网上也有很多关于这类的文章,但网上很多资料往往只告诉了思路,并未将实现代码展示,本次我的博客将分为上下两篇,阐述关于进程保活的所有方法,以及实现的方式,若有错漏之处,大家可以在博客进行留言。

    **

    1.进程保活-背景知识

    **
    (1)什么时候系统会去杀死进程?
    nk" target="_blank">Android系统会在内存不足的时候去将进程杀死,俗称Low Memory Killer,它是 基于linux内核的 OOM Killer(Out-Of-Memory killer)机制,内存不足时,优先杀oom_adj值高的进程。

    既然知道了oom_adj值,那大家肯定想知道,如何去查看应用的oom_adj值呢?
    系统进程oom值小于0,应用进程大于0,可以发现,系统的就是叼
    我们可以通过 adb命令,去查看相应进程的oom_adj值,命令如下:

    查看命令:adb shell ps | grep 进程名 | awk ‘{print $2}’ | xargs -i adb shell cat /proc/{}/oom_adj

    这里我总结了各种类型进程的oom_adj值
    这里写图片描述

    (2)进程被杀的场景有哪些?
    进程被杀死的场景很多,例如被第三方应用杀死(360管家等),关机等等,不同的场景调用的系统接口也是不同,同时杀死的oom_adj值范围也是不同的,于是我将这些场景总结成了一个表格,方便大家了解:
    这里写图片描述

    2.常见的保活拉起方式

    了解进程被杀死的相关场景后,相信大家对进程保活已经有了初步的认识,接下来我将给大家介绍一下,现在市面上存在的各种常见的保活拉起方式,这些保活方式如下:

    **a) 将Service设置为前台服务
    b) 在service的onstart方法里返回 STATR_STICK
    c) 添加Manifest文件属性值为android:persistent=“true”
    d) 覆写Service的onDestroy方法
    e) 监听一堆系统静态广播
    f) 监听第三方应用的静态广播
    g) AlarmManager唤醒
    h) 账户同步,定时唤醒
    i) 1像素悬浮层
    j) GCM或其它3方推送
    k) 应用间互相拉起
    l) 心跳唤醒
    m)Native进程拉起
    n) 双进程守护**

    1) 将Service设置为前台服务
    思路:启用前台服务,主要是startForeground()
    保活程度:一般情况下不被杀,部分定制ROM会在应用切到后台即杀
    ,会被 force stop 杀死
    代码实现:

    Notificationnotification = newNotification(R.drawable.queen2 '有消息来了'  System.currentTimeMillis());notification.setLatestEventInfo(this '双11,上天猫!' '一律5折' null);//设置通知默认效果notification.flags = Notification.FLAG_SHOW_LIGHTS;startForeground(1 notification);

    2) 在service的onstart方法里返回 STATR_STICK
    思路:其实就是onstartCommand中返回STATR_STICK
    保活程度:有次数和时间的限制
    ,会被 force stop 杀死
    代码实现:

    @Overridepublic int onstartCommand(Intent intent int flags int startId) { // TODO Auto-generated method stub return START_STICKY; //return super.onstartCommand(intent flags startId);}

    3) 添加Manifest文件属性值为android:persistent=“true”
    代码实现(清单文件中配置):
    保活程度:一般情况下不被杀,会被 force stop 杀死

    <application android:name=&#39;PhoneApp&#39; android:persistent=&#39;true&#39; android:label=&#39;@string/dialerIconLabel&#39; android:icon=&#39;@drawable/ic_launcher_phone&#39;>

    注意:该方法需要系统签名

    4) 覆写Service的onDestroy方法
    思路:在onDestroy中再次启动该服务
    保活程度:很弱,只在两种情况下work:正在运行里杀服务、DDMS里stop进程
    代码实现:

    @Overridepublic void onDestroy() { Intent intent = new Intent(this KeeLiveService.class); startService(intent); super.onDestroy();}

    5) 监听一堆系统静态广播
    思路:在发生特定系统事件时,系统会发出响应的广播,通过在 nk" target="_blank">AndroidManifest 中“静态”注册对应的广播监听器,即可在发生响应事件时拉活。
    可以监听的系统静态广播列表如下:
    这里写图片描述

    保活强度:我们可以发现,这个方法都是监听系统的一些广播,所以我们需要在我们的应用中注册静态广播,但是静态广播又会出现问题,那就是在4.0版本以上,没有启动过的应用或Force-Stop后收不到静态广播,也就是说4.0以后,如果我们应用从未启动过,或者被Force-Stop杀死过,是无法接收到静态广播的。

    如果是两个应用相互拉起,那么在一个应用内可发送带FLAG_INCLUDE_STOPPED_PACKAGES的Intent,那即使另一个应用也是以上两种情况,也可以接收到系统的广播
    应用1的代码实现:

    //应用1,发送拉起服务的广播Intent intent = new Intent();intent.setAction(&#39;com.action.keepLive&#39;);intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);this.sendBroadcast(intent);

    应用2的代码实现:

    <receiver android:name=&#39;com.yzy.supercleanmaster.receiver.KeepLiveReceiver&#39;> <intent-filter> <action android:name=&#39;com.action.keepLive&#39; /> </intent-filter></receiver>
    public class KeepLiveReceiver extends BroadcastReceiver{ //应用2中,接受应用1发送的广播,进行服务的拉起 @Override public void onReceive(Context context Intent intent) { Intent i = new Intent(context KeeLiveService.class); context.startService(i); }}

    6) 监听第三方应用的静态广播
    思路:通过反编译第三方 Top 应用,如:手机QQ、微信、支付宝、UCnk" target="_blank">浏览器等,以及友盟、信鸽、个推等 SDK,找出它们外发的广播,在应用中进行监听,这样当这些应用发出广播时,就会将我们的应用拉活。

    保活强度:
    该方案的局限性除与系统广播一样的因素外,主要受如下因素限制:
    1) 反编译分析过的第三方应用的多少
    2) 第三方应用的广播属于应用私有,当前版本中有效的广播,在后续版本随时就可能被移除或被改为不外发,这些因素都影响了拉活的效果。

    7) AlarmManager唤醒
    思路:通过AlarmManager设置一个定时器,定时的唤醒服务
    **保活强度:**killBackgroundProcess下,大部分情况work,
    不敌force-stop,闹钟会被清除。
    代码实现:

    public void startKeepLiveService(Context context int timeMillisString action) { //获取AlarmManager系统服务 AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); //包装Intent Intent intent = newIntent(contextKeepLiveServie.class); intent.setAction(action); PendingIntent pendingIntent = PendingIntent.getService(context0intent PendingIntent.FLAG_UPDATE_CURRENT); //添加到AlarmManager alarmManager.setRepeating(AlarmManager.RTC_WAKEUPSystem.currentTimeMillis()timeMillispendingIntent);}

    8) 账户同步,定时唤醒
    **思路:**android系统里有一个账户系统,系统定期唤醒账号更新服务,同步的事件间隔是有限制的,最短1分钟。
    难点:需要手动设置账户,你如何骗你的用户给你手动设置账户完了之后不卸载你,必须联网
    代码实现:
    ① 建立数据同步系统(ContentProvider)
    通过一个ContentProvider用来作数据同步,由于并没有实际数据同步,所以此处就直接建立一个空的ContentProvider即可

    public class XXAccountProvider extends ContentProvider { public static final String AUTHORITY = &#39;包名.provider&#39;; public static final String CONTENT_URI_base = &#39;content://&#39; + AUTHORITY; public static final String TABLE_NAME = &#39;data&#39;; public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_base + &#39;/&#39; + TABLE_NAME); @Override public boolean onCreate() { return true; } @Nullable @Override public Cursor query(Uri uri String[] projection String selection String[] selectionArgs String sortOrder) { return null; } @Nullable @Override public String getType(Uri uri) { return new String(); } @Nullable @Override public Uri insert(Uri uri ContentValues values) { return null; } @Override public int delete(Uri uri String selection String[] selectionArgs) { return 0; } @Override public int update(Uri uri ContentValues values String selection String[] selectionArgs) { return 0; }}

    然后再Manifest中声明

    <provider android:name=&#39;**.XXAccountProvider&#39; android:authorities=&#39;@string/account_auth_provider&#39; android:exported=&#39;false&#39; android:syncable=&#39;true&#39;/>

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

    public class XXSyncService extends Service { private static final object sSyncAdapterLock = new object(); private static XXSyncAdapter sSyncAdapter = null; @Override public void onCreate() { synchronized (sSyncAdapterLock) { if (sSyncAdapter == null) { sSyncAdapter = new XXSyncAdapter(getApplicationContext() true); } } } @Override public IBinder onBind(Intent intent) { return sSyncAdapter.getSyncAdapterBinder(); } static class XXSyncAdapter extends AbstractThreadedSyncAdapter { public XXSyncAdapter(Context context boolean autoInitialize) { super(context autoInitialize); } @Override public void onPerformSync(Account account Bundle extras String authority ContentProviderClient provider SyncResult syncResult) { getContext().getContentResolver().notifyChange(XXAccountProvider.CONTENT_URI null false); } }}
    声明Sync服务
    <service android:name=&#39;**.XXSyncService&#39; android:exported=&#39;true&#39; android:process=&#39;:core&#39;> <intent-filter> <action android:name=&#39;android.content.SyncAdapter&#39;/> </intent-filter> <meta-data android:name=&#39;android.content.SyncAdapter&#39; android:resource=&#39;@xml/sync_adapter&#39;/></service>

    其中sync_adapter为:

    <sync-adapter xmlns:android=&#39;http://schemas.android.com/apk/res/android&#39; android:accountType=&#39;@string/account_auth_type&#39; android:allowParallelSyncs=&#39;false&#39; android:contentAuthority=&#39;@string/account_auth_provide&#39; android:isAlwaysSyncable=&#39;true&#39; android:supportsUploading=&#39;false&#39; android:userVisible=&#39;true&#39;/>

    参数说明:
    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实现
    手动更新
    public void triggerRefresh() { Bundle b = new Bundle(); b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL true); b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED true); ContentResolver.requestSync( account CONTENT_AUTHORITY b);}

    添加账号

    Account account = AccountService.GetAccount();AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);accountManager.addAccountExplicitly(...)``同步周期设置

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


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

    public class XXAuthService extends Service { private XXAuthenticator mAuthenticator; @Override public void onCreate() { mAuthenticator = new XXAuthenticator(this); } private XXAuthenticator getAuthenticator() { if (mAuthenticator == null) mAuthenticator = new XXAuthenticator(this); return mAuthenticator; } @Override public IBinder onBind(Intent intent) { return getAuthenticator().getIBinder(); } class XXAuthenticator extends AbstractAccountAuthenticator { private final Context context; private AccountManager accountManager; public XXAuthenticator(Context context) { super(context); this.context = context; accountManager = AccountManager.get(context); } @Override public Bundle addAccount(AccountAuthenticatorResponse response String accountType String authTokenType String[] requiredFeatures Bundle options) throws NetworkErrorException { // 添加账号 示例代码 final Bundle bundle = new Bundle(); final Intent intent = new Intent(context AuthActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE response); bundle.putParcelable(AccountManager.KEY_INTENT intent); return bundle; } @Override public Bundle getAuthToken(AccountAuthenticatorResponse response Account account String authTokenType Bundle options) throws NetworkErrorException { // 认证 示例代码 String authToken = accountManager.peekAuthToken(account getString(R.string.account_token_type)); //if not might be expired register again if (TextUtils.isEmpty(authToken)) { final String passnk" target="_blank">word = accountManager.getPassnk" target="_blank">word(account); if (password != null) { //get new token authToken = account.name + password; } } //without password need to sign again final Bundle bundle = new Bundle(); if (!TextUtils.isEmpty(authToken)) { bundle.putString(AccountManager.KEY_ACCOUNT_NAME account.name); bundle.putString(AccountManager.KEY_ACCOUNT_TYPE account.type); bundle.putString(AccountManager.KEY_AUTHTOKEN authToken); return bundle; } //no account data at all need to do a sign final Intent intent = new Intent(context AuthActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE response); intent.putExtra(AuthActivity.ARG_ACCOUNT_NAME account.name); bundle.putParcelable(AccountManager.KEY_INTENT intent); return bundle; } @Override public String getAuthTokenLabel(String authTokenType) {// throw new UnsupportedOperationException(); return null; } @Override public Bundle editProperties(AccountAuthenticatorResponse response String accountType) { return null; } @Override public Bundle confirmCredentials(AccountAuthenticatorResponse response Account account Bundle options) throws NetworkErrorException { return null; } @Override public Bundle updateCredentials(AccountAuthenticatorResponse response Account account String authTokenType Bundle options) throws NetworkErrorException { return null; } @Override public Bundle hasFeatures(AccountAuthenticatorResponse response Account account String[] features) throws NetworkErrorException { return null; } }
    声明Account服务
    <service android:name=&#39;**.XXAuthService&#39; android:exported=&#39;true&#39; android:process=&#39;:core&#39;> <intent-filter> <action android:name=&#39;android.accounts.AccountAuthenticator&#39;/> </intent-filter> <meta-data android:name=&#39;android.accounts.AccountAuthenticator&#39; android:resource=&#39;@xml/authenticator&#39;/></service>

    其中authenticator为:

    <?xml version=&#39;1.0&#39; encoding=&#39;utf-8&#39;?><account-authenticator xmlns:android=&#39;http://schemas.android.com/apk/res/android&#39; android:accountType=&#39;@string/account_auth_type&#39; android:icon=&#39;@drawable/icon&#39; android:smallIcon=&#39;@drawable/icon&#39; android:label=&#39;@string/app_name&#39; />
    使用Account服务
    同SyncAdapter,通过AccountManager使用
      - 申请Token主要是通过 [AccountManager.getAuthToken]系列方法
      - 添加账号则通过 [AccountManager.addAccount]
      - 查看是否存在账号通过 [AccountManager.getAccountsByType]

    保活强度:
    该方案适用于所有的 Android 版本,包括被 forestop 掉的进程也可以进行拉活。最新 Android 版本(Android N)中系统好像对账户同步这里做了变动,该方法不再有效。

    本篇介绍了进程保活的一些方法,由于篇幅原因,1像素悬浮层、应用间互相拉起、心跳唤醒等方法将在下一篇进行介绍。


本站所有站内信息仅供娱乐参考,不作任何商业用途,不以营利为目的,专注分享快乐,欢迎收藏本站!
所有信息均来自:百度一下 (威尼斯人官网)