android 跨进程通信及回调

前言

我们在应用开发当中经常会需要把一些耗时和复杂的操作放这另外一个服务当中去处理,如果这个服务有一些公共的处理需求的话,我们就需要把这个服务当都的作为一个apk来处理,现在介绍一下在不同的apk当中的通信,目前有两种方式,一种是通过broadcast,另外一种是通过AIDL的方式。broadcast的方式比较的简单,下面介绍的是AIDL的方式。

AIDL方式跨进程通信

Service端

首先新建一个工程,并在工程中定义一个service

<service   
      android:name=".HHTService"    
      android:enabled="true"    
      android:exported="true">  
  <intent-filter>      
  <action android:name="com.hht.service" />    </intent-filter>
</service>

然后就是显示service其中的代码,

public class HHTService extends Service {
    public static final String TAG = "HHTService";
    final RemoteCallbackList<ITaskCallback> mCallbacks = new RemoteCallbackList<ITaskCallback>();

    public HHTService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG,"onBind-----");
        // TODO: Return the communication channel to the service.
//        throw new UnsupportedOperationException("Not yet implemented");
        return  stub;
    }

    /**
     * @param commandType
     */
    public void func(int commandType, final ITaskCallback callback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    try {
                        callback.actionPerformed(10);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public void disconnected(){

        for (int i = 0; i <mCallbacks.getRegisteredCallbackCount();i ++){
            try {
                mCallbacks.getBroadcastItem(i).disconnected();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {
        @Override
        public void registerCallback(ITaskCallback cb) throws RemoteException {
            if (cb != null){
                mCallbacks.register(cb);
            }
        }

        @Override
        public void unregisterCallback(ITaskCallback cb) throws RemoteException {
            if (cb != null) {
                mCallbacks.unregister(cb);
            }
        }

        @Override
        public void func1(int commandType,ITaskCallback callback) throws RemoteException {
            func(commandType,callback);
        }
    };
}

同时我们需要定义2个aidl的文件IMyAidlInterface.aidl (这个文件是给到另外一个apk,可以叫client端调用)
和ITaskCallback.aidl(这个是给服务端处理完数据后回调通知客户端更新ui用的)。

import service.hht.com.serviceapplication.ITaskCallBack; //这个很重要,需要自己手动import一下,否则编译不过的
interface IMyAidlInterface {
    //    /**
    //     * Demonstrates some basic types that you can use as parameters
    //     * and return values in AIDL.
    //     */
    //    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
    //            double aDouble, String aString);
   void registerCallback(ITaskCallback cb);
   void unregisterCallback(ITaskCallback cb);
   void func1(int commandType,ITaskCallback callback);
}

interface ITaskCallback  {
    void actionPerformed(int actionId);
    void disconnected();
}

写完后需要更新make或者build一下工程。

service端的工作基本就是这些了,下面到client,

Client 端

client端需要把刚才定义的aidl文件夹整个复制到client工程目录下,也就是跟你的源码放在一起,否则找不到接口编译不过的,我们client端需要实现ITaskCallback接口 和 mServiceConnection,
代码如下:

package service.hht.com.myhttcllent;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import service.hht.com.serviceapplication.IMyAidlInterface;
import service.hht.com.serviceapplication.ITaskCallback;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView)findViewById(R.id.textview);
        Button button1 = (Button)findViewById(R.id.button);
        Button button2 = (Button)findViewById(R.id.button2);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                //在5.0及以上版本必须要加上这个
                intent.setPackage("service.hht.com.serviceapplication");
                intent.setAction("com.hht.service");
                bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
                textView.setText("正在启动服务运行的服务!");
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (mService != null){
                        mService.func1(10,mCallback);
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                    textView.setText("未检测到正在运行的服务!");
                }
            }
        });
    }

    private ITaskCallback mCallback = new ITaskCallback.Stub() {
        public void actionPerformed(int id) {
//            textView.setText("通过服务回调获取 id:"+id);
            Log.e("TAG","通过服务回调获取 id:"+id);
        }
        public void disconnected(){

        }
    };

    IMyAidlInterface mService;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            textView.setText("服务已连接!");
            mService = IMyAidlInterface.Stub.asInterface(service);
            try {
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {

            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            textView.setText("服务已断开!");
            try {
                mService.unregisterCallback(mCallback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mService = null;
        }
    };

}
package service.hht.com.myhttcllent;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import service.hht.com.serviceapplication.IMyAidlInterface;
import service.hht.com.serviceapplication.ITaskCallback;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView)findViewById(R.id.textview);
        Button button1 = (Button)findViewById(R.id.button);
        Button button2 = (Button)findViewById(R.id.button2);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                //在5.0及以上版本必须要加上这个
                intent.setPackage("service.hht.com.serviceapplication");
                intent.setAction("com.hht.service");
                bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
                textView.setText("正在启动服务运行的服务!");
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (mService != null){
                        mService.func1(10,mCallback);
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                    textView.setText("未检测到正在运行的服务!");
                }
            }
        });
    }

    private ITaskCallback mCallback = new ITaskCallback.Stub() {
        public void actionPerformed(int id) {
//            textView.setText("通过服务回调获取 id:"+id);
            Log.e("TAG","通过服务回调获取 id:"+id);
        }
        public void disconnected(){

        }
    };

    IMyAidlInterface mService;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            textView.setText("服务已连接!");
            mService = IMyAidlInterface.Stub.asInterface(service);
            try {
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {

            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            textView.setText("服务已断开!");
            try {
                mService.unregisterCallback(mCallback);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mService = null;
        }
    };

}

