美文网首页
Android的Service_AIDL笔记

Android的Service_AIDL笔记

作者: 逐悦 | 来源:发表于2016-10-04 19:39 被阅读61次

    service:

    service运行在主ui线程,所以执行耗时操作要在线程中    
    
    应用场景:
        用于后台数据处理(音乐播放器)
        空服务(当内存不足时,不会优先关闭应用)    
        流氓软件(双服务)    
    

    aidl:

    进程间的数据通信
    

    首先创建一个AIDL_S项目,创建一个interface接口,定义接口,在把接口的后缀名改名为aidl,刷新项目即可,环境会自动根据aidl创建相应的java类(gen/com.aidl.remoteserver.IRemoteService.java)

    com.aidl.remoteserver.IRemoteService.aidl
    
        package com.aidl.remoteserver;
    
        interface IRemoteService {
            //获得id
            int getId();
            
            //获得自定义对象,在下面讲解如何把自定义对象作为通信数据
        }
    

    然后,创建一个服务

    RemoteService.java
    
    
    package com.aidl.remoteserver;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;
    
    public class RemoteServer extends Service {
    
    //会自动继承IBinder接口
    private IRemoteService.Stub iRs = new IRemoteService.Stub() {
        
        @Override
        public int getId() throws RemoteException {
            // TODO Auto-generated method stub
            return 123;
        }
    };
    
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
    }
    
    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return iRs;
        }
    }
    

    在AndroidManifest.xml中注册服务

     <service 
          android:name="com.aidl.remoteserver.RemoteServer"
          android:exported="true" //可以被外界调用
          android:process=":remote"> //这个地方如果没有,那么同一个程序同时作为客户端/服务端的时候,iRs = IRemoteService.Stub.asInterface(service);这个地方会爆空指针异常
          <intent-filter>
             <action android:name="com.aidl.remoteserver"/>
          </intent-filter>
      </service>
    

    创建AIDL_C项目,在该工程下创建和服务端一样的包,并把服务端的aidl复制到该包下(com.aidl.remoteserver.IRemoteService.aidl)

    在MainActivity.java中调用远程服务,核心代码如下:

    private static final String REMOTE_SERVICE_ACTION = "com.aidl.remoteserver";
    
    Intent service = new Intent(REMOTE_SERVICE_ACTION);
    bindService(service, conn, Context.BIND_AUTO_CREATE);
    
    private  ServiceConnection conn = new ServiceConnection() {
        
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
    
            #mBackTextVeiw.setText("Disconnected");
        }
        
        @Override
        public void onServiceConnected(ComponentName arg0, IBinder service) {
    
            iRs = IRemoteService.Stub.asInterface(service);
            #isBind = true;
            
            try {
                int result = iRs.getId();
                #mBackTextVeiw.setText("Result: " + result);
            } catch (RemoteException e) {
                Log.e("连接", "出错!");
                #isBind = false;
                e.printStackTrace();
            }
        }
    };
    
    记得在销毁的时候解绑服务
    protected void onDestroy() {
        if(isBind){
            this.stopService(service);
        }
        super.onDestroy();
    };
    

    在上面IRemoteService.idle中有一个注释 //获得自定义对象

    把自定义对象作为数据通信,需要

    ①让对象继承Parcelable接口,实现
        //把需要序列化的数据写入到Parcel out对象中
        public void writeToParcel(Parcel out, int arg1) {}
        public int describeContents() {}两个函数
    
    ②创建一个public static final 修饰的Parcelable.Creator<T>对象,实现
        //实现从Parcel source对象创建User对象的功能
        @Override
        public User createFromParcel(Parcel source) {
            return null;
        }
    
        //创建一个类型为T,长度为size的数组(提供外部进行反序列化)
        @Override
        public User[] newArray(int size) {
            return null;
        }
    

    例如:
    User.java

    package com.aidl.remoteserver;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class User implements Parcelable{
    
    private int id;
    private String name;
    
    public User(){}
    
    //构造函数,参数为Parcel对象
    public User(Parcel in) {
        // TODO Auto-generated constructor stub
        readFromParcel(in);
    }
    
    private void readFromParcel(Parcel in) {
        this.id = in.readInt();
        this.name = in.readString();
        
    }
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    
    /*  -----------------------  Parcelable接口函数 */
    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }
    
    //把需要序列化的数据写入到Parcel out对象中
    @Override
    public void writeToParcel(Parcel out, int arg1) {
        // TODO Auto-generated method stub
        out.writeInt(id);
        out.writeString(name);
    }
    
    /*   ------------------------ */
    
    public static final Parcelable.Creator<User> CREATOR = new Creator<User>() {
    
        //实现从Parcel source对象创建User对象的功能
        @Override
        public User createFromParcel(Parcel source) {
            // TODO Auto-generated method stub
            return new User(source);
        }
    
        //创建一个类型为T,长度为size的数组(提供外部进行反序列化)
        @Override
        public User[] newArray(int size) {
            // TODO Auto-generated method stub
            return new User[size];
            }
        };
    }
    

    然后需要在User.java所在包下创建一个User.aidl

    package com.aidl.remoteserver;
    //Parcelable 的P更改为p,表示一种类型
    parcelable User;
    

    然后再IRemoteService.aidl中注释下加入

    User getUser();
    你会发现报错,原因是没有导包,import com.aidl.remoteserver.User;
    
    如
    package com.aidl.remoteserver;
    import com.aidl.remoteserver.User;
    
    interface IRemoteService {
        //获得id
        int getId();
        
        //获得对象
        User getUser();
    }
    

    最后把User.java,User.aidl,IRemoteService.aidl复制到AIDL_C的com.aidl.remoteserver包下
    在MainActivity中就可以使用了

    int result = iRs.getId();
    User user = iRs.getUser();            
    #mBackTextVeiw.setText("Result: " + result + "\n" + user.getId() + " -- " + user.getName());
    

    那么,如何限制其他应用调用该远程服务只让指定的应用调用?
    只需要在服务端的IRemoteService.Stub的实现中重写onTransact方法做限制即可

    RemoteServer.java

    public class RemoteServer extends Service {
    ...
    
        protected String PACKAGE_NAME_PASS = "com.aidl.client";
        private IRemoteService.Stub iRs = new IRemoteService.Stub() {
            
            @Override
            public int getId() throws RemoteException {
                // TODO Auto-generated method stub
                return 123;
            }
    
            @Override
            public User getUser() throws RemoteException {
                // TODO Auto-generated method stub
                User user = new User();
                user.setId(11235);
                user.setName("hello kitty");
                return user;
            }
            
            //在这里做权限认证,return false表示客户端调用远程服务失败,假设允许包名为"com.aidl.client"的客户端通过
            public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws RemoteException {
                String packageName = null;
                String[] packagesForUid = RemoteServer.this.getPackageManager().getPackagesForUid(getCallingUid());
                if(packagesForUid != null && packagesForUid.length > 0){
                    packageName = packagesForUid[0];
                }
                
                if(!packageName.equals(PACKAGE_NAME_PASS )){
                    return false;
                }
                return super.onTransact(code, data, reply, flags);
            };
        };
    ...
    }

    相关文章

      网友评论

          本文标题:Android的Service_AIDL笔记

          本文链接:https://www.haomeiwen.com/subject/nocsyttx.html