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 天气对象接口。





android 中的跨进程 android 跨进程通信_跨进程





(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];
        }
    };

}


运行后界面大致如下:



android 中的跨进程 android 跨进程通信_跨进程_02

 

android 中的跨进程 android 跨进程通信_aidl_03




基本上是这样子的了,欢迎交流,工程源码可到github下载


https://github.com/zjc3909/TestWeather.git