分享

Activity四大组件之Service(上)

 昵称11482448 2013-04-10

        Service是一种可以在后台执行长时间任务的没有交互界面的应用组件,Service运行在进程中的主线程中,它不能自己建立线程处理长时间的事务,所以需要你手动为你的Service创建独立的线程来避免系统没有响应(ANR)等问题。

  Service生命周期

  1.  startService

    startService()->onCreate()->onStartCommand()->running->onDestory()->shut down

            多次调用startService()方法会多次执行Service的onStartCommand()方法,然后只需调用一次stopService()去停止Service。

    当Service启动后,独立运行在后台,即使应用组件被Destory了,Service也不会停止,Service应该应该在完成事务后调用stopSelf(),或者在其他应用组件中调用stopService()

  2.  bindService

    bindService()->onCreate()->onBind()->客户端绑定Service->onUnBind()->onDestory()->shut down

    多个应用组件可以绑定同一个Service,当客户端和Service绑定后,调用unbindService()来进行解绑,直到所有调用该Service的客户端都解绑了,系统才会去销毁Service。

    这类Service不会独立运行在后台(设置->运行的服务中看到不你的Service),它的生命周期与绑定它的应用组件相关,当应用组件销毁了,Service会调用onDestory。

    如果在一个Activity中多次绑定Service,在解绑的时候需要多次调用unbindService(),但此时系统会抛出异常:

    java.lang.IllegalArgumentException: Service not registered:

    所以按照官方的要求(忘记在哪看到了,找不到了,应该就在官网上有说明),需要在绑定和解绑过程中做一个标志位,当Service已经解绑了,才能再次被绑定。官网的实例代码也是这么做的。

  处理多个请求

  •   IntentService

    IntentService继承Service,使用工作队列来处理所有请求,一次处理一件。当有多个请求同时发出,可以保证线程的安全性。你所要做的只需要实现onHandleIntent()方法在后台处理从不同的Intent发来的请求。

    下面写了一个小实例,发送3个请求,每隔5秒钟打印信息,并且在所有请求完成后销毁Service。

01//Client
02//start service
03Intent intent = new Intent(ServiceStartActivity.this,
04                HelloIntentService.class);
05intent.putExtra("value", "From ServiceStartActivity");
06startService(intent);
07 
08//stop service
09Intent intent = new Intent(ServiceStartActivity.this,
10            HelloIntentService.class);
11stopService(intent);
12 
13//Service
14public class HelloIntentService extends <span style="color:#e53333;">IntentService </span>{
15 
16@Override
17    protected void onHandleIntent(Intent intent) {
18 
19        logger.i("onHandleIntent " + intent.getStringExtra("value"));
20 
21        long endTime = System.currentTimeMillis() + 5 * 1000;
22        while (System.currentTimeMillis() < endTime) {
23            synchronized (this) {
24                try {
25                    wait(endTime - System.currentTimeMillis());
26                } catch (Exception e) {
27                    logger.e(e.getMessage());
28                }
29            }
30        }
31    }
32}

  •  Service

    如果你还是想让Service来执行多线程任务,那么你需要继承Service处理每一个intent。

    官网上的例子还是一次只能执行一个请求,请求都在一个线程中等待上一个请求结束,我修改了下,让请求可以同时进行,所有请求处理结束后,销毁Service。

