Activity四大组件之Service(上)

        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。

//Client
//start service
Intent intent = new Intent(ServiceStartActivity.this,
				HelloIntentService.class);
intent.putExtra("value", "From ServiceStartActivity");
startService(intent);

//stop service
Intent intent = new Intent(ServiceStartActivity.this,
			HelloIntentService.class);
stopService(intent);

//Service
public class HelloIntentService extends IntentService {

@Override
	protected void onHandleIntent(Intent intent) {

		logger.i("onHandleIntent " + intent.getStringExtra("value"));

		long endTime = System.currentTimeMillis() + 5 * 1000;
		while (System.currentTimeMillis() < endTime) {
			synchronized (this) {
				try {
					wait(endTime - System.currentTimeMillis());
				} catch (Exception e) {
					logger.e(e.getMessage());
				}
			}
		}
	}
}

  •  Service

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

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

//Client
//start service
Intent intent = new Intent(ServiceStartActivity.this,HelloService.class);
startService(intent);

//stop service
Intent intent = new Intent(ServiceStartActivity.this,HelloService.class);
stopService(intent);

//Service
//用来处理接受的请求
public class HelloService extends Service {
	private final class ServiceHandler extends Handler {
		public ServiceHandler(Looper looper) {
			super(looper);
	        }

		@Override
		public void handleMessage(Message msg) {
			logger.i("handleMessage1");
			long endTime = System.currentTimeMillis() + 5 * 1000;
			while (System.currentTimeMillis() < endTime) {
				synchronized (this) {
					try {
						wait(endTime - System.currentTimeMillis());
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						logger.e(e.getMessage());
					}
				}
			}
			logger.i("stopSelf " + msg.arg1);
			stopSelf(msg.arg1);
			// stopSelfResult(msg.arg1);
		}
	}


@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		logger.i("onStartCommand " + startId);

		//为每一次的请求分配线程
		HandlerThread thread = new HandlerThread("ServiceStartArguments",
				Process.THREAD_PRIORITY_BACKGROUND);
		thread.start();

		Looper looper = thread.getLooper();

		Handler handler = new ServiceHandler(looper);
		Message msg1 = handler.obtainMessage();
		msg1.arg1 = startId;
		handler.sendMessage(msg1);

		return START_STICKY;
	}
}

  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,那么通知也会从状态栏移除。看下实例代码。

//Client
//start service
Intent intent = new Intent(ServiceStartActivity.this,LocalService.class);
intent.putExtra("value", "1234");
startService(intent);

//stop service
Intent intent = new Intent(ServiceStartActivity.this,LocalService.class);
stopService(intent);

//Service
public class LocalService extends Service {

    private NotificationManager mNm;

    private static final Class[] mStartForegroundSignature = new Class[] {
			int.class, Notification.class };
    private static final Class[] mStopForegroundSignature = new Class[] { boolean.class };

    private Method mStartForeground;
    private Method mStopForeground;
    private Object[] mStartForegroundArgs = new Object[2];
    private Object[] mStopForegroundArgs = new Object[1];

    //开启Foreground Service,为了兼容早期版本,2.0之前的版本调用setForeground()方法
    void startForegroundCompat(int id, Notification notification) {
		
		if (mStartForeground != null) {
			mStartForegroundArgs[0] = Integer.valueOf(id);
			mStartForegroundArgs[1] = notification;
			try {
				mStartForeground.invoke(this, mStartForegroundArgs);
			} catch (InvocationTargetException e) {
				
				logger.e(e.getMessage());
			} catch (IllegalAccessException e) {
				
				logger.e(e.getMessage());
			}
			return;
		}
		setForeground(true);
		mNm.notify(id, notification);
	}

    //移除Foreground Service
    void stopForegroundCompat(int id) {
		
		if (mStopForeground != null) {
			mStopForegroundArgs[0] = Boolean.TRUE;
			try {
				mStopForeground.invoke(this, mStopForegroundArgs);
			} catch (InvocationTargetException e) {
				
				logger.e(e.getMessage());
			} catch (IllegalAccessException e) {
				
				logger.e(e.getMessage());
			}
			return;
		}

		mNm.cancel(id);
		setForeground(false);
	}

    @Override
    public void onCreate() {
		super.onCreate();

		logger.w("onCreate threadID" + Thread.currentThread().getId());

		mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

		try {
			mStartForeground = getClass().getMethod("startForeground",
					mStartForegroundSignature);
			mStopForeground = getClass().getMethod("stopForeground",
					mStopForegroundSignature);
		} catch (NoSuchMethodException e) {
			
			mStartForeground = mStopForeground = null;
		}

		showBroadCast();
    }

    private void showBroadCast() {
		Notification notification = new Notification(R.drawable.btn_star,
				"afasdf", System.currentTimeMillis());
		Intent intent = new Intent(this, ServiceStartActivity.class);
		PendingIntent pi = PendingIntent.getBroadcast(this, 123, intent, 0);
		notification.setLatestEventInfo(this, "AAA", "BBB", pi);

		startForegroundCompat(R.string.dialog_alert_title, notification);
    }
}

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

转载于:https://my.oschina.net/stevegym/blog/55290

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值