android的进程间的通信

本文详细介绍了Android中的四种进程间通信方式:通过Activity启动其他进程的Activity、使用Broadcast进行广播通信、通过Content Provider共享数据以及使用AIDL服务实现跨进程服务调用。每种方式都有其适用场景,如Activity适用于需要可视化交互,Content Provider适合返回记录集,Broadcast是被动接收,而AIDL Service则能像本地对象一样跨进程调用。
      我们知道每个app运行在自己独立的进程中,如果进程间要进行通信,直接调用会报安全异常的。但是有时我们必须去和其他app间进行交互,所以google给出了规范。我们学习进程间的通信其实就是了解这个规范。
     进程间的通信也是针对android四大主件来的,下面我分别讲解一下activity/broadcast/content provider/service是如何来实现的。

     activity的进程通信
              
a.同一个进程(应用)
没有返回值的
Intent intent = new  Intent(this , Test.class ); 
startActivity(intent);
有返回值的
Intent intent = new  Intent(this , Test.class ); 
startActivityforresult(intent,requestCode);
b.其他的进程
例如:          Intent callIntent = new  Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" ); 
startActivity(callIntent);
         c.自定义
步骤:          
1.在Android manifest.xml文件中指定Action。指定action要使用<action>标签,并在该标签的Android:name属性中指定Action;
2.在Android manifest。xml文件中指定访问协议。在指定Uri(Intent类的第2个参数)时需要访问协议。访问协议需要使 用<data>标签的android:scheme属性来指定。如果该属性的值是“abc”,那么Uri就应该是“abc://Uri的主体 部分”,也就是说,访问协议是Uri的开头部分;
3.通过getIntent().getdata.getHost()方法获得协议后的Uri的主题部分。这个host只是个称为,并不一定是主机名。
4.从Bundle 对象中获得其他应用程序传递过来的数据。
5.获得数据后的处理

下面我们来共享一个Activity。项目名称叫actionactivity

package  net.blogjava.mobile.actionactivity; 
... ... 
public  class  Main extends  Activity implements  OnClickListener 

    private  EditText editText; 
    @Override  
    public  void  onClick(View view) 
    { 
        //  单击按钮,会显示文本框中的内容(以Toast信息框形式显示)  
        Toast.makeText(this , editText.getText().toString(), Toast.LENGTH_LONG) 
                .show(); 
    } 
    @Override  
    public  void  onCreate(Bundle savedInstanceState) 
    { 
        super .onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        Button button = (Button) findViewById(R.id.button); 
        button.setOnClickListener(this ); 
        editText = (EditText) findViewById(R.id.edittext); 
        //  获得其他应用程序传递过来的数据  
        if  (getIntent().getData() != null ) 
        { 
            //  获得Host,也就是info://后面的内容  
            String host = getIntent().getData().getHost(); 
            Bundle bundle = getIntent().getExtras(); 
            //  其他的应用程序会传递过来一个value值,在该应用程序中需要获得这个值  
            String value = bundle.getString("value" ); 
            //  将Host和Value组合在一下显示在EditText组件中  
            editText.setText(host + ":"  + value); 
            //  调用了按钮的单击事件,显示Toast信息提示框  
            onClick(button); 
        } 
    } 


配置AndroidManifest.xml
<activity android:name=".Main"  android:label="@string/app_name" > 
    <intent-filter>     
        <action android:name="net.blogjava.mobile.MYACTION"  /> 
        <data android:scheme="info"  />             
        <category android:name="android.intent.category.DEFAULT"  /> 
    </intent-filter> 
< /activity> 

在创建一个项目InvokeActivity,看它是如何调用公共activity的。点击按钮调用:

public void onClick(View view) 

    //  需要使用Intent类的第2个参数指定Uri 
    Intent intent = new Intent("net.blogjava.mobile.MYACTION", Uri 
            .parse("info://调用其他应用程序的Activity")); 
    //  设置value属性值 
    intent.putExtra("value", "调用成功"); 
    //  调用ActionActivity中的Main 
    startActivity(intent); 


运行 InvokeActivity之前,先运行actionactivity,点击按钮我们会看到传过来的数据。

