Android的Service_AIDL笔记

2016-10-04  本文已影响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);
        };
    };
...
}
上一篇下一篇

猜你喜欢

热点阅读