Android的Service使用

来源:互联网 发布:淘宝的免费模板在哪里 编辑:程序博客网 时间:2024/05/27 20:51

Service

Service属于Android的四大组件(只要是安卓四大组件就必须在AndroidMainFest中进行注册才可以使用),Service运行不赖于界面,即使退出应用,Service也将运行在后台中。服务在创建后,默认运行在主线程中,所以我们需要自己去开辟子线程,如果不开辟就有可能出现主线程被阻塞。

Android多线程

对于很多耗时操作(比如:发起网络请求,由于网络很慢,服务器不一定立刻响应),我们一般把操作放在子线程中进行。

线程基本用法

  • 定义一个线程需要继承Thread类,重写run()方法:
  class XXThread extends Thread {    @Override    public void run() {        // 处理具体的逻辑    }}  

启动该线程:

    new XXThread().start();
  • 使用Runable接口方法来定义一个线程
    class XXThread implements Runnable{        @Override        public void run() {        }}

启动该线程

    XXThread mThread = new XXThread();    new Thread(mThread).start();
  • 使用匿名类的方法
        new Thread(new Runnable() {            @Override            public void run() {                // 处理具体的逻辑            }        }).start();

异步消息处理机制

我们是不能在子线程中更新UI中的内容,但是有时候我们却需要把子线程的内容更新到UI,Android提供一套异步消息处理机制来解决这个问题。

从图中可以发现,异步消息处理有4部分,分别是Message,Handle,MessageQuery,Looper四部分组成。在线程中我们调用 hanler.setMessage() 方法来发送消息给 MessageQuery,然后通过 Looper 来取出带处理消息,并且传回给 handler,然后调用在 UI 中的 handler.handleMessage() 方法来实现对 UI中的内容更新。

代码示例

TEXT_UPDATE自己定义的一个常量

  • UI线程中的代码
    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case TEXT_UPDATE:                    tvShow.setText("你改变了TextView的内容");                    break;                default:                    break;            }        }    };
  • 子线程中的代码
    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btnUpdate:                new Thread(new Runnable() {                    @Override                    public void run() {                        Message message = new Message();                        message.what = TEXT_UPDATE;                        handler.sendMessage(message);                    }                }).start();                break;            default:                break;        }    }

Service应用

前面讨论这么多关于多线程编程的问题都为在Service的使用上做准备的。先讨论最简单的基本用法

基本用法

Service和Activity一样都拥有 onCreate()onDestroy()方法,来实现Service的创建和销毁。当然他们也有区别,在Service中我们必须实现 onBind() 方法,该方法用于与UI绑定(先不讨论)。在Service还有一个方法叫 onStartCommand(),这个方法和 onCreate() 区别是:onStartCommand()每次Service启动(包括第一次创建)都会启动这个方法,但是onCreate()只会在第一次创建的时候启动,之后不会再启动该方法

代码示例

  • 创建一个Service
public class TestService extends Service {    @Nullable    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onCreate() {        super.onCreate();    }    @Override    public void onDestroy() {        super.onDestroy();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        return super.onStartCommand(intent, flags, startId);    }}
  • 启动一个Service
    Intent startIntent = new Intent(this, TestService.class);    startService(startIntent);
  • 停止一个Service
    停止Service需要注意,如果Service不仅被创建了,而且被绑定了,那么如果要停止这个Service,就需要同时执行 stopService()unbindService(),才可以停止Service。
    Intent stopIntent = new Intent(this, TestService.class);    stopService(stopIntent);

将Service绑定到UI中

如果要将Service绑定到UI中去,必须在 onBind() 方法中传回一个对象,使得UI可以操作Service中的方法,这个对象就好比一座桥梁连接着UI和Service。

