Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android的Service使用

$
0
0

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);
    }
}

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

作者:u011665459 发表于2016/8/13 21:17:36 原文链接
阅读:142 评论:1 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>