Android service学习

根据疯狂Android讲义一书学习:

Service介绍

Service作为安卓四大组建之一,也需要在AndroidManifest.xml中配置,并且根据<intent-filter.../>属性指定可以被哪些Intent启动。

Service和Activity来自相同基类Context,也可一调用getResoureces(),getContentResolver()等方法。


Service的生命周期:

  • IBinder onBind(Intent intent): 该方法是Service子类必须实现的方法,返回一个IBinder对象,应用程序通过该IBinder对象与Service组建通信。
  • void onCreate():第一次创立时回调。
  • void onDestroy():被关闭之前回调。
  • void onStartCommand(Intent intent, int flags, int startId):早期版本为void onStart(Intent intent, int startId),每次客户端调用startService(Intent)方法启动Service时回调。
  • boolean onUnbind(Intent intent):当该Service上绑定的客户端都断开链接时回调。

下边定义了一个Service组件

/**
 *
 */
package org.crazyit.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class FirstService extends Service
{
	// 必须实现的方法
	@Override
	public IBinder onBind(Intent arg0)
	{
		return null;
	}
	// Service被创建时回调该方法。
	@Override
	public void onCreate()
	{
		super.onCreate();
		System.out.println("Service is Created");
	}
	// Service被启动时回调该方法
	@Override
	public int onStartCommand(Intent intent, int flags, int startId)
	{
		System.out.println("Service is Started");
		return START_STICKY;
	}
	// Service被关闭之前回调。
	@Override
	public void onDestroy()
	{
		super.onDestroy();
		System.out.println("Service is Destroyed");
	}
}
上边代码是基础的Service组建必须实现的方法。


然后需要在AndroidManifest.xml中配置:

		<!-- 配置一个Service组件 -->
		<service android:name=".FirstService">
			<intent-filter>
				<!-- 为该Service组件的intent-filter配置action -->
				<action android:name="org.crazyit.service.FIRST_SERVICE" />
			</intent-filter>
		</service>

和Activity类似,指定intent-filter标签说明该Service可以被哪些Intent启动。

和Activity不同的是,不需要android:label等属性,因为Service永远在后台运行,没必要指定标签。


介绍完基础的Service类和配置后说一下Service的两种启动方式

  • 通过Context的startService()方法启动:如果用这种方式启动,访问者和Service之间没有关联,如果访问者退出,Service依然运行。
  • 通过Context的bindService()方法启动:如果用这种方式启动,访问者和Service绑定在了一起,如果访问者退出,Service也就终止。


启动和停止Service

下边的Activity作为Service的访问者,由两个按钮组成,一个用来启动Service,另一个用来关闭Service。

package org.crazyit.service;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class StartServiceTest extends Activity
{
	Button start, stop;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 获取程序界面中的start、stop两个按钮
		start = (Button) findViewById(R.id.start);
		stop = (Button) findViewById(R.id.stop);
		// 创建启动Service的Intent
		final Intent intent = new Intent();
		// 为Intent设置Action属性
		intent.setAction("org.crazyit.service.FIRST_SERVICE");
		start.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View arg0)
			{
				// 启动指定Serivce
				<strong>startService(intent);</strong>
			}
		});
		stop.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View arg0)
			{
				// 停止指定Serivce
				<strong>stopService(intent);</strong>
			}
		});
	}
}

可以看到启动Service的方法为startService(Intent intent),关闭Service的方法为stopService(Intent intent)。

如果连续点击三次启动按钮,可以发现onStartCommand()方法被调用了3次,而onStart()方法只有第一次被回调。


绑定本地Service并与之通信

使用startService()、stopService()的方式启动、停止 Service,访问者和Service之间没有关联,也无法进行通信和数据交换。

如果需要进行方法调用或数据交换,应该使用bindService()和unbindService()方式。

先介绍Context的bindService(Intent service,ServiceConnection conn,int flags)方法的三个参数:

  • Intent service:该参数通过Intent指定要启动哪个Service。
  • ServiceConnection conn:该参数是一个ServiceConnection对象,该对象用于监听访问者和Service之间的连接情况。当访问者与Service连接成功时回调ServiceConnection对象的onServiceConnected(ComponentName name, IBinder binder)方法;当连接断开时回调onServiceDisconnected(ComponentName name)方法。
  • Int flags:该参数指定绑定时是否自动创建Service。如果为0则不自动创建,或者为BIND_AUTO_CREATE。

