AIDL 用于进程间通信,广泛应用于安卓开发中.
从Android 5.0开始,Google推荐使用Android Studio 进而取代原来的Eclipse.
下面以一简单例子介绍AIDL的使用(非概念) ,基于 Android Studio (1.0.1).
PS: AIDL 基础概念可以参考: http://blog.csdn.net/stonecao/article/details/6425019
1. 主要模块
(1) AIDLClient: AIDL 客户端,提供一个Button 和 TextView.
UI(TextView)更新有两个方式:a. 点击Button,从服务端获取信息主动更新; b. 服务端定时请求客户端更新。
从而实现客户端和服务端之间的通信.
(2) AIDLService: AIDL 服务端,启动一个线程计数(模拟获取数据). 主要功能:
a. 提供客户端调用方法 b.主动请求更新
2. AIDL文件
Client & Service 端需要相同的.aidl 文件, 并且包名实现.
在Android Studio 中创建AIDL 文件非常方便,File -> New ->AIDL, 之后在java 的同目录下会创建"aidl" 文件夹.
以下两个文件在客户端和服务端都要有一份(IMyService 在服务端实现,IAdapter 在客户端实现 )
// IMyService.aidl
package com.xx.myaidlservice;
// Declare any non-default types here with import statements
import com.xx.aidlapp.iadapter;
interface IMyService {
void setAdapter(in IAdapter adapter);
String getMsg();
}
// IAdapter.aidl
package com.xx.aidlapp;
// Declare any non-default types here with import statements
interface IAdapter {
void resetTextView(String text);
}
3. 服务端实现:
(1)注意黄色部分,即mBinder为实现IMyService.Stub (其中Stub 是IMyService的内部类),
在客户端绑定服务后返回mBinder给客户端,因此客户端可以通过mBinder调用服务端的方法
(2) 蓝色背景部分,为服务端调用客户端方法. 其中mAdapter 是由客户端传过的值实例化. 从而调用客户端方法resetTextView。
package com.xx.myaidlservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.xx.aidlapp.IAdapter;
public class AIDLService extends Service {
private final String LOG_TAG = "AIDLService";
public IMyService mImyService;
private int cnt;
private AutoCountThread thread;
private boolean stopThread;
private IAdapter mAdapter;
public AIDLService() {
}
@Override
public void onCreate() {
Log.d(LOG_TAG, "OnCreate " );
super.onCreate();
cnt = 0;
stopThread = false;
thread = new AutoCountThread();
thread.start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(LOG_TAG, "onStartCommand " );
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d(LOG_TAG, "onDestroy " );
stopThread = true;
super.onDestroy();
}
private final IMyService.Stub mBinder = new IMyService.Stub(){
@Override
public void setAdapter(IAdapter adapter) throws RemoteException {
Log.d(LOG_TAG, "setAdapter adapter " + adapter );
mAdapter = adapter;
}
@Override
public String getMsg() throws RemoteException {
String serviceStr;
serviceStr = "Msg(Service) " + cnt;
Log.d(LOG_TAG, "serviceStr = " + serviceStr + " cnt = " + cnt);
return serviceStr;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.d(LOG_TAG, "onBind");
if(mBinder == null){
Log.d(LOG_TAG, "bind service fail -- mBinder == null");
}
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(LOG_TAG, " onUnbind -- intent" + intent);
return super.onUnbind(intent);
}
public class AutoCountThread extends Thread{
@Override
public void run() {
//super.run();
while(stopThread != true) {
try {
cnt++;
Thread.sleep(5000);
Log.d(LOG_TAG, "stopThread " + stopThread + " cnt = " + cnt);
resetText();
if(cnt == 50){
stopThread = true;
}
} catch (InterruptedException e) {
Log.d(LOG_TAG, "AutoCountThread InterruptedException e " + e);
}
}
}
@Override
public synchronized void start() {
super.start();
Log.d(LOG_TAG, "AutoCountThread -- start");
}
}
public void resetText(){
Log.d(LOG_TAG, "resetText --mAdapter = " + mAdapter );
if( mAdapter!= null){
String serviceStr;
serviceStr = "Auto Update Msg(Service) " + cnt;
try {
Log.d(LOG_TAG, "serviceStr = " + serviceStr );
mAdapter.resetTextView(serviceStr);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
4. 客户端实现:
(1) 黄色部分即为调用服务端方法
(2) 蓝色部分即为客服端实现的AIDL接口相关
注意: 在服务端请求更新UI, 客服端需要在主线程中更新UI, 否则setText不生效, 该处使用了Hanlder处理.
package com.xx.aidlapp;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.xx.myaidlservice.IMyService;
public class AIDLMainActivity extends Activity {
private final String LOG_TAG = "AIDLMainActivity";
private Button aidlTestBtn;
private TextView messageText;
private IMyService mService;
private MyServiceConnection conn;
private static final int MSG_UPDATE_TEXT = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_UPDATE_TEXT:
//Log.d(LOG_TAG, " handleMessage msg.obj = " + msg.obj);
updateTextView((String)msg.obj);
}
}
};
private final IAdapter mAdapter = new IAdapter.Stub(){
@Override
public void resetTextView(String text) throws RemoteException {
//Log.d(LOG_TAG, "resetTextView text = " + text);
Message msg = mHandler.obtainMessage(MSG_UPDATE_TEXT, text);
mHandler.sendMessage(msg);
//messageText.setText(text);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
//Log.d(LOG_TAG, " onCreate + mAdapter = " + mAdapter);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl_main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
aidlTestBtn = (Button) findViewById(R.id.aidlButton1);
messageText = (TextView) findViewById(R.id.msgText);
//Log.d(LOG_TAG, "onCreate -- messageText " + messageText);
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.xx.myaidlservice", "com.xx.myaidlservice.AIDLService"));
conn = new MyServiceConnection();
bindService(intent, conn, Context.BIND_AUTO_CREATE);
aidlTestBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String msg = "Init";
try {
if (mService != null ) {
msg = mService.getMsg();
}else{
Toast.makeText(AIDLMainActivity.this, "Service has been kill, can not get message from AIDLService", Toast.LENGTH_SHORT);
}
} catch (RemoteException e) {
Log.d(LOG_TAG, " mService.getMsg fail");
}
messageText.setText(msg);
}
});
}
@Override
protected void onDestroy() {
Log.d(LOG_TAG, "onDestroy");
if(mService != null){
Log.d(LOG_TAG, "onDestroy --> unbindService");
unbindService(conn);
}
super.onDestroy();
}
protected class MyServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(LOG_TAG, "onServiceConnected-- ComponentName = " + name);
mService = IMyService.Stub.asInterface(service);
setAdapter();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(LOG_TAG, "onServiceDisconnected -- ComponentName = " + name);
mService = null;
}
}
private void setAdapter(){
Log.d(LOG_TAG, "mService = " + mService + " mAdapter = " + mAdapter);
if(mService != null){
try {
mService.setAdapter(mAdapter);
} catch (RemoteException e) {
Log.d(LOG_TAG, " mService.setAdapter fail");
}
}
}
public void updateTextView(String text){
Log.d(LOG_TAG, "updateTextView " + text);
//Log.d(LOG_TAG, "updateTextView -- messageText " + messageText);
messageText.setText(text);
}
}
5. 总结
1. 客户端和服务端需要相同的AIDL文件,包括包名需要一样。可以在写好一份拷贝过去。
2.定义了AIDL接口文件后,需要实现该接口, 另一方通过 xx.Stub.asInterface 获取代理对象.
3. UI更新需要在主线程中.
4. AIDLService 在启动一段时间后,会被强制kill掉,不知是不是内存泄露了...
未完待续。。