【转】 Pro Android学习笔记(八十):服务(5):访问远程服务

目录(?)[-]

  1. Client的AIDL文件
  2. Client的代码
    1. 建立连接
    2. 请求服务
    3. 断开连接

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处:http://blog.csdn.net/flowingflying/

和Local service不同,remote service可以被其他进程,即其他应用所调用。

Client的AIDL文件在onBind()中将stub对象返回给client,client对stub对象的操作,就如同操作service的对外接口。继上学习了AIDL文件和远程服务的实现,我们将学习client如何访问远程服务。

Client的AIDL文件

Client要访问远程服务,必须清楚服务接口,因此同样需要用AIDL文件来将该接口描述清楚,可以直接从remote service中copy过来,AIDL文件是client和server之间的契约,对双方之间的接口进行说明。

我们将remote service中的IStockQuoteService.aidl文件copy到client的src下面。由于aidl中的包名和client的包名不一样,我们将看到下面的视图:

Client的代码

客户端的activity很简单,主要又两个button组成,第一个是ToggleButton,有两个状态,点击分别连接remote service和断开连接,即bind和unbind。第二个button是请求远程服务,得到的结果用toast显示,只有成功连接远程服务后才能请求服务,故button根据连接状态确实是否enable。

public class MainActivity extends Activity { 
    private ToggleButton bindButton = null; 
    private Button callButton = null; 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
        bindButton = (ToggleButton)findViewById(R.id.bindButton); 
        callButton = (Button)findViewById(R.id.callButton); 
    }

@Override  
    protected void onDestroy() {  
        Log.v("client","Activity onDestroy() is called");
        if(bindButton.isChecked()){  
            unbindService(servCon); //【3】在activity退出时断开连接
        } 
        super.onDestroy(); 
    }

// 按Button的处理 
    public void doClick(View v){ 
        switch(v.getId()){ 
        case R.id.bindButton: 
            Log.v("client","bindButton check ? " + bindButton.isChecked());
            if(bindButton.isChecked()){ //want bind 
               /* 【1】连接远程服务:通过bindService()进行,有三个参数:
                 * 1、service的名字,我们在remote service的AndroidManifest.xml中在<intent-fliter>定义
                 * 2、ServiceConnetion,连接  
                 * 3、flag,表明如服务部存在则自动创建。 
                 * */  
                bindService(new Intent(IStockQuoteService.class.getName()),
                        servCon, 
                        Context.BIND_AUTO_CREATE);
            
            }else{//want unbind  
                callButton.setEnabled(false); 
                unbindService(servCon);    //【3】断开服务连接 
            } 
            break; 
        
        case R.id.callButton: 
            callService(); 
            break; 
            
        default: 
            return; 
        } 
    } 
    //【1】连接远程服务 
    private IStockQuoteService remoteService = null;
   // 【1.1】存储远程服务返回的stub中的代理实例 
    private ServiceConnection servCon = new ServiceConnection() {         
        @Override //【1.2】当远程服务出现异常,如crash,导致连接中断时触发,注意,unBindService()并不会触发此。 
        public void onServiceDisconnected(ComponentName name) { 
            Log.v("client","onServiceDisconnected() is called");
            bindButton.setChecked(false); 
            callButton.setEnabled(false); 
            remoteService = null; 
        } 
        
        @Override //【1.3】当远程服务连接成功时,即bindService()成功时触发,在此需要获得Stub对象,实际是获得Stub中的代理实例,操作这个代理对象,就如同直接操作远程服务,使用起来很是方便
        public void onServiceConnected(ComponentName name, IBinder service) { 
            Log.v("client","onServiceConnected() is called");
           //从接口中获取代理对象,操作这个对象,就如同操作接口一般,下面是自动生成的接口文件中相关的代码, 
            /* 在 IStockQuoteService.Stub:在接口中获取一个IBinder对象作为proxy(代理),有则返回,无则生成之。
             * Cast an IBinder object into an cn.wei.flowingflying.proandroidservice.IStockQuoteService interface,
             * generating a proxy if needed. 
             *  public static cn.wei.flowingflying.proandroidservice.IStockQuoteServiceasInterface(android.os.IBinder obj) 
                { 
                    if ((obj==null)) { 
                        return null; 
                    } 
                    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                    if (((iin!=null)&&(iin instanceof cn.wei.flowingflying.proandroidservice.IStockQuoteService))) {
                        return ((cn.wei.flowingflying.proandroidservice.IStockQuoteService)iin);
                    } 
                    return new cn.wei.flowingflying.proandroidservice.IStockQuoteService.Stub.Proxy(obj);
                } 
             * */ 
            remoteService = IStockQuoteService.Stub.asInterface(service);
            bindButton.setChecked(true); 
            callButton.setEnabled(true); 
        } 
    };

