zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

【Android 进程保活】应用进程拉活 ( 系统 Service 机制拉活 | Service 组件 onStartCommand 方法分析 | 源码资源 )

2023-06-13 09:17:49 时间

文章目录

一、 Service 组件 onStartCommand 方法分析


1、 onStartCommand 函数返回值分析

Service 的生命周期函数 onStartCommand 方法 , 返回一个整型值 ;

Service 中的 mStartCompatibility 标记默认是 false , 因此 onStartCommand 函数默认返回的整型值是 Service.START_STICKY 值 ;

mStartCompatibility 值在 Service 中的 attach 方法中赋值 , 其值为 getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.ECLAIR , 即手机的 API Level 版本号是否小于 5 , 现在肯定没有版本号小于 5 的手机 , 该值默认是 false ;

public abstract class Service extends ContextWrapper 
	implements ComponentCallbacks2,
        ContentCaptureManager.ContentCaptureClient {

    /**
     * Called by the system every time a client explicitly starts the service by calling 
     * {@link android.content.Context#startService}, providing the arguments it supplied and a 
     * unique integer token representing the start request.  Do not call this method directly.
     * 
     * <p>For backwards compatibility, the default implementation calls
     * {@link #onStart} and returns either {@link #START_STICKY}
     * or {@link #START_STICKY_COMPATIBILITY}.
     * 
     * <p class="caution">Note that the system calls this on your
     * service's main thread.  A service's main thread is the same
     * thread where UI operations take place for Activities running in the
     * same process.  You should always avoid stalling the main
     * thread's event loop.  When doing long-running operations,
     * network calls, or heavy disk I/O, you should kick off a new
     * thread, or use {@link android.os.AsyncTask}.</p>
     *
     * @param intent The Intent supplied to {@link android.content.Context#startService}, 
     * as given.  This may be null if the service is being restarted after
     * its process has gone away, and it had previously returned anything
     * except {@link #START_STICKY_COMPATIBILITY}.
     * @param flags Additional data about this start request.
     * @param startId A unique integer representing this specific request to 
     * start.  Use with {@link #stopSelfResult(int)}.
     * 
     * @return The return value indicates what semantics the system should
     * use for the service's current started state.  It may be one of the
     * constants associated with the {@link #START_CONTINUATION_MASK} bits.
     * 
     * @see #stopSelfResult(int)
     */
    public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
        onStart(intent, startId);
        // mStartCompatibility 标记默认是 false , 因此其返回值默认是 START_STICKY
        return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
    }

    /**
     * @hide
     */
    @UnsupportedAppUsage
    public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
        attachBaseContext(context);
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;

        setContentCaptureOptions(application.getContentCaptureOptions());
    }

    @UnsupportedAppUsage
    private boolean mStartCompatibility = false;

onStartCommand 的返回值有以下几种情况 :

  • Service.START_STICKY_COMPATIBILITY
  • Service.START_STICKY
  • Service.START_NOT_STICKY
  • Service.START_REDELIVER_INTENT

2、 onStartCommand 函数 START_STICKY_COMPATIBILITY 返回值

Service.START_STICKY_COMPATIBILITY : START_STICKY 兼容版本 , onStartCommand 方法返回该返回值时 , 不能保证服务可以重启 ;

    /**
     * Constant to return from {@link #onStartCommand}: compatibility
     * version of {@link #START_STICKY} that does not guarantee that
     * {@link #onStartCommand} will be called again after being killed.
     */
    public static final int START_STICKY_COMPATIBILITY = 0;

3、 onStartCommand 函数 START_STICKY 返回值

Service.START_STICKY : onStartCommand 方法返回该 START_STICKY 返回值时 , 如果在执行 onStartCommand 后 , 如果 Service 服务进程被杀掉 , 系统会保留 Service 状态 , 但是不保留启动服务的 Intent ; 之后系统会尝试重新创建该 Service 服务 ; ( 更详细的信息查看下方的源码注释 )

    /**
     * Constant to return from {@link #onStartCommand}: if this service's
     * process is killed while it is started (after returning from
     * {@link #onStartCommand}), then leave it in the started state but
     * don't retain this delivered intent.  Later the system will try to
     * re-create the service.  Because it is in the started state, it will
     * guarantee to call {@link #onStartCommand} after creating the new
     * service instance; if there are not any pending start commands to be
     * delivered to the service, it will be called with a null intent
     * object, so you must take care to check for this.
     * 
     * <p>This mode makes sense for things that will be explicitly started
     * and stopped to run for arbitrary periods of time, such as a service
     * performing background music playback.
     */
    public static final int START_STICKY = 1;

4、 onStartCommand 函数 START_NOT_STICKY 返回值

