Android的跨进程通信

Android的应用程序之间是不能共享内存的,所以如何在不同的应用程序之前进行传递数据呢?

1、自定义Intent的Action和Uri,访问对应的Activity

例如:我们可以在APP内可拨打电话,通常代码如下

Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:13712345678"));
startActivity(callIntent);

所以我们可以采用相同的方式进行跨进程通信。通过定义常量字符串为Action,同时还要定义访问协议。详细说明下:

1)首先要在AndroidManifest文件中对要共享的Acticity,指定Action。对应的标签为android:name

2)在AndroidManifest文件中指定访问协议。对应的android:scheme。访问协议的格式为"Uri的标记://Uri的主体部分/传递参数",可以通过//后面的进行区分处理不同的事情。

举例说明:

1)在APP中定义一个Activity可以供另外一个APP访问,在AndroidManifest文件中定义如下:

        <activity android:name="com.j1.widget.RecyclerViewActivity">
            <intent-filter>
                <action android:name="com.j1.RecyclerViewActivity" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="j1" />
            </intent-filter>
        </activity>

注意如果不添加<category android:name="android.intent.category.DEFAULT" />则会抛出以下异常:

android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.j1.RecyclerViewActivity dat=j1://main/tag=1/name=2 }
        at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1936)
        at android.app.Instrumentation.execStartActivity(Instrumentation.java:1615)
        at android.app.Activity.startActivityForResult(Activity.java:4472)
        at android.app.Activity.startActivityForResult(Activity.java:4430)
        at android.app.Activity.startActivity(Activity.java:4791)
        at android.app.Activity.startActivity(Activity.java:4759)

2)传递数据

第一种方式可以通过Uri进行传递数据

   Intent intent = new Intent("com.j1.RecyclerViewActivity", Uri.parse("j1://main/tag=1/name=2"));
     startActivity(intent);

在目标Activity可以通过解析j1://main/tag=1/name=2来获取到传递的数据

   Uri uri = getIntent().getData();   
   if (uri == null) {
        return;
   }
   String host = uri.getHost(); //上面的例子获取的就是main
   List<String> pathSegments = uri.getPathSegments();//上面的例子获取的集合就是[tag=1, name=2]

第二种方式通过Bundle

Bundle bundle = getIntent().getExtras();//从Bundle中解析对应的key和value值即可

2、创建ContentProvider

Android中比较明显的应用就是读取通讯录。同样我们在自己的应用中创建ContentProvider供其他APP使用,注意在定义的时候要去在AndroidManifest中声明并授权。

3、广播

广播是一种被动跨进程通讯的方式。应用只能被动接收发送的广播数据。

广播分为普通广播、有序广播和粘性广播、系统广播、APP的本地广播

普通广播 所有的接收者都可以匹配该广播

sendBroadcast

有序广播 根据接收者的IntentFiter的priority的大小依次调用,并且可以通过abortBroadcast()阻止广播继续向下传播

1、sendOrderedBroadcast

2、对应的接收者只能按照优先级依次接收intent,对应AndroidManifest文件中的android:priority,取值范围为-1000~1000,数值越大优先级越大

3、前面的接收者可以对接收到的intent进行处理,并将处理结果放到广播的intent中,然后传递给下一个接收者,并且有权调用abortBroadcast来终止该广播

粘性广播 可以在接收者注册之前发出的广播,在发送后会一直存在系统的消息容器中,等待对应的接收者进行接收。若没有则一直等待

1、sendStickyBroadcast:

2、发送时没有注册,一旦注册,马上可以接收到

3、5.0之后已经失效

系统广播

系统广播有对应的IntentFilter,随着Android SDK的升级,有些系统广播可以静态注册,但有些系统广播只能动态注册。

系统会主动发送

 
APP的本地广播

相对于普通广播的区别在于该广播的exported为false,而IntentFilter中的exported默认为true(android:exported);

仅用于APP内单独使用,其他APP无法接收。这个和跨进程通信无关

1、多用于提高APP的安全性,效率高;

2、注册时exported设置为false(android:exported),同时注意设置receiverPermission权限(android:permission),用于权限验证

3、发送广播的时候,指定广播的包名

4、对应LocalBroadcastManager

对应的发送广播的方法:

public void sendBroadcast(Intent intent) 
public void sendBroadcast(Intent intent, int userId) 
public void sendBroadcast(Intent intent, String receiverPermission) 
public void sendOrderedBroadcast(Intent intent, String receiverPermission) 
public void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) 
public void sendStickyBroadcast(Intent intent) 
public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

