AndroidAndroid开发经验谈Android开发

IPC机制之AIDL的学习笔记(一)

2017-08-21  本文已影响63人  vison123

前言

ipc机制简介

IPC机制是什么?最简单的名词解释就叫跨进程通信,意思是两个不同的进程之间可以相互协作完成一些事情。比如a进程调用b进程的一些方法来达到更新列表的目的。在Android系统中一个应用默认只有一个进程,每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源,系统给每个进程分配的内存会有限制。如果一个进程占用内存超过了这个内存限制,就会报OOM的问题,很多涉及到大图片的频繁操作或者需要读取一大段数据在内存中使用时,很容易报OOM的问题,为了彻底地解决应用内存的问题,Android引入了多进程的概念,它允许在同一个应用内,为了分担主进程的压力,将占用内存的某些页面单独开一个进程,比如Flash、视频播放页面,频繁绘制的页面等。

Andriod多进程使用方法

效果图展示

gif

正文

AIDL简介

描述

语法

AIDL的使用步骤

目录两个module的目录接口

客户端
服务端

服务端

1.创建Person.java【关于Parcelable的创建不在这里介绍,aidl中客户端和服务端的数据必须是可以序列化的】

package com.example.newserver;

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

/**
 * Created by Administrator on 2017/8/21.
 */

public class Person implements Parcelable {
    private int id;
    private String name;
    private String age;

    public Person() {
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    protected Person(Parcel in) {
        id = in.readInt();
        name = in.readString();
        age = in.readString();
    }

    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

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

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

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

    public void readFromParcel(Parcel dest){
        id = dest.readInt();
        name = dest.readString();
        age = dest.readString();
    }
}

2.创建AIDL文件
两种AIDL文件:在我的理解里,所有的AIDL文件大致可以分为两类。一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“Android接口定义语言”。
注:所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用
首先创建 Person.aidl

// Person.aidl
package com.example.newserver;

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

parcelable Person;

然后创建IMyAidlInterface.aidl

// IMyAidlInterface.aidl
package com.example.newserver;
import com.example.newserver.Person;

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

interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void addPerson(in Person person);
     List<Person> getPersonList();
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

注意点:

sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

这样他就会在aidl文件夹找java文件了,其实发现后来运行的时候不配置也没有报错。不知道什么原因,如果你的工程包类找不到。你就尝试加这个东西吧。
当然还有另外一种解决方法。就是保证aidl【com.example.server】和java【com.example.server】的文件夹名字是相同的。然后把person.java文件放在java文件夹下面就不会有什么问题啦,不过我一般都会采用第二种方法,放在java文件下面。关于两种方法你都可以试一下。感觉第一种比较简单实用。

3.创建service【MyService.java】


public class MyService extends Service {

   ArrayList<Person> mPersonArrayList = new ArrayList<>();

   IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
       @Override
       public void addPerson(Person person) throws RemoteException {
           if (null == mPersonArrayList) {
               mPersonArrayList = new ArrayList<>();
           }
           mPersonArrayList.add(person);
       }

       @Override
       public List<Person> getPersonList() throws RemoteException {
           if (null != mPersonArrayList) {
             return mPersonArrayList;
           }
           return null;
       }

       @Override
       public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

       }
   };

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

基本上就是实现接口的方法,这里会创建一个Binder返回给客户端,这个Binder是已经实现了定义的接口方法的,所以客户端会根据这个binder调用相关的接口方法。这样就实现了客户端调用了服务端定义的接口方法了,关于binder的原理可以看看这一篇的,写的比较详细Binder的原理讲解,我们可以在这里了解一下binder的原理。通过上述步骤实际上就实现了客户端与服务端的通信了,关于客户端怎么拿到binder,具体又怎么用binder的,我们等会看看客户端的代码就知道了。

客户端实现

1.将aidl整个文件夹复制到客户端module下面去
而且在aidl中涉及的java文件也要拷贝过去。例如本例中的person文件。由于我在服务端module中的person.java文件放在了java文件目录下面,所以我拷贝的时候也将它放在了客户端module对应的java文件夹下面。这个时候就有个问题要注意的,因为这里真的是完全拷贝。
我们看看客户端person.java

person

这里package的目录页必须是server目录的person.java文件的目录,也就是

package com.example.newserver;

而不是

package com.example.client;

否则变异会报错。上面标红只是警告,编译不会有问题的。
还有客户端这里的aidl文件夹下面的aidl文件,涉及到person导包的,都是要导入server下面的person的,即

image.png

这里复制过来的文件路径都不能因为迁移到客户端改动。

2.我们来看看 MainActivity中怎么获取和使用Binder吧

public class MainActivity extends AppCompatActivity {
  
    IMyAidlInterface binder;

    ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binder = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        initView();
    }

    private void initView() {
        etId = (EditText) findViewById(R.id.et_id);
        etName = (EditText) findViewById(R.id.et_name);
        etAge = (EditText) findViewById(R.id.et_age);
        btnAdd = (Button) findViewById(R.id.btn_add);
        btnGet = (Button) findViewById(R.id.btn_get);
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
        final MyAdapter myAdapter = new MyAdapter(mContext);
        mRecyclerView.setAdapter(myAdapter);
        btnAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                name = etName.getText().toString();
                id = etId.getText().toString();
                age = etAge.getText().toString();
                if (checkData()) {
                    try {
                        Person person = new Person();
                        person.setName(name);
                        person.setId(Integer.valueOf(id));
                        person.setAge(age);
                        binder.addPerson(person);
                        clearPerson();
                        Toast.makeText(mContext,"添加英雄成功,点击获取任务可查看英雄列表",Toast.LENGTH_LONG).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        btnGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    if (null != binder) {
                        mList = (ArrayList<Person>) binder.getPersonList();
                    }
                    if (null != mList && mList.size() > 0) {
                        myAdapter.addList(mList);
                    } else {
                        Toast.makeText(mContext,"还没有添加英雄,赶紧添加一个吧~",Toast.LENGTH_SHORT).show();
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        Intent intent = new Intent();
        intent.setPackage("com.example.newserver");
        intent.setAction("com.example.newserver.aidl.IMyAidlInterface");
        bindService(intent,mConnection,mContext.BIND_AUTO_CREATE);
    }
}

上面我只看核心的实现。定义一个刚才我们在服务端定义的接口变量。然后在ServiceConnection中的onServiceContected方法中将IBinder接口转换一下这样我们就可以使用IMyAidlInterface 这个接口来调用服务端定义的方法了。

运行代码

结语

我们看到关于实现是非常简单的,今天先写一下实现过程,下一章在写它到底是怎么实现,
然后我们可以脱离aidl代码来实现这个功能。

代码传送门
传送门~

上一篇下一篇

猜你喜欢

热点阅读