通信是通过onServiceConnected方法的IBinder对象进行的,Service类必须实现onBind(IBinder binder)方法,该方法的参数binder会传给ServiceConnect的onServiceConnected方法,这样访问者就可以通过IBinder对象与Service进行通信。

下边的例子实现了onBind()方法,并且实现了自己的IBinder的实现类:

package org.crazyit.service;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class BindService extends Service
{
	private int count;
	private boolean quit;
	// 定义onBinder方法所返回的对象
	private MyBinder binder = new MyBinder();
	// 通过继承Binder来实现IBinder类
	public class MyBinder extends Binder //①
	{
		public int getCount()
		{
			// 获取Service的运行状态:count
			return count;
		}
	}
	// 必须实现的方法,绑定该Service时回调该方法
	@Override
	public IBinder onBind(Intent intent)
	{
		System.out.println("Service is Binded");
		// 返回IBinder对象
		return binder;
	}
	// Service被创建时回调该方法。
	@Override
	public void onCreate()
	{
		super.onCreate();
		System.out.println("Service is Created");
		// 启动一条线程、动态地修改count状态值
		new Thread()
		{
			@Override
			public void run()
			{
				while (!quit)
				{
					try
					{
						Thread.sleep(1000);
					}
					catch (InterruptedException e)
					{
					}
					count++;
				}
			}
		}.start();
	}
	// Service被断开连接时回调该方法
	@Override
	public boolean onUnbind(Intent intent)
	{
		System.out.println("Service is Unbinded");
		return true;
	}
	// Service被关闭之前回调该方法。
	@Override
	public void onDestroy()
	{
		super.onDestroy();
		this.quit = true;
		System.out.println("Service is Destroyed");
	}
}

上边代码中的①部分是一个继承了Binder(实现了IBinder接口)的内部类。

下边的Activity会绑定上边的Service,并且通过MyBinder获取Service的内部数据(Count)。该Activity包含三个按钮,第一个用于绑定Service,第二个用于解除绑定,第三个用于获取Service的

package org.crazyit.service;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class BindServiceTest extends Activity
{
	Button bind, unbind, getServiceStatus;
	// 保持所启动的Service的IBinder对象
	BindService.MyBinder binder;
	// 定义一个ServiceConnection对象
	private ServiceConnection conn = new ServiceConnection()
	{
		// 当该Activity与Service连接成功时回调该方法
		@Override
		public void onServiceConnected(ComponentName name
			, IBinder service)
		{
			System.out.println("--Service Connected--");
			// 获取Service的onBind方法所返回的MyBinder对象
			binder = (BindService.MyBinder) service; //①
		}

		// 当该Activity与Service断开连接时回调该方法
		@Override
		public void onServiceDisconnected(ComponentName name)
		{
			System.out.println("--Service Disconnected--");
		}
	};

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// 获取程序界面中的start、stop、getServiceStatus按钮
		bind = (Button) findViewById(R.id.bind);
		unbind = (Button) findViewById(R.id.unbind);
		getServiceStatus = (Button) findViewById(R.id.getServiceStatus);
		// 创建启动Service的Intent
		final Intent intent = new Intent();
		// 为Intent设置Action属性
		intent.setAction("org.crazyit.service.BIND_SERVICE");
		bind.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View source)
			{
				// 绑定指定Serivce
				bindService(intent, conn, Service.BIND_AUTO_CREATE);
			}
		});
		unbind.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View source)
			{
				// 解除绑定Serivce
				unbindService(conn);
			}
		});
		getServiceStatus.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View source)
			{
				// 获取、并显示Service的count值
				Toast.makeText(BindServiceTest.this,
					"Serivce的count值为:" + binder.getCount(),
					Toast.LENGTH_SHORT).show(); //②
			}
		});
	}
}
上边代码中的①部分把onBind(Intent intent)方法返回的Binder对象给到ServiceConnection对象里。

代码②通过Binder的getCount()方法访问Service的数据。

当activity调用unbindService()时会调用Service的onUnbind()方法和onDestroy()方法。

与之前通过startService访问Service不同,之前每次点击启动服务按钮都会回调Service的onStartCommand()方法,但如果多次按下绑定按钮,只调用一次onBind()方法。