Service.START_NOT_STICKY : " 非粘性 " , onStartCommand 方法返回该返回值时 , 如果在执行 onStartCommand 后 , 服务被杀死 , 系统不会重启 Service 服务 ;

    /**
     * Constant to return from {@link #onStartCommand}: if this service's
     * process is killed while it is started (after returning from
     * {@link #onStartCommand}), and there are no new start intents to
     * deliver to it, then take the service out of the started state and
     * don't recreate until a future explicit call to
     * {@link Context#startService Context.startService(Intent)}.  The
     * service will not receive a {@link #onStartCommand(Intent, int, int)}
     * call with a null Intent because it will not be restarted if there
     * are no pending Intents to deliver.
     * 
     * <p>This mode makes sense for things that want to do some work as a
     * result of being started, but can be stopped when under memory pressure
     * and will explicit start themselves again later to do more work.  An
     * example of such a service would be one that polls for data from
     * a server: it could schedule an alarm to poll every N minutes by having
     * the alarm start its service.  When its {@link #onStartCommand} is
     * called from the alarm, it schedules a new alarm for N minutes later,
     * and spawns a thread to do its networking.  If its process is killed
     * while doing that check, the service will not be restarted until the
     * alarm goes off.
     */
    public static final int START_NOT_STICKY = 2;

5、 onStartCommand 函数 START_REDELIVER_INTENT 返回值

Service.START_REDELIVER_INTENT : 重传 Intent ; onStartCommand 方法返回该返回值时 , 如果在执行 onStartCommand 后 , 服务被杀死 , 系统会自动重启 , 并传入 Intent 值 , 不会传入 null ;

    /**
     * Constant to return from {@link #onStartCommand}: if this service's
     * process is killed while it is started (after returning from
     * {@link #onStartCommand}), then it will be scheduled for a restart
     * and the last delivered Intent re-delivered to it again via
     * {@link #onStartCommand}.  This Intent will remain scheduled for
     * redelivery until the service calls {@link #stopSelf(int)} with the
     * start ID provided to {@link #onStartCommand}.  The
     * service will not receive a {@link #onStartCommand(Intent, int, int)}
     * call with a null Intent because it will only be restarted if
     * it is not finished processing all Intents sent to it (and any such
     * pending events will be delivered at the point of restart).
     */
    public static final int START_REDELIVER_INTENT = 3;

二、 系统 Service 机制拉活


根据上述 onStartCommand 方法返回值分析 , 只要返回值是 START_STICKY , 那么被杀掉的应用就会被重新拉起 ;

1、 Service 代码

package kim.hsl.keep_progress_alive.stick_service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class StickService extends Service {
    public StickService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
}

2、 清单配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kim.hsl.keep_progress_alive">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Keep_Progress_Alive">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!--
            设置最近任务列表中不显示该 Activity 组件 ( 不要被用户察觉 )
            android:excludeFromRecents="true"

            设置 Activity 亲和性
            让该界面在一个独立的任务栈中 , 不要与本应用的其它任务栈放在一起
            避免解除锁屏后 , 关闭 1 像素界面 , 将整个任务栈都唤醒
            android:taskAffinity="kim.hsl.keep_progress_alive.alive"
        -->
        <activity
            android:name=".one_pixel_activity.OnePixelActivity"
            android:excludeFromRecents="true"
            android:taskAffinity="kim.hsl.keep_progress_alive.onepixel"
            android:theme="@style/OnePixelActivityTheme" />

        <!-- 用于提权的前台进程 -->
        <service
            android:name=".foreground_service.ForegroundService"
            android:enabled="true"
            android:exported="true" />

        <!-- 用于提权的前台进程, 关闭通知操作 -->
        <service
            android:name=".foreground_service.CancelNotificationService"
            android:enabled="true"
            android:exported="true" />

        <!-- 系统 Service 机制拉活 -->
        <service
            android:name=".stick_service.StickService"
            android:enabled="true"
            android:exported="true" />

    </application>

</manifest>

3、启动服务

package kim.hsl.keep_progress_alive;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;

import kim.hsl.keep_progress_alive.foreground_service.ForegroundService;
import kim.hsl.keep_progress_alive.one_pixel_activity.KeepProgressAliveManager;
import kim.hsl.keep_progress_alive.stick_service.StickService;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1.  1 像素 Activity 提升应用权限
        // 注册广播接收者 , 1 像素 Activity 启动的 广播接收者
        //KeepProgressAliveManager.getmInstance().registerReceiver(this);

        // 2. 通过前台 Service 提升应用权限
        // 启动普通 Service , 但是在该 Service 的 onCreate 方法中执行了 startForeground
        // 变成了前台 Service 服务
        //startService(new Intent(this, ForegroundService.class));

        // 3. 使用 Service 机制拉活
        startService(new Intent(this, StickService.class));


    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // 1. 取消注册广播接收者, 也可以不取消注册
        //KeepProgressAliveManager.getmInstance().registerReceiver(this);
    }
}

三、 测试效果


程序正常启动 , 运行正常 ,

查询 oom_adj 值 ,

0

, 前台进程 ;

在手机中手动杀掉进程 , 杀掉进程后 , 又重新启动了一个相同进程 , 进程号改变了 ;

( 测试时没有抓到两个进程同框的画面 , 只截取了下面一张图 )

拉起后是个后台进程 , 任务栈中看不到 ;

四、 系统 Service 机制拉活总结


系统 Service 机制拉活 , 不是 100% 有效 , 有一定成功几率 ;

有些机型 ROM , 拉活无效 ; 测试的 Google Pixel2 Android 10 可以拉活 ; 有相当大的一部分手机不支持该 Service 机制拉活 ;

( 是否支持 , 与系统有关 , 与手机厂商有关 )

每次杀掉 Service 所在应用进程 , 重启都比上一次慢 , 大约杀掉几次进程后 ( 5 次内 ) , 系统就不再拉起该应用 ;

五、 源码资源


源码资源 :