Service(五) - IntentService

1. 概述


对于开发中的一些耗时操作,比如上传多张图片、下载任务等,然后把 应用置于后台,自己干别的事情,此时Activity可能会被杀死,做法是可以直接交给Service去做,担心Service被杀死,可以设置前台Service;

对于Service执行耗时操作,有2种处理方式:
方式1:在 Service中开子线程;
方式2:用 IntentService;

2. IntentService


IntentService 继承于 Service,用于处理 异步请求,用 startService开启服务,完成任务后会自己自动关闭服务,并且请求是在子线程中完成的;

好处:
1>:不用自己 new Thread() 开线程;
2>:不用考虑什么时候关闭Service;

3. IntentService使用


效果图如下:

图片.png

每点击一次按钮,就会创建一个上传任务,然后交给后台Service处理,后台Service每处理完一个任务,就会用发送一个广播,通知该Activity进行更新UI,当所有任务完成,后台Service会自动退出,不会占用内存;

sendBroadcast发送广播用法和EventBus用法类似:
需要在该Activity中 注册、反注册广播,然后创建BroadcastReceive,在 onReceive()方法处理更新UI即可;

代码如下:
1>:activity_intentservice.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/id_ll_taskcontainer"
    >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加任务"
        android:onClick="addTask"
        />
</LinearLayout>
2>:IntentServiceActivity:
/**
 * ================================================
 * Email: 2185134304@qq.com
 * Created by Novate 2018/12/24 10:22
 * Version 1.0
 * Params:
 * Description:
 *
 *      备注:广播用法 和EventBus一样
 *
 *      注册、反注册相当于EventBus中的注册和反注册
 *      BroadcastReceiver中的 onReceive()方法相当于EventBus通知的 onEventMainThread() 方法
 * ================================================
*/

public class IntentServiceActivity extends AppCompatActivity {

    private LinearLayout id_ll_taskcontainer;


    /**
     * 广播接收者:
     *      用于接收 UploadImgService 中 sendBroadcast发送的 UPLOAD_RESULT和IMG_PATH
     */
    private BroadcastReceiver uploadImgReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // 这里取出intent.getAction(),判断是否和UPLOAD_RESULT一样,如果一样
            if (intent.getAction()  == "UPLOAD_RESULT"){
                // 就取出传递过来的 图片IMG_PATH路径
                String path = intent.getStringExtra("IMG_PATH") ;

                // 处理上传成功后的结果
                handleResult(path) ;
            }
        }
    } ;


    /**
     * 处理上传成功后的结果
     */
    private void handleResult(String path) {
        // 根据标记取出给每个子view tv 的控件,设置值
        TextView tv = (TextView) id_ll_taskcontainer.findViewWithTag(path);
        tv.setText(path + "upload success ...");
    }


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_intentservice);

        id_ll_taskcontainer = (LinearLayout) findViewById(R.id.id_ll_taskcontainer);

        // 注册广播
        registerReceiver() ;
    }

    /**
     * 注册广播
     */
    private void registerReceiver() {
        IntentFilter intentFilter = new IntentFilter() ;
        intentFilter.addAction("UPLOAD_RESULT");
        registerReceiver(uploadImgReceiver , intentFilter) ;
    }

    /**
     * 注销广播
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(uploadImgReceiver);
    }


    int i = 0;
    /**
     * 场景:
     *      1. 每点击一次按钮,就创建一个上传任务,交给 UploadImgService处理;
     *      2. 当Service的每个任务完成后,发送一个广播,然后在 此Activity注册、反注册广播,当接收到广播后就更新指定的UI;
     */
    public void addTask(View view) {
        // 模拟路径
        String path = "/sdcard/imgs/" + (++i) + ".png" ;
        // 开启一个UploadImgService 开始上传任务
        Intent intent = new Intent(this , UploadImgService.class) ;
        intent.setAction("UPLOAD_IMG");
        intent.putExtra("path" , path) ;
        IntentServiceActivity.this.startService(intent);


        // 动态的 把子view 添加 到 父view容器中
        TextView tv = new TextView(this) ;
        id_ll_taskcontainer.addView(tv);
        tv.setText(path + "is uploading ... ");

        // 给子view的tv设置一个标记,用path
        tv.setTag(path);
    }
}
3>:UploadImgService
/**
 * ================================================
 * Email: 2185134304@qq.com
 * Created by Novate 2018/12/24 10:58
 * Version 1.0
 * Params:
 * Description:    用于处理耗时任务
 * ================================================
*/

public class UploadImgService extends IntentService {

    /**
     * 需要一个无参构造方法
     */
    public UploadImgService(){
        super("UploadImgService");
    }


    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }


    /**
     * 此方法是在主线程,由源码可知:
     *       @Override
     *       public void handleMessage(Message msg) {
     *           onHandleIntent((Intent)msg.obj);
     *           stopSelf(msg.arg1);
     *       }
     */
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if (intent != null){
            // IntentServiceActivity 中传递过来的 action
            String action = intent.getAction();
            if ("UPLOAD_IMG".equals(action)){
                // IntentServiceActivity 中传递过来的 path
                String path = intent.getStringExtra("path");

                // 获取到 IntentServiceActivity 中传递过来的action与path之后,开始上传任务
                handleUploadImg(path) ;
            }
        }
    }


    /**
     * 开始上传任务
     */
    private void handleUploadImg(String path) {
        try {
            // 模拟3秒耗时上传任务
            Thread.sleep(3000);

            // 3秒后就表示上传成功,然后用广播通知IntentServiceActivity更新UI
            // 这里设置 UPLOAD_RESULT和IMG_PATH用于在IntentServiceActivity中做判断,然后取出值
            Intent intent = new Intent() ;
            intent.setAction("UPLOAD_RESULT");
            intent.putExtra("IMG_PATH" , path);

            // 这里的发送广播和 EventBus.getDefault().post(orderSuccess); 一样
            sendBroadcast(intent);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4. IntentService源码分析


public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    // ServiceHandler  继承 Handler
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        
        // 在handleMessage()中取出 msg消息,然后调用 onHandleIntent()方法,这个方法就是我们自己实现的
        @Override
        public void handleMessage(Message msg) {
            // 自己在代码中实现的方法 
            onHandleIntent((Intent)msg.obj);
            // 这里自动关闭 Service
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 创建 HandlerThread 对象,这个是子线程,继承Thread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        // 这里用 mServiceHandler,其实就是 handler发送消息 msg, msg中包含intent
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        // 只要调用onStartCommand()方法,就会调用 onStart()方法,
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

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

    // 自己实现的 onHandleIntent()方法
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

1>:在 onCreate()方法中 创建 HandlerThread对象,继承Thread,是子线程,每次调用 onStartCommand()方法都会调用 onStart()方法;
2>:在 onStart()方法中用 mServiceHandler(其实就是 Handler)调用mServiceHandler.sendMessage()发送消息,然后在 ServiceHandler 的 handleMessage()方法中调用 onHandleIntent() ,这个 onHandleIntent()就是我们自己重写的方法,把要处理的 耗时任务放到 这个方法就可以;
3>:stopSelf(msg.arg1):执行完所有耗时任务后,回调这个方法,msg.arg1是一个int值,相当于请求的一个标识,每发送一个请求,就生成一个唯一标识,然后把请求放入队列,耗时任务全部执行完后,会销毁Service;
4>:销毁Service会 回调onDestroy(),在 onDestroy()中调用 mServiceLooper.quit(),停止looper;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容