Android 认知与理解Service(二)

Android Service 基础知识点

IntentService


概念

IntentService是Android里面的一个封装类,继承自四大组件之一的Service

特性

  1. 在后台执行耗时的异步任务,当任务完成后会自动销毁
  2. 拥有较高的优先级,不易被系统杀死(继承自Service的缘故),因此比较适合执行一些高优先级的异步任务
  3. 内部通过HandlerThread和Handler实现异步操作
  4. 创建IntentService时,只需实现onHandleIntent和构造方法,onHandleIntent为异步方法,可以执行耗时操作

由来

说了那么多,感觉IntentService与Service的区别主要是能实现异步操作,因为Service是默认运行在主线程中的,不能做一些耗时操作,否则容易ANR。而 IntentService 则是直接在子线程中工作的。另外当我们使用 startService(Intent) 方法启动 IntentService 后,IntentService 就会在工作线程中处理每一个 Intent ,并且在完成这些任务后停止它自己。


常规使用,如何使用?

需要继承 IntentService ,并提供一个构造函数,并且必须在其内部调用父类的有参构造函数,然后重写 onHandleIntent(Intent intent)方法,在 onHandleIntent 方法中可以去处理一些具体的业务逻辑,而且不用担心ANR的问题,因为这个方法已经是在子线程中运行了

MyIntentService的实例代码如下:

public class MyIntentService extends IntentService {

    private final String TAG = "MyIntentService";

    public MyIntentService() {
        super("MyIntentService");
        Log.e(TAG,"MyIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG,"onCreate");
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.e(TAG, "onHandleIntent intent = " + intent.getStringExtra("info"));
        Log.e(TAG, "Thread id = " + Thread.currentThread().getId()+" Thread name = "+ Thread.currentThread().getName());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy");
    }
}

在Activity中开启代码如下:

    Intent intent = new Intent(MyActivity.this, MyIntentService.class);
    intent.putExtra("info","threadInfo");
    startService(intent);

开启后关闭然后再次打开,打印日志如下:

    2021-06-09 16:31:47.274 5851-5851 E/MyActivity: Thread id = 1 Thread name = main
    2021-06-09 16:31:47.277 5851-5851 E/MyIntentService: MyIntentService
    2021-06-09 16:31:47.279 5851-5851 E/MyIntentService: onCreate
    2021-06-09 16:31:47.281 5851-5851 E/MyIntentService: onStartCommand
    2021-06-09 16:31:47.285 5851-5909 E/MyIntentService: onHandleIntent intent = threadInfo
    2021-06-09 16:31:47.285 5851-5909 E/MyIntentService: Thread id = 131 Thread name = IntentService[MyIntentService]
    2021-06-09 16:31:47.286 5851-5851 E/MyIntentService: onDestroy
    
    2021-06-09 16:31:50.951 5851-5851 E/MyIntentService: MyIntentService
    2021-06-09 16:31:50.952 5851-5851 E/MyIntentService: onCreate
    2021-06-09 16:31:50.952 5851-5851 E/MyIntentService: onStartCommand
    2021-06-09 16:31:50.952 5851-5916 E/MyIntentService: onHandleIntent intent = threadInfo
    2021-06-09 16:31:50.953 5851-5916 E/MyIntentService: Thread id = 132 Thread name = IntentService[MyIntentService]
    2021-06-09 16:31:50.963 5851-5851 E/MyIntentService: onDestroy 

日志总结

  1. Activity(1)线程与MyIntentService(131)不在同一线程
  2. onHandleIntent打印完日志后很快就执行了onDestroy,并且我们没有调用stopService,说明是完成了任务自动销毁的
  3. onHandleIntent(@Nullable Intent intent)里面的intent是可以获取传递过来的Extra数据的,可以根据这个判断请求去分别做什么事
  4. 每次IntentService结束后再startService都会开启一个与上次不同的线程去处理请求(131/132)

IntentService 用来做耗时操作,如何使用?

MyIntentService中代码实例如下:

public class MyIntentService extends IntentService {

