美文网首页Android技术点
Android IntentService

Android IntentService

作者: 撑起头顶的天 | 来源:发表于2018-02-04 09:15 被阅读5次

    特点

    • 它本质是一种特殊的Service,继承自Service并且本身就是一个抽象类
    • 它可以用于在后台执行耗时的异步任务,当任务完成后会自动停止
    • 它拥有较高的优先级,不易被系统杀死(继承自Service的缘故),因此比较适合执行一些高优先级的异步任务
    • 它内部通过HandlerThread和Handler实现异步操作
    • 创建IntentService时,只需实现onHandleIntent和构造方法,onHandleIntent为异步方法,可以执行耗时操作

    使用实例

    public  class MyIntentService extends IntentService {
        public static final String DOWNLOAD_URL="download_url";
        public static final String INDEX_FLAG="index_flag";
        public static UpdateUI updateUI;
    
    
        public static void setUpdateUI(UpdateUI updateUIInterface){
            updateUI=updateUIInterface;
        }
    
        public MyIntentService(){
            super("MyIntentService");
        }
    
        /**
         * 实现异步任务的方法
         * @param intent Activity传递过来的Intent,数据封装在intent中
         */
        @Override
        protected void onHandleIntent(Intent intent) {
    
            //在子线程中进行网络请求
            Bitmap bitmap=downloadUrlBitmap(intent.getStringExtra(DOWNLOAD_URL));
            Message msg1 = new Message();
            msg1.what = intent.getIntExtra(INDEX_FLAG,0);
            msg1.obj =bitmap;
            //通知主线程去更新UI
            if(updateUI!=null){
                updateUI.updateUI(msg1);
            }
            //mUIHandler.sendMessageDelayed(msg1,1000);
    
            LogUtils.e("onHandleIntent");
        }
        //----------------------重写一下方法仅为测试------------------------------------------
        @Override
        public void onCreate() {
            LogUtils.e("onCreate");
            super.onCreate();
        }
    
        @Override
        public void onStart(Intent intent, int startId) {
            super.onStart(intent, startId);
            LogUtils.e("onStart");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            LogUtils.e("onStartCommand");
            return super.onStartCommand(intent, flags, startId);
    
        }
    
        @Override
        public void onDestroy() {
            LogUtils.e("onDestroy");
            super.onDestroy();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            LogUtils.e("onBind");
            return super.onBind(intent);
        }
    
    
        public interface UpdateUI{
            void updateUI(Message message);
        }
    
    
        private Bitmap downloadUrlBitmap(String urlString) {
            HttpURLConnection urlConnection = null;
            BufferedInputStream in = null;
            Bitmap bitmap=null;
            try {
                final URL url = new URL(urlString);
                urlConnection = (HttpURLConnection) url.openConnection();
                in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
                bitmap= BitmapFactory.decodeStream(in);
            } catch (final IOException e) {
                e.printStackTrace();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                try {
                    if (in != null) {
                        in.close();
                    }
                } catch (final IOException e) {
                    e.printStackTrace();
                }
            }
            return bitmap;
        }
    
    }
    

    通过代码可以看出,我们继承了IntentService,这里有两个方法是必须实现的,一个是构造方法,必须传递一个线程名称的字符串,另外一个就是进行异步处理的方法onHandleIntent(Intent intent) 方法,其参数intent可以附带从activity传递过来的数据。这里我们的案例主要利用onHandleIntent实现异步下载图片,然后通过回调监听的方法把下载完的bitmap放在message中回调给Activity(当然也可以使用广播完成),最后通过Handler去更新UI。下面再来看看Acitvity的代码:

    public class IntentServiceActivity extends Activity implements MyIntentService.UpdateUI{
        /**
         * 图片地址集合
         */
        private String url[] = {
                "http://img.blog.csdn.net/20160903083245762",
                "http://img.blog.csdn.net/20160903083252184",
                "http://img.blog.csdn.net/20160903083257871",
                "http://img.blog.csdn.net/20160903083257871",
                "http://img.blog.csdn.net/20160903083311972",
                "http://img.blog.csdn.net/20160903083319668",
                "http://img.blog.csdn.net/20160903083326871"
        };
    
        private static ImageView imageView;
        private static final Handler mUIHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                imageView.setImageBitmap((Bitmap) msg.obj);
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_intent_service);
            imageView = (ImageView) findViewById(R.id.image);
    
            Intent intent = new Intent(this,MyIntentService.class);
            for (int i=0;i<7;i++) {//循环启动任务
                intent.putExtra(MyIntentService.DOWNLOAD_URL,url[i]);
                intent.putExtra(MyIntentService.INDEX_FLAG,i);
                startService(intent);
            }
            MyIntentService.setUpdateUI(this);
        }
    
        //必须通过Handler去更新,该方法为异步方法,不可更新UI
        @Override
        public void updateUI(Message message) {
            mUIHandler.sendMessageDelayed(message,message.what * 1000);
        }
    }
    

    代码比较简单,通过for循环多次去启动IntentService,然后去下载图片,注意即使我们多次启动IntentService,但IntentService的实例只有一个,这跟传统的Service是一样的,最终IntentService会去调用onHandleIntent执行异步任务。这里可能我们还会担心for循环去启动任务,而实例又只有一个,那么任务会不会被覆盖掉呢?其实是不会的,因为IntentService真正执行异步任务的是HandlerThread+Handler,每次启动都会把下载图片的任务添加到依附的消息队列中,最后由HandlerThread+Handler去执行.

    源码解析

    IntentService的onCreate方法:

    @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);
    }
    

    当第一启动IntentService时,它的onCreate方法将会被调用,其内部会去创建一个HandlerThread并启动它,接着创建一个ServiceHandler(继承Handler),传入HandlerThread的Looper对象,这样ServiceHandler就变成可以处理异步线程的执行类了(因为Looper对象与HandlerThread绑定,而HandlerThread又是一个异步线程,我们把HandlerThread持有的Looper对象传递给Handler后,ServiceHandler内部就持有异步线程的Looper,自然就可以执行异步任务了),那么IntentService是怎么启动异步任务的呢?其实IntentService启动后还会去调用onStartCommand方法,而onStartCommand方法又会去调用onStart方法,我们看看它们的源码:

    @Override
    public void onStart(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(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    

    从源码我们可以看出,在onStart方法中,IntentService通过mServiceHandler的sendMessage方法发送了一个消息,这个消息将会发送到HandlerThread中进行处理(因为HandlerThread持有Looper对象,所以其实是Looper从消息队列中取出消息进行处理,然后调用mServiceHandler的handleMessage方法),我们看看ServiceHandler的源码:

    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);
       }
    }
    

    这里其实也说明onHandleIntent确实是一个异步处理方法(ServiceHandler本身就是一个异步处理的handler类),在onHandleIntent方法执行结束后,IntentService会通过 stopSelf(int startId)方法来尝试停止服务。这里采用stopSelf(int startId)而不是stopSelf()来停止服务,是因为stopSelf()会立即停止服务,而stopSelf(int startId)会等待所有消息都处理完后才终止服务。最后看看onHandleIntent方法的声明:

    protected abstract void onHandleIntent(Intent intent);
    

    到此我们就知道了IntentService的onHandleIntent方法是一个抽象方法,所以我们在创建IntentService时必须实现该方法,通过上面一系列的分析可知,onHandleIntent方法也是一个异步方法。这里要注意的是如果后台任务只有一个的话,onHandleIntent执行完,服务就会销毁,但如果后台任务有多个的话,onHandleIntent执行完最后一个任务时,服务才销毁。最后我们要知道每次执行一个后台任务就必须启动一次IntentService,而IntentService内部则是通过消息的方式发送给HandlerThread的,然后由Handler中的Looper来处理消息,而Looper是按顺序从消息队列中取任务的,也就是说IntentService的后台任务时顺序执行的,当有多个后台任务同时存在时,这些后台任务会按外部调用的顺序排队执行,我们前面的使用案例也很好说明了这点。

    参考

    http://blog.csdn.net/javazejian/article/details/52426425

    相关文章

      网友评论

        本文标题:Android IntentService

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