Android 四大组件中,Activity是大家最熟悉的,对于初学Service组件的新手来说,可能很难理解Service的存在的意义何在?这篇文章主要告诉大家Service主要用在哪些地方的。
关于Service的使用方法,这个链接有详细的讲法,大家可以好好看下这个大牛的博客http://www.360doc.com/content/14/0415/18/2793098_369238276.shtml(PS:这篇博客主要是讲解关于Service的一些基础的知识和同一进程中,跨进程的Service的使用,包括Service和Activity的交互)。
大家也许觉得Service这样组件到底扮演什么演的角色呢?
我想给大家说Service的几个特点:
1.首先Service是没有界面的,这是Activity最大的区别,其实很多人会有误区,认为Service是后台的,其实不是,Service是和Activity同样在主线程中的(可能不会在同一个进程中,但是二者都是主线程的),这两个类都是继承了Context的父类,这两者在很多上是一样的,比如startActivity,获取进程上下文,都可以作为参数,比如Toast.showToast()的第一个参数。可以传调serivce参数。
2.serivce这个组件是被动打开的,一般我们使用这个组件是调用startService()或者 bindService()方法来启动一个Serivce的。
3.关于Serivce的生命周期的阐述:
大家可以看到,两种方法调用的生命流程是不一样的,这里简单说下两种方法的用法:
如果是startService方法的话,经过的oncreate()->onstart()->onstartCommand()->Running,Service运行态。如果我们之前已经使用过startService这个方法,就说是 Service 已经被启动,其他代码再试图调用 startService() 方法,是不会执行 onCreate() 的,但会重新执行一次 onStart() 。
另外一种 bindService() 方法的意思是,这种生命周期的流程的是:
onCreate()->onBind()->Runnning(注: 由于Service 的onStart()方法只有在startService()启动Service的情况下才调用,故使用onStart()的时候要注意这点)把这个 Service 和调用 Service 的客户类绑起来,如果调用这个客户类被销毁,Service 也会被销毁。用这个方法的一个好处是,bindService() 方法执行后 Service 会回调上边提到的 onBind() 方发,你可以从这里返回一个实现了 IBind 接口的类,在客户端操作这个类就能和这个服务通信了,比如得到 Service 运行的状态或其他操作。如果 Service 还没有运行,使用这个方法启动 Service 就会 onCreate() 方法而不会调用 onStart()。
如何绑定呢?一般这两种方法:
public void onServiceConnected(ComponentName className, IBinder myBinder) {
QuerySerivcesBinder binder = (QuerySerivcesBinder) myBinder;
QueryService service = ((QuerySerivcesBinder) myBinder).getService();
service.function1();
service.function2();
......
这种方法是得到一个Serivce对象,然后调用这个Servcice对象的某个方法来实现绑定Service的作用。
还有一种方法:
public void onServiceConnected(ComponentName className, IBinder myBinder) {
QuerySerivcesBinder binder = (QuerySerivcesBinder) myBinder;
myBinder.function1();
myBinder.funtion2();
........
这里得到在在onBind方法中返回的Binder对象,来调用Binder对象的方法。实现绑定。
这里使用bindSerivce有两点要注意:1.如果绑定的serivce的客户端(一般是activity),如果activity销毁后,一定要在activity的onDestory()方法中调用unbindService()方法来,不然的话,会出现 serviceconnectionleak这个异常,导致程序崩溃.2.客户端调用onbindSerivce这个方法之后,传入的参数是ServiceConnection 类,一般会走onServiceConnected这个方法。但是这里说下比如这样的代码:
this.getApplicationContext().bindService(remoteInfoSerivceInt,
conInfoService, Service.BIND_AUTO_CREATE);
//调用绑定service的方法,
someClass.function();
这是客户端调用的方法
private ServiceConnection conInfoService = new ServiceConnection() {
public void onServiceConnected(ComponentName arg0, IBinder service) {
A someClass=new A();
}
这是在serviceConnetion类中的onSericeConneted方法。
大家可以看到以上方法中,在onSericeConneted()方法实例化了一个类,然后在之后的函数中调用someclass的一个方法,事实告诉我,这里一般都会出现异常(引用为空)。原因是什么呢?因为onSericeConneted()方法是异步的。也就是说,在bingservice()之后,onSericeConneted()方法是在另外一个线程中运行的,而客户端不会阻塞等待方法执行完成,继续执行自己的代码。
4.关于service的优先级:现在大家可以至少明白了Service是运行在后台的,那么,是否存在会被KILL的问题呢?
说下Android OS的一个特点:毕竟属于嵌入式领域的OS,在内存管理上一般是有限制,下面列出android OS的进程优先级:
1.前台进程( FOREGROUND_APP)
2.可视进程(VISIBLE_APP )
3. 次要服务进程(SECONDARY_SERVER )
4.后台进程 (HIDDEN_APP)
5.内容供应节点(CONTENT_PROVIDER)
6.空进程(EMPTY_APP)
一般来说,当内存不足的时候,Android 从上述清单中,选择优先级低的来kill,这里,我们可以看到,如果我们的serivce运行在后台中的话,优先级是很低的,那么,我们怎么提高优先级呢?
Notification notification = new Notification(R.drawable.ic_launcher,
getString(R.string.app_name), System.currentTimeMillis());
PendingIntent pendingintent = PendingIntent.getActivity(this, 0,
new Intent(this, AppMain.class), 0);
notification.setLatestEventInfo(this, "uploadservice", "请保持程序在后台运行",
pendingintent);
startForeground(0x111, notification);
这个代码里面,是抛出了一个Notifcation,这样的话,Serivce就变成了一个前台进程,不容易被OS 回收掉。
5.service和activity交互的几种方式:
网上应该有很多关于service和activity的交互方式,我这里就说说平时我在项目里面用的几种交互方式:
1.在同一个进程内部交互:
一般来说,activity和service会绑定,通过ServiceConnection 的onServiceConnected方法中的参数IBinder myBinder,然后我们一般自定义一个binder类,在service中的的onbind方法中返回这个bind类,然后在activity中调用,我们就可以得到service的这个组件的实例。然后调用service组件的方法。但是我们怎么交互呢?比如在service中的有什么消息要传递给activity,去进行UI更新。这个要怎么做呢?其实要是理解了handler机制之后,就可以运用了。
我们首先在service里面写一个内置的接口,`
public interface MsgSender {
public void sendMsgLocationToShow(String userLcation);
public void sendMsgError(int state);
}
这实质上一个接口。这个接口实现了两个方法,一个是向activity发送正常信息的方法,还有一个发送错误信息的方法。然后我们在service写一个setter/getter public方法:
public void setMsgSender(MsgSender msgSender) {
this.mMessageSender = msgSender;
}
public MsgSender getmMessageSender() {
return this.mMessageSender;
}
``
我们在这个被绑定的service要传递的消息信息的activity中这样写:
this.mMessageSender = new QueryService.MsgSender() {
public void sendMsgLocationToShow(String userLcation) {
Message msg = new Message();
Bundle bd = new Bundle();
bd.putInt("errorstate", ComParameter.STATE_RIGHT);
bd.putString("userlocation", userLcation);
msg.setData(bd);
queryServiceHandler.sendMessage(msg);
}
public void sendMsgError(int state) {
Message msg = new Message();
Bundle bd = new Bundle();
bd.putInt("errorstate", state);
msg.setData(bd);
queryServiceHandler.sendMessage(msg);
}
};
上述代码把service中的接口实现了具体功能:其实就是用hanlder来发出请求,接下来我们实例化QueryService,传入hanlder,然后在onServiceConnected中去设置MsgSender为上述代码实例化的MsgSender,就可以实现service和activity的近程通信了
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder myBinder) {
QuerySerivcesBinder binder = (QuerySerivcesBinder) myBinder;
QueryService service = ((QuerySerivcesBinder) myBinder).getService();
service.setMsgSender(mMessageSender);
binder.startQuery();
//绑定到轮询线程要做的事情
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
然后,我们就可以在service中的方法中调用set之后方法,就可以实现service和activity的通信了。这种是近程通信。(在同一个进程内部的)
2.跨进程通信:
我们平时通过AIDL方式进行通信。这种方法在很多资料上也有,这次我们说下另外一种方法:信使
信使,Messenger对象,Messenger对象,是一个基于handler机制的对象,这个对象使用有以下需要注意的
Messenger对象的构造函数:
1.Messenger(Handler handler),这里需要我们传入一个handler,也就是消息处理者。这种messager一般是和handler绑定,用来接收回复的Message对象的
2。Messenger(IBinder Binder),这里可以传入一个binder对象。这种messager对象一般是activity向service发送消息用的。
3.send(Message msg)发送消息给对对方信使:(注:这里的msg比如注明replyTo对象)
我下面给出示例Demo来,给大家看看如何使用:
首先是activity的代码:
public class TipsActivity extends Activity {
private Button test_str_btn;
private Button test_stop_btn;
private GetInfoBinder mybinder;
**private Messenger rMessager;
private Messenger mMessager;**
private UserInfoMessage userInfo;
private IMyService myRemoteSerivce;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.tips_activity);
this.test_str_btn=(Button)this.findViewById(R.id.test_serivce);
this.test_stop_btn=(Button)this.findViewById(R.id.test_stop);
final Intent myintent=new Intent();
myintent.setClass(this, GetInfoSerivce.class);
this.test_stop_btn.setOnClickListener(new OnClickListener(){
public void onClick(View v)
{
sendMessage();
}
});
this.test_str_btn.setOnClickListener(new OnClickListener(){
public void onClick(View v)
{
TipsActivity.this.getApplicationContext().bindService(myintent,connection,Service.BIND_AUTO_CREATE);
}
});
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
//实现按下两次退出
if(keyCode==KeyEvent.KEYCODE_BACK)//选择事件
{
long currentTime=System.currentTimeMillis();
if((currentTime-com.travel_love.paramters.CommonParams.touchTime)>=3000)
{
Toast.makeText(getApplicationContext(), "再按一次退出", Toast.LENGTH_SHORT).show();
com.travel_love.paramters.CommonParams.touchTime=currentTime;
return(true);
}else{
return(false);//注:return为true表示系统拦截这个按下的事件,false表示不拦截事件,执行按键
}
}
return(super.onKeyDown(keyCode,event));
}
private ServiceConnection connection=new ServiceConnection(){
public void onServiceConnected(ComponentName arg0, IBinder getBinder)
{
**rMessager=new Messenger(getBinder);**
**mMessager=new Messenger(handler);**
sendMessage();
}
public void onServiceDisconnected(ComponentName arg0) {
}
};
private final Handler handler=new Handler(Looper.getMainLooper()){
public void handleMessage(Message msg)
{
super.handleMessage(msg);
switch(msg.what)
{
case 1:
if(msg.getData().getString("wait").equals("load_server"))
{
Toast.makeText(getApplicationContext(), "load_server", 1000).show();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
TipsActivity.this.sendMessage();
break;
}
else if(msg.getData().getString("wait").equals("ready_server"))
{
Toast.makeText(getApplicationContext(), "getinfo_my", 1000).show();
break;
}
case 2:
userInfo=(UserInfoMessage)msg.getData().getSerializable("info");
Toast.makeText(getApplicationContext(), userInfo.getUserJob()+"", 1000).show();
Toast.makeText(getApplicationContext(), userInfo.getUserage()+"", 1000).show();
Toast.makeText(getApplicationContext(), userInfo.getUserNickname()+"", 1000).show();
Toast.makeText(getApplicationContext(), userInfo.getUserSex()+"", 1000).show();
}
}
};
private void sendMessage()
{
Message msg=Message.obtain(null, 1);
Bundle bd=new Bundle();
bd.putString("loadUserinfo", "load");
msg.what=1;
**msg.replyTo=mMessager;**
msg.setData(bd);
try {
if(this.rMessager!=null)
{//connection 的方法可能是异步的
this.rMessager.send(msg);//向service发送消息
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
然后是service的代码:
public class GetInfoSerivce extends Service {
private GetInfoBinder mybinder;
private Messenger mMessager;
private UserInfoMessage userInfoMsg;
private Map<String,String> res_map=null;
public void onCreate()
{
super.onCreate();
this.mybinder=new GetInfoBinder();
this.mMessager=new Messenger(handler);
}
public IBinder onBind(Intent intent)
{
return (this.mMessager.getBinder());
}
private final Handler handler=new Handler(Looper.getMainLooper()){
public void handleMessage(Message msg)
{
super.handleMessage(msg);
switch(msg.what)
{
case 1:
Bundle bd=new Bundle();
Toast.makeText(getApplicationContext(), msg.getData().getString("loadUserinfo"), 1000).show();
**mMessager=msg.replyTo;//作为客户端的信使**
Message message=Message.obtain(null, 1);
if(mybinder.loadthread.get_Res_Map()==null)
{
bd.putString("wait", "load_server");
}else{
bd.putString("wait", "ready_server");
}
message.setData(bd);
try {
mMessager.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
};
上述代码中,activity的rMessager是向service发送message对象的信使。`rMessager=new Messenger(getBinder); `
而activity的mMessager是接收service对象的信使。我们可以通过加粗的代码来看看:
1.填入mesage对象的replyTo数值。
msg.replyTo=mMessager;
msg.setData(bd);
try {
if(this.rMessager!=null)
{//connection 的方法可能是异步的
this.rMessager.send(msg);//向service发送消息
}
} catch (RemoteException e) {
e.printStackTrace();
}
2.在service中收到:mesage后,设置service中的信使mMessager为向activity发送的msg的信使:
Bundle bd=new Bundle();
Toast.makeText(getApplicationContext(), msg.getData().getString("loadUserinfo"), 1000).show();
mMessager=msg.replyTo;//作为客户端的信使
Message message=Message.obtain(null, 1);
if(mybinder.loadthread.get_Res_Map()==null)
{
bd.putString("wait", "load_server");
}else{
bd.putString("wait", "ready_server");
}
message.setData(bd);
try {
mMessager.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
“`
这样,我们就可以建立service和activity的通信了。
以上就是我关于service组件的理解:第一次学习这个组件的时候,觉得很抽象,很不好懂,也不知道要怎么用,但是这次经过几次项目的练习之后,对service的理解和用法算是基本了解。
抱歉,这篇博客其实前半部分在三月底写好了,后半部分是这学期结束了才写的,大三下实在太忙。哎,总归是过去了,一切还是有结果。耶!!!!