Android知識體系梳理筆記一:Android跨進程通信:AIDL

前言

畢業已經有一個多月瞭,Android也自學瞭2年多瞭(都是晚上學一點),因為自己的木訥,不自信,最近很迷茫,再加上公司給自己的定位是Android前端開發,還有一部分C#中間層代碼的編寫(嘴賤說自己大學用c#做過網頁-o-),工作因為同事出差很忙,讓我變得很閑(不知道幹啥),更讓我對未來產生瞭很大的恐懼;於是今天就決定梳理下自己的Android知識體系,讓自己認識到真實的自己,對未來不在迷茫;同時這也應該是對Android各個知識(使用?)方面的總結,我之所以分享出來也是因為這方面的考慮;這都是網上資料+我自己的感悟及補充,有什麼不足的地方請指正,也請寬容(畢竟也算是新人瞭,哈哈-0-)! 不多說瞭,看正文!!!

AIDL作用

AIDL (Android Interface Definition Language) 是一種IDL 語言,是安卓系統接口定義語言。 在 Android 上,一個進程通常無法訪問另一個進程的內存。 要想如此,進程需要將其對象分解成操作系統能夠識別的原語,並將對象編組成跨越邊界的對象。 編寫執行這一編組操作的代碼是一項繁瑣的工作,因此 Android 會使用 AIDL 來處理。 AIDL IPC機制是面向接口的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。 AIDL的作用是讓你可以在自己的APP裡綁定一個其他APP的service,這樣你的APP可以和其他APP交互

AIDL的使用場合

隻有允許不同應用的客戶端用 IPC 方式訪問服務,並且想要在服務中處理多線程時,才有必要使用 AIDL。 如果您不需要執行跨越不同應用的並發 IPC,就應該通過實現一個 Binder 創建接口;或者,如果您想執行 IPC,但根本不需要處理多線程,則使用 Messenger 類來實現接口。無論如何,在實現 AIDL 之前,請您務必理解綁定服務。

在Android Studio 上使用AIDL

常用類型AIDL的創建

在Module上右鍵,如下圖:
這裡寫圖片描述

輸入名稱後,sutido就幫我們創建瞭一個AIDL文件。

// IMyAidlInterface.aidl
package com.example.happyghost.studytest;

// 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 basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

在這個aidl文件裡可以定義任何基於上面類型的方法,要想自定義類型,自定義的類型需實現Parcelable接口,下面和細節一塊講;

定義一個基本類型方法

// IMyAidlInterface.aidl
package com.example.happyghost.studytest;

// Declare any non-default types here with import statements
interface IMyAidlInterface {

    String getName();
}

在StudyTest Module內創建一個服務類,繼承Service,然後在這個類中寫一個繼承IMyAidlInterface.Stub的內部類,實現其方法!

public class MyAidlService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBind();
    }
    class MyBind extends IMyAidlInterface.Stub{
        @Override
        public String getName() throws RemoteException {
            return "I am aidl in the ipc";
        }
    }
}

而這個服務就是自己的APP裡綁定一個其他APP(其他進程)中的service

然後將你的AIDL文件夾Copy到另一個工程AidlTest Module中,放到同級目錄下,包命不要改,看下圖:

這裡寫圖片描述

接下來就是在Module中綁定服務:

public class MainActivity extends AppCompatActivity {

    private IMyAidlInterface iMyAidlInterface;
    private TextView tvAidl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         //這個“com.example.happyghost.studytest.MyAidlService”是在AndroidManifest.xml中聲明的Service
         //的過濾意圖action 具體樣式如下:
         /**
         *
         *   
         *       
         *   
         * 
         */
        Intent intent = new Intent("com.example.happyghost.studytest.MyAidlService");
        //顯示啟動服務
        //intent.setComponent(new ComponentName("com.example.happyghost.studytest", "com.example.happyghost.studytest.MyAidlService"));

        //隱式啟動服務===》轉成顯示啟動服務
        Intent implicitIntent = createExplicitFromImplicitIntent(this, intent);
        MyAidlServiceConnection conn = new MyAidlServiceConnection();
        bindService(implicitIntent,conn,BIND_AUTO_CREATE);

        tvAidl = (TextView) findViewById(R.id.aidl);
        tvAidl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    tvAidl.setText(iMyAidlInterface.getName());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private class MyAidlServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
    public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
        //檢索所有的服務,以匹配給定的意圖
        PackageManager pm = context.getPackageManager();
        List resolveInfo = pm.queryIntentServices(implicitIntent, 0);

        //確保隻進來一次
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }

        //獲取組件信息並創建ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);

        //創建一個新的目的
        Intent explicitIntent = new Intent(implicitIntent);

        //將組件設置為顯式
        explicitIntent.setComponent(component);

        return explicitIntent;
    }
}

註意:在這裡有個坑,在5.0系統以下,隱式,顯式開啟服務都可以,但是在5.0系統以上隱式開啟服務會讓程序崩潰掉,具體有原因請看這篇博客。所以我們需要用工具方法createExplicitFromImplicitIntent()將隱式意圖轉成顯式意圖,代碼中有詳細的註釋;

看結果:

這裡寫圖片描述

自定義類型

自定義一個類型,使其實現Parcelable接口

public class Student implements Parcelable{
    String name;

    int age;
    public  Student(){}

    public Student(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

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

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

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

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

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

註意:如果你在原始目錄下又新建瞭一個包,而自定義類型放在瞭這個包中,你需要在AIDL文件原始目錄下同樣需要建一個包名相同的包,看下圖:

這裡寫圖片描述vc7Sw8fQwr2otcSw/NbQDQo8cHJlIGNsYXNzPQ==”brush:java;”>
// Student.aidl
package com.example.happyghost.studytest.aidldemo;

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

parcelable Student ;
修改原來的IMyAidlInterface.aidl文件,將我們的自定義類型導入進去(import…),然後創建我們想要實現的方法。

// IMyAidlInterface.aidl
package com.example.happyghost.studytest;

// Declare any non-default types here with import statements
import com.example.happyghost.studytest.aidldemo.Student;
interface IMyAidlInterface {
   List getStudent();
   void addStudent(in Student student);
}

記得每次修改aidl文件需要同步一下。。。。

修改MyAidlService類

public class MyAidlService extends Service {
    private List mStudents = new ArrayList();
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyBind();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Student student = new Student();
        for (int i = 0; i < 10; i++) {
            student.setName("xiao ming"+i);
            student.setAge(10+i);
            mStudents.add(student);
        }
    }

    class MyBind extends IMyAidlInterface.Stub{

        @Override
        public List getStudent() throws RemoteException {
            return mStudents;
        }

        @Override
        public void addStudent(Student student) throws RemoteException {
            mStudents.add(student);
        }
    }
}

將AIDL文件夾及自定義類型類文件Copy到另一個AidlTest module中,包名路徑要和原工程中的包名路徑相同。看下圖:

這裡寫圖片描述

綁定服務,這裡就看一下較上面改變的代碼

 tvAidl.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    List student = iMyAidlInterface.getStudent();
                    Student student1 = student.get(0);
                    tvAidl.setText(student1.getName()+"\n"+"\r"+student1.getAge());
//                    tvAidl.setText(iMyAidlInterface.getName());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

這裡看結果:
這裡寫圖片描述

若有錯誤,敬請指正!!!

拼搏在技術道路上的一隻小白And成長之路

You May Also Like