代码示例

  • 创建一个Biner对象,来实现Service中的公共方法
   private DownloadBinder mBinder = new DownloadBinder();    class DownloadBinder extends Binder {        public void startDownload() {            Log.e(TAG, "开始下载");        }        public int getProgress() {            Log.e(TAG, "获取进度表");            return 0;        }        public void stopDownload() {            Log.e(TAG, "停止下载");        }    }
  • 桥梁的建立
    @Nullable    @Override    public IBinder onBind(Intent intent) {        //返回一个连接通道给service        return mBinder;    }

到此UI还没有和Service建立真正的桥梁,在UI中必须调用 bindService() 方法来绑定服务,在使用此方法之前,我们还需要 ServiceConnection的实现,来建立UI与Ser的连接。ServiceConnection 中有两个方法需要实现分别是 onServiceConnected()onServiceDisconnected(),前者在调用bindService() 来建立连接的时候,会去调用此方法,但是在解除绑定的时候,却不会调用onServiceDisconnected(),只要发生系统意外的时候,才会调用此方法。我们在onServiceConnected()中来调用Service中的公共方法

  • ServiceConnection的实现
    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            downloadBinder = (TestService.DownloadBinder) service;            downloadBinder.startDownload();            downloadBinder.getProgress();        }        @Override        public void onServiceDisconnected(ComponentName name) {            //系统发生意外中断的时候才会调用此方法            //客户端取消绑定的时候不会调用        }    };

到此我们还是不能调用Service中的方法,因为我们还没有使用onServiceConnected()来绑定。BIND_AUTO_CREATE 代表自动完成绑定。

  • 桥梁的完成
    Intent bindIntent = new Intent(this, TestService.class);    bindService(bindIntent, connection, BIND_AUTO_CREATE);

到此我们就可以任意调用Service中的公共方法了

  • 桥梁的爆破
    解除绑定是非常简单的,只需要调用 unbindService() 方法,需要注意的是:如果已经解除绑定了,再次调用会报错
    unbindService(connection);

到这我们就已经学习了Service的创建、删除、绑定、取消绑定。但是这些都是在UI主线程中进行,就像我们前面提到如果遇到了耗时任务,将会被阻塞。所以我们要使用多线程技术。

IntendService的应用

IntendService的使用,可以使得开发者不必去停止服务,系统会在请求结束后自动停止服务。我们只需要在 onHandleIntent() 来完成UI所提出的任务即可,不过我们还需要为IntendService提供一个构造函数

代码示例

public class IntentServiceDemo extends IntentService {    private static final String TAG_INTENT_SERVICE = "IntentService";    public IntentServiceDemo() {        super("IntentServiceDemo");    }    @Override    protected void onHandleIntent(Intent intent) {        Log.e(TAG_INTENT_SERVICE, "线程id是:" + Thread.currentThread().getId());    }    @Override    public void onDestroy() {        super.onDestroy();        Log.e(TAG_INTENT_SERVICE,"服务已经被销毁");    }}

启动IntendService

     Log.e("MainActivity", "线程id:" + Thread.currentThread().getId());     Intent intentService = new Intent(this, IntentServiceDemo.class);     startService(intentService);

这样Service就运行在子线程中。当然我们也可以不使用IntendService来实现,我们可以自己在 onStartCommand() 方法中,开启一个子线程。但是这样开启一个子线程将永远在执行,所以我们必须在任务完成后,调用 stopSelf() 或者 stopService()方法来关闭。

代码示例

    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        new Thread(new Runnable() {            @Override            public void run() {                  Log.e(TAG,"每次服务被创建");                stopSelf();            }        }).start();        return super.onStartCommand(intent, flags, startId);    }

前台Service

应用场景,如果一个天气app,当后台数据更新后,会一直在前台显示更新后的数据情况。这就可以使用前台Service来实现。要在前台一直显示数据,这有点类似于Notification,当时我们不使用NotificationManger来显示出来,而是使用 startForeground()来实现。要注意 startForeground() 方法中的第一个参数id,不能使用0作为id。

代码示例

    @Override    public void onCreate() {        super.onCreate();        //  Log.e(TAG, "服务被创建");        //前台服务        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);        Intent notificationIntent = new Intent(this, MainActivity.class);        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);        builder.setContentTitle("通知");        builder.setContentText("吃饭了么");        builder.setContentIntent(pendingIntent);        builder.setSmallIcon(R.drawable.ic_launcher);        builder.setDefaults(Notification.DEFAULT_ALL);        builder.setAutoCancel(true);        startForeground(1, builder.build());    }

后台定时执行任务

通过AlarmManager(在这不多说)来实现定时功能,同样首先我们通过 getSystemService()方法获得系统服务。然后通过set() 方法来定时唤醒 PendingIntent.getBroadcast()* 方法来发送一个广播,然后再广播内启动Service。整个流程如下图

代码示例

  • Service
    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        new Thread(new Runnable() {            @Override            public void run() {                Log.e(TAG, "执行时间:" + new Date().toString());            }        }).start();        AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);        int aSecond = 60 * 1000;//1分钟        long triggerAtTime = SystemClock.elapsedRealtime() + aSecond;        Intent i = new Intent(this, AlarmReceiver.class);        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, i, 0);        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);        //Log.e(TAG,"每次服务被创建");        return super.onStartCommand(intent, flags, startId);    }
  • Broadcoast
public class AlarmReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        Log.e("AlarmReceiver", "接收到了");        Intent i = new Intent(context, ServiceDemo.class);        context.startService(i);    }}

最后还是感谢《第一行代码》的作者,郭神,文中很多都是来自于这本书。

2 0
原创粉丝点击