• Android IntentService完全解析 当Service遇到Handler


    一 概述

    大家都清楚,在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

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

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

    Activity

     1 public class IntentServiceActivity extends AppCompatActivity
     2 {
     3 
     4     public static final String UPLOAD_RESULT = "com.zhy.blogcodes.intentservice.UPLOAD_RESULT";
     5 
     6     private LinearLayout mLyTaskContainer;
     7 
     8     private BroadcastReceiver uploadImgReceiver = new BroadcastReceiver()
     9     {
    10         @Override
    11         public void onReceive(Context context, Intent intent)
    12         {
    13             if (intent.getAction() == UPLOAD_RESULT)
    14             {
    15                 String path = intent.getStringExtra(UploadImgService.EXTRA_IMG_PATH);
    16 
    17                 handleResult(path);
    18 
    19             }
    20 
    21         }
    22     };
    23 
    24     private void handleResult(String path)
    25     {
    26         TextView tv = (TextView) mLyTaskContainer.findViewWithTag(path);
    27         tv.setText(path + " upload success ~~~ ");
    28     }
    29 
    30 
    31     @Override
    32     protected void onCreate(Bundle savedInstanceState)
    33     {
    34         super.onCreate(savedInstanceState);
    35         setContentView(R.layout.activity_intent_service);
    36 
    37         mLyTaskContainer = (LinearLayout) findViewById(R.id.id_ll_taskcontainer);
    38 
    39         registerReceiver();
    40     }
    41 
    42     private void registerReceiver()
    43     {
    44         IntentFilter filter = new IntentFilter();
    45         filter.addAction(UPLOAD_RESULT);
    46         registerReceiver(uploadImgReceiver, filter);
    47     }
    48 
    49     int i = 0;
    50 
    51     public void addTask(View view)
    52     {
    53         //模拟路径
    54         String path = "/sdcard/imgs/" + (++i) + ".png";
    55         UploadImgService.startUploadImg(this, path);
    56 
    57         TextView tv = new TextView(this);
    58         mLyTaskContainer.addView(tv);
    59         tv.setText(path + " is uploading ...");
    60         tv.setTag(path);
    61     }
    62 
    63 
    64     @Override
    65     protected void onDestroy()
    66     {
    67         super.onDestroy();
    68         unregisterReceiver(uploadImgReceiver);
    69     }
    70 }

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

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

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

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

    三 IntentService源码解析

    直接看IntentService源码

     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,

    转载标明出处:
    http://blog.csdn.net/lmj623565791/article/details/47143563; 

    就是每次调用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来做,可以尽可能的使用,设计的还是相当赞的。当然了,如果你需要考虑并发等等需求,那么可能需要自己去扩展创建线程池等。

  • 相关阅读:
    三、Vue CLI-单页面
    width100%,设置padding或border溢出解决方法
    一、Linux平台部署ASP.NET、ASP.NET CORE、PHP
    二、Core授权-2 之.net core 基于Identity 授权
    一、doT.js使用笔记
    一、域名认证信息
    HB-打包
    一、模型验证CoreWebApi 管道方式(非过滤器处理)2(IApplicationBuilder扩展方法的另一种写法)
    python 写的几道题
    美团面试总结
  • 原文地址:https://www.cnblogs.com/itpepe/p/4765119.html
Copyright © 2020-2023  润新知