1. 跨进程通信
在android应用中不同进程是不能共享内存的,所以在不同进程间传递对象就需要用到跨进程通信。
2. 应用内多进程
一般一个应用一个进程就足够了,但如果像一些大型的应用经常会看到不止一个进程,比如微信、QQ之类的。一个进程的内存是定死的,如果有耗内存的动作就容易OOM,这时候就可以考虑多进程,提高内存的限制,还有就是不同进程间可以相互监听达到互相守护的功能,提高应用后台保持运行几率。
比如本人所在公司开发的软件是关于VoIP通信类,一个进程是那些通讯录、消息、历史记录、用户等等相关的Activity,而另一个进程是PJSIP通信相关的Service。这个service就相当于远程服务,独立运行,而Activity经常需要跟这个Service交互比如打VoIP电话,这就需要用到跨进程通信。
3. Android跨进程通信接口
Android跨进程通信可以采用AIDL来公开服务的接口,采用远程过程调用和代理模式来实现跨进程通信。AIDL英文全称Android Interface Definition Language 即 Android接口描述语言,ADT会根据AIDL在gen下生成相应的JAVA接口文件。
4. 实例解析
这个实例本来是我给来公司面试的人员出的一道面试题,后来闲着没事就自己写了个demo,题目大体如下:展示天气的demo,Activity负责展示,Service负责获取天气,Acitivty和Service在不同的进程,之间必须通过AIDL来传递对象。过程是Activity点击获取天气的BUTTON后Service开始获取天气,并封装成一个对象以回调的形式回传给Activity,Activity展示该天气信息。
(1) Android工程结构
以前用Eclipse,现在改用Android Studio,感觉效率提高了不少。结构如下图包括MainActivity 展示天气,WeatherBean 天气对象,WeatherService 获取天气的服务。 AIDL 包括 IWeatherInterface 天气服务的接口,IWeatherServiceCallback 天气服务回调的接口, WeatherBean 天气对象接口。
(2) AIDL文件
IWeatherInterface.aidl
这个AIDL主要是Activity绑定Service后注册和解注册回调接口以及通知Service开始获取天气的接口方法。源码如下:
// IWeatherInterface.aidl
package com.easiio.test.weather;
import com.easiio.test.weather.IWeatherServiceCallback;
interface IWeatherInterface {
void registerCallback(int hash, IWeatherServiceCallback callback);
void unregisterCallback(int hash);
void startGetWeather(String citypinyin);
}
IWeatherServiceCallback.aidl
这个是Service将天气回传给Activity的接口文件,源码如下:
// IWeatherServiceCallback.aidl
package com.easiio.test.weather;
import com.easiio.test.weather.WeatherBean;
interface IWeatherServiceCallback {
void showWeather(in WeatherBean weather);
}
WeatherBean.aidl
AIDL支持传递实现了android.os.Parcelable接口的复杂类型,这里的天气对象就是implements Parcelable 的复杂类型对象
// IWeatherServiceCallback.aidl
package com.easiio.test.weather;
parcelable WeatherBean;
(3) 主程序
MainActivity.java
package com.easiio.test.weather;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.design.widget.Snackbar;
import android.support.v4.util.LogWriter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "[EASIIO]MainActivity";
private static final int MSG_WHAT_GET_SUCCESS = 0;
private TextView mShowWeatherView;
private EditText mCityPinyinET;
private ProgressDialog mProgressDialog;
private IWeatherInterface mIWeatherInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate...");
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mShowWeatherView = (TextView) this.findViewById(R.id.weather_text_view);
mCityPinyinET = (EditText) this.findViewById(R.id.city_pinyin_edittext);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage("Loading...");
this.findViewById(R.id.button_get_weather).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String pinyin = mCityPinyinET.getEditableText().toString();
if (TextUtils.isEmpty(pinyin)){
Toast.makeText(MainActivity.this, "Not empty", Toast.LENGTH_SHORT).show();
return;
}
mProgressDialog.show();
try {
if (mIWeatherInterface != null){
mIWeatherInterface.startGetWeather(pinyin);
}
} catch (RemoteException ex){
Log.e(TAG, "Start get weather failed, ex : " + ex.getLocalizedMessage());
}
}
});
if(!bindService(new Intent(this, WeatherService.class), mServiceConnection, Context.BIND_AUTO_CREATE)){
Toast.makeText(this, "Bind service failed.", Toast.LENGTH_SHORT);
finish();
return;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mServiceConnection != null){
this.unbindService(mServiceConnection);
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
try {
mIWeatherInterface = IWeatherInterface.Stub.asInterface(iBinder);
mIWeatherInterface.registerCallback(mIWeatherServiceCallback.hashCode(), mIWeatherServiceCallback);
} catch (Exception ex){
Log.e(TAG, "onServiceConnected failed : " + ex.getLocalizedMessage());
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
try {
mIWeatherInterface.unregisterCallback(mIWeatherServiceCallback.hashCode());
} catch (Exception ex){
Log.e(TAG, "onServiceConnected failed : " + ex.getLocalizedMessage());
}
}
};
private IWeatherServiceCallback mIWeatherServiceCallback = new IWeatherServiceCallback.Stub () {
@Override
public void showWeather(WeatherBean weather) throws RemoteException {
Log.i(TAG, "shoWeather : " + weather.toString());
Message msg = mHandler.obtainMessage();
msg.what = MSG_WHAT_GET_SUCCESS;
msg.obj = weather;
mHandler.sendMessage(msg);
}
};
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == MSG_WHAT_GET_SUCCESS){
mProgressDialog.dismiss();
if (msg.obj == null){
mShowWeatherView.setText("Weather is null.");
return;
}
WeatherBean weather = (WeatherBean) msg.obj;
if (weather == null){
mShowWeatherView.setText("Weather is null.");
return;
}
mShowWeatherView.setText(weather.toString());
}
}
};
}<span style="color:#4169e1;font-weight: bold;">
</span>
WeatherService.java
package com.easiio.test.weather;
import android.app.Service;
import android.content.Intent;
import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
/**
* Created by gavin on 10/30/15.
*/
public class WeatherService extends Service{
private static final String TAG = "[EASIIO]WeatherService";
private HashMap<Integer, IWeatherServiceCallback> m_callback = new HashMap<Integer, IWeatherServiceCallback>();
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate");
}
@Override
public IBinder onBind(Intent intent) {
return new WeatherBinder();
}
private class WeatherBinder extends IWeatherInterface.Stub{
@Override
public void registerCallback(int hash, IWeatherServiceCallback callback) throws RemoteException {
if (m_callback != null && !m_callback.containsKey(hash)){
m_callback.put(hash, callback);
Log.w(TAG, "Add callback hash = " + hash);
}
}
@Override
public void unregisterCallback(int hash) throws RemoteException {
if (m_callback != null) {
Iterator<Integer> it = m_callback.keySet().iterator();
while (it.hasNext()) {
Integer i_hash = it.next();
if (i_hash.equals(hash)) {
it.remove();
break;
}
}
Log.i(TAG, "Removed callback: " + hash + " callbacks: " + m_callback.size());
}
}
@Override
public void startGetWeather(final String citypinyin) throws RemoteException {
Log.w(TAG, "startGetWeather");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
WeatherBean weather = null;
String urlStr = "http://apistore.baidu.com/microservice/weather?citypinyin=" + citypinyin;
try{
URL url = new URL(urlStr);
URLConnection connection = url.openConnection();
connection.connect();
InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
StringBuffer sb = new StringBuffer();
String str = "";
while ((str = reader.readLine()) != null)
{
sb.append(str).append("\n");
}
str = sb.toString();
Log.w(TAG, "Get weather result = " + str);
weather = parseJsonForWeather(str);
} catch (Exception ex){
Log.e(TAG, "Open url failed, ex : " + ex.getLocalizedMessage());
}
if (weather == null){
weather = new WeatherBean();
weather.errMsg = "Weather is Null.";
}
callbackShowWeather(weather);
}
});
thread.start();
}
}
private void callbackShowWeather(WeatherBean weather){
Set<Integer> clientsHash = null;
if (m_callback != null) {
clientsHash = m_callback.keySet();
}
if (clientsHash == null) {
return;
}
for (Integer hash : clientsHash) {
IWeatherServiceCallback callback = m_callback.get(hash);
if (callback != null){
try {
callback.showWeather(weather);
} catch (DeadObjectException e_do) {
Log.w(TAG, "Callback removed. DeadObjectException: hash " + hash);
m_callback.remove(hash);
} catch (RemoteException re) {
Log.w(TAG, "RemoteException:", re);
}
}
}
}
private WeatherBean parseJsonForWeather(String str){
if (TextUtils.isEmpty(str)){
return null;
}
try{
WeatherBean weather = new WeatherBean();
JSONObject json = new JSONObject(str);
weather.errNum = json.getInt("errNum");
weather.errMsg = json.getString("errMsg");
if (weather.errNum == 0){
JSONObject dataJson = json.getJSONObject("retData");
weather.city = dataJson.getString("city");
weather.weather = dataJson.getString("weather");
weather.temp = dataJson.getString("temp");
weather.l_tmp = dataJson.getString("l_tmp");
weather.h_tmp = dataJson.getString("h_tmp");
weather.wd = dataJson.getString("WD");
weather.ws = dataJson.getString("WS");
}
return weather;
} catch (JSONException ex){
Log.e(TAG, "parseJson failed : " + ex.getLocalizedMessage());
return null;
}
}
}
WeatherBean.java
package com.easiio.test.weather;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by gavin on 10/30/15.
*/
public class WeatherBean implements Parcelable {
public String city;
public String weather;
public String temp;
public String l_tmp;
public String h_tmp;
public String wd;
public String ws;
public int errNum;
public String errMsg;
public WeatherBean(){
}
@Override
public String toString(){
StringBuilder builder = new StringBuilder();
builder.append("Result:").append(errMsg).append("\n")
.append("City = ").append(city).append("\n")
.append("Weather = ").append(weather).append("\n")
.append("Temp = ").append(temp).append("\n")
.append("Low Temp = ").append(l_tmp).append("\n")
.append("High Temp = ").append(h_tmp).append("\n")
.append("WD = ").append(wd).append("\n")
.append("WS = ").append(ws).append("\n");
return builder.toString();
}
private Object mLock = new Object();
private WeatherBean(Parcel in){
readFromParcel( in );
}
public void readFromParcel( Parcel in ){
synchronized (mLock) {
errNum = in.readInt();
errMsg = in.readString();
city = in.readString();
weather = in.readString();
temp = in.readString();
l_tmp = in.readString();
h_tmp = in.readString();
wd = in.readString();
ws = in.readString();
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
synchronized (mLock) {
dest.writeInt(errNum);
dest.writeString(errMsg);
dest.writeString(city);
dest.writeString(weather);
dest.writeString(temp);
dest.writeString(l_tmp);
dest.writeString(h_tmp);
dest.writeString(wd);
dest.writeString(ws);
}
}
public static final Creator<WeatherBean> CREATOR = new Creator<WeatherBean>() {
public WeatherBean createFromParcel( Parcel in ){
return new WeatherBean(in);
}
public WeatherBean[] newArray( int size){
return new WeatherBean[size];
}
};
}
运行后界面大致如下:
基本上是这样子的了,欢迎交流,工程源码可到github下载
https://github.com/zjc3909/TestWeather.git