首页 > 其他 > 详细

嵌在Service组件中的Binder实例

时间:2015-10-03 15:42:51      阅读:462      评论:0      收藏:0      [点我收藏+]

其实是为了弄明白,Android的Binder架构,才从四大组件之一的Service组件下手(service大概有两种使用,绑定的和不绑定的),主要目的还是看Binder,service以及aidl都不是必须的。

(这里也不会去讲Binder,只是顺带着把Binder引出来)

没事儿补充个基础,我复习,你回顾,挺好的:


一、采用start的方式开启服务

生命周期如下:(这种方式里面onStart()过时了,代替的是onStartCommand())

                                          

开启服务: onCreate()---> onStartCommand()  ---> onDestory();

如果服务已经开启,不会重复的执行onCreate(), 而是会调用onStart()和 onStartCommand();

服务停止的时候 onDestory().

服务只会被停止一次

 

二、绑定的方式开启服务

onCreate() --->onBind();--->onUnbind()-->onDestory();

绑定服务不会调用onstartcommand();


嵌入在Service组件中的Binder,如果你使用起来都不是很清楚或者刚刚才知道使用模式,请记得我的经验:这个时候,你就不要去管什么原理了,因为跟你说了也白说,就算你现在懂了,也会马上忘记。(长得帅的和天才除外啊:-P)
Binder架构在Android框架层广泛应用,设计思想有利有弊,要花时间。


记住它的使用模式,一定记清楚它的使用模式,以及清楚内部有哪些方法,都是怎么用的(能不能被客户端直接调用)或者都是谁在调用。

最后我还是会总结一下的,啰嗦到此完毕,请看作者的原文:)

 

本文转载自:http://blog.csdn.net/zhgxhuaa/article/details/33742561


一个Binder实例

我们Binder的学习将从下面的一个实例开始。根据Android文档中的描述,创建一个Binder服务主要包括如下3步:

技术分享

(你看别人英文用词还是蛮准确的呀)

下面具体看一下在eclipse中是如何开发一个Binder应用的。

第一步:在工程目录下定义aidl文件。

package com.zxh.ipc;
 
import com.zxh.ipc.PermissionInfo;
 
interface ITestManager{
    int checkPermission(String permName, String pkgName);
     
    int checkUidPermission(String permName, int uid);
     
    boolean addPermission(in PermissionInfo info);
     
    void removePermission(String name);
 
    boolean isProtectedBroadcast(String actionName);
}

第二步:在定义aidl后,eclipse在工程目录的gen目录下会自动生成对应的接口,下面是有aidl自动生成的Java代码: (看方法名,类名,签名就好,具体实现大致看看就行)

package com.zxh.ipc;
  
