手把手教你如何用AIDL实现进程通信

1.前言

在研究DroidPlugin插件和service进程与主进程需要交互时都会用到AIDL来实现进程通信,今天回头整理资料的时候,发现AIDL有些模糊了,所以有些比较重要的知识点还是有必要把它记录下来。

2.绪论

AIDL,全称是Android interface definition language,Android接口描述语言。我们知道,每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,互不影响。那我们要实现进程间通信,该怎么做呢?这个时候AIDL就起作用了,下面就带大家一步一步实现进程通信。

3.实现进程通信

(由于界面排版问题,下面是以图片的形式讲解,在最后面会将源码贴出来)
1.在Android工程中,新建一个AIDL文件,此时as会自动生成AIDL目录,跟java目录并列。如图:

手把手教你如何用AIDL实现进程通信_第1张图片

2.编写AIDL接口方法,就以登录功能为例。写完后,一定要build下工程,让系统生成对应的java文件。

手把手教你如何用AIDL实现进程通信_第2张图片

3.创建一个服务进程,实现刚写的AIDL接口,提供检查登录输入的用户名和密码是否正确的功能。并在onBind方法中返回AIDL接口对象。

手把手教你如何用AIDL实现进程通信_第3张图片

4.在清单文件里注册该service,并以进程形式存在。android:process=”:checkLogin”:表示新开进程

手把手教你如何用AIDL实现进程通信_第4张图片

5.现在我们就可以在主进程调用该接口实现进程通信了,先上代码

手把手教你如何用AIDL实现进程通信_第5张图片
手把手教你如何用AIDL实现进程通信_第6张图片
手把手教你如何用AIDL实现进程通信_第7张图片

代码还是很清晰,第一块是创建serviceConnection回调对象,在连接成功后会回调onServiceConnected()方法,然后通过IMyAidl.Stub.asInterface()来获取IMyAidl接口对象。第二块是绑定检查登录服务,绑定时将serviceConnection回调对象传入。第三块是当点击登录按钮时通过IMyAidl对象调用checkLogin方法去判断登录是否成功。此时就是进程通信了。第四块是当activity被销毁时,将此服务解绑。

6.编译生成apk后,运行效果:

手把手教你如何用AIDL实现进程通信_第8张图片

手把手教你如何用AIDL实现进程通信_第9张图片

进程通信基本上结束了,但是,有些需求是需要进程间传递自定义数据结构,这个怎么实现呢?那我们也来一起试试吧,毕竟这个需求也是很大的。

4.进程通信之自定义数据结构

1.新建一个数据结构LoginBean类,要实现在进程间传递的话就必须实现Parcleable接口,为啥不能用Serializable ? 因为 第一,Parcleable序列化和反序列化的效率均比Serializable接口高;第二,Parcelable接口是Android所特有的,AIDL进行在进程间通信(IPC),都是需要实现Parcelable接口。实现Parcleable接口就需要实现writeToParcel、describeContents函数以及静态的CREATOR变量,请看图:

手把手教你如何用AIDL实现进程通信_第10张图片

这个类最好是放在AIDL包下,方便移植,如果说我们需要与别的应用进行通信时,那么在别的应用里也需要放一个AIDL包,而且包名和内容都要一样。

2.在AIDL包下新建一个LoginBean.aidl文件,表示引入了一个序列化对象 LoginBean 供其他的AIDL文件使用,如果不做这一步就会编译不通过;

手把手教你如何用AIDL实现进程通信_第11张图片

3.在IMyAidl接口里新增login方法,在接口里引用了LoginBean类。

手把手教你如何用AIDL实现进程通信_第12张图片

此时编译肯定不通过。第一,没有导包;就是上面第一个红框。记得LoginBean这个类一定要导包,虽然在同一个包下;第二,as是使用 Gradle 来构建 Android 项目的, 默认是将 java 代码的访问路径设置在 java 包下的,而LoginBean类是在AIDL包下的,所以不认识这个类。我们可以在build文件中配置下就可以解决问题。

手把手教你如何用AIDL实现进程通信_第13张图片

4.在服务端实现这个接口方法。

手把手教你如何用AIDL实现进程通信_第14张图片

5.在主进程调用这个接口。

手把手教你如何用AIDL实现进程通信_第15张图片

6.编译生成apk,运行效果也是一样。

5.总结