当然,也可以使用startActivityForResult方法来启动其他应用程序的Activity,以便获得Activity的返回值。例如,可以将ActionActivity中Main类的onClick代码修改为下面的形式。
public  void  onClick(View view) 

    Toast.makeText(this , editText.getText().toString(), Toast.LENGTH_LONG).show(); 
    Intent intent = new  Intent(); 
    //  设置要返回的属性值  
    intent.putExtra("result" , editText.getText().toString()); 
    //  设置返回码和Intent对象  
    setResult(2 , intent); 
    //  关闭Activity  
    finish(); 


demo地址  http://download.youkuaiyun.com/detail/u010020111/9460622

    broadcast的进程通信

广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。
在应用程序中发送广播比较简单。只需要调用sendBroadcast方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据。
先建一个android工程:sendbroadcast。在XML布局文件中放两个组件:EditText和Button,当单击按钮后,会弹出显示EditText组件中文本的对话框,关闭对话框后, 会使用sendBroadcast方法发送消息,并将EditText组件的文本通过Intent对象发送出去。完整的代码如下:

package net.blogjava.mobile.sendbroadcast; 
... ... 
public class Main extends Activity implements OnClickListener 

     private EditText editText; 
     @Override 
    public void onClick(View view) 
    { 
         new AlertDialog.Builder(this).setMessage(editText.getText().toString()) 
                .setPositiveButton("确定", null).show();      
        //  通过Intent类的构造方法指定广播的ID 
         Intent intent = new Intent("net.blogjava.mobile.MYBROADCAST"); 
         //  将要广播的数据添加到Intent对象中   
         intent.putExtra("text", editText.getText().toString()); 
         //  发送广播   
         sendBroadcast(intent); 
     } 
     ... ... 

发送广播并不需要在AndroidManifest.xml文件中注册,但接收广播必须在AndroidManifest.xml文件中注册receiver。下面来编写一个接收广播的应用程序。首先建立一个an droid工程:receiver。然后编写一个MyReceiver类,该类是BroadcastReceiver的子类,代码如下:
package net.blogjava.mobile.receiver; 
... ... 
public class MyReceiver extends BroadcastReceiver 

    //  当sendbroadcast发送广播时,系统会调用onReceive方法来接收广播 
     @Override 
    public void onReceive(Context context, Intent intent) 

    //  判断是否为sendbroadcast发送的广播 
        if ("net.blogjava.mobile.MYBROADCAST".equals(intent.getAction())) 
        { 
            Bundle bundle = intent.getExtras(); 
            if (bundle != null) 
            { 
                String text = bundle.getString("text"); 
                Toast.makeText(context, "成功接收广播:" + text, Toast.LENGTH_LONG).show(); 
            } 
        } 
    } 

当应用程序发送广播时,系统会调用onReceive方法来接收广播,并通过intent.getAction()方法返回广播的ID,也就是在发送广播时Intent构造方法指定的字符串。然后就可 以从Bundle对象中获得相应的数据了。

最后还需要在AndroidManifest.xml文件中注册receiver,代码如下:
<!--  注册receiver  
< receiver android:name="MyReceiver"> 
    <intent-filter> 
        <action android:name="net.blogjava.mobile.MYBROADCAST" /> 
    </intent-filter> 
< /receiver> 
       在注册MyReceiver类时需要使用<receiver>标签,android:name属性指定MyReceiver类,<action>标签的android:name指定了广播的ID。

首先运行receiver程序,然后就可以关闭receiver程序了。接收广播并不依赖于程序的状态。就算程序关闭了,仍然可以接收广播。然后再启动sendbroadcast程序。并在文本框中输入“android”,然后单击按钮,会弹出一个显示文本框内容的对话框,如图9所示。当关闭对话框后,会 显示一个Toast信息提示框,这个信息框是由receiver程序弹出的。

给个例子  http://download.youkuaiyun.com/detail/u010020111/9460639
    content provider的进程通信
