Aidl内耗时操作并供客户端调用

项目中有个需求,在项目中实现基站定位,并将基站定位写成aidl,供给其他应用远程调用,实现方式就是 在项目中监听附近的基站,通过获取到基站的信息,去服务器上获取基站的经纬度,所以 问题来了,一般形式的aidl的做法是写一个aidl接口,需要的地方直接绑定服务,然后通过服务调用aidl接口,直接把结果返回给调用端,但是如果aidl的服务端里面是耗时操作的话,直接返回,那么会返回空值,因为数据加载的速度远远要小于代码执行速度,所以需要一个特殊的变种的aidl。

先看一下一般形式的aidl:`

1.第一步 写一个接口,参数类型为int,返回类型为int.
interface ILocationService {

 int registerCallback(int sum);

}
2.第二步 在服务端新建一个Service 
public class RemoteService extends Service {
 public IBinder onBind(Intent intent) {
        return mBinder;
    }
    private final ILocationService.Stub mBinder = new ILocationService.Stub() {
    
        @Override
        public int  registerCallback(int data) throws RemoteException {
   //直接返回计算结果给调用端 (问题就是 如果计算的过程是一个耗时操作,那么没等耗时操作做完就调用了return 那么客户端收到的是一个空值)
       return  data+666; 
    }
    };
}
第三步:在客户端需要调用这个方法的时候加入
  private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,  IBinder service) {
            mService = ILocationService.Stub.asInterface(service);
        }
        public void onServiceDisconnected(ComponentName className) {
            mService = null;
        }
    };
   //获得计算结果
int sum= mService .registerCallback(22); //打印结果为688
第四步 也是最重要的一步:切记 在AndroidManifest.xml里面注册Service,另外如果是分成两个应用调用的时候
要把aidl文件一模一样的从服务端复制到客户端,如果参数是实体类的话,要把实体类的路径在服务端和客户端统一
     <service
            android:name=".RemoteService"
            android:enabled="true"
            android:process=":remote"
            android:exported="true">
            <intent-filter>
                <action android:name="com.daking.aidl.RemoteService" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

------------------------------------------------------华丽的分割线--------------------------------------------------------
以上方案只能解决正常的aidl通讯问题,参数是自定义实体类,和耗时访问操作还没有解决,所以以下是针对这个问题的解决方案:

第一步 依旧是新建aidl文件,区别是新增一个回调的aidl接口
package com.easyliu.demo.aidl;
//不要忽略这个import  aidl文件不会自动生成,需要自己去引用这个包,地址要修改成你自己的地址
import com.easyliu.demo.aidl.IRemoteServiceCallback;

    interface IRemoteService {

    void registerCallback(IRemoteServiceCallback cb);

    void unregisterCallback(IRemoteServiceCallback cb);
}
第二步 按你的需要建一个回调实体类的接口
package com.easyliu.demo.aidl;
//Station是我的实体类 里面存放着经纬度,基站等信息
import com.easyliu.demo.aidl.Station;

 interface IRemoteServiceCallback {
 //不需要返回类型,因为做耗时操作 return是没用的  传入的参数类型是Station 
    void valueChanged(in Station value); //in 输入,out 输出,
}
第三步 新建实体类 并建立实体类的aidl
首先先建立 实体类的aidl 否则可能编辑器会报文件必须唯一的问题
package com.easyliu.demo.aidl;
parcelable Station;

然后再建真正的实体类Station 切记 一定要实现Parcelable接口 给实体类序列化
这个实体类要存放在和aidl文件一直的java目录下,否则会报找不到这个实体类的错误
public class Station implements Parcelable{
    private String cell_id;// cellid连接基站编码
    private String lac;// lac连接基站位置区域码
    private String mcc;// mcc MCC国家码
    private String mnc;// mnc MNC网号
    private String signalstrength;// signalstrength连接基站信号强度
    private String NetType;
    private String lat;
    private String log;
    public Station(){}
      ……
    }
}
第四步 Service改造
public class RemoteService extends Service {

    Station station2;
     RemoteCallbackList<IRemoteServiceCallback> mCallbacks   = new RemoteCallbackList<IRemoteServiceCallback>();
    private int mValue = 0;
    private static final int REPORT_MSG = 1;
    private int position=0;
    @Override
    public void onCreate() {
       //在oncreate里面可以放你的耗时操作
        doInBackGround(stationBeans);
    }

    @Override
    public void onDestroy() {
        mCallbacks.kill();
        mHandler.removeMessages(REPORT_MSG);
    }
    
    @Override
    public IBinder onBind(Intent intent) {
            return mBinder;
    }

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        public void registerCallback(IRemoteServiceCallback cb) {
            if (cb != null) mCallbacks.register(cb);
        }
        public void unregisterCallback(IRemoteServiceCallback cb) {
            if (cb != null) mCallbacks.unregister(cb);
        }
    };


    private final Handler mHandler = new Handler() {
        @Override public void handleMessage(Message msg) {
            switch (msg.what) {
                case REPORT_MSG: {
                .//开始回调 一定要加上 否则回调失败
                    final int N = mCallbacks.beginBroadcast();
                    for (int i=0; i<N; i++) {
                        try {
                            //将收到的值回调给aidl定义的方法
                            mCallbacks.getBroadcastItem(i).valueChanged(station2);
                        } catch (RemoteException e) {
                        }
                    }
                    //结束回调
                    mCallbacks.finishBroadcast();
      
                } break;
                default:
                    super.handleMessage(msg);
            }
        }
    };

    public void doInBackGround(final List<Station> data) {
         //网络请求等耗时操作 
            OkGo.<String>post(Static.Url)
                    .tag(this)
                    .params("cid", data.get(0).getCell_id())
                    .params("lac", data.get(0).getLac())
                    .params("mnc", data.get(0).getMnc())
                    .execute(new StringCallback() {
                        @Override
                        public void onSuccess(Response<String> response) {
                            ResultData newInstructionEntity = new Gson().fromJson(response.body(), ResultData.class);
                            station2  = new Station();
                            station2.setLac(String.valueOf(newInstructionEntity.getData().getLatitude()));
                            station2.setLog(String.valueOf(newInstructionEntity.getData().getLongitude()));
                            //获得请求的返回值,并通过handler发消息通知
                            mHandler.sendEmptyMessage(REPORT_MSG);

                        }
                    })
      
            }

        }}
    }

第五步:
客户端调用
          // 绑定启动Service
            Intent intent=new Intent(IRemoteService.class.getName());
            intent.setPackage("com.easyliu.demo.aidldemo");
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

 private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,  IBinder service) {
            mService = ILocationService.Stub.asInterface(service);
            /加上绑定监听/
             try {
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {
            }
        }
        public void onServiceDisconnected(ComponentName className) {
            mService = null;
        }
    };

 private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
        @Override
        public void valueChanged(Station value) throws RemoteException {
        //这个回调就是获得结果的回调  value就是从服务器的请求结果  成功在客户端获取到服务端请求
            ……
        }
    };

笔记:
1.RemoteCallbackList。 使用这个类只要在Service使用单例模式就可以了,使用register和unregister方法来添加客户端的回调,使用时,先beginBroadcast,在getBroadcastItem,最后finishBroadcast。他的主要作用是可以把多个callback保存到列表里,在合适的时机同时回调,也可以防止重复的调用相同的任务
2. aidl通讯失败的大多数原因就是 aidl文件放的目录不对,或者aidl里面引用的实体类或者其他文件没有导包,和存放真正实体类的目录客户端和服务端没有统一。
3. 实体类一定要实现系列化才可以通过aidl通信

源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值