0%

Android开发 Activity通过AIDL与Service通讯 实现进程间通讯

AIDL全称Android Interface Definition Language,即Android接口定义语言,通过它可以在进程间通讯,今天的例子演示的是在Activity与Service间通讯。 程序结构: 包com.example.aidl中有五个文件 AIDLService.java: Service MainActivity.java: 第一个Activity,在这里启动Service,并与之通讯 People.java: JavaBean,两字段,name,age,一方法say SecondActivity.java: 第二个Activity,与在MainActivity中启动的AIDLService通讯 PeopleAIDL.aidl AIDL定义文件,其实就是一个Interface,但扩展名不是java,添加这个文件后,在gen目录下会自动生成PeopleAIDL.java. 本例使用了AndroidAnnotations框架,没接触过的童鞋可能会看不太懂,但其实这个框架也蛮简单的,参考前文AndroidAnnotations框架@Ebean,@RootContext,@Background,@UiThread,@AfterInject,@AfterTextChange标签的使用方法,一看就明白。 布局:main_activity.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="绑服务" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="22dp"
        android:layout_toRightOf="@+id/button1"
        android:text="setName" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/textView1"
        android:text="setAge" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/button1"
        android:layout_marginTop="16dp"
        android:text="say" />

    <Button
        android:id="@+id/button5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/button4"
        android:layout_alignLeft="@+id/button2"
        android:text="SecondActivity" />

</RelativeLayout>

second_activity.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Bind Service" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Say" />

</LinearLayout>

接下来是Java代码: People.java:

package com.example.aidl;

import android.content.Context;
import android.widget.Toast;

import com.googlecode.androidannotations.annotations.EBean;
import com.googlecode.androidannotations.annotations.RootContext;

@EBean
public class People {
    @RootContext
    Context context;
    String name;
    int age;
    public void say()
    {
        Toast.makeText(context, "I'm "+name+" I "+age+" years old.", Toast.LENGTH_LONG).show();
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

用了androidannotations的@RootContext,上面的context会自动取到,不会为空。 PeopleAIDL.aidl:

package com.example.aidl;

interface PeopleAIDL {
    void setAge(int age);
    void setName(String name);
    void say();
}

网上的文章都称这个文件要放在包目录下,如果是多个包,到底是AndroidManifest.xml中声明的package还是其他包,没测试。保存后,ADT会在gen下的同名包下生成PeopleAIDL.java. AIDLService.java:

package com.example.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

import com.googlecode.androidannotations.annotations.Bean;
import com.googlecode.androidannotations.annotations.EService;

@EService
public class AIDLService extends Service {
    @Bean
    People people=new People();
    PeopleAIDL.Stub stub=new PeopleAIDL.Stub() {
        //下面的方法都是供远程调用的
        public void setName(String name) throws RemoteException {
            // TODO Auto-generated method stub
            people.setName(name);
        }
        
        public void setAge(int age) throws RemoteException {
            // TODO Auto-generated method stub
            people.setAge(age);
        }
        
        public void say() throws RemoteException {
            // TODO Auto-generated method stub
            people.say();
        }
    };
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        //onBind方法要返回PeopleAIDL.Stub
        return stub;
    }
    
}

MainActivity.java:

package com.example.aidl;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.widget.Button;

import com.googlecode.androidannotations.annotations.Click;
import com.googlecode.androidannotations.annotations.EActivity;
import com.googlecode.androidannotations.annotations.ViewById;

@EActivity(R.layout.activity_main)
public class MainActivity extends Activity {
    PeopleAIDL peopleAIDL;
    @ViewById
    Button button1,button2,button3,button4,button5;

    ServiceConnection conn = new ServiceConnection(){
        public void onServiceConnected(ComponentName className, IBinder service) {
            //服务绑定成功,绑定动作好像是异步的
            peopleAIDL=PeopleAIDL.Stub.asInterface(service);
        }

        public void onServiceDisconnected(ComponentName className) {
        
        }
    };
    //下面是几个按钮的点击事件
    @Click
    void button1()
    {
        //绑定启动服务
        bindService(new Intent(this, AIDLService_.class), conn, Context.BIND_AUTO_CREATE);
        startService(new Intent(this, AIDLService_.class));
    }
    @Click
    void button2()
    {
        try {
            peopleAIDL.setName("zhangshan");
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    @Click
    void button3()
    {
        try {
            peopleAIDL.setAge(23);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    @Click
    void button4()
    {
        try {
            peopleAIDL.say();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    @Click
    void button5()
    {
        //SecondActivity后加_是因为用了androidannotations
        Intent intent=new Intent(this,SecondActivity_.class);
        startActivity(intent);
    }
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        //退出时必须unbindService
        unbindService(conn);
        //如果Service仍需要在后台运行,不需要下面的这句
        stopService(new Intent(this, AIDLService_.class));
    }
}

SecondActivity.java:

package com.example.aidl;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.widget.Button;

import com.googlecode.androidannotations.annotations.Click;
import com.googlecode.androidannotations.annotations.EActivity;
import com.googlecode.androidannotations.annotations.ViewById;

@EActivity(R.layout.second_activity)
public class SecondActivity extends Activity {
    PeopleAIDL peopleAIDL;
    @ViewById
    Button button1,button2;
    
    private ServiceConnection conn = new ServiceConnection(){
        public void onServiceConnected(ComponentName className, IBinder service) {
            peopleAIDL=PeopleAIDL.Stub.asInterface(service);
        }

        public void onServiceDisconnected(ComponentName className) {
        }
    };
    
    @Click
    void button1()
    {
        bindService(new Intent(this, AIDLService_.class), conn, Context.BIND_AUTO_CREATE);
               //如果调用的是另一个应用的Service,使用new Intent("Service的Action");
    }
    @Click
    void button2()
    {
        try {
            peopleAIDL.say();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

若调用的是另一个应用的Service,需要那个应用声明Service时加上Action,如:

        <service android:name=".service.GCService" >
            <intent-filter>
                <action android:name="com.pocketdigi.service.GCservice" />
            </intent-filter>
        </service>

在bindService的Intent里传入这个Action. 需要注意的是,两个应用的AIDL文件必须完全一致,包括在包名。