    private final String TAG = "MyIntentService";

    private LocalBroadcastManager mBroadcastManager;

    private int progress = 0;
    private int progress_again = 0;

    public MyIntentService() {
        super("MyIntentService");
        Log.e(TAG, "MyIntentService");
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        super.onStart(intent, startId);
        Log.e(TAG, "onStart" + " startId = " + startId);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate");
        mBroadcastManager = LocalBroadcastManager.getInstance(this);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.e(TAG, "onHandleIntent intent = " + intent.getStringExtra("info"));
        Log.e(TAG, "Thread id = " + Thread.currentThread().getId() + " Thread name = " + Thread.currentThread().getName());
        String info = intent.getStringExtra("info");
        if (TextUtils.isEmpty(info)) {
            return;
        }
        switch (info) {
            case "thread1":
                boolean isRunning = true;
                while (isRunning) {
                    progress++;
                    if (progress >= 5) {
                        isRunning = false;
                    }
                    sendThreadStatus(Constants.ACTION_ONE, progress);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case "thread2":
                boolean isRunning_again = true;
                while (isRunning_again) {
                    progress_again++;
                    if (progress_again >= 5) {
                        isRunning_again = false;
                    }
                    sendThreadStatus(Constants.ACTION_TWO, progress_again);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }


    // 发送线程结果
    private void sendThreadStatus(String action, int progress) {
        Log.e(TAG, "action=" + action + ",progress=" + progress);
        Intent intent = new Intent(action);
        intent.putExtra("progress", progress);
        mBroadcastManager.sendBroadcast(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy");
    }
}

Activity中的实例代码如下:

public class MyActivity extends AppCompatActivity {

    private static String TAG = "MyActivity";

    private TextView mTvMsg;
    private TextView mTvMsgAgain;

    private LocalBroadcastManager mBroadcastManager;
    private MyBroadCast mBroadCast;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        mTvMsg = (TextView) findViewById(R.id.msg);
        mTvMsgAgain = (TextView) findViewById(R.id.msg2);
        
        mBroadcastManager = LocalBroadcastManager.getInstance(this);
        mBroadCast = new MyBroadCast();
        Log.e(TAG, "thread id = " + Thread.currentThread().getId());
        Log.e(TAG, "thread name =" + Thread.currentThread().getName());

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Constants.ACTION_ONE);
        intentFilter.addAction(Constants.ACTION_TWO);
        mBroadcastManager.registerReceiver(mBroadCast, intentFilter);

        findViewById(R.id.startService).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MyActivity.this, MyIntentService.class);
                intent.putExtra("info","thread1");
                startService(intent);
            }
        });

        findViewById(R.id.stopService).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MyActivity.this, MyIntentService.class);
                stopService(intent);
            }
        });

        findViewById(R.id.startServiceAgain).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MyActivity.this, MyIntentService.class);
                intent.putExtra("info","thread2");
                startService(intent);
            }
        });
    }

    public class MyBroadCast extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            String action = intent.getAction();
            if (action != null) {
                switch (action) {
                    case Constants.ACTION_ONE:
                        int progress = intent.getIntExtra("progress", 0);
                        mTvMsg.setText(String.format(Locale.getDefault(), "ACTION_THREAD 当前进度 = %S", progress));
                        break;
                    case Constants.ACTION_TWO:
                        int progress_again = intent.getIntExtra("progress", 0);
                        mTvMsgAgain.setText(String.format(Locale.getDefault(), "ACTION_THREAD_AGAIN 当前进度 = %S", progress_again));
                        break;
                }
            }
        }
    }
}

