Android进程间通讯——使用Kotlin实现AIDL
导读
学习了几天kotlin,想着实践下试试手,于是乎就想起了aidl,好吧,就拿它练练
<a name="label">1.服务的两种开启方式</a>
<a name="label">2.使用AIDL调用远程服务</a>
ps:AndroidStudio还没配置好Kotlin开发环境的童鞋,可以先移步 <a href="http://www.jianshu.com/p/bad94c8f554f">AndroidStudio使用kotlin入门</a>
Android进程间通讯
进程间通讯,可以理解为两个不同进程间进行数据交互的一个行为,AIDL是android特有的一种调用远程服务方式
一、先复习下服务的两种开启方式
服务的开启方式有两种,一种是直接startService,另一种是bindServeice;两者的区别呢?
前者:服务长期后台运行,不可以调用服务里面的方法
后者:可以调用服务的方法,但是不能长期后台运行
因为我们是需要进行进程间通讯,所有需要使用到服务里面的方法,这里主要是回顾下绑定服务,除了以下代码外,别忘了service是需要注册清单文件哦
1-1、Deservice里面的代码
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
class DemoService : Service() {
val TAG :String = "DemoService";
override fun onBind(intent: Intent?): IBinder {
Log.v(TAG,"服务绑定成功");
return DemoBinder();
}
class DemoBinder : Binder() {
fun callService(a:Int,b:Int): String? {
var result :String? = null;
result = "服务端收到,这是给你的回复 a+b= ${a+b}"
return result;
}
}
}
1-2、MainActivity里面的代码
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.support.annotation.IntegerRes
import android.support.v7.app.AppCompatActivity
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity(), View.OnClickListener,ServiceConnection {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_bind.setOnClickListener(this)
btn_unbind.setOnClickListener(this)
}
override fun onClick(v: View?) {
when(v?.id){
R.id.btn_bind -> {
if (et1.text.toString().isEmpty()||et2.text.toString().isEmpty()){
Toast.makeText(this,"请输入内容哦",Toast.LENGTH_SHORT).show()
return
}
val intent = Intent(this, DemoService::class.java)
bindService(intent,this,BIND_AUTO_CREATE)
}
}
}
//当服务失去连接时调用
override fun onServiceDisconnected(name: ComponentName?) {
}
//当服务被成功连接的时候调用的方法
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
var binder : DemoService.DemoBinder = service as DemoService.DemoBinder;
val serviceInfo:String = binder.callService(a = parseInt(et1.text.toString())!!, b = parseInt(et2.text.toString())!!)!!;
if(!TextUtils.isEmpty(serviceInfo)){
Toast.makeText(this,serviceInfo,Toast.LENGTH_SHORT).show();
}
}
fun parseInt(s: String): Int? {
if(s == null || s == "")
return null;
return Integer.parseInt(s);
}
override fun onDestroy() {
super.onDestroy()
unbindService(connection);
}
}
1-3.activity_main.xml
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.myapplication.MainActivity">
<LinearLayout
android:orientation="horizontal"
android:layout_width="368dp"
android:layout_height="50dp"
tools:layout_editor_absoluteY="0dp"
tools:layout_editor_absoluteX="8dp">
<EditText
android:id="@+id/et1"
android:inputType="phone"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
tools:layout_editor_absoluteY="4dp"
tools:layout_editor_absoluteX="41dp" />
<EditText
android:inputType="phone"
android:id="@+id/et2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
tools:layout_editor_absoluteY="4dp"
tools:layout_editor_absoluteX="123dp" /></LinearLayout>
<Button
android:id="@+id/btn_bind"
android:layout_width="110dp"
android:layout_height="90dp"
android:text="绑定服务并调用远程方法"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="0.149"
app:layout_constraintVertical_bias="0.096" />
</android.support.constraint.ConstraintLayout>
二.使用AIDL调用远程服务
还记得Java中我们是如何使用AIDL的吗?<br />
1.服务端,创建aidl接口文件,暴露需要让客户端调用的方法
2.服务端,创建好服务类,并且在清单文件中配置好服务的action
3.服务端,实现aidl接口类,以*****.Stub()
的方式,重写需要暴露给客户端调用的方法
4.客户端,复制服务端aidl文件到项目中,必须包名一致
5.客户端,bind方式开启远程服务,用*****.Stub.asInterface(service)
方法获取aidl接口类的实例
6.客户端,使用获取到的aidl接口类实例调用远程服务方法
7.客户端,onDestory方法中,解除注册绑定服务
1.服务端,创建AIDL文件,这里需要注意下,使用AS提供的方式去创建,不要手动自己创建,否则后面会问有题,这点和之前eclipse有点不一样
创建AIDL文件1-1.aidl接口类的代码
// IMyAidlInterface.aidl
package com.example.myapplication.aidl;
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String callService(int a,int b);
}
2.服务端,Service代码
package com.example.myapplication
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.example.myapplication.aidl.IMyAidlInterface
class DemoService : Service() {
val TAG :String = "DemoService";
override fun onBind(intent: Intent?): IBinder {
Log.v(TAG,"服务绑定成功");
return DemoBinder();
}
class DemoBinder : IMyAidlInterface.Stub() {
override fun callService(a:Int,b:Int): String? {
var result :String? = null;
result = "服务端收到,这是给你的回复 a+b= ${a+b}"
return result;
}
}
}
3.客户端,复制服务的aidl文件,包名必须保持一致
复制服务的aidl文件4.客户端Activity代码,activity_main.xml的代码就不贴了,其实就两个Button和两个EditText
class MainActivity : AppCompatActivity(), View.OnClickListener {
private val TAG = "MainActivity"
var iservice : IMyAidlInterface? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_bind.setOnClickListener(this)
btn_call.setOnClickListener(this)
}
override fun onClick(v: View?) {
when(v?.id){
R.id.btn_bind -> {
bind();
}
R.id.btn_call ->{
if (et1.text.toString().isEmpty()||et2.text.toString().isEmpty()){
Toast.makeText(this,"请输入内容哦",Toast.LENGTH_SHORT).show()
return
}
callServiceMethod()
}
}
}
/**
* 绑定远程服务
* @param view
*/
fun bind() {
//android5.0以下才能用下面的方式开启远程服务,5.0以上则不支持隐式调用服务,否则会报错
// val service = Intent()
// service.action = "com.example.myapplication"
// bindService(service, this, BIND_AUTO_CREATE)
//5.0以上需要使用下面的代码实现隐式调用
val intent = Intent()
intent.action = "com.example.myapplication"
val eintent = Intent(createExplicitFromImplicitIntent(this, intent))
bindService(eintent, connection, Service.BIND_AUTO_CREATE)
}
fun callServiceMethod(){
val serviceInfo:String = iservice?.callService(parseInt(et1.text.toString())!!, parseInt(et2.text.toString())!!)!!;
if(!TextUtils.isEmpty(serviceInfo)){
Toast.makeText(this,serviceInfo,Toast.LENGTH_SHORT).show();
}
}
fun parseInt(s: String): Int? {
if(s == null || s == "")
return null;
return Integer.parseInt(s);
}
var connection = object :ServiceConnection{
//当服务失去连接时调用
override fun onServiceDisconnected(name: ComponentName?) {
Toast.makeText(this@MainActivity,"解绑服务成功",Toast.LENGTH_SHORT).show()
}
//当服务被成功连接的时候调用的方法
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
iservice = IMyAidlInterface.Stub.asInterface(service);
Toast.makeText(this@MainActivity,"服务绑定成功",Toast.LENGTH_SHORT).show()
}
}
fun createExplicitFromImplicitIntent(context : Context, implicitIntent :Intent ):Intent?{
val pm = context.packageManager
val resolveInfo = pm.queryIntentServices(implicitIntent, 0)
if (resolveInfo == null || resolveInfo.size != 1) {
return null
}
val serviceInfo = resolveInfo[0]
val packageName = serviceInfo.serviceInfo.packageName
val className = serviceInfo.serviceInfo.name
val component = ComponentName(packageName, className)
val explicitIntent = Intent(implicitIntent)
explicitIntent.setComponent(component)
return explicitIntent
}
override fun onDestroy() {
super.onDestroy()
unbindService(connection);
}
}