public interface ITestManager extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.zxh.ipc.ITestManager
    {
        private static final java.lang.String DESCRIPTOR = "com.zxh.ipc.ITestManager";
  
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
  
        /**
         * Cast an IBinder object into an com.zxh.ipc.ITestManager interface, generating a proxy if needed.
         */
        public static com.zxh.ipc.ITestManager asInterface(android.os.IBinder obj)
        {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zxh.ipc.ITestManager))) {
                return ((com.zxh.ipc.ITestManager) iin);
            }
            return new com.zxh.ipc.ITestManager.Stub.Proxy(obj);
        }
  
        @Override
        public android.os.IBinder asBinder()
        {
            return this;
        }
  
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
                throws android.os.RemoteException
        {
            switch (code)
            {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_checkPermission: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    int _result = this.checkPermission(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_checkUidPermission: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.checkUidPermission(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_addPermission: {
                    data.enforceInterface(DESCRIPTOR);
                    com.zxh.ipc.PermissionInfo _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.zxh.ipc.PermissionInfo.CREATOR.createFromParcel(data);
                    }
                    else {
                        _arg0 = null;
                    }
                    boolean _result = this.addPermission(_arg0);
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
                case TRANSACTION_removePermission: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.removePermission(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_isProtectedBroadcast: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    boolean _result = this.isProtectedBroadcast(_arg0);
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
  
        private static class Proxy implements com.zxh.ipc.ITestManager
        {
            private android.os.IBinder mRemote;
  
            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }
  
            @Override
            public android.os.IBinder asBinder()
            {
                return mRemote;
            }
  
            public java.lang.String getInterfaceDescriptor()
            {
                return DESCRIPTOR;
            }
  
            @Override
            public int checkPermission(java.lang.String permName, java.lang.String pkgName)
                    throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(permName);
                    _data.writeString(pkgName);
                    mRemote.transact(Stub.TRANSACTION_checkPermission, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
  
            @Override
            public int checkUidPermission(java.lang.String permName, int uid) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(permName);
                    _data.writeInt(uid);
                    mRemote.transact(Stub.TRANSACTION_checkUidPermission, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
  
            @Override
            public boolean addPermission(com.zxh.ipc.PermissionInfo info) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((info != null)) {
                        _data.writeInt(1);
                        info.writeToParcel(_data, 0);
                    }
                    else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPermission, _data, _reply, 0);
                    _reply.readException();
                    _result = (0 != _reply.readInt());
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
  
            @Override
            public void removePermission(java.lang.String name) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(name);
                    mRemote.transact(Stub.TRANSACTION_removePermission, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
  
            @Override
            public boolean isProtectedBroadcast(java.lang.String actionName) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                boolean _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(actionName);
                    mRemote.transact(Stub.TRANSACTION_isProtectedBroadcast, _data, _reply, 0);
                    _reply.readException();
                    _result = (0 != _reply.readInt());
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
  
        static final int TRANSACTION_checkPermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_checkUidPermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addPermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_removePermission = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_isProtectedBroadcast = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
    }
  
    public int checkPermission(java.lang.String permName, java.lang.String pkgName) throws android.os.RemoteException;
  
    public int checkUidPermission(java.lang.String permName, int uid) throws android.os.RemoteException;
  
    public boolean addPermission(com.zxh.ipc.PermissionInfo info) throws android.os.RemoteException;
  
    public void removePermission(java.lang.String name) throws android.os.RemoteException;
  
    public boolean isProtectedBroadcast(java.lang.String actionName) throws android.os.RemoteException;
}

第三步:继承上一步生成的接口,实现自己的Service类:

package com.zxh.server;
 
import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.zxh.ipc.ITestManager;
 
import com.zxh.ipc.PermissionInfo;
public class TestService extends Service{
    private final static String TAG = "zxh";
 
    class TestManagerService extends ITestManager.Stub{
         
        @Override
        public int checkPermission(String permName, String pkgName)
                throws RemoteException {
            Log.i(TAG, "checkPermission server");
            return 0;
        }
 
        @Override
        public int checkUidPermission(String permName, int uid)
                throws RemoteException {
            Log.i(TAG, "checkUidPermission server");
            return 0;
        }
 
        @Override
        public boolean addPermission(PermissionInfo info) throws RemoteException {
            Log.i(TAG, "addPermission server");
            return false;
        }
 
        @Override
        public void removePermission(String name) throws RemoteException {
            Log.i(TAG, "removePermission server");
        }
 
 
        @Override
        public boolean isProtectedBroadcast(String actionName)
                throws RemoteException {
            Log.i(TAG, "isProtectedBroadcast server");
            return false;
        }
    }
     
     
    private TestManagerService mService = new TestManagerService();
     
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("zxh", "unbindService");
        return mService;
    }
     
    @Override
    public void unbindService(ServiceConnection conn) {
        super.unbindService(conn);
        Log.i("zxh", "unbindService");
    }
}


客户端调用:

package com.zxh.server;
 
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.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
 
import com.zxh.ipc.ITestManager;
 
public class MainActivity extends Activity {
 
    private ITestManager manager = null;
     
    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i("binder", "onServiceDisconnected");
        }
         
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            manager = ITestManager.Stub.asInterface(service);
        }
    };
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.startServer).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService(new Intent(MainActivity.this, TestService.class), connection, Service.BIND_AUTO_CREATE);
            }
        });
         
        findViewById(R.id.stop).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(connection);
            }
        });
         
        findViewById(R.id.check).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    manager.checkPermission("permName", "pkgName");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