private void callService(){ 
        try{ 
            double value = remoteService.getQuote("WEI"); //【2】请求服务:操作接口代理实例,如果本地操作远程服务接口 
            Log.v("client","callService(): get stockQuote from reference :" + value);
            Toast.makeText(this, "Stock quote is " + value, Toast.LENGTH_LONG).show();
        }catch(RemoteException e){ 
            Log.e("client",e.toString()); 
        } 
    } 
}

client访问远程服务分为三步:【1】建立连接、【2】请求服务、【3】断开连接。

建立连接

远程服务可以被本应用和其他应用调用,client通过bindService()和远程服务建立连接,并从中获取接口代理对象。对应的在远程服务中,如果服务不存在,则服务启动,执行onCreate(),然后执行onBind();如果服务存在则直接执行onBind()。

bindService()是异步,即asynchronouse call,也就是它不会等到服务执行完onCreate()、onBind()然后返回,因此我们需要serviceConnection中的onServiceConnected()回调函数来表明已经成功连接。serviceConnection中的onServiceDisconnected()是在service崩溃的时候触发,如果原来存在连接,Android系统会自动重启服务。我们可以通过DDMS中强制终止remote Service的进程来实现,可以看到触发了onServiceDisConnected(),remote Service会被重新启动,接着连接的onServiceConnected()会被触发。

请求服务

直接操作接口代理实例,操作远程服务就如同在本地操作一样便捷。

断开连接

通过unBindService()断开远程服务的连接,相应地执行远程服务的onDestroy()。也许你会担心,如果我在程序中进行unBindService(),或不会影响其他应用对远程服务的调用。这不需要担心。在上一次学习中,我们在remote service中定义一个静态变量(static int count)进行跟踪。当我们unbind,从新再bind时,我们发现count继续计数,而不是重头开始,说明service实际一直在后台存在,这点也可以通过DDMS查看进程来确定。这就很有趣了,Stub是针对每个接口,而remote service中的onCreate(),onDestroy(),onBind()也是针对连接,或者说针对为每个接口分配的Stub。因此不同应用去连接远程服务,并请求服务或者访问服务时,他们之间不会发生混淆,服务可以分隔每一个连接。当然如果我们不使用静态变量,而采用普通的变量,unbind后再bind,是会从新计数。

本笔记涉及的例子代码,可以在Pro Android学习:Android service小例子中下载。

相关链接: 我的Android开发相关文章

时间: 2024-10-06 00:07:03

【转】 Pro Android学习笔记(八十):服务(5):访问远程服务的相关文章

【转】 Pro Android学习笔记(十九):用户界面和控制(7):ListView

目录(?)[-] 点击List的item触发 添加其他控件以及获取item数据 ListView控件以垂直布局方式显示子view.系统的android.app.ListActivity已经实现了一个只含有一个ListView的Activity,并通过setListAdapter()方法来管理adapter.我们可以通过扩展ListActivity来实现. 我们要在整个屏幕上显示ListView,我们直接继承使用ListActivity,不需要在定义自己的layout XML文件,我们在上一学习中

Pro Android学习笔记(十二):了解Intent(下)

解析Intent,寻找匹配Activity 如果给出component名字(包名.类名)是explicit intent,否则是implicit intent.对于explicit intent,关键就是component 名字,在<intent-fliter>中声明的其他属性被忽略.对于implicit intent,则根据action,category和data来进行匹配.然而一个intent fliter中可以声明多个actions,多个categories,多个data属性,因此可以满

