Android 四大组件之 Service (下)

来源:互联网 发布:dblp数据集 编辑:程序博客网 时间:2024/06/05 22:40

       上篇文章中我们学习了服务的概念、服务的基本用法,包括启动和停止服务、活动和服务进行通信、服务的生命周期,如何销毁服务等内容,这些都是我们日常用到的,不过还有一些更加高端的服务技术需要我们学习,如果你还对服务不是很了解可以去查看上一篇文章,文章地址:Android 四大组件之 Service(上),今天我们就一起来学习更多 Service 相关的内容,接下来我们开始演绎


一、Service 和 Thread 之间的关系


      这里我们先来探究一下这两个事物之间的概念关系问题,其实这个东西本来就是没有任何关系的,但不知为什么面试中还会经常问道,既然说起来了,那我们不妨就来理一下,Service 是四大组件,是后台服务,用来处理长时间运行在后台的场景下的任务,Thread 是用来开启一个子线程的,在子线程里去处理一些耗时的操作,总结如下:

  • Service 是 Andriod 中的系统组件,它运行在独立进程的主线程中,不可以执行耗时操作,Thread 是程序执行的最小单元,分配 cpu 的基本单位,可以开启子线程执行耗时操作
  • Service 在不同的 Activity 中可以获取自身实例,可以方便的对 Service 进行操作,Thread 在不同的 Activity 中难以获取自身的实例,如果Activity 被销毁,Thread 实例就很难再获取得到

我们通过代码来具体的看一下吧,这样更明了一点


1)在 MainActivity 中打印当前线程:




2)在 MyService 中打印当前线程:



       

       从打印情况可以到 MainActivity 和 MyService 的线程 id 是完全一样的,那么由此证实 Service 是运行在主线程里的,到这里想必大家就知道为什么一开始就说 Service 和 Thread 之间没有任何关系,还要注意的是既然 Service 是运行在主线程里的,那么也就是说我们在编代码时要特别注意,不能在 Service 里面编写非常耗时的操作,否则程序会出现 ANR(Application Not Responding),这样的话我们在做耗时的任务时就需要在 Service 中再创建一个子线程,然后在子线程里去处理耗时逻辑的问题,在这里还要注意的是,我们为什么要在 Service 里去创建一个子线程,而不直接在 Activity 中去创建,这是因为 Activity 很难对 Thread 进行控制,当 Acitivty 被销毁后,就没有其他办法获取到之前创建的的子线程实例,而且在一个 Activity 中创建子线程,另一个 Activity 无法对其进行操作,而 Service 就不一样,所有 Activity 都可以与 Service 进行关联,然后操作其中的方法,即使 Activity 被销毁了,之后只要与 Service 建立关联,就可以重新获取到原有 Service 中 Binder 中的实例,所以,使用 Service 来处理后台任务,Activity 即使销毁,后台任务任然可以运行


我们在 MyService 的 onCreate() 方法中添加如下操作:

    @Override    public void onCreate() {        Log.i(TAG, "MyService 所在的线程是:" + Thread.currentThread().getId());        super.onCreate();        try {            Thread.sleep(60000);        } catch (InterruptedException e) {            e.printStackTrace();        }    }

下面我们看一下如果在主线程有耗时操作的情况:




可以看到程序无效应,我们再看一下日志的打印情况:




说我们可能在主线程中做了耗时的操作