到这里我们就实现了一个完整的Binder实例,下面是工程目录的截图:

技术分享



下面来介绍一下这个实例中涉及到的一些知识点。

AIDL

除了eclipse,也可以使用Android开发包中提供的aidl命令工具来生成,如下是adil命令:

技术分享


关于adil的详细介绍可以查看Android官方文档,地址为:http://developer.android.com/intl/zh-cn/guide/components/aidl.html

想要了解aidl处理过程的朋友也可以自己查看aidl的源代码,位于@/frameworks/base/tools/adil。

AIDL编译产生的类

下面我们来分析一下aidl编译产生的ITestManager类。

从ITestManager中可以看出,ITestManager实现了IInterface接口。在ITestManager类中共包含了三部分内容,分别是:

  • 我们在ITestManager.adil中定义的接口。

  • Stub类:主要由Server端实现,Server端通过继承本类,来提供具体的处理逻辑。

  • Proxy类:客户端代理,通过mRemote成员变量访问远程Binder服务。

为了实现我们在ITestManager自定义接口的跨进程通信,Stub类和Proxy都实现了ITestManager接口。Stub自己继承了Binder类来实现Binder机制,Proxy则以引用的方式实现Binder机制。

客户端和服务端的通信主要通过onTransact方法实现,客户端和服务端的数据传递通过Parcel类完成。这里需要注意一个问题:就是在客户端写入数据的顺序与在服务端读取数据的顺序必须是一致的。

虽然在上面的实现中,我们定义了一个Service类TestService,但这里要说明的是Service甚至adil都不是必须的,使用者完全可以自己定义。






为了不枉费原作者花了这么大的精力,写这么好的文章,我还是给总结总结扩展扩展:(把作者写的精华捧出来)


昨天刚刚看过代理模式。你看这里用aidl或者谈论binder的时候,多像。(那个aidl定义的接口是个酱油么?)

Binder说到底也就是跨进程的一种实现方式。

其他组件,调用service组件的时候(你自定义的服务),也就是在跨进程,自然要用到Binder机制,然而Android可能在设计的时候留有余地,或为了访问控制,或为了安全因素,或为了后期调整架构的方便,所以它绝对不直接返回给你Binder对象,它返回给你IBinder的代理。


然后通过Binder你才能实现夸进程通信(意思是你写的服务和你的应用进程才能通信,service组件才能为你的应用提供服务)。

 manager = ITestManager.Stub.asInterface(service);

 /**
         * Cast an IBinder object into an com.zxh.ipc.ITestManager interface, generating a proxy if needed.
         */
        public static com.zxh.ipc.ITestManager asInterface(android.os.IBinder obj)
        {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zxh.ipc.ITestManager))) {
                return ((com.zxh.ipc.ITestManager) iin);
            }
            return new com.zxh.ipc.ITestManager.Stub.Proxy(obj);
        }

看到没有,通常情况下,你用的就是binder的代理(代理完成任务)(代码全部取自原文)


再来说aidl定义的接口是个酱油么?

原作者,最后也说啦,对于binder机制,service啊,aidl啊,都不是必须的。

但是我要说adil定义的接口在这个案例中不能说是酱油啊,也就是必须要的意思。

回顾我一下,我再转载“代理模式”时,提出过的重点:代理和实体必须实现同样的接口(client调用时都是通过接口调用的,这是面向抽象编程的基础)。

接着你在跑去看看原文。(Binder是IBinder的默认实现,可以吧Sub当做Binder的实体或者引用,我建议你理解的方便就叫实体,实际上它应该叫做引用,如果你看过源码的话) 

