首页 > 移动平台 > 详细

Android:进程间通信交互

时间:2015-12-08 11:37:20      阅读:298      评论:0      收藏:0      [点我收藏+]

Intent 的 ComponentName

Intent作为我们最常用的数据传输渠道,特别是通过Intent打开一个Activity,想必每个人都不会陌生。通常我们用到的都是通过Intent打开同一个进程(App)内部的Activity,如果想实现跨进程通讯,就需要把Intent对象发送到另一个(App)中,并解析出来,这时就需要ComponentName来为我们做这件事情了。既然可以发送数据到另外的进程,也就可以实现不同进程间的交互了。
注意事项:如果A要打开另一个进程中的B中的Activity,那么要在B项目中的AndroidManifest文件中,把要打开的Activity的exported设置为true ,否则将会报错。

android:exported="true"

我们将要用到ComponentName的构造函数如下:

    public ComponentName(String pkg, String cls) {
        if (pkg == null) throw new NullPointerException("package name is null");
        if (cls == null) throw new NullPointerException("class name is null");
        mPackage = pkg;
        mClass = cls;
    }

它需要两个参数,第一个参数是一个存在的pakage(包),第二个是这个包中你要打开的类的名字(注意是带完整包名的),下面是使用例子:
进程AndroidAIDL(com.example.androidaidl)中的MainActivity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //接收传过来的Intent
        if (getIntent() != null){
            System.out.println("----------"+getIntent().getIntExtra("id", 0)+"----------");
        }
    }

另一个进程AndroidTest,在这里的MainActivity中我们写如下代码:

/**指定包名和带包名的Activity的名字*/
ComponentName componentName = new ComponentName("com.example.androidaidl", "com.example.androidaidl.MainActivity");
Intent intent = new Intent();
intent.putExtra("id", 1001);
intent.setComponent(componentName);
startActivity(intent);

运行上面代码后,会看到LogCat中打印出 1001,表示接收AndroidTest进程传来的Intent正常。

注意事项:上面代码中我们传递的是基本的数据类型,对于基本数据类型,比如Int,String等接收时可以像上面那样直接读取,但是如果发送的是复杂的对象,该对象需要实现Serializable或者Parcelable接口。
比如我们定义一个 SendData 对象作为传递对象,它实现 Parcelable 接口:

public class SendData implements Parcelable{
    int id;
    String content;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public static final Parcelable.Creator<SendData> CREATOR = new Parcelable.Creator<SendData>() {

        @Override
        public SendData createFromParcel(Parcel source) {
            // TODO Auto-generated method stub
            SendData data = new SendData();
            data.setId(source.readInt());
            data.setContent(source.readString());
            return data;
        }

        @Override
        public SendData[] newArray(int size) {
            // TODO Auto-generated method stub
            return new SendData[size];
        }

    };

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO Auto-generated method stub
        dest.writeInt(id);
        dest.writeString(content);
    }

}

发送代码:

Intent intent = new Intent();
SendData data = new SendData();
data.setId(1001);
data.setContent("hellow world");
//发送序列化对象
intent.putExtra("data", data);
startActivity(intent);

接收代码

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (getIntent() != null){
        if (getIntent().getParcelableExtra("data") == null)
            return;
        //读取序列化对象,并转化为SendData类型
        SendData data = (SendData)getIntent().getParcelableExtra("data");
        System.out.println("----------"+data.getContent()+"-----------");
    }
}

如果在同一个进程的话,上面的方法没有任何问题,如果在不同进程中,就要注意,SendData 这个bean所在的包名,在各个项目中必须一样,否则接收方,无法解析

广播-BroadcastReceiver

Android的广播是系统级的,只要传递的Action一样(下面的例子中,都使用 Action_Test),就可以接收到其他进程广播的消息,广播中可以通过Intent传递数据。

发送方代码:

Intent intent = new Intent("Action_Test");
SendData data = new SendData();
data.setId(1001);
data.setContent("hellow world");
intent.putExtra("data", data);
intent.putExtra("id", 1001);
getActivity().sendBroadcast(intent);

接收方代码(动态注册广播):

innerReceiver = new InnerReceiver();
IntentFilter filter = new IntentFilter("Action_Test");
registerReceiver(innerReceiver, filter);

class InnerReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
        if (intent.getAction().equals("Action_Test")){
            SendData data = (SendData)intent.getParcelableExtra("data");
            System.out.println("-----------"+data.getContent()+"------------");
        }
    }   
}

因为是通过Intent传递数据,所以对复杂对象的要求和第一种方式一样,要求bean所在的包名一样

ContentProvider

