美文网首页
Android中跨进程通讯的4种方式

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

作者: f44148db1e8c | 来源:发表于2019-05-19 17:05 被阅读0次

    均属于笔记,仅供个人参考,有问题欢迎指正,整理模式

    这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外部访问的权限控制

    相关文章

      网友评论

          本文标题:Android中跨进程通讯的4种方式

          本文链接:https://www.haomeiwen.com/subject/ijsqzqtx.html