public static abstract class Stub extends android.os.Binder implements com.zxh.ipc.ITestManager

然后作为Binder的Stub内部类Proxy是这样的:(把自己的代理放在内部,也是蛮机智的呀,好处我就不多说了哈哈,我忍不住,还是说说吧:1. 一方面自己的内部类,private起来,不容易让别人动,2.另一方面,代理对象实际上干事儿还是得实体对象上场,内部类作为外部类的后门儿,调用外部类的东西真是方便之极啊)

private static class Proxy implements com.zxh.ipc.ITestManager

如此一来,我们调用的时候,就可以通过ITestManager接口调用特殊的Binder了。

为啥我又说是特殊的Binder? 因为Stub这货是个fake。英文全称 Substitute(你用eclipse自动生成代码的时候没有看到Stub这个缩写么?),意思是替代,暂代,有待完善的。(简单的说就是默认实现的意思)

abstract class Stub. 被代理的应该是实体才是啊,这里居然用到了abstract,也就是在设计的时候,其实是留给app开发人员空间让它们实现的。也就是说,代理实际上代理的是, Stub的继承类(非abstract)这个真正的Binder实体(好吧,我这里只能说它是实体了,先这样,不谈Binder架构,只谈此处代理模式),证据是:(看代码,自定义service类的成员)

class TestManagerService extends ITestManager.Stub{
         
        @Override
        public int checkPermission(String permName, String pkgName)
                throws RemoteException {
            Log.i(TAG, "checkPermission server");
            return 0;
        }
 
        @Override
        public int checkUidPermission(String permName, int uid)
                throws RemoteException {
            Log.i(TAG, "checkUidPermission server");
            return 0;
        }
 
        @Override
        public boolean addPermission(PermissionInfo info) throws RemoteException {
            Log.i(TAG, "addPermission server");
            return false;
        }
 
        @Override
        public void removePermission(String name) throws RemoteException {
            Log.i(TAG, "removePermission server");
        }
 
 
        @Override
        public boolean isProtectedBroadcast(String actionName)
                throws RemoteException {
            Log.i(TAG, "isProtectedBroadcast server");
            return false;
        }
    }
     
     
    private TestManagerService mService = new TestManagerService();


总结了说,就一句话,用aidl的接口,既方便了我们实现service中自己的逻辑,又顺顺当当通过代理模式用上了android 已经实现的Binder机制。这一石二鸟的事儿,你看别人设计师多牛!!


至于后面的client调用的时候(就是MainActivity啦),一旦onBinder传入Connection对象,我们也就正式通过代理模式获取了Binder的代理,当然你看到的是,ITestManager接口的对象(毕竟我们是通过接口调用的,编译类型是ITestManager,运行时类型不是哦,是Stub.Proxy类型)


对了还有一件事儿:

public interface ITestManager extends android.os.IInterface

IInterface是个啥?我们的接口为啥要继承它?难道就是什么标记接口(比如Serializable接口)?

原文中Stub类中有这么一段:(这段话好像是在表明身份啊,“我就是Binder”----看到IBinder接口直接就可以这么记忆了 )

 @Override
 public android.os.IBinder asBinder()
 {
      return this;
 }

好吧,我们不猜了,看源码(5.1)

技术分享



客户端,也就是那些英文注释说的Local用的是代理模式,达到调用特殊的Binder的目的,

那么真正在底层服务的Binder(也就是英文注释中的Remote)是咋搞的呢?

说太多,说不透,还容易暴露我水平差,那就不好了。

哈哈。不说了,不贪多。

(如果你想了解完整的思路,建议你去系统地看老罗的书----老罗博客中说销量不大满意,所以我给推荐推荐)


merlin

2015/10/3

嵌在Service组件中的Binder实例

原文:http://my.oschina.net/wizardmerlin/blog/513334

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!