注意这个里面有一个receiverPermission的参数,这个是广播权限,就是可以设置发送广播的权限,在接收的时候也需要相应的权限。 

因为我们要在不同APP之间传递数据,那么就要采用普通广播这种方式,相关数据可以设置到bundle绑定到intent中。在接收到广播的时候,在接收intent中解析对应的数据。

注意在跨进程的发送广播的时候要注意有以下两种方式:

1)限制发送方

接收APP设置权限,即只有对应权限的APP(发送方)才可以发送到声明权限的APP(接受方)中,对应代码如下:

  • 在接收方APP的AndroidManifest文件中定义权限和BroadcastReceiver
<!--定义权限-->
<permission android:name="com.j1.permission.for.sender"
        android:protectionLevel="normal"/>
<!--使用权限-->
 <uses-permission android:name="com.j1.permission.for.sender"/>
<!--该receiver要声明权限-->
 <receiver
      android:name="com.j1.CrossProcessBroadcastReceiver"
      android:permission="com.j1.permission.for.sender">
      <intent-filter>
           <action android:name="com.j1.CrossProcessBroadcastReceiver" />
      </intent-filter>
 </receiver> 

注意这里一定要在接收方APP中也要使用该<uses-permission android:name="com.j1.permission.for.sender"/>,否则系统会抛出下面的异常,接收不到广播 

Permission Denial: receiving Intent { act=com.j1.CrossProcessBroadcastReceiver flg=0x10 } to com.j1.aidl/com.j1.CrossProcessBroadcastReceiver requires com.j1.permission.for.sender due to sender com.j1.xxx (uid 10083)
  • 在发送方的AndroidManifest文件中要使用该权限<uses-permission android:name="com.j1.permission.for.sender"/>
  • 在发送方的代码中调用发送广播的代码
sendBroadcast(new Intent("com.j1.CrossProcessBroadcastReceiver"));

2)限制接收方

发送APP设置权限,即只有对应权限接收方APP的receiver才可以收到广播

  • 在发送方APP的AndroidManifest文件中定义权限
<!--定义权限-->
<permission android:name="com.j1.permission.for.receiver"/>
       
<!--使用权限-->
<uses-permission android:name="com.j1.permission.for.receiver"/>

同样注意这里发送APP中也要声明<uses-permission android:name="com.j1.permission.for.receiver"/>,否则系统会抛出下面异常,接收APP无法收到广播。

 Permission Denial: broadcasting Intent { act=com.j1.CrossProcessBroadcastReceiver flg=0x10 } from com.j1.xxx (pid=3619, uid=10083) requires com.j1.permission.for.receiver due to receiver com.j1.aidl/com.j1.CrossProcessBroadcastReceiver
  • 接收方APP的AndroidManifest文件中使用权限并且声明对应有该权限的BroadcastReceiver
<!--使用权限-->
 <uses-permission android:name="com.j1.permission.for.receiver"/>
<!--定义BroadcastReceiver-->
        <receiver
            android:name="com.j1.CrossProcessBroadcastReceiver"
            android:permission="com.j1.permission.for.receiver">
            <intent-filter>
                <action android:name="com.j1.CrossProcessBroadcastReceiver" />
            </intent-filter>
        </receiver>
  • 最后发送方APP在代码中发送广播即可
sendBroadcast(new Intent("com.j1.CrossProcessBroadcastReceiver"))

上面有一点我想不通的是 为什么我在发送广播的时候,必须采用在发送方的APP的AndroidManifest文件必须要去使用对应的权限,而不能采用下面这种方式去发送广播,采用下面的这种方式不管1或者2都会抛出异常,还希望有人能帮忙解释下。对应的Android 7.1以上

sendBroadcast(new Intent("com.j1.CrossProcessBroadcastReceiver"),"com.j1.permission.for.receiver");

4、AIDL服务

这种跨进程服务相比较上面的四种可以传递更多的数据,数据也可以更多样化。所以另进行分析。Android的AIDL跨进程通信

总结

上面不同的四种方式各有的特点:

Activity是通过Uri调用其他的APP的Activity,ContentProvider是通过共享数据库的方式提供给其他APP的Cursor,广播是通过被动接收的方式来接收其他APP传递的数据,当然AIDL 服务则是通过将接口公开,访问AIDL服务。

所以可以根据应用的场景进行选择不同的方式。

猜你喜欢

转载自blog.csdn.net/nihaomabmt/article/details/82870426