其实AIDL没有想象的那么难,只是有些繁琐和需要多些细心就好了,下面是我总结需要注意的几个点:

  1. 在有些文章上看到接口方法里用到了“in”“out”“inout”,它们什么意思呢?
    in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。inout不要乱用,用的不好会加大系统开销。
    如 boolean login(in LoginBean data); 服务端将收到这个对象的完整数据,但客户端的那个对象不会因为服务端对传参的修改而发生变动。
    boolean login(out LoginBean data); 服务端将收到个空对象,但在服务端对接收到的空对象有任何修改之后客户端将会同步变动。
    boolean login(inout LoginBean data); 服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

  2. 当进程通信用到自定义数据结构时,如LoginBean.java,此时就必须要生成对应的AIDL文件,不然导致编译不成功。
    如:// LoginBean.aidl的内容
    package com.dalongtech.textapk;
    parcelable LoginBean;//注意parcelable是小写

  3. 当第二步做完后,发现还是编译不通过,原因是LoginBean.java在AIDL文件中不认识,解决方法:
    在build文件中添加:
    sourceSets {
    main {
    java.srcDirs = [‘src/main/java’, ‘src/main/aidl’]
    }
    }

  4. 可序列化数据结构需要实现Parcelable接口

到此呢,进程通信讲解就真正结束了!如果屏前的你有一定的收获的话,那么请您给笔者点个赞或者留个言吧~

谢谢!


核心代码

IMyAidl.aidl

package com.dalongtech.textapk;

import com.dalongtech.textapk.LoginBean;

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

interface IMyAidl {
       boolean checkLogin(String name,String psw);
       boolean login(in LoginBean data);
}

LoginBean.aidl

package com.dalongtech.textapk;
parcelable LoginBean;//注意parcelable是小写

LoginBean.java

package com.dalongtech.textapk;

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

/**
 * Created by sunmoon on 2017/7/6 0006.
 */

public class LoginBean implements Parcelable {

    private String loginName;
    private String loginPsw;
    private int loginSucc;


    public LoginBean(){}

    protected LoginBean(Parcel in) {
        loginName = in.readString();
        loginPsw = in.readString();
        loginSucc = in.readInt();
    }

    public static final Creator CREATOR = new Creator() {
        @Override
        public LoginBean createFromParcel(Parcel in) {
            return new LoginBean(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(loginName);
        dest.writeString(loginPsw);
        dest.writeInt(loginSucc);
    }

    public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        loginName = dest.readString();
        loginPsw = dest.readString();
        loginSucc = dest.readInt();
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getLoginPsw() {
        return loginPsw;
    }

    public void setLoginPsw(String loginPsw) {
        this.loginPsw = loginPsw;
    }

    public int getLoginSucc() {
        return loginSucc;
    }

    public void setLoginSucc(int loginSucc) {
        this.loginSucc = loginSucc;
    }
}

CheckLoginService.java

package com.dalongtech.textapk;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

/**
 *
 * Created by sunmoon on 2017/7/6 0005.
 */

public class CheckLoginService extends Service {

    private static final String UserName = "sunmoon";
    private static final String UserPsw = "sunmoon001";

    private IMyAidl.Stub mBinder = new IMyAidl.Stub() {
        @Override
        public boolean checkLogin(String name, String psw) throws RemoteException {
            return UserName.equals(name) && UserPsw.equals(psw);
        }

        @Override
        public boolean login(LoginBean data) throws RemoteException {
            return UserName.equals(data.getLoginName())&&UserPsw.equals(data.getLoginPsw());
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

MainActivity.java

package com.dalongtech.textapk;

import android.app.Service;
import android.content.ComponentName;
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.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private EditText mUserNameEt;
    private EditText mUserPswEt;
    private IMyAidl myAidl;

    //与service连接
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myAidl = IMyAidl.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            myAidl = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.loginAct_toolbar);
        toolbar.setTitle("登录页面");
        mUserNameEt = (EditText)findViewById(R.id.loginAct_userName);
        mUserPswEt = (EditText)findViewById(R.id.loginAct_userPsw);
        //绑定检查登录服务
        Intent intent = new Intent(this,CheckLoginService.class);
        bindService(intent,mConnection, Service.BIND_AUTO_CREATE);
        findViewById(R.id.loginAct_loginBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
//                    boolean loginSucc = myAidl.checkLogin(mUserNameEt.getText().toString(),
//                            mUserPswEt.getText().toString());
                    LoginBean bean = new LoginBean();
                    bean.setLoginName(mUserNameEt.getText().toString());
                    bean.setLoginPsw(mUserPswEt.getText().toString());
                    boolean loginSucc = myAidl.login(bean);
                    if(loginSucc) {
                        startActivity(new Intent(MainActivity.this,SecondActivity.class));
                        return;
                    }
                } catch (Exception e) {}
                Toast.makeText(MainActivity.this,"用户名或密码错误",Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }
}

你可能感兴趣的:(android)