Service的生命周期

上边讲的两种情况,Service的生命周期也有所不同。

第一种通过startService()启动的生命周期为:onCreate()-->onStartCommand()-->onDestroy();

第二种通过bindService()启动的生命周期为:onCreate()-->onBind()-->onUnbind()-->onDestroy()。

还有一种情况是通过startService()启动,然后再通过bindService()去绑定:

onCreate()-->onStartCommand()-->onBind()-->onUnbind()-->onRebind()。

可以看到这时Context的unBindService()方法并不会回调Service的onDestory()方法。这是因为该service是通过startService启动起来的,这种方式建立的Service并没有和访问者真正的绑定,因此生命周期不会受到影响。解除绑定后Service依然存在,需要通过stopService()方法关闭。


使用IntentService

IntentService是Service的一个子类。

Service和它所在的应用位于同一个线程,因此不应该用来处理耗时任务。如果需要执行耗时任务,需要自己新起一个子线程,就像上边的例子,在onCreate方法中新起得子线程。

IntentService可以直接解决这个问题,对于异步的startService()请求,IntentService会建立一个队列并在新建的worker线程里依次处理Intent。

归纳IntentService的特征:

  • IntentService会新建单独的worker thread来处理所有的intent请求。
  • IntentService的onHandleIntent()方法已经在新的线程里运行,开发者无需维护多线程。
  • 当IntentService处理完所有的Intent后会自动停止,无需开发者再调用Service的stopSelf()方法(相当于访问Context的stopService()方法)来停止该Service。
  • 默认实现了onBind()方法,返回null。
  • 默认实现了onStartCommand()方法,将Intent放入队列中。

因此实现IntentService无须必须重写onBind和onStartCommand方法,而需要重写onHandleIntent()方法。

下边的activity的两个按钮分别启动一个普通service一个intentService来进行耗时任务:

package org.crazyit.service;

import android.os.Bundle;
import android.view.View;
import android.app.Activity;
import android.content.Intent;

public class IntentServiceTest extends Activity
{
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	}

	public void startService(View source)
	{
		// 创建需要启动的Service的Intent
		Intent intent = new Intent(this, MyService.class);
		// 启动Service
		startService(intent);
	}

	public void startIntentService(View source)
	{
		// 创建需要启动的IntentService的Intent
		Intent intent = new Intent(this, MyIntentService.class);
		// 启动IntentService
		startService(intent);
	}
}
MyService继承Service,MyIntentService继承IntentService,下边是两个Service的实现:

MyService:

package org.crazyit.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MyService extends Service
{
	@Override
	public IBinder onBind(Intent intent)
	{
		return null;
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId)
	{
		// 该方法内执行耗时任务可能导致ANR(Application Not Responding)异常
		long endTime = System.currentTimeMillis() + 20 * 1000;
		System.out.println("onStart");
		while (System.currentTimeMillis() < endTime)
		{
			synchronized (this)
			{
				try
				{
					wait(endTime - System.currentTimeMillis());
				}
				catch (Exception e)
				{
				}
			}
		}
		System.out.println("---耗时任务执行完成---");
		return START_STICKY;
	}
}
上边的 service在onStartCommand方法里执行了一个耗时20秒的任务,因为阻塞主线程所以会造成ANR(Application not responding)异常。
然后是IntentService:

package org.crazyit.service;

import android.app.IntentService;
import android.content.Intent;

public class MyIntentService extends IntentService
{
	public MyIntentService()
	{
		super("MyIntentService");
	}

	// IntentService会使用单独的线程来执行该方法的代码
	@Override
	protected void onHandleIntent(Intent intent)
	{
		// 该方法内可以执行任何耗时任务,比如下载文件等,此处只是让线程暂停20秒
		long endTime = System.currentTimeMillis() + 20 * 1000;
		System.out.println("onStart");
		while (System.currentTimeMillis() < endTime)
		{
			synchronized (this)
			{
				try
				{
					wait(endTime - System.currentTimeMillis());
				}
				catch (Exception e)
				{
				}
			}
		}
		System.out.println("---耗时任务执行完成---");
	}
}
可以看到在IntentService里并不需要再重写onBind和onStartCommand方法。而onHandleIntent里的任务会自动在新的worker thread里执行,从而不会阻塞主线程,因此不会造成ANR异常。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值