Android中跨进程通讯的4种方式

均属于笔记,仅供个人参考,有问题欢迎指正,整理模式
这4种方式正好对应于android系统中4种应用程序组件:Activity、Content Provider、Broadcast和Service。其中Activity可以跨进程调用其他应用程序的Activity;Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应用程序的数据进行增、删、改操作;Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播;Service和Content Provider类似,也可以访问其他应用程序中的数据,但不同的是,Content Provider返回的是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。
1,访问其他应用程序的Activity
1.1,进程内调用
Activity既可以在进程内(同一个应用程序)访问,也可以跨进程访问。如果想在同一个应用程序中访问Activity,需要指定Context对象和Activity的Class对象,代码如下:
Intent intent = newIntent(this , Test.class );
startActivity(intent);
1.2,进程间调用
在android系统中有很多应用程序提供了可以跨进程访问的Activity,例如,下面的代码可以直接调用拨打电话的Activity。
Intent callIntent = newIntent(Intent.ACTION_CALL,Uri.parse("tel://13888888888" );
startActivity(callIntent);
1.3,如何将应用程序的Activity共享出来
1). 在AndroidManifest.xml文件中指定Action。指定Action要使用标签,并在该标签的android:name属性中指定Action
2). 在AndroidManifest.xml文件中指定访问协议。在指定Uri(Intent类的第2个参数)时需要访问协议。访问协议需要使用标签的android:scheme属性来指定。如果该属性的值是“mdm”,那么Uri就应该是“mdm://Uri的主体部分”,也就是说,访问协议是Uri的开头部分。
3). 通过getIntent().getData().getHost()方法获得协议后的Uri的主体部分。这个Host只是个称谓,并不一定是主机名。读者可以将其看成是任意的字符串。
4). 从Bundle对象中获得其他应用程序传递过来的数据。
5). 这一步当然是获得数据后做进一步的处理了。至于如何处理这些数据,就得根据具体的需求决定了。
1.4,注意事项
1)在配置AndroidManifest.xml时要注意,不能在同一个中配置多个动作,否则会覆盖MAIN动作以使该程序无法正常启动(虽然其他应用程序调用Main是正常的).
标签指定了Url的协议,则在调用Main时需要使用如下的URL:
mdm://任意字符串
一般标签的android:name属性值可以设成android.intent.category.DEFAULT,这个必须要设置,不然无法访问到。
2)跨进程访问Activity(访问其他应用程序中的Activity)主要是通过一个Action来完成的,如果要传递数据,还需要指定一个Uri。当然,传递数据也可以通过Intent来完成。传递数据的过程可以是双向的。如果要想从调用的Activity中返回数据,就需要使用startActivityForResult方法来启动Activity了。
2, Content Provider
2.1概述
Android应用程序可以使用文件或SqlLite数据库来存储数据。Content
Provider提供了一种在多个应用程序之间数据共享的方式(跨进程共享数据)。应用程序可以利用Content Provider完成下面的工作.
Android系统本身提供了很多Content
Provider,例如,音频、视频、联系人信息等等。我们可以通过这些Content Provider获得相关信息的列表。这些列表数据将以Cursor对象返回。因此,从Content Provider返回的数据是二维表的形式。
对于访问Content Provider的程序,需要使用ContentResolver对象。该对象需要使用getContentResolver方法获得,代码如下:
ContentResolver cr = getContentResolver();
与Activity一样,Content Provider也需要与一个URI对应。每一个Content Provider可以控制多个数据集,在这种情况下,每一个数据集会对应一个单独的URI。所有的URI必须以“content://”开头。
2.2设置步骤
具体可以参考Android四大组件之Content Provider详解;
2.3注意事项
1,内容提供者中的oncreate方法
@Override
publicboolean onCreate() {
dbHelper= new DBHelper(getContext(), "Provider", null, 1);
returntrue;
}
如果数据库版本为2时,则数据库中的onupdate方法就要重写,并且查询是会更新数据库版本,使数据丢失
2,命名问题
android:name="com.hjt.provide.contentprovider.provider.DatabaseProvider"
android:authorities="com.hjt.provide.provider"
android:exported="true">

在配置文件中注册provider时的android:authorities设置为包明+“.provider”,即和代码中的路径中的权限部分一样;
public static final String AUTHORITY ="com.hjt.provide.provider";
上面两部分是提供者命名时的规则
在调用方使用
Uri uri1 = Uri.parse("content://com.hjt.provide.provider/Person");
路径为权限+数据表名,权限就是为了区分那个应用,规定写法就是包名+“.provider”.
不管是提供者还是调用者,其中的包名是提供者具体应用的包名,和内容提供者类所在的包名没有关系,毕竟数据库和数据表是对于应用来说的。
3,在配置文件中声明provider时
android:name="com.hjt.provide.contentprovider.provider.DatabaseProvider"
android:authorities="com.hjt.provide.provider"
android:exported="true">