所以为防止 ANR,在写 Service 时可以如下:

 //模仿方法        public void startDownload() {            new Thread(new Runnable() {                @Override                public void run() {                    // 执行具体的下载任务                }            }).start();            Log.d(TAG, "startDownload");        }

    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        new Thread(new Runnable() {            @Override            public void run() {                //处理具体的逻辑                                //这个方法一定切记要记得写,自动关闭功能                stopSelf();            }        }).start();        return super.onStartCommand(intent, flags, startId);    }

       服务一旦启动后,就会一直运行在后台,处于运行状态,必须调用 stopService() 或者 stopSelf() 方法才能让服务停下来,所以要想让一个服务执行完毕自动停止就得像上面一样写 


二、前台 Service


       服务几乎都是在后台运行的,但是服务系统的优先级比较低,当系统出现内存情况不足时,就有可能回收掉后台正在运行的服务,如果服务需要一直保持运行状态,不被由于系统内存不足回收,就可以使用前台服务,前台服务和普通服务最大的区别就是,它可以在状态栏看到,接下来我们就来创建一个前台服务

这里我们新建一个 WeatherService 实现前台服务:

public class WeatherService extends Service {    private static final int NOTIFY_ID = 1;    public WeatherService() {    }    @Override    public IBinder onBind(Intent intent) {        throw new UnsupportedOperationException("Not yet implemented");    }    @Override    public void onCreate() {        super.onCreate();        showNotification();    }    /**     * 在通知栏显示天气信息     */    private void showNotification() {        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)                .setSmallIcon(R.mipmap.ic_launcher)                .setContentTitle("2017年8月10日")                .setContentText("今天天气不错");        //创建通知被点击时触发的 Intent        Intent intent = new Intent(this, MainActivity.class);        //创建任务栈 Builder        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);        stackBuilder.addParentStack(MainActivity.class);        stackBuilder.addNextIntent(intent);        PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);        mBuilder.setContentIntent(pendingIntent);        NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);        //构建通知        final Notification notification = mBuilder.build();        //显示通知        mNotificationManager.notify(NOTIFY_ID, notification);        //启动为前台服务        startForeground(NOTIFY_ID, notification);    }}


        我们在 onCreate() 方法中调用 showNotification() 方法显示通知,并且在最后调用 startForeground() 方法将服务设置为前台服务,并且在 MainActivity 中启动此服务,效果如下:




到这里我们的一个前台服务就启动起来了,并且在状态栏中显示了出来


三、IntentService


       从上面文章中我们知道服务是运行在主线程里的,如果直接去服务里处理一些耗时的操作,很容易出现 ANR 的情况,上面我们也对这种情况做了处理,就是开启子线程,其实 Android 系统也早就洞察了这个需求,专门提供了一个 IntentService 类,来解决这个问题,接下来我们就来看看 IntentService 的使用

这里我们创建了一个 MyIntentService 类继承自 IntentService,代码如下:

/** * IntentService 测试类 * Created by qiudengjiao on 2017/8/10. */public class MyIntentService extends IntentService {    private static final String TAG = "MyIntentService";    public MyIntentService() {        super("MyIntentService");    }    @Override    protected void onHandleIntent(@Nullable Intent intent) {        Log.i(TAG, "MyIntentService 所在的线程是:" + Thread.currentThread().getId());    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i(TAG, "onDestroy");    }}

       这里提供了一个无参的构造函数,并且必须在其内部调用父类的有参构造函数,然后在子类中实现 onHandleIntent() 这个抽象方法,在这个方法里处理一些具体的逻辑,而不用担心 ANR 的问题,因为这个方法已经是在主线程中去运行的了,而且运行结束后服务会自动停止,为了证实,我们在 onHandleIntent() 方法中打印了当前的线程 id,并且在  onDestory() 方法中也打印了一行日志,证实服务是否会自动停止,我们来看一下打印情况:




我们看到它们的线程确实不一样,而且进行了自动关闭

我们来看一下  IntentService 的源码:


public abstract class IntentService extends Service {    private volatile Looper mServiceLooper;    private volatile ServiceHandler mServiceHandler;    private String mName;    private boolean mRedelivery;    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);        }    }    public IntentService(String name) {        super();        mName = name;    }    public void setIntentRedelivery(boolean enabled) {        mRedelivery = enabled;    }    @Override    public void onCreate() {                super.onCreate();        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");        thread.start();        mServiceLooper = thread.getLooper();        mServiceHandler = new ServiceHandler(mServiceLooper);    }    @Override    public void onStart(Intent intent, int startId) {        Message msg = mServiceHandler.obtainMessage();        msg.arg1 = startId;        msg.obj = intent;        mServiceHandler.sendMessage(msg);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        onStart(intent, startId);        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;    }    @Override    public void onDestroy() {        mServiceLooper.quit();    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    protected abstract void onHandleIntent(Intent intent);}

       相信大家都明白了,其实 onHandleInent() 方法的实现思路也是和我们一样的,大家可以看到,在 handleMesage() 中调用了onHandleIntent() 方法,接下下来就调用了 stopService,另外在 onCreate() 方法中初始化了一个 HandlerThread,调用 onStartCommand() 方法的时候,通过 mServiceHandler 发送了一个消息,消息中包含我们的 Intent,然后在该 mServiceHandler 的 handleMessage 中去回调 onHandleIntent(intent) 就可以了,当前任务完成销毁 Service 回调 onDestory() 方法,在 onDestory() 方法中释放我们的 Looper:mServiceLooper.quit()

到这里我们就介绍完了,如有错误请指出

参考:郭神第一行代码