Pro Android学习笔记(十):了解Intent(上)

Android引入了Intent的概念来唤起components,component包括:1.Activity(UI元件) 2.Service(后台代码) 3.Broadcast receiver(处理广播消息的代码) 4.Content provider(抽象数据的代码) Intent基本含义 intent是通知平台处理(唤起)的动作.Android唤起的动作将取决于注册了什么动作.例如我们有个简单的Activity:IntentBaiscViewActivity.在AndroidManife

【转】Pro Android学习笔记(十):了解Intent(上)

目录(?)[-] Intent基本含义 系统的Intent Android引入了Intent的概念来唤起components,component包括:1.Activity(UI元件) 2.Service(后台代码) 3.Broadcast receiver(处理广播消息的代码) 4.Content provider(抽象数据的代码) Intent基本含义 intent是通知平台处理(唤起)的动作.Android唤起的动作将取决于注册了什么动作.例如我们有个简单的Activity:IntentBa

【转】Pro Android学习笔记(十四):用户界面和控制(2):Text类控制

目录(?)[-] TextView 例子1在XML中设置autoLink属性 例子2在代码中设置autoLink属性 EditText AutoCompleteTextView MultiAutoCompleteTextView TextView TextView之前已经使用过很多,直接显示,比较简单.但是我们可以同“autoLink”属性,使用户可以点击一个网络连接.电话号码.邮箱地址.地图地址,通过系统应用打开它们.除此之外,还有其他的font属性,minLines,maxLines等等,都

【转】Pro Android学习笔记(十八):用户界面和控制(6):Adapter和AdapterView

目录(?)[-] SimpleCursorAdapter 系统预置的layout ArrayAdapter 动态数据增插删排序 自定义TextView风格 其他Adapter AdapterView不仅仅是UI,同时还将数据关联到UI上,例如在手机中经常使用的ListView就是AdapterView. ListView.GridView.Spinner和Gallery都是AdapterView,AdapterView是ViewGroup,也就是容器,含有多个UI布局相同的子view.对于Ada

【转】Pro Android学习笔记(二五):用户界面和控制(13):LinearLayout和TableLayout

目录(?)[-] 布局Layout 线性布局LinearLayout 表格布局TableLayout 布局Layout Layout是容器,用于对所包含的view进行布局.layout是view的子类,所以可以作为view嵌入到其他的layout中.Android的layout有LinearLayout.TableLayout,RelativeLayout.FrameLayout.GridLayout. 线性布局:LinearLayout 这是最常用的,有anroid:orientation来确

Android学习笔记(十八)——使用意图筛选器和实现浏览网页(附源码)

使用意图筛选器 点击下载源码 1.创建一个Intents项目,给该项目添加一个新类,命名为MyBrowserActivity,在res/layout文件夹下新增一个browser.xml: 2.在AndroidManifest.xml文件中添加如下代码: 添加权限: <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="a

【转】 Pro Android学习笔记(四十):Fragment(5):适应不同屏幕或排版

目录(?)[-] 设置横排和竖排的不同排版风格 改写代码 对于fragment,经常涉及不同屏幕尺寸和不同的排版风格.我们在基础小例子上做一下改动,在横排的时候,仍是现实左右两个fragment,在竖排时,如下图显示: 屏幕上只显示一个fragment,点击列表上的数目,进入到简介的activity.下面介绍实现的方式. 设置横排和竖排的不同排版风格 在 Pro Android学习笔记(四):了解Android资源(下)的“资源和配置的变更”中,我们介绍了如何同资源文件夹名设置不同资源.缺省的l

【转】 Pro Android学习笔记(八二):了解Package(1):包和进程

文章转载只能用于非商业性质,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处:http://blog.csdn.net/flowingflying/ 在之前,我们已经学习了如何签发apk,见Pro Android学习笔记(六四):安全和权限(1):签发apk,我们将对package做进一步了解. 每个apk都有一个唯一的根包名,在AndroidManifest.xml中定义,如下.开发者为包进行签发,前面和包名绑定,其他开发者不能对这个包进行更新. <?xml version="1