ContentProvider通常用来操作数据集,Android本身就提供了不少的ContentProvider访问,比如联系人、相册等。
访问ContentProvider,需要通过Uri,需要以“content://”开头。下面看看使用方法:
在进程AndroidAIDL(com.example.androidaidl),我们定义一个继承自ContentProvider的类,需要重载它的方法,这里我们以query为例

public class ProviderTest extends ContentProvider {
    private static final UriMatcher MATCHER = new UriMatcher(  
            UriMatcher.NO_MATCH);  
    static{
    //添加访问字符串,对应配置文件中的android:authorities属性
        MATCHER.addURI("com.mh.getdata", "stock", 10001);
    }

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    /**
     * @param uri URI 路径
     * @param projection 要保护的列集合,如果传 null,所有列都会被包含进去.
     * @param selection 用来过滤数据集的规则,null表示不筛选.
     * @param selectionArgs 类似字符串的格式化,selection参数中可以包含 ?s, 这个将被selectionArgs的内容替换
     * @param sortOrder 排序.
     */
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
        System.out.println("--------------------query----------------------");
        return null;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

}

定义了这个类后,需要在AndroidManifest对它进行声明,android:exported=true,记得要写,否则会提示权限错误

        <provider android:name="ProviderTest" android:authorities="com.mh.getdata"
            android:exported="true">            
        </provider>

上面的配置有两个属性
android:name就是类名成
android:authorities这个就是前面提到的Uri,外界需要通过这个来访问Provider

下面看看调用者,在另一个进程中,我们有如下代码:

ContentResolver resolver = getActivity().getContentResolver();
/**com.mh.getdata/stock这个要和Provider所在进程中添加的Uri一致*/
Uri uri = Uri.parse("content://com.mh.getdata/stock");
Cursor cursor = resolver.query(uri, null, null, null, null);

调用上面代码后,LogCat中打印出 “query”字样,表名Provider调用成功,我们通过resolver就可以和AndroidAIDL进程中的Provider对象进行交互了。

AIDL

一种接口定义语言,Android会自动生成通讯代码,通过AIDL我们可以在一个进程中,调用另一个进程中的方法,据说一些大公司的杀不死的Service就是通过这个AIDL来保活的,可以好好研究下。个人认为AIDL更适合做插件化系统,或者有多个app共同组成一个系统,比如WebView因为内存泄露比较严重,所以一些公司就单独把WebView封装成一个app,单独调用,这时,我们通过AIDL技术,就可以调用到这个app中的接口,来操作WebView的行为。
下面开始介绍使用方法:
在两个项目中新建普通文件(Eclipse中是:new ->General->File),记得同时写上后缀名(aidl),两个项目中这个文件所在的包名要保持一致,内容也要一样,如图

技术分享
编译之后, 会在gen目录下,自动产生同名的,后缀为 java 的文件。里面有我们要用到的 Stub类。

public static abstract class Stub extends android.os.Binder implements com.example.aidl.AidlFunctions

在接口文件AIDLFunctions.aidl中,我们定义一个方法 show

interface AidlFunctions{
    void show();
}

服务端:
AIDL的使用,需要一个Service配合,所以我们在服务端还要声明一个Service

public class AIDLService extends Service {
//stub就是系统自动产生的
    AidlFunctions.Stub binder;

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        binder = new AidlFunctions.Stub() {

            @Override
            //这里是我们在接口中声明的方法的实现
            public void show() throws RemoteException {
                // TODO Auto-generated method stub
                System.out.println("--------------------收到----------------------");
            }
        };
        return binder;
    }   
}

在AndroidManifest声明Service

        <service android:name="com.example.androidaidl.AIDLService">
            <intent-filter>
                <action android:name="com.example.androidaidl.AIDLService" />
            </intent-filter>
        </service>

客户端:

//绑定服务,要用到ServiceConnection 
private ServiceConnection serviceConnection;
//自定义的接口,和服务端一样
private AidlFunctions aidlFunctions;

serviceConnection = new ServiceConnection() {

    @Override
    public void onServiceDisconnected(ComponentName name) {
        // TODO Auto-generated method stub
        System.out.println("--------------------ServiceDisconnected----------------------");
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // TODO Auto-generated method stub
        System.out.println("--------------------ServiceConnected----------------------");
        aidlFunctions = AidlFunctions.Stub.asInterface(service);
    }
};
Intent intent = new Intent("com.example.androidaidl.AIDLService");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
//调用show方法
try {
    aidlFunctions.show();
} catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Android:进程间通信交互

原文:http://blog.csdn.net/bdmh/article/details/50206737

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!