Content Provider提供了一种在多个应用程序之间数据共享的方式(跨进程共享数据)。应用程序可以利用Content Provider完成查,改,加,删  的工作.
Android系统本身提供了很多Content Provider,例如,音频、视频、联系人信息等等。我们可以通过这些Content Provider获得相关信息的列表。这些列表数据将以Cu rsor对象返回。因此,从Content Provider返回的数据是二维表的形式。
对于访问Content Provider的程序,需要使用ContentResolver对象。该对象需要使用getContentResolver方法获得,代码如下:
ContentResolver cr = getContentResolver();
与Activity一样,Content Provider也需要与一个URI对应。每一个Content Provider可以控制多个数据集,在这种情况下,每一个数据集会对应一个单独的URI。所有的 URI必须以“content://”开头。
为了程序更容易维护,也为了简化程序代码,一般将URI定义成一个常量。例如,下面的常量表示系统的联系人电话号码 
android.provider.Contacts.Phones.CONTENT_URI 

下面来看一下编写Content Provider的具体步骤。
1.  编写一个继承于android.content.ContentProvider的子类。该类是ContentProvider的核心类。在该类中会实现query、insert、update及delete方法。实际上调用ContentResolver类的这4个方法就是调用ContentProvider类中与之要对应的方法。在本文中只介绍query。至于insert、update、delete和query的用法类 似。也是通过Uri传递参数,然后在这些方法中接收这些参数,并做进一步地处理。
2.  在AndroidManifest.xml文件中配置ContentProvider。要想唯一确定一个ContentProvider,需要指定这个ContentProvider的URI,除此之外,还需要指定URI所对应的ContentProvider类。这有些象Servlet的定义,除了要 指定Servlet对应的Web地址,还要指定这个地址所对应的Servlet类。
现在来看一下Uri的具体格式,先看一下如图5所示的URI。


A:Content Provider URI的固定前缀,也就是说,所有的URI必须以content://开头。
B:URI中最重要的部分。该部分是Content Provider的唯一标识。对于第三方应用程序来说,该部分最后使用完整的类名(包名+类名),以确保URI的唯一性。该部分需 要在AndroidManifest.xml文件中<provider>标签中定义,代码如下:
<provider name=".TransportationProvider"  authorities="com.example.transportationprovider"   . . .  >
C:这部分是URI的路径(path)。表示URI中各种被请求的数据。这部分是可选的, 如果Content Provider仅仅提供一种请求的数据,那么这部分可以省略。如果Content Provider要提供多种请求数据。就需要添加多个路径,甚至是子路径。例如,“land/bus”、“land/train”、“sea/ship” 就指定了3种可能提供的数据。
D:这部分也是可选的。如果要传递一个值给Content Provider,可以通过这部分传递。当然,如果不需要传值,这部分也可以省略,省略后的URI如下所示:
content://com.example.transportationprovider/trains




    service的进程通信
如果想让应用程序可以跨进程通讯,就要使用我们这节讲的AIDL服 务,AIDL的全称是Android Interface Definition Language,也就是说,AIDL实际上是一种接口定义语言。通过这种语言定义接口后,Eclipse插件(ODT)会自动生成相应的Java代码接 口代码。下面来看一下编写一个AIDL服务的基本步骤。
1.  在Eclipse工程的package目录中建立一个扩展名为aidl的文件。package目录就是Java类所在的目录。该文件的语法类似于Java代码。aidl文件中定义的是AIDL服 务的接口。这个接口需要在调用AIDL服务的程序中访问。
2.  如果aidl文件的内容是正确的,Eclipse插件会自动生成一个Java接口文件(*.java)。
3.  建立一个服务类(Service的子类)。
4.  实现由aidl文件生成的Java接口。
5.  在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签的android:name属性值就是客户端要引用该服务的ID,
也就是Intent类构造方法的参数值。

现在我们来编写一个AIDL服务,首先建立一个android工程:aidlservice。在aidlservice工程中有一个Main类,在Main类所有的目录建立一个IMyService.aidl文件
内容如下:
package net.blogjava.mobile.aidlservice; 
interface IMyService 

     String getValue();  //  为AIDL服务的接口方法,调用AIDL服务的程序需要调用该方法 
在保存IMyService.aidl文件后,ODT会在gen目录下产生一个IMyService.java文件,读者可以不必管这个文件中的内容,也 不需要修改该文件的内容。
     这个文件是由ODT自动维护的,只要修改了IMyService.aidl文件的内容,IMyService.java文件的内 容就会随之改变。然后建立一个MyService类,该类是Service的子类,
     代码如下:
