远程服务例子3

来源:互联网 发布:网络剧广告植入 编辑:程序博客网 时间:2024/06/04 17:41

本例子将会介绍远程服务回调接口的用法以及注销回调接口的方法、权限校验、远程服务挂掉后重连的方法等。

直接上代码,远程服务aidl接口及服务代码:

package com.example.aidl4.aidl;import java.util.List;import com.example.aidl4.aidl.PersionParcel;interface IAddPersionListener {    void addPersion(in PersionParcel newPersion);}


 

package com.example.aidl4.aidl;import java.util.List;import com.example.aidl4.aidl.PersionParcel;import com.example.aidl4.aidl.IAddPersionListener;interface IPersionBinder {    String getName(in PersionParcel p);    List<PersionParcel> getAllPersions();    void registerListener(IAddPersionListener listener);    void unregisterListener(IAddPersionListener listener);}


 

public class AidlService extends Service {    private static final String mAllowPackages[] = { "com.example.aidl5" };    private AtomicBoolean mIsServiceRunning = new AtomicBoolean(true);    private CopyOnWriteArrayList<PersionParcel> mPersionList = new CopyOnWriteArrayList<PersionParcel>();    private RemoteCallbackList<IAddPersionListener> mListenerLists = new RemoteCallbackList<IAddPersionListener>();    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    @Override    public void onCreate() {        super.onCreate();        mPersionList.add(new PersionParcel("zhangsan", 20, "男"));        new Thread() {            @Override            public void run() {                // 每隔30秒添加一人并调用接口中定义的方法                while (mIsServiceRunning.get()) {                    SystemClock.sleep(30 * 1000);                    PersionParcel persionParcel = new PersionParcel("lili", 20,                            "女");                    onAddPersion(persionParcel);                }            }        }.start();    }    private void onAddPersion(PersionParcel persionParcel) {        mPersionList.add(persionParcel);        final int N = mListenerLists.beginBroadcast();        for (int i = 0; i < N; i++) {            try {                mListenerLists.getBroadcastItem(i).addPersion(persionParcel);            } catch (RemoteException e) {                e.printStackTrace();            }        }        mListenerLists.finishBroadcast();    }    @Override    public void onDestroy() {        super.onDestroy();        mIsServiceRunning.set(false);    }    private Binder mBinder = new IPersionBinder.Stub() {        @Override        public String getName(PersionParcel p) throws RemoteException {            return p.getName();        }        @Override        public List<PersionParcel> getAllPersions() throws RemoteException {            return mPersionList;        }        @Override        public void registerListener(IAddPersionListener listener)                throws RemoteException {            mListenerLists.register(listener);        }        @Override        public void unregisterListener(IAddPersionListener listener)                throws RemoteException {            mListenerLists.unregister(listener);        };        @Override        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)                throws RemoteException {            String pkgName = null;            String[] packages = AidlService.this.getPackageManager()                    .getPackagesForUid(getCallingUid());            if (packages != null && packages.length > 0) {                pkgName = packages[0];            }            for (String item : mAllowPackages) {                if (!item.equals(pkgName)) {                    System.out.println("pkgName:" + pkgName + "被拒绝访问服务");                    return false;                }            }            return super.onTransact(code, data, reply, flags);        }    };}


客户端调用代码:

public class MainActivity extends Activity {    private IPersionBinder mBinder;    private Handler mHandler = new Handler() {        public void handleMessage(android.os.Message msg) {            System.out.println("添加了新人");        };    };    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {                @Override        public void binderDied() {            System.out.println("线程名1:" + Thread.currentThread().getName());            if (mBinder == null) {                return;            }            mBinder.asBinder().unlinkToDeath(mDeathRecipient, 0);            mBinder = null;            // 这里重新绑定远程服务        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bindAidlService();        Button button = (Button) findViewById(R.id.button);        button.setOnClickListener(new OnClickListener() {                        @Override            public void onClick(View v) {                if (mBinder != null) {                    try {                        List<PersionParcel> list = mBinder.getAllPersions();                        for (PersionParcel item : list) {                            System.out.println("姓名:" + item.getName() + ", 年龄:"                                    + item.getAge() + ", 性别:" + item.getSex());                        }                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }            }        });    }    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(mConn);        if (mBinder != null && mBinder.asBinder().isBinderAlive()) {            try {                mBinder.unregisterListener(mListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }    }    private void bindAidlService() {        Intent service = new Intent("com.example.aidl4.AidlService");        bindService(service, mConn, Context.BIND_AUTO_CREATE);    }    private ServiceConnection mConn = new ServiceConnection() {                @Override        public void onServiceDisconnected(ComponentName name) {            mBinder = null;            System.out.println("线程名:" + Thread.currentThread().getName());        }                @Override        public void onServiceConnected(ComponentName name, IBinder service) {            mBinder = IPersionBinder.Stub.asInterface(service);            try {                mBinder.registerListener(mListener);                service.linkToDeath(mDeathRecipient, 0);            } catch (RemoteException e) {                e.printStackTrace();            }        }    };    private IAddPersionListener mListener = new IAddPersionListener.Stub() {        @Override        public void addPersion(PersionParcel newPersion)                throws RemoteException {            mHandler.sendEmptyMessage(0);        }    };}


注销自定义接口时,需要使用RemoteCallbackList,它是系统专门提供的用于删除跨进程listener接口的。

public class RemoteCallbackList<E extends IInterface>

RemoteCallbackList是一个泛型,支持任意的AIDL接口。

工作原理:它内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型。

ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();

其中Callback中封装了真正的远程listener,当客户端注册listener时,它会把这个listener的信息存入mCallbacks中,key和value分别通过下面的方式获得:

IBinder key = listener.asBinder();

Callback value = new Callback(listener, cookie);

虽然说跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这些新生成的对象有一个共同点,那就是它们底层的Binder对象是同一个。RemoteCallbackList还有一个很有用的功能,那就是当客户端进程终止后,它能够自动移除客户端所注册的listener。

遍历RemoteCallbackList时必须按照代码中所示,其中beginBroadcast和finishBroadcast必须配对使用。

Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,此时客户端到服务端的Binder连接断裂,会导致远程调用失败。为了解决这个问题,BInder提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath可以给Binder设置一个死亡代理,当Binder死亡时就会收到通知,此时可以重新发起连接请求从而恢复连接。

使用方法如代码上所示。在onServiceDisconnected方法中也可以重连远程服务。另外通过Binder的isBinderAlive方法也可以判定Binder是否死亡。

区别:onServiceDisconnected在客户端的UI线程中被回调,而binderDied是在客户端的Binder线程池中被回调。也就是说,在binderDied方法中不能访问UI。

0 0
原创粉丝点击