最近打算重新认识一下 Binder
,所以第一步就是熟练使用 AIDL
进行跨进程通信(简单来说就是远程 Service
)。之后呢,当然是开始写 Demo
,然而巧了我用的测试手机是魅族手机,之后就尴尬了,每每客户端在绑定服务的时候就崩溃,一度怀疑自己的能力,找 log
发现还没有,没有。。。迫不得已之下,换了一台测试机,发现莫名其妙的通了,于是问了问度娘,度娘说:“魅族手机呀,你需要这样这样在那样。”
于是这篇文章就被你们看到了,,,
这里关于 AIDL
的代码我就不在叙述了,文末会留下链接,供大家下载。
在客户端绑定时的代码:
/**
* 这样写在 vivo,三星,一加,锤子(我就测了这么多机型)都是没问题的
* 但是在魅族手机上就会绑定失败,导致崩溃。
*/
Bundle args = new Bundle();
Intent intent = new Intent();
intent.setAction("MyService");
intent.setPackage("com.example.randy.observerdemo");
intent.putExtras(args);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
之后改成了下面这段代码就OK了
// 项目名称,都是之前写Demo的时候建的,这次借用了一下,哈哈
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.example.randy.observerdemo", "com.example.randy.observerdemo.MyService");
intent.setComponent(componentName);
intent.setAction("MyService");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
在这里用到了 ComponentName
,顾名思义,就是组建名称,通过调用 Intent
中的 setComponent
方法,我们可以打开另外一个应用中的 Activity
或者服务。
实例化一个 ComponentName
需要两个参数,第一个参数是要启动应用的包名称,这个包名称是指 AndroidManifest
文件中列出的应用的包名称,如下图:
第二个参数是你要启动的
Activity
或者 Service
的全称(包名+类名),代码如上述第二段代码块。
注意:
如果你要的启动的其他应用的 Activity
不是该应用的入口 Activity
,那么在清单文件中,该 Activity
节点一定要加上 android:exported="true"
,表示允许其他应用打开,对于所有的 Service
,如果想从其他应用打开,也都要加上这个属性:
对于除了入口
Activity
之外的其他组件,如果不加这个属性,都会抛出 “java.lang.SecurityException: Permission Denial.....”
异常。那么为什么入口
Activity
不用添加这个属性就可以被其他应用启动呢?我们来看一段入口 Activity
的注册代码:
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
入口 Activity
和普通 Activity
唯一不同的地方就是入口 Activity
多了一个过滤器,对于包含了过滤器的组件,意味着该组件可以提供给外部的其他应用来使用,它的 exported
属性默认为 true
,相反,如果一个组件不包含任何过滤器,那么意味着该组件只能通过指定明确的类名来调用,也就是说该组件只能在应用程序的内部使用,在这种情况下,exported
属性的默认值是 false
。
最后,参考链接及项目地址:
关于ComponentName的使用
项目地址:
客户端代码
服务端代码