package net.blogjava.mobile.aidlservice; 
... ... 
public class MyService extends Service 

     //  IMyService.Stub类是根据IMyService.aidl文件生成的类,该类中包含了接口方法(getValue) 
    public class MyServiceImpl extends IMyService.Stub 
     { 
         @Override 
        public String getValue() throws RemoteException 
        { 
             return "从AIDL服务获得的值."; 
        } 
     } 
     @Override 
     public IBinder onBind(Intent intent) 
{         
//  该方法必须返回MyServiceImpl类的对象实例 
        return new MyServiceImpl(); 
    } 

     最后需要在AndroidManifest.xml文件中配置MyService类,代码如下:
<!--  注册服务--> 
< service android:name=".MyService"> 
    <intent-filter> 
        <!--  指定调用AIDL服务的ID  --> 
        <action android:name="net.blogjava.mobile.aidlservice.IMyService" /> 
    </intent-filter> 
< /service> 
下面来看看如何调用这个AIDL服务。首先建立一个android工程:aidlclient。然后将aidlservice工程中自动生成的IMyService.java文件复制到aidlclient工程中。在调用 AIDL服务之前需要先使用bindService方法绑定AIDL服务。bindService方法需要一个ServiceConnection对象。ServiceConnection有一个onServiceConnected方法,当成 功绑定AIDL服务且,该方法被调用。并通过service参数返回AIDL服务对象。下面是调用AIDL服务的完成代码。
package net.blogjava.mobile.aidlclient; 
... ... 
public class Main extends Activity implements OnClickListener 

private IMyService myService = null; 
//  创建ServiceConnection对象 
    private ServiceConnection serviceConnection = new ServiceConnection() 
    { 
        @Override 
        public void onServiceConnected(ComponentName name, IBinder service) 
        { 
            // 获得AIDL服务对象 
            myService = IMyService.Stub.asInterface(service); 
            try 
            { 
                //  调用AIDL服务对象中的getValue方法,并以对话框中显示该方法的返回值 
                new AlertDialog.Builder(Main.this).setMessage( 
                        myService.getValue()).setPositiveButton("确定", null) 
                        .show(); 
            } 
            catch (Exception e) 
            { 
            } 
        } 
        @Override 
        public void onServiceDisconnected(ComponentName name) 
        { 
        } 
    }; 
    @Override 
    public void onClick(View view) 

    //  绑定AIDL服务 
        bindService(new Intent("net.blogjava.mobile.aidlservice.IMyService"), 
                serviceConnection, Context.BIND_AUTO_CREATE); 
    } 
    ... ... 

 
   在编写AIDL服务和客户端时要注意如下两点:
1.  AIDL服务中的onBind方法必须返回AIDL接口对象(MyServiceImpl对象)。该对象也是onServiceConnected事件方法的第2个参数值。
2.  bindService方法的第1个参数是Intent对象,该对象构造方法的参数需要指定AIDL服务的ID,也就是在AndroidManifest.xml文件中<service>标签的<action>子标签的android:name属性 的值。
 
 现在先运行aidlservice程序,以便安装AIDL服务,然后运行aidlclient程序,并单击按钮,会显示对话框。对话框中的信息就是AIDL服务接口中getValue方法的返回值。

给个例子 http://download.youkuaiyun.com/detail/u010020111/9460645



总结
      本文介绍了4种跨进程通讯的方式:Activity、ContentProvider、Broadcast和AIDL Service。其中Activity可以跨进程调用其他应用程序的Activity;ContentProvider可以访问其他应用程序返回的Cursor对象;Broadcast采用的是被动接收的方法,也就是说,客户端只能接收广播数据,而不能向发送广播的程序发送信息。AIDL Service可以将程序中的某个接口公开,这样在其他的应用程序中就可以象访问本地对象一样访问AIDL服务对象了。这4种跨进程通讯的方式可以应用在 不同的场合,例如,在需要显示可视化的界面时可以用Activity,需要返回记录集时可以用ContentProvider。至于在应用程序中具体要用 到哪一种或几种方式进行跨进程通讯,读者可以根据实际情况进行选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值