01//Client
02//start service
03Intent intent = new Intent(ServiceStartActivity.this,HelloService.class);
04startService(intent);
05 
06//stop service
07Intent intent = new Intent(ServiceStartActivity.this,HelloService.class);
08stopService(intent);
09 
10//Service
11//用来处理接受的请求
12public class HelloService extends Service {
13    private final class ServiceHandler extends Handler {
14        public ServiceHandler(Looper looper) {
15            super(looper);
16            }
17 
18        @Override
19        public void handleMessage(Message msg) {
20            logger.i("handleMessage1");
21            long endTime = System.currentTimeMillis() + 5 * 1000;
22            while (System.currentTimeMillis() < endTime) {
23                synchronized (this) {
24                    try {
25                        wait(endTime - System.currentTimeMillis());
26                    } catch (InterruptedException e) {
27                        // TODO Auto-generated catch block
28                        logger.e(e.getMessage());
29                    }
30                }
31            }
32            logger.i("stopSelf " + msg.arg1);
33            stopSelf(msg.arg1);
34            // stopSelfResult(msg.arg1);
35        }
36    }
37 
38 
39@Override
40    public int onStartCommand(Intent intent, int flags, int startId) {
41        logger.i("onStartCommand " + startId);
42 
43        //为每一次的请求分配线程
44        HandlerThread thread = new HandlerThread("ServiceStartArguments",
45                Process.THREAD_PRIORITY_BACKGROUND);
46        thread.start();
47 
48        Looper looper = thread.getLooper();
49 
50        Handler handler = new ServiceHandler(looper);
51        Message msg1 = handler.obtainMessage();
52        msg1.arg1 = startId;
53        handler.sendMessage(msg1);
54 
55        return START_STICKY;
56    }
57}

  onStartCommand()的返回值用来描述,系统将如何处理被kill的Service。

  START_NOT_STICKY

    如果系统在onStartCommand()返回之后杀掉Service,不会重建Service,除非一个intent被传递给Service。可以避免你在必须要的时候运行你的Service。

  START_STICKY

    如果系统在onStartCommand()返回之后杀掉Service,重建Service并调用onStartCommand(),但不会传递最后一次Intent,换句话说,onStartCommand()所接受到的Intent是null值,除非有一个intent启动了Service。

  START_REDELIVER_INTENT

    如果系统在onStartCommand()返回之后杀掉Service,重建Service并调用onStartCommand(),并传递最后一次Intent给Service。

  运行一个Foreground Service

  一个Foreground Service即便在内存较低的情况下也不会被系统kill掉。Foreground Service提供了一个通知在状态栏,这个通知在“ongoing”栏中,意思是通知不会消失除非Service停止或者从Foreground移除。调用startForeground()启动foreground service,从前台移除Service,调用stopForeground()方法,这个方法不会让Service停止,如果停止了Service,那么通知也会从状态栏移除。看下实例代码。

01//Client
02//start service
03Intent intent = new Intent(ServiceStartActivity.this,LocalService.class);
04intent.putExtra("value", "1234");
05startService(intent);
06 
07//stop service
08Intent intent = new Intent(ServiceStartActivity.this,LocalService.class);
09stopService(intent);
10 
11//Service
12public class LocalService extends Service {
13 
14    private NotificationManager mNm;
15 
16    private static final Class[] mStartForegroundSignature = new Class[] {
17            int.class, Notification.class };
18    private static final Class[] mStopForegroundSignature = new Class[] { boolean.class };
19 
20    private Method mStartForeground;
21    private Method mStopForeground;
22    private Object[] mStartForegroundArgs = new Object[2];
23    private Object[] mStopForegroundArgs = new Object[1];
24 
25    //开启Foreground Service,为了兼容早期版本,2.0之前的版本调用setForeground()方法
26    void startForegroundCompat(int id, Notification notification) {
27         
28        if (mStartForeground != null) {
29            mStartForegroundArgs[0] = Integer.valueOf(id);
30            mStartForegroundArgs[1] = notification;
31            try {
32                mStartForeground.invoke(this, mStartForegroundArgs);
33            } catch (InvocationTargetException e) {
34                 
35                logger.e(e.getMessage());
36            } catch (IllegalAccessException e) {
37                 
38                logger.e(e.getMessage());
39            }
40            return;
41        }
42        setForeground(true);
43        mNm.notify(id, notification);
44    }
45 
46    //移除Foreground Service
47    void stopForegroundCompat(int id) {
48         
49        if (mStopForeground != null) {
50            mStopForegroundArgs[0] = Boolean.TRUE;
51            try {
52                mStopForeground.invoke(this, mStopForegroundArgs);
53            } catch (InvocationTargetException e) {
54                 
55                logger.e(e.getMessage());
56            } catch (IllegalAccessException e) {
57                 
58                logger.e(e.getMessage());
59            }
60            return;
61        }
62 
63        mNm.cancel(id);
64        setForeground(false);
65    }
66 
67    @Override
68    public void onCreate() {
69        super.onCreate();
70 
71        logger.w("onCreate threadID" + Thread.currentThread().getId());
72 
73        mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
74 
75        try {
76            mStartForeground = getClass().getMethod("startForeground",
77                    mStartForegroundSignature);
78            mStopForeground = getClass().getMethod("stopForeground",
79                    mStopForegroundSignature);
80        } catch (NoSuchMethodException e) {
81             
82            mStartForeground = mStopForeground = null;
83        }
84 
85        showBroadCast();
86    }
87 
88    private void showBroadCast() {
89        Notification notification = new Notification(R.drawable.btn_star,
90                "afasdf", System.currentTimeMillis());
91        Intent intent = new Intent(this, ServiceStartActivity.class);
92        PendingIntent pi = PendingIntent.getBroadcast(this, 123, intent, 0);
93        notification.setLatestEventInfo(this, "AAA", "BBB", pi);
94 
95        startForegroundCompat(R.string.dialog_alert_title, notification);
96    }
97}

需要注意的是在android2.0之后引入 startForeground()stopForeground()方法,如果你的程序想在android2.0之前运行Foreground Service,你必须使用之前的方法 setForeground() 。 


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多