美文网首页Android开发
android 跨进程通信及回调

android 跨进程通信及回调

作者: JeremyDai | 来源:发表于2017-01-04 17:06 被阅读1664次

前言

我们在应用开发当中经常会需要把一些耗时和复杂的操作放这另外一个服务当中去处理,如果这个服务有一些公共的处理需求的话,我们就需要把这个服务当都的作为一个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中处理消息。

相关文章

网友评论

    本文标题:android 跨进程通信及回调

    本文链接:https://www.haomeiwen.com/subject/ccswyttx.html