Android IntentService完全解析 当Service遇到Handler

来源:互联网 发布:福建知鱼科技有限公司 编辑:程序博客网 时间:2024/05/01 03:18




http://blog.csdn.net/double2hao/article/details/51073253?ref=myread

http://blog.csdn.net/double2hao/article/details/51073253?ref=myread

http://blog.csdn.net/double2hao/article/details/51073253?ref=myread




 

Android IntentService完全解析 当Service遇到Handler

标签: androidintentService
 77人阅读 评论(0) 收藏 举报
 分类:
 

目录(?)[+]

转载请标明出处: 
http://blog.csdn.net/lmj623565791/article/details/47143563; 
本文出自:【张鸿洋的博客】

一 概述

大家都清楚,在Android的开发中,凡是遇到耗时的操作尽可能的会交给Service去做,比如我们上传多张图,上传的过程用户可能将应用置于后台,然后干别的去了,我们的Activity就很可能会被杀死,所以可以考虑将上传操作交给Service去做,如果担心Service被杀,还能通过设置startForeground(int, Notification)方法提升其优先级。

那么,在Service里面我们肯定不能直接进行耗时操作,一般都需要去开启子线程去做一些事情,自己去管理Service的生命周期以及子线程并非是个优雅的做法;好在Android给我们提供了一个类,叫做IntentService,我们看下注释。