要加上android:exported="true",否则会报java.lang.SecurityException:
Permission Denial: opening provider异常
4, android:exported的作用
在Activity中该属性用来标示:当前Activity是否可以被另一个Application的组件启动:true允许被启动;false不允许被启动。
android:exported 是Android中的四大组件Activity,Service,Provider,Receiver 四大组件中都会有的一个属性。
总体来说它的主要作用是:是否支持其它应用调用当前组件。
3,Broadcast
广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。
在应用程序中发送广播比较简单。只需要调用sendBroadcast方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据。
3.1发送广播
先建一个android工程:sendbroadcast。在XML布局文件中放两个组件:EditText和Button,当单击按钮后,会弹出显示 EditText组件中文本的对话框,关闭对话框后,会使用sendBroadcast方法发送消息,并将EditText组件的文本通过Intent对象发送出去。完整的代码如下:
package net.blogjava.mobile.sendbroadcast;
... ...
public class Main extends Activityimplements OnClickListener{
private EditTexteditText;


@Override
public void onClick(View view){
new AlertDialog.Builder(this).setMessage(editText.getText().toString())
.setPositiveButton("确定",null).show();
//通过Intent类的构造方法指定广播的ID
Intent intent = newIntent("net.blogjava.mobile.MYBROADCAST");
//将要广播的数据添加到Intent对象中
intent.putExtra("text", editText.getText().toString());
//发送广播
sendBroadcast(intent);
}
... ...
}
3.2接受广播
发送广播并不需要在AndroidManifest.xml文件中注册,但接收广播必须在AndroidManifest.xml文件中注册 receiver(静态注册)。下面来编写一个接收广播的应用程序。首先建立一个android工程:receiver。然后编写一个MyReceiver类,该类是 BroadcastReceiver的子类,代码如下:
package net.blogjava.mobile.receiver;
... ...
public class MyReceiver extendsBroadcastReceiver
{
//当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,代码如下:





在注册MyReceiver类时需要使用标签,android:name属性指定MyReceiver类,标签的android:name指定了广播的ID。
首先运行receiver程序,然后就可以关闭receiver程序了。接收广播并不依赖于程序的状态。就算程序关闭了,仍然可以接收广播。然后再启动 sendbroadcast程序。并在文本框中输入“android”,然后单击按钮,会弹出一个显示文本框内容的对话框,如图9所示。当关闭对话框后,会显示一个Toast信息提示框,这个信息框是由receiver程序弹出的。
3.3注意事项
1)Calling
startActivity() from outside of an Activity context requires the
FLAG_ACTIVITY_NEW_TASK fla异常
从一个非Activity的Context中要通过intent调出另一个Activity的话,需要使用FLAG_ACTIVITY_NEW_TASK
否则的话,会有force close:
03-01 18:49:37.888 E/AndroidRuntime( 2706):FATAL EXCEPTION: main
03-01 18:49:37.888 E/AndroidRuntime( 2706):android.util.AndroidRuntimeException: Calling startActivity() from outside ofan Activitycontext requires theFLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:
如果调出的Activtivity只是一个功能片段,并没有实际的意义,也没有必要出现在长按Home键调出最近使用过的程序类表中,那么使用FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
Intent intent = new Intent(this,WaitingFallBackDialog.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
4,Service
4.1概述
服务(Service)是android系统中非常重要的组件。Service可以脱离应用程序运行。也就是说,应用程序只起到一个启动Service的作用。一但Service被启动,就算应用程序关闭,Service仍然会在后台运行。
android系统中的Service主要有两个作用:后台运行和跨进程通讯。后台运行就不用说了,当Service启动后,就可以在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服务,尤其要注意的是,标签的android:name属性值就是客户端要引用该服务的ID,也就是Intent类构造方法的参数值。
4.2 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服务,尤其要注意的是,标签的android:name属性值就是客户端要引用该服务的ID,也就是Intent类构造方法的参数值。
4.3设置自定义权限
Exported service does not require
permission问题。
但是这次遇到的是“外部的service不需要权限”,其意思是:外部的其他service根本不需要添加权限就能够轻易地访问我们编写的这个aidl,或者说这个service。这是出于安全性的考虑而给我们的警告。而不是告诉我们缺少相应的权限。自己傻傻的还想require这个单词是不是还有其他意思,会不会有“需要”的意思,查有道,没有。翻牛津。还是没有。最后甚至想是不是编辑文档的人写错了,把acquire错写成了require?最后才发现只是理解错了,习惯性思维犯的错。
就是说默认值取决于该service是否包含intent过滤器filter。如果没有,那只能通过指定其准确的类名访问。这意味着该service打算只在该应用内部使用(因为其他应用可能根本不知道这个类的名字)。在这种情况下,默认值是“false”,另一方面,当存在至少一个filter时,就表明该service打算供外部来使用,因此默认值是“true”。写到这里相信大家知道问什么了吧。原来api里面写的清清楚楚,看来api文档才是最好的资料这句话一点没错。是真正的宗。
其实还有一种解决的办法。那就是我们在这里自己定义一个权限。既然外部的其他service不需权限就能访问我们的service,那么我们自己定义权限,然后在需要调用该service的应用的配置文件里面声明调用该service的所需要的权限不就可以了吗?代码更改为:
android:permission="com.exmaple.myaidldemo.myaidlservice">




然后在我们需要调用该service的其他应用的配置文件中加入下面这行代码:

4.4注意事项
1. AIDL服务中的onBind方法必须返回AIDL接口对象(MyServiceImpl对象)。该对象也是onServiceConnected事件方法的第2个参数值。
2. bindService方法的第1个参数是Intent对象,该对象构造方法的参数需要指定AIDL服务的ID,也就是在 AndroidManifest.xml文件中标签的子标签的android:name属性的值。
3,引用者中aidl文件和提供者中的aidl文件一样,直接复制提供者中的aidl即可,需要注意的是这里的复制是全路径复制,即报名等都要一致,这样才能访问到该aidl(Service)。
参考:http://www.jianshu.com/p/61f681145cd8主要是Activity
http://blog.csdn.net/happyq/article/details/46682285
http://blog.csdn.net/cjjky/article/details/7562652主要是Service
http://blog.csdn.net/zhuangyalei/article/details/50515039主要是Service的几个示例
http://blog.csdn.net/sugar_z_/article/details/49384153是上面的补充
【Android中跨进程通讯的4种方式】http://www.cnblogs.com/myorange/p/5425835.html主要是Service外部访问的权限控制

    推荐阅读