浅谈对Android Service 的理解

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的生命周期的阐述:这就是关于Service的两种生命周期图解
大家可以看到,两种方法调用的生命流程是不一样的,这里简单说下两种方法的用法:
如果是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的理解和用法算是基本了解。
抱歉,这篇博客其实前半部分在三月底写好了,后半部分是这学期结束了才写的,大三下实在太忙。哎,总归是过去了,一切还是有结果。耶!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值