日志如下:

    //点击开启服务后
    2021-06-11 11:22:04.833 9200-9200/ E/MyActivity: thread id = 1
    2021-06-11 11:22:04.833 9200-9200/ E/MyActivity: thread name =main
    2021-06-11 11:22:06.851 9200-9200/ E/MyIntentService: MyIntentService
    2021-06-11 11:22:06.852 9200-9200/ E/MyIntentService: onCreate
    2021-06-11 11:22:06.852 9200-9200/ E/MyIntentService: onStartCommand
    2021-06-11 11:22:06.853 9200-9229/ E/MyIntentService: onHandleIntent intent = thread1
    2021-06-11 11:22:06.853 9200-9229/ E/MyIntentService: Thread id = 150 Thread name = IntentService[MyIntentService]
    2021-06-11 11:22:06.853 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=1
    2021-06-11 11:22:08.854 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=2
    
    //点击再次开启服务
    2021-06-11 11:22:10.620 9200-9200/ E/MyIntentService: onStartCommand
    2021-06-11 11:22:10.856 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=3
    2021-06-11 11:22:12.857 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=4
    
    //第二次点击再次开启服务
    2021-06-11 11:22:13.044 9200-9200/ E/MyIntentService: onStartCommand
    2021-06-11 11:22:14.858 9200-9229/ E/MyIntentService: action=com.wang.thread,progress=5
    2021-06-11 11:22:16.860 9200-9229/ E/MyIntentService: onHandleIntent intent = thread2
    2021-06-11 11:22:16.860 9200-9229/ E/MyIntentService: Thread id = 150 Thread name = IntentService[MyIntentService]
    2021-06-11 11:22:16.860 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=1
    2021-06-11 11:22:18.861 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=2
    2021-06-11 11:22:20.861 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=3
    
    //点击停止服务
    2021-06-11 11:22:21.390 9200-9200/ E/MyIntentService: onDestroy
    2021-06-11 11:22:22.862 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=4
    2021-06-11 11:22:24.864 9200-9229/ E/MyIntentService: action=com.wang.thread2,progress=5

日志总结如下

  1. 开启服务后,任务如果没有执行完,也就是onHandleIntent没有处理完成,将不会重新创建onCreate只会调用onStartCommand,将请求放入队列中等待执行
  2. Thread id 都是150说明是同一个线程在运行,只是在处理不同的任务,排队按顺序执行
  3. 如果执行最后一个请求时,调用stopService任务(onDestroy->progress=4->progress=5)还是会继续执行完毕
  4. 故IntentService比较适用于单线程操作不耗时任务,不支持多并发的操作

源码解析

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
        
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            //处理任务
            onHandleIntent((Intent)msg.obj);
            //处理完后销毁stopSelf 
            //注意:这里不是立马就去销毁而是去判断还有没有消息没有了才去销毁
            stopSelf(msg.arg1);
        }
    }
    public IntentService(String name) {
        super();
        mName = name;
    }
    //设置Intent是否重投递,如果重投递,在onHandleIntent方法返回前,
    //如果进程死了,会重启进程,重新分发Intent
    //但是只会分发最近的一个Intent
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        //通过实例化HandlerThreade新建线程&启动
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        //获取工程线程的Looper&维护自己的工作队列
        mServiceLooper = thread.getLooper();
        //新建ServiceHandler,绑定上面的Looper
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //获取ServiceHandler消息的引用
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //发送消息,添加到消息队列
        mServiceHandler.sendMessage(msg);
    }
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        //调用onStart()方法
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        //Looper停止
        mServiceLooper.quit();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

IntentService的源码不复杂,我们来总结看看它做了些什么?

  • 当IntentService被创建时,就初始化了一个HandlerThread线程,故使用IntentService时,不需要额外新建线程。
  • HandlerThread继承自Thread,内部封装了Looper,创建了一个ServiceHandler任务将会被传入mServiceLooper中执行。
  • onStartCommand()中去调用onStart()方法,onStart()中获取ServiceHandler发送消息,添加到消息队列
  • 最终在ServiceHandler中去处理任务onHandleIntent并调用stopSelf去判断还没有消息需要去处理销毁

