一篇复习了Android生命周期和本地Service的使用,这一篇继续总结一下Android远程Service的使用,远程Service就是在新的进程中开启service,这样会遇到一个问题,就是进程间通信的问题。Android系统的进程之间不能共享内存,那怎么传递对象呢,需要把对象弄成操作系统可以识别的形式,在Android中,可以采用AIDL来公开服务的接口,采用远程过程调用(Remote Procedure Call,RPC)和代理模式来实现跨进程通信。AIDL:Android Interface Definition Language,即Android接口描述语言,ADT会根据aidl文件在gen目录下生成对应的java接口文件。我们需要手工创建一个Service的子类并实现生成的java接口,然后在AndroidManifest.xml文件中进行配置。远程服务可以为多个客户端服务,由于涉及到数据通信,一般采用bindService的方式。

下面我们通过一个demo来看看AIDL是如何实现的。

首先创建服务端Android工程。目录结构如图


代码如下
User.java,为了实现跨进程数据传递,需要实现Parcelable 接口,是一种序列化方式。

public class User implements Parcelable {     private int id;    private String name;     public User() {    }     public User(Parcel parcel) {        this.id = parcel.readInt();        this.name = parcel.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;    }     @Override    public int describeContents() {        // TODO Auto-generated method stub        return 0;    }     @Override    public void writeToParcel(Parcel dest, int flags) {        //顺序需与构造函数中read保持一致        dest.writeInt(id);        dest.writeString(name);    }     public static final Parcelable.Creator<User> CREATOR = new Creator<User>() {         @Override        public User createFromParcel(Parcel source) {            return new User(source);        }         @Override        public User[] newArray(int size) {            return new User[size];        }    }; }

User.adil

parcelable User;

IRemoteService.aidl

/**  远程的服务IRemoteService.aidl*/interface IRemoteService {    //返回基本类型    int getId();    //返回对象    User getUser(); }

RemoteService.java

public class RemoteService extends Service {     @Override    public void onCreate() {        Log.i(this.getClass().getName(), "onCreate");    }     @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i(this.getClass().getName(), "onStartCommand");        return super.onStartCommand(intent, flags, startId);    }     @Override    public void onDestroy() {        Log.i(this.getClass().getName(), "onDestroy");    }     @Override    public IBinder onBind(Intent intent) {        return mRemoteServiceBinder;    }     @Override    public boolean onUnbind(Intent intent) {        Log.i(this.getClass().getName(), "onUnbind");        return super.onUnbind(intent);    }     @Override    public void onRebind(Intent intent) {        Log.i(this.getClass().getName(), "onRebind");        super.onRebind(intent);    }     IRemoteService.Stub mRemoteServiceBinder = new IRemoteService.Stub() {         @Override        public User getUser() throws RemoteException {            User user = new User();            user.setId(123456);            user.setName("alexzhou");            return user;        }         @Override        public int getId() throws RemoteException {            return 123456;        }     };}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.alexzhou.aidl.server"    android:installLocation="internalOnly"    android:versionCode="1"    android:versionName="1.0" >     <uses-sdk        android:minSdkVersion="8"        android:targetSdkVersion="10" />     <application        android:icon="@drawable/ic_launcher"        android:label="@string/app_name" >        <activity            android:name=".RemoteServiceActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                 <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>         <!-- android:process="name" name的值是随便取的 ,android:exported:是否允许被其它程序调用-->        <service            android:name="com.alexzhou.aidl.server.RemoteService"            android:exported="true"            android:process=":remote" >            <intent-filter>                <action android:name="com.alexzhou.service.REMOTE_SERVICE" />            </intent-filter>        </service>     </application> </manifest>

服务端的Activty是自动生成的。没写任何其他代码,这里就不贴出来了。
接着需要创建一个客户端Android工程,目录结构如下图:


代码如下:
先把User.java,User.aidl,IRemoteService.aidl三个文件复制到客户端,注意包名必须跟服务端所在的包名一致。
创建客户端主界面类ClientActivity.java

public class ClientActivity extends Activity implements OnClickListener{     private TextView callbackView;    private Button bindButton;    private boolean isBind;    private final String REMOTE_SERVICE_ACTION = "com.alexzhou.service.REMOTE_SERVICE";     private IRemoteService mRemoteService;     /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        findViews();        setListeners();        callbackView.setText("no callback");    }     private void findViews() {        callbackView = (TextView)this.findViewById(R.id.callback);        bindButton = (Button)this.findViewById(R.id.bind);     }     private void setListeners() {        bindButton.setOnClickListener(this);    }     @Override    public void onClick(View view) {        switch(view.getId()) {            case R.id.bind:                this.bindService(new Intent(REMOTE_SERVICE_ACTION), mConntectin, Context.BIND_AUTO_CREATE);                callbackView.setText("binding...");                break;        }    }     private ServiceConnection mConntectin = new ServiceConnection() {         @Override        public void onServiceDisconnected(ComponentName arg0) {            callbackView.setText("Disconnected!");        }         @Override        public void onServiceConnected(ComponentName name, IBinder binder) {            mRemoteService = IRemoteService.Stub.asInterface(binder);            isBind = true;            try {                int id = mRemoteService.getId();                User user = mRemoteService.getUser();                StringBuffer buffer = new StringBuffer();                buffer.append("id:");                buffer.append(id);                buffer.append("name");                buffer.append(user.getName());                callbackView.setText(buffer.toString());            } catch (RemoteException e) {                e.printStackTrace();            }        }    };     @Override    protected void onDestroy() {        if(isBind) {            this.unbindService(mConntectin);            isBind = false;        }        super.onDestroy();    }}

布局文件main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:orientation="vertical" >  <TextView android:id="@+id/callback"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            />     <Button android:id="@+id/bind"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@string/bind_remote_service_text"/> </LinearLayout>

现在运行你的客户端程序,点击绑定远程服务按钮,如果一切顺利,将会看到服务端返回的信息。如图:


通过eclipse控制台Devices视图,可以看到远程服务进程已启动,如图:



可能会遇到的问题和解决办法:
(1)AIDL unable to start service not found
客户端和服务端activty包名相同了,改成不同就可以
(2)Not allowed to bind to service Intent
在服务端配置文件中把android:exported = false 改成 true,android:exported表示是否允许被其它程序调用
(3)Binder invocation to an incorrect interface
客户端aidl文件的包名跟service的包名不一样,改成一样的就ok


转载请注明来自:Alex Zhou,本文链接:http://codingnow.cn/android/529.html

更多相关文章

  1. Android自学笔记:应用程序基本原理
  2. Android中String资源文件的format方法
  3. Android(安卓)应用安全开发总结
  4. Android使用cmake+ndk输出原生Log
  5. 关于AVD启动失败的问题--找不到文件篇
  6. android之视频播放
  7. Android: Binder
  8. Android(安卓)分区挂载
  9. NPM 和webpack 的基础使用

随机推荐

  1. android中数据存储的contentprovider的使
  2. android AlertDialog自定义大小
  3. android音频播放简单示例
  4. Install NDK in Eclipse/Android(安卓)St
  5. Android(安卓)实现程序开机自启动
  6. Android(安卓)WIFI
  7. android实现余额宝收益播报中数字显示动
  8. android得到系统时间如何判断是白天还是
  9. android studio 3.0 报错 Unable to find
  10. Android判断文件类型(视频、音频、图片等)