美文网首页Android开发经验谈Android开发Android开发
Android源码解析之IntentService(四)

Android源码解析之IntentService(四)

作者: 大虾啊啊啊 | 来源:发表于2018-08-22 15:54 被阅读11次

    前言

    上一章我们介绍了HandlerThread组件可以使得子线程可以发消息给主线程,甚至线程之间也可以通信。那么本章要讲的IntentService也正是这个原理。
    本章先讲解IntentService的简单使用,然后在从源码的角度对IntentService进行分析。

    1.什么是IntentService,IntentService有什么作用?

     * @see android.support.v4.app.JobIntentService
     * @see android.os.AsyncTask
     */
    public abstract class IntentService extends Service {
    

    我们看到IntentService这个类,发现了IntentService继承了Service。所以说IntentService就是一个Service。Service我们都知道是安卓的四大组件之一,主要作用就是执行一些后台任务。和Activity不一样的是Service是看不见的,无法和用户交互。但是和Activity一样的是,当没有开启子线程的时候,执行的任务都是在UI线程中执行的。当我们在UI线程执行耗时任务的时候,会导致UI线程堵塞,接着会卡顿。所以IntentService正是用来解决这个问题,也就是说,IntentService执行的后台任务是在子线程中执行的。这样一来就不会导致UI线程堵塞。

    2.IntentService的简单使用

    public class MyIntentService extends IntentService {
        private static final String TAG ="MyIntentService" ;
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e(TAG, "onCreate: ");
        }
        @Override
        public void onStart(@Nullable Intent intent, int startId) {
            super.onStart(intent, startId);
            Log.e(TAG, "onStart: " );
        }
        @Override
        public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
            Log.e(TAG, "onStartCommand: " );
            return super.onStartCommand(intent, flags, startId);
        }
        /**
         * 刷新UI接口回调
         */
        public interface UpdateUi {
            void update(String url);
        }
        private static UpdateUi mUpdateUi;
    
        public static void updateUi(UpdateUi updateUi) {
            mUpdateUi = updateUi;
        }
        public MyIntentService() {
            super("MyIntentService");
        }
        @Override
        protected void onHandleIntent(@Nullable Intent intent) {
            String url = intent.getStringExtra("url");
            String result = downLoadPhoto(url);
            mUpdateUi.update(result);
        }
    
        /**
         * 在这里执行后台任务
         * @param url
         * @return
         */
        private String downLoadPhoto(String url) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "下载成功:"+url;
        }
    }
    

    首先我们新建一个MyIntentService 类继承IntentService 类并重写Service的生命周期方法onCreate、onDestroy、onStart、onStartCommand。以及最重要的onHandleIntent方法。重写生命周期方法主要是为了观察生命周期的变化。创建完MyIntentService 类之后,我们来看下具体的使用:

    public class MainActivity extends AppCompatActivity implements MyIntentService.UpdateUi {
        private static final String TAG = "MainActivity";
        private TextView tvText;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            tvText = (TextView) findViewById(R.id.tv_text);
            final Intent intent = new Intent(this, MyIntentService.class);
            tvText.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    for (int i = 0; i <5 ; i++) {
                        Log.e(TAG, "点击事件的线程:" + Thread.currentThread().getName());
                        intent.putExtra("url", "http://baidu.com");
                        startService(intent);
                    }
    
                }
            });
            tvText.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    Log.e(TAG, "onLongClick: 长按事件...");
                    return false;
                }
            });
            MyIntentService.updateUi(this);
        }
        @Override
        public void update(String url) {
            Log.e(TAG, "返回结果的线程:" + Thread.currentThread().getName());
            //Log.e(TAG, "update: " + url);
            Message msg = Message.obtain();
            msg.obj = url;
            handler.sendMessage(msg);
        }
        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                String url = (String) msg.obj;
                //Log.e(TAG, "处理后的结果: "+url );
                Log.e(TAG, "处理后的结果所在线程: "+Thread.currentThread().getName() );
            }
        };
    }
    

    使用方法很简单,首先创建Intent对象

           final Intent intent = new Intent(this, MyIntentService.class);
    

    在TextView的点击事件中我们循环了5次,调用startService(intent);

       tvText.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    for (int i = 0; i <5 ; i++) {
                        Log.e(TAG, "点击事件的线程:" + Thread.currentThread().getName());
                        intent.putExtra("url", "http://baidu.com");
                        startService(intent);
                    }
    
                }
            });
    

    执行了5次任务,我们来观察IntentService的生命周期的变化

    08-22 15:21:39.008 4735-4735/com.mujin.keji.myapplication E/MyIntentService: onCreate: 
        onStartCommand: 
        onStart: 
        onStartCommand: 
        onStart: 
        onStartCommand: 
        onStart: 
        onStartCommand: 
        onStart: 
        onStartCommand: 
        onStart: 
    08-22 15:21:44.014 4735-4735/com.mujin.keji.myapplication E/MyIntentService: onDestroy: 
    

    onCreat、onDestroy只调用了一次,onStartCommand和onStart调用了5次,也就是说, startService(intent);一次就执行了一次任务,每次执行的后台任务都在同一个Service中。当全部任务执行完毕才会把Service销毁掉。接着会回调到
    IntenetService中最重要的方法onHandleIntent:

     @Override
        protected void onHandleIntent(@Nullable Intent intent) {
            String url = intent.getStringExtra("url");
            String result = downLoadPhoto(url);
            mUpdateUi.update(result);
        }
    

    在onHandleIntent方法中我们拿到intent对象,这个对象也就是在Activity中startService(intent)的那个intent。
    在这个方法里我们可以执行相应的后台任务。在这里最重要的区别就是onHandleIntent方法执行的任务是在子线程中执行的。而startService(intent)是在UI线程中执行的。我们把onHandleIntent方法处理后的结果回调到Activity的update方法,我们把线程的名字打印出来:

     @Override
        public void update(String url) {
            Log.e(TAG, "返回结果的线程:" + Thread.currentThread().getName());
            //Log.e(TAG, "update: " + url);
            Message msg = Message.obtain();
            msg.obj = url;
            handler.sendMessage(msg);
        }
       @Override
        public void update(String url) {
            Log.e(TAG, "返回结果的线程:" + Thread.currentThread().getName());
            //Log.e(TAG, "update: " + url);
            Message msg = Message.obtain();
            msg.obj = url;
            handler.sendMessage(msg);
        }
        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                String url = (String) msg.obj;
                //Log.e(TAG, "处理后的结果: "+url );
                Log.e(TAG, "处理后的结果所在线程: "+Thread.currentThread().getName() );
            }
        };
    
    08-22 15:29:07.428 4807-4831/com.mujin.keji.myapplication E/MainActivity: 返回结果的线程:IntentService[MyIntentService]
    08-22 15:29:07.429 4807-4807/com.mujin.keji.myapplication E/MainActivity: 处理后的结果所在线程: main
    08-22 15:29:08.432 4807-4831/com.mujin.keji.myapplication E/MainActivity: 返回结果的线程:IntentService[MyIntentService]
    08-22 15:29:08.432 4807-4807/com.mujin.keji.myapplication E/MainActivity: 处理后的结果所在线程: main
    08-22 15:29:09.432 4807-4831/com.mujin.keji.myapplication E/MainActivity: 返回结果的线程:IntentService[MyIntentService]
    08-22 15:29:09.432 4807-4807/com.mujin.keji.myapplication E/MainActivity: 处理后的结果所在线程: main
    08-22 15:29:10.435 4807-4831/com.mujin.keji.myapplication E/MainActivity: 返回结果的线程:IntentService[MyIntentService]
    08-22 15:29:10.435 4807-4807/com.mujin.keji.myapplication E/MainActivity: 处理后的结果所在线程: main
    08-22 15:29:11.436 4807-4831/com.mujin.keji.myapplication E/MainActivity: 返回结果的线程:IntentService[MyIntentService]
    08-22 15:29:11.437 4807-4807/com.mujin.keji.myapplication E/MainActivity: 处理后的结果所在线程: main
    

    我们通过Handler把update的结果发送到主线程,我们打印两个地方的线程名字。你会发现在IntentService中执行的任务是在名字为MyIntentService的线程中执行的。也就是在IntenetService中我们自定义的名字,由此可知在startService的时候,我们开启了一个线程。

    public MyIntentService() {
            super("MyIntentService");
        }
    

    而通过Handler处理的任务是在UI(名字为Main)线程。
    综上可知我们就能知道,在我们调用startService(intent)的时候。IntentService会回调onHandleIntent方法,在这个方法中执行的任务是在子线程中执行的。我们可以通过Handler把消息发送到主线程,主线程再做相应的操作。这样一来我们就完成了在子线程中执行后台任务。避免UI线程的堵塞。

    3.IntentService的源码分析

    我们首先看下IntentService的onCreat方法:

      @Override
        public void onCreate() {
            // TODO: It would be nice to have an option to hold a partial wakelock
            // during processing, and to have a static startService(Context, Intent)
            // method that would launch the service & hand off a wakelock.
    
            super.onCreate();
            HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
            thread.start();
    
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
        }
    

    在onCreat方法中,我们创建了HandlerThread 对象,并调用start()方法。开启一个子线程。获取Looper对象,然后创建处理消息的ServiceHandler mServiceHandler 对象。所以onCreat方法调用完之后,其实IntentService开启了一个子线程,并为当前线程设置了轮询机制。
    接着再看onStart和onStartCommand方法。

     public void onStart(@Nullable Intent intent, int startId) {
            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            msg.obj = intent;
            mServiceHandler.sendMessage(msg);
        }
    
        /**
         * You should not override this method for your IntentService. Instead,
         * override {@link #onHandleIntent}, which the system calls when the IntentService
         * receives a start request.
         * @see android.app.Service#onStartCommand
         */
        @Override
        public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
            onStart(intent, startId);
            return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
        }
    

    我们在上面调用startService(intent)的时候我们知道,调用了5次这两个方法就调用了5次。我们看到onStart方法,每次调用都创建了一条消息,并把intent对象设置到消息里面。然后调用mServiceHandler.sendMessage(msg);方法,发送消息。接着我们继续看mServiceHandler的实现。

      private final class ServiceHandler extends Handler {
            public ServiceHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                onHandleIntent((Intent)msg.obj);
                stopSelf(msg.arg1);
            }
        }
    

    看到这我们应该一目了然,在handleMessage方法中,调用了onHandleIntent方法

       @WorkerThread
        protected abstract void onHandleIntent(@Nullable Intent intent);
    

    并且我们看到onHandleIntent方法是一个空方法,onHandleIntent就是由我们来实现的。
    所以很容易得出结论就是:
    在我们启动IntenetService的时候调用了startService(intent)方法,IntenetService内部会创建了HandlerThred和Handler机制。HandlerThred用来开启子线程并为当前线程设置Looper,轮询消息。Handler用来处理对应的消息并回调到onHandleIntent方法。我们在开发的时候便可以在onHandleIntent方法执行相应的任务,此时此刻的任务是在子线程中执行的。当任务执行完毕之后会调用onDestroy方法。我们不用手动关闭Service。

    相关文章

      网友评论

        本文标题:Android源码解析之IntentService(四)

        本文链接:https://www.haomeiwen.com/subject/ylfyiftx.html