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 = new  Intent(this , Test.class );

startActivity(intent);

1.2,进程间调用

在android系统中有很多应用程序提供了可以跨进程访问的Activity,例如,下面的代码可以直接调用拨打电话的Activity。

Intent callIntent = new  Intent(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时要注意,不能在同一个<activity>中配置多个动作,否则会覆盖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,命名问题

<provider

         android:name="com.hjt.provide.contentprovider.provider.DatabaseProvider"

           android:authorities="com.hjt.provide.provider"

           android:exported="true">

</provider>

在配置文件中注册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时

<provider

           android:name="com.hjt.provide.contentprovider.provider.DatabaseProvider"

           android:authorities="com.hjt.provide.provider"

           android:exported="true">

</provider>

要加上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,代码如下:

<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程序弹出的。

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 Activity  context 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服务,尤其要注意的是,<action>标签的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服务,尤其要注意的是,<action>标签的android:name属性值就是客户端要引用该服务的ID,也就是Intent类构造方法的参数值。

4.3设置自定义权限

Exported service does not require

permission问题。

但是这次遇到的是“外部的service不需要权限”,其意思是:外部的其他service根本不需要添加权限就能够轻易地访问我们编写的这个aidl,或者说这个service。这是出于安全性的考虑而给我们的警告。而不是告诉我们缺少相应的权限。自己傻傻的还想require这个单词是不是还有其他意思,会不会有“需要”的意思,查有道,没有。翻牛津。还是没有。最后甚至想是不是编辑文档的人写错了,把acquire错写成了require?最后才发现只是理解错了,习惯性思维犯的错。

就是说默认值取决于该service是否包含intent过滤器filter。如果没有<intent-filter>,那只能通过指定其准确的类名访问。这意味着该service打算只在该应用内部使用(因为其他应用可能根本不知道这个类的名字)。在这种情况下,默认值是“false”,另一方面,当存在至少一个filter时,就表明该service打算供外部来使用,因此默认值是“true”。写到这里相信大家知道问什么了吧。原来api里面写的清清楚楚,看来api文档才是最好的资料这句话一点没错。是真正的宗。

其实还有一种解决的办法。那就是我们在这里自己定义一个权限。既然外部的其他service不需权限就能访问我们的service,那么我们自己定义权限,然后在需要调用该service的应用的配置文件里面声明调用该service的所需要的权限不就可以了吗?代码更改为:

<service android:name=".AidlService"

  android:permission="com.exmaple.myaidldemo.myaidlservice">

<intent-filter>

<action android:name="com.example.myaidldemo.action.AIDL_SERVICE"/>

</intent-filter>

</service>

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

<uses-permission android:name="com.exmaple.myaidldemo.myaidlservice"/>

4.4注意事项

1. AIDL服务中的onBind方法必须返回AIDL接口对象(MyServiceImpl对象)。该对象也是onServiceConnected事件方法的第2个参数值。

2. bindService方法的第1个参数是Intent对象,该对象构造方法的参数需要指定AIDL服务的ID,也就是在 AndroidManifest.xml文件中<service>标签的<action>子标签的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是上面的补充

http://www.cnblogs.com/myorange/p/5425835.html主要是Service外部访问的权限控制

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容

  • 由于android系统中应用程序之间不能共享内存。 因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些。...
    IT枫阅读 6,699评论 0 5
  • 由于android系统中应用程序之间不能共享内存。因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些。在...
    Ten_Minutes阅读 8,416评论 1 7
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情况下的生命周期:在用户参与的情况下...
    AndroidMaster阅读 3,005评论 0 8
  • 1.什么是Activity?问的不太多,说点有深度的 四大组件之一,一般的,一个用户交互界面对应一个activit...
    JoonyLee阅读 5,722评论 2 51
  • 我做了一个梦,一个美梦。 我梦见我们家的房子变成了一个巨大的万能机器人。我们家就在它的肚子...
    浅浅的简书阅读 257评论 0 0