这里的stopSelf我们继续往下看:

    //Service.java
    public final void stopSelf(int startId) {
        if (mActivityManager == null) {
            return;
        }
        try {
            mActivityManager.stopServiceToken(
                    new ComponentName(this, mClassName), mToken, startId);
        } catch (RemoteException ex) {
        }
    }
    //ActivityManagerService.java    
    @Override
    public boolean stopServiceToken(ComponentName className, IBinder token,
            int startId) {
        synchronized(this) {
            return mServices.stopServiceTokenLocked(className, token, startId);
        }
    }
    //ActiveServices.java
    boolean stopServiceTokenLocked(ComponentName className, IBinder token,
            int startId) {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "stopServiceToken: " + className
                + " " + token + " startId=" + startId);
        ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
        if (r != null) {
            if (startId >= 0) {
                // Asked to only stop if done with all work.  Note that
                // to avoid leaks, we will take this as dropping all
                // start items up to and including this one.
                ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
                if (si != null) {
                    while (r.deliveredStarts.size() > 0) {
                        ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
                        cur.removeUriPermissionsLocked();
                        if (cur == si) {
                            break;
                        }
                    }
                }

                if (r.getLastStartId() != startId) {
                    return false;
                }

                if (r.deliveredStarts.size() > 0) {
                    Slog.w(TAG, "stopServiceToken startId " + startId
                            + " is last, but have " + r.deliveredStarts.size()
                            + " remaining args");
                }
            }

            synchronized (r.stats.getBatteryStats()) {
                r.stats.stopRunningLocked();
            }
            r.startRequested = false;
            if (r.tracker != null) {
                r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
                        SystemClock.uptimeMillis());
            }
            r.callStart = false;
            final long origId = Binder.clearCallingIdentity();
            bringDownServiceIfNeededLocked(r, false, false);
            Binder.restoreCallingIdentity(origId);
            return true;
        }
        return false;
    }

StopSelf方法调用停止服务,那么多个任务来临时,后面的任务岂不是不会被执行了?

并不是的,stopSelf方法调用时传入了一个startId,这个startId是在每次startService时都会往onStartCommand传入一个新值。调用stopSelf,如果传入的是一个大于0的值,会判断该值是否等于最后一次传入的startId(r.getLastStartId() != startId) ,如果是最后一个Service会走到onDestory流程,如果不是则Service仍然存活)

每处理一个请求就调用一次stopSelf(msg.arg1),这个参数是onStartCommand方法第三个参数,并不是真的销毁自己,会判断如果ServiceLooper的队列里还有消息,就不会销毁自己

如果开启了多个请求,如A,B,C三个请求,那就startService3次,如果在处理A请求调用了stopService,那A请求会继续执行完,但是B和C请求就不会执行;如果在执行C请求时候调用了stopService,C请求还是会执行完

原因:

    //只要调用stopService后,后续请求就不会得到执行,因为消息队列清空了
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

IntentService与Service的区别总结:

  • Service是依附于主线程的(远程服务除外),直接做耗时操作会阻塞主线程;而IntentService是工作在工作线程,对主线程没影响
  • 使用IntentService不需要像在Service里自己手动开启线程去处理耗时请求
  • Service是需要手动调用stopService来销毁,而IntentService是自动销毁,不用担心内存泄露
  • IntentService内部采用了HandleThread和Handler的实现,IntentService又比线程优先级高,相对而言不容易被系统杀死来保证服务运行。

附加:

在Android 8.0 (API level 26) 也就是Android O上用JobIntentService替代IntentService


JobIntentService与IntentService都是可以用来执行后台任务,在Android O之前,使用起来效果没多大差别,但是Android O以后JobIntentService不会立即执行,等手机进入一定状态后才会执行任务,所以不能用来执行及时的后台任务,感兴趣的可以去看看,这里不做过多介绍。

结语:感谢各位大佬的分享,如有错误的需要改进的地方,请留言评论指出,谢谢!

参考文章

  • https://blog.csdn.net/qq_30993595/article/details/78452064
  • https://juejin.cn/post/6962407237784436750
  • https://juejin.cn/post/6844903847027015693

你可能感兴趣的:(Android 认知与理解Service(二))