我们需要拿到mService,然后才可以调用服务端的接口,服务端在处理完数据之后就会回调mCallback通知client端进行相关的操作,这样服务端的service(一个单独apk),客户端的activity(一个单独用的apk)就可以显示通信了。这个比较重要的就是通过设置callback的方式通知client端的更新。client端把一些公共的数据处理交给了service去做,如果有多个client就把大家公共的逻辑交给service去做,昨晚了通知我们更新就好。
这个需要注意的是aidl的数值传递是几个基本的数据类型:


int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString

如果需要传递其他类型的话需要自己去实现implement Parcelable协议 才行。后面我再继续补充自定义类型的传递实现!
下面补充自定类型通过AIDL的传递。
通过aidl跨进程传递的数据必须是可序列化的,java默认的有seriable,而可能是google开发android的时候觉得它太慢了,所有自己实现了一套类似的叫parcelable,下面我们开始定义一个类MyData 继承自Parcelable接口。

package service.hht.com.serviceapplication;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by Techer on 2017/1/4.
 */

public class MyData implements Parcelable {

    private int id;
    private String name;

    public MyData(int id,String name){
        this.id = id;
        this.name = name;
    }

    public static final Creator<MyData> CREATOR = new Creator<MyData>() {
        @Override
        public MyData createFromParcel(Parcel in) {
            return new MyData(in.readInt(),in.readString());
        }

        @Override
        public MyData[] newArray(int size) {
            return new MyData[size];
        }
    };

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }



    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.id);
        dest.writeString(this.name);
    }
}

有几个重要的方法需要复写:writeToParcel和Creator ,顺序也不能乱。
然后在这个类的同目录下定义一个aidl文件。

// MyData.aidl
package service.hht.com.serviceapplication;

// Declare any non-default types here with import statements

parcelable MyData;

需要注意的是,aidl和java的包名必须一样,如果是分开放的话,就必须是包名一样的,可以不在同一个目录下面,比如aidl放在aidl文件夹下,java文件放在java文件夹下,但是完整的报名一样就可,不然你会报找不到类文件的,然后就是在你需要的地方加入你传的这个MyData的值。

// IMyAidlInterface.aidl
package service.hht.com.serviceapplication;
import service.hht.com.serviceapplication.ITaskCallBack;
import service.hht.com.serviceapplication.MyData; // 不要忘记import,需要自己手动,不然会编译不过的。

interface IMyAidlInterface {
    //    /**
    //     * Demonstrates some basic types that you can use as parameters
    //     * and return values in AIDL.
    //     */
    //    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
    //            double aDouble, String aString);
   void registerCallback(String packageName,ITaskCallback cb);
   void unregisterCallback(String packageName,ITaskCallback cb);
   void func1(int commandType,ITaskCallback callback);
    void func2(String packageName,int commandType);
   void greet(in MyData data);
}

在client也需要MyData.java的直接把这个包拷贝过去就行了。
我的处理方式是将这些aidl及java文件打包到了一个aar的包里面,这样client需要用的话,直接依赖这个aar就可以了影响它自己的包结构。

在android当中也可以使用Messenger来进行跨进程的通信,该方式的实现原理就是通过AIDL来实现的,但是Messenger是不支持并发操作,也就是说,他是一条一条信息进行处理,所以它是线程安全的。同样是通过Handle进行操作,然后在handleMessage中处理消息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容