IntentService is a base class for {@link Service}s that handle asynchronous 
requests (expressed as {@link Intent}s) on demand. Clients send requests 
through {@link android.content.Context#startService(Intent)} calls; the 
service is started as needed, handles each Intent in turn using a worker 
thread, and stops itself when it runs out of work.

意思说IntentService是一个基于Service的一个类,用来处理异步的请求。你可以通过startService(Intent)来提交请求,该Service会在需要的时候创建,当完成所有的任务以后自己关闭,且请求是在工作线程处理的。

这么说,我们使用了IntentService最起码有两个好处,一方面不需要自己去new Thread了;另一方面不需要考虑在什么时候关闭该Service了。

好了,那么接下来我们就来看一个完整的例子。

二 IntentService的使用

我们就来演示一个多个图片上传的案例,当然我们会模拟上传的耗时,毕竟我们的重心在IntentService的使用和源码解析上。

首先看下效果图

效果图

每当我们点击一次按钮,会将一个任务交给后台的Service去处理,后台的Service每处理完成一个请求就会反馈给Activity,然后Activity去更新UI。当所有的任务完成的时候,后台的Service会退出,不会占据任何内存。

Service

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.zhy.blogcodes.intentservice;  
  2.   
  3. import android.app.IntentService;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.util.Log;  
  7.   
  8. public class UploadImgService extends IntentService  
  9. {  
  10.     private static final String ACTION_UPLOAD_IMG = "com.zhy.blogcodes.intentservice.action.UPLOAD_IMAGE";  
  11.     public static final String EXTRA_IMG_PATH = "com.zhy.blogcodes.intentservice.extra.IMG_PATH";  
  12.   
  13.     public static void startUploadImg(Context context, String path)  
  14.     {  
  15.         Intent intent = new Intent(context, UploadImgService.class);  
  16.         intent.setAction(ACTION_UPLOAD_IMG);  
  17.         intent.putExtra(EXTRA_IMG_PATH, path);  
  18.         context.startService(intent);  
  19.     }  
  20.   
  21.   
  22.     public UploadImgService()  
  23.     {  
  24.         super("UploadImgService");  
  25.     }  
  26.   
  27.     @Override  
  28.     protected void onHandleIntent(Intent intent)  
  29.     {  
  30.         if (intent != null)  
  31.         {  
  32.             final String action = intent.getAction();  
  33.             if (ACTION_UPLOAD_IMG.equals(action))  
  34.             {  
  35.                 final String path = intent.getStringExtra(EXTRA_IMG_PATH);  
  36.                 handleUploadImg(path);  
  37.             }  
  38.         }  
  39.     }  
  40.   
  41.     private void handleUploadImg(String path)  
  42.     {  
  43.         try  
  44.         {  
  45.             //模拟上传耗时  
  46.             Thread.sleep(3000);  
  47.   
  48.             Intent intent = new Intent(IntentServiceActivity.UPLOAD_RESULT);  
  49.             intent.putExtra(EXTRA_IMG_PATH, path);  
  50.             sendBroadcast(intent);  
  51.   
  52.         } catch (InterruptedException e)  
  53.         {  
  54.             e.printStackTrace();  
  55.         }  
  56.   
  57.   
  58.     }  
  59.   
  60.     @Override  
  61.     public void onCreate()  
  62.     {  
  63.         super.onCreate();  
  64.         Log.e("TAG","onCreate");  
  65.     }  
  66.   
  67.     @Override  
  68.     public void onDestroy()  
  69.     {  
  70.         super.onDestroy();  
  71.         Log.e("TAG","onDestroy");  
  72.     }  
  73. }  

代码很短,主要就是继承IntentService,然后复写onHandleIntent方法,根据传入的intent来选择具体的操作。startUploadImg是我写的一个辅助方法,省的每次都去构建Intent,startService了。

Activity

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.zhy.blogcodes.intentservice;  
  2.   
  3. import android.content.BroadcastReceiver;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.content.IntentFilter;  
  7. import android.os.Bundle;  
  8. import android.support.v7.app.AppCompatActivity;  
  9. import android.view.Menu;  
  10. import android.view.MenuItem;  
  11. import android.view.View;  
  12. import android.widget.LinearLayout;  
  13. import android.widget.TextView;  
  14.   
  15. import com.zhy.blogcodes.R;  
  16.   
  17. public class IntentServiceActivity extends AppCompatActivity  
  18. {  
  19.   
  20.     public static final String UPLOAD_RESULT = "com.zhy.blogcodes.intentservice.UPLOAD_RESULT";  
  21.   
  22.     private LinearLayout mLyTaskContainer;  
  23.   
  24.     private BroadcastReceiver uploadImgReceiver = new BroadcastReceiver()  
  25.     {  
  26.         @Override  
  27.         public void onReceive(Context context, Intent intent)  
  28.         {  
  29.             if (intent.getAction() == UPLOAD_RESULT)  
  30.             {  
  31.                 String path = intent.getStringExtra(UploadImgService.EXTRA_IMG_PATH);  
  32.   
  33.                 handleResult(path);  
  34.   
  35.             }  
  36.   
  37.         }  
  38.     };  
  39.   
  40.     private void handleResult(String path)  
  41.     {  
  42.         TextView tv = (TextView) mLyTaskContainer.findViewWithTag(path);  
  43.         tv.setText(path + " upload success ~~~ ");  
  44.     }  
  45.   
  46.   
  47.     @Override  
  48.     protected void onCreate(Bundle savedInstanceState)  
  49.     {  
  50.         super.onCreate(savedInstanceState);  
  51.         setContentView(R.layout.activity_intent_service);  
  52.   
  53.         mLyTaskContainer = (LinearLayout) findViewById(R.id.id_ll_taskcontainer);  
  54.   
  55.         registerReceiver();  
  56.     }  
  57.   
  58.     private void registerReceiver()  
  59.     {  
  60.         IntentFilter filter = new IntentFilter();  
  61.         filter.addAction(UPLOAD_RESULT);  
  62.         registerReceiver(uploadImgReceiver, filter);  
  63.     }  
  64.   
  65.     int i = 0;  
  66.   
  67.     public void addTask(View view)  
  68.     {  
  69.         //模拟路径  
  70.         String path = "/sdcard/imgs/" + (++i) + ".png";  
  71.         UploadImgService.startUploadImg(this, path);  
  72.   
  73.         TextView tv = new TextView(this);  
  74.         mLyTaskContainer.addView(tv);  
  75.         tv.setText(path + " is uploading ...");  
  76.         tv.setTag(path);  
  77.     }  
  78.   
  79.   
  80.     @Override  
  81.     protected void onDestroy()  
  82.     {  
  83.         super.onDestroy();  
  84.         unregisterReceiver(uploadImgReceiver);  
  85.     }  
  86. }  


Activity中,每当我点击一次按钮调用addTask,就回模拟创建一个任务,然后交给IntentService去处理。

注意,当Service的每个任务完成的时候,会发送一个广播,我们在Activity的onCreate和onDestroy里面分别注册和解注册了广播;当收到广播则更新指定的UI。

布局文件

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <LinearLayout android:id="@+id/id_ll_taskcontainer"  
  2.               xmlns:android="http://schemas.android.com/apk/res/android"  
  3.               xmlns:tools="http://schemas.android.com/tools"  
  4.               android:layout_width="match_parent"  
  5.               android:layout_height="match_parent"  
  6.               android:orientation="vertical"  
  7.              >  
  8.   
  9.   
  10.     <Button android:layout_width="wrap_content" android:layout_height="wrap_content"  
  11.             android:onClick="addTask" android:text="add Task"/>  
  12. </LinearLayout>  

ok,这样我们就完成了我们的效果图的需求;通过上例,大家可以看到我们可以使用IntentService非常方便的处理后台任务,屏蔽了诸多细节;而Service与Activity通信呢,我们选择了广播的方式(当然这里也可以使用LocalBroadcastManager)。

学会了使用之后,我们再一鼓作气的看看其内部的实现。

三 IntentService源码解析

直接看IntentService源码

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * Copyright (C) 2008 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package android.app;  
  18.   
  19. import android.content.Intent;  
  20. import android.os.Handler;  
  21. import android.os.HandlerThread;  
  22. import android.os.IBinder;  
  23. import android.os.Looper;  
  24. import android.os.Message;  
  25.   
  26.   
  27. public abstract class IntentService extends Service {  
  28.     private volatile Looper mServiceLooper;  
  29.     private volatile ServiceHandler mServiceHandler;  
  30.     private String mName;  
  31.     private boolean mRedelivery;  
  32.   
  33.     private final class ServiceHandler extends Handler {  
  34.         public ServiceHandler(Looper looper) {  
  35.             super(looper);  
  36.         }  
  37.   
  38.         @Override  
  39.         public void handleMessage(Message msg) {  
  40.             onHandleIntent((Intent)msg.obj);  
  41.             stopSelf(msg.arg1);  
  42.         }  
  43.     }  
  44.   
  45.   
  46.     public IntentService(String name) {  
  47.         super();  
  48.         mName = name;  
  49.     }  
  50.   
  51.   
  52.     public void setIntentRedelivery(boolean enabled) {  
  53.         mRedelivery = enabled;  
  54.     }  
  55.   
  56.     @Override  
  57.     public void onCreate() {  
  58.                 super.onCreate();  
  59.         HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");  
  60.         thread.start();  
  61.   
  62.         mServiceLooper = thread.getLooper();  
  63.         mServiceHandler = new ServiceHandler(mServiceLooper);  
  64.     }  
  65.   
  66.     @Override  
  67.     public void onStart(Intent intent, int startId) {  
  68.         Message msg = mServiceHandler.obtainMessage();  
  69.         msg.arg1 = startId;  
  70.         msg.obj = intent;  
  71.         mServiceHandler.sendMessage(msg);  
  72.     }  
  73.   
  74.   
  75.     @Override  
  76.     public int onStartCommand(Intent intent, int flags, int startId) {  
  77.         onStart(intent, startId);  
  78.         return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;  
  79.     }  
  80.   
  81.     @Override  
  82.     public void onDestroy() {  
  83.         mServiceLooper.quit();  
  84.     }  
  85.   
  86.   
  87.     @Override  
  88.     public IBinder onBind(Intent intent) {  
  89.         return null;  
  90.     }  
  91.   
  92.   
  93.     protected abstract void onHandleIntent(Intent intent);  
  94. }  

可以看到它在onCreate里面初始化了一个HandlerThread,关于HandlerThread的使用和源码 
分析参考:Android HandlerThread 完全解析,看到这估计已经能猜到它的逻辑了:

就是每次调用onStartCommand的时候,通过mServiceHandler发送一个消息,消息中包含我们的intent。然后在该mServiceHandler的handleMessage中去回调onHandleIntent(intent);就可以了。

那么我们具体看一下源码,果然是这样,onStartCommand中回调了onStart,onStart中通过mServiceHandler发送消息到该handler的handleMessage中去。最后handleMessage中回调onHandleIntent(intent)。

注意下:回调完成后回调用 stopSelf(msg.arg1),注意这个msg.arg1是个int值,相当于一个请求的唯一标识。每发送一个请求,会生成一个唯一的标识,然后将请求放入队列,当全部执行完成(最后一个请求也就相当于getLastStartId == startId),或者当前发送的标识是最近发出的那一个(getLastStartId == startId),则会销毁我们的Service.

如果传入的是-1则直接销毁。

那么,当任务完成销毁Service回调onDestory,可以看到在onDestroy中释放了我们的Looper:mServiceLooper.quit()。

ok~ 如果你的需求可以使用IntentService来做,可以尽可能的使用,设计的还是相当赞的。当然了,如果你需要考虑并发等等需求,那么可能需要自己去扩展创建线程池等。

源码点击下载

ok~~



0 0