Android的IPC進程間通信

使用方法(AndroidStudio)

我發現現在AIDL的教程基本上還是eclipse的,但是在AndroidStudio里面使用AIDL還是有一些不同的,來看看怎么用,首先新建一個工程當做server服務端:

創建好后在任意文件夾右鍵 New-->AIDL-->AIDL File ,編輯文件名后會自動在 src/main 目錄下面新建 aidl 文件夾,包的目錄結構如下:

  • main
    • aidl
      • com.example.tee.testapplication.aidl
      </li>
    • java
      • com.example.tee.testapplication
      • </ul> </li>
      • res
      • AndroidManifest.xml
      • </ul> </li> </ul>

        自動生成的aidl文件如下:

        // AidlInterface.aidl
        package com.example.tee.testapplication.aidl;

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

        interface AidlInterface { /**

         * 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);
        

        }</code></pre>

        我們可以看到aidl文件的代碼格式跟java很像,支持java的基礎類型以及List、Map等,如果是自定義類的話需要手動導入,我們后面再說,先來最簡單的,新建一個 IMyAidlInterface.aidl 文件,修改如下:

        package com.example.tee.testapplication.aidl;

        interface IMyAidlInterface { String getValue(); }</code></pre>

        在接口中定義一個 getValue 方法,返回一個字符串,現在可以編譯一下工程,找到 app/build/generated/source/aidl/debug 目錄,在我們應用包名下會發現生成了一個Interface類,名字跟我們定義的aidl的文件名字一樣,這說明其實aidl文件在最后還是會轉換成接口來實現,而且這個文件不需要我們維護,在編譯后自動生成。

        然后新建一個類繼承Service:

        public class MAIDLService extends Service{
            public class MAIDLServiceImpl extends IMyAidlInterface.Stub{
                @Override
                public String getValue() throws RemoteException {
                    return "get value";
                }
            }
            @Nullable
            @Override
            public IBinder onBind(Intent intent) {
                return new MAIDLServiceImpl();
            }
        }

        在MAIDLService類中定義一個內部類繼承 IMyAidlInterface.Stub ,并且重寫我們在aidl也就是在接口中定義的 getValue 方法,返回字符串 get value

        到了這里,我們就新建好了這個服務端,作用是在調用后返回一個字符串,最后在AndroidManifest文件中聲明:

        <service
             android:name=".MAIDLService"
             android:process=":remote"http://加上這句的話客戶端調用會創建一個新的進程
             android:exported="true"http://默認就為true,可去掉,聲明是否可以遠程調用
            >
             <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="com.example.tee.testapplication.aidl.IMyAidlInterface" />
             </intent-filter>
        </service>

        android:process=":remote"這一行的作用是聲明是否調用時 新建進程 ,接下來寫客戶端代碼,新建一個工程,將剛才創建的aidl文件拷貝到這個工程中,注意同樣也是要放在aidl文件夾下,然后在MainActivity中編寫代碼如下:

        public class MainActivity extends AppCompatActivity {
            private TextView mValueTV;
            private IMyAidlInterface mAidlInterface = null;
            private ServiceConnection mServiceConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
                }

            @Override
            public void onServiceDisconnected(ComponentName name) {
        
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            Intent intent = new Intent("com.example.tee.testapplication.aidl.IMyAidlInterface");
            bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
            mValueTV = (TextView) findViewById(R.id.tv_test_value);
            mValueTV.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        mValueTV.setText(mAidlInterface.getValue());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        
        @Override
        protected void onDestroy() {
            if(mAidlInterface != null){
                unbindService(mServiceConnection);
            }
            super.onDestroy();
        }
        

        }</code></pre>

        注意這里新建Intent的傳入的參數字符串是在 manifest 里面自定義的 action 標簽,并且在 onDestroy 記得取消綁定服務。

        執行結果就是我們在點擊TextView時會顯示服務端給我們返回的 get value 字符串

        自定義的對象

        剛才我們使用的是基礎類型 String ,在使用我們自己定義的類的時候用上面的方法是不行的,用我們自定義的類需要手動導入,修改剛才我們創建的作為服務端的工程

        首先在開始生成的aidl包下( 所有aidl相關的文件都要放在這個包下 )新建Student.java

        public class Student implements Parcelable{
            public String name;
            public int age;
            protected Student(Parcel in) {
                readFromParcel(in);
            }
            public Student() {
            }

        public static final Creator<Student> CREATOR = new Creator<Student>() {
            @Override
            public Student createFromParcel(Parcel in) {
                return new Student(in);
            }
        
            @Override
            public Student[] newArray(int size) {
                return new Student[size];
            }
        };
        
        @Override
        public int describeContents() {
            return 0;
        }
        
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(age);
            dest.writeString(name);
        }
        
        public void readFromParcel(Parcel in){
            age = in.readInt();
            name = in.readString();
        }
        
        @Override
        public String toString() {
            return String.format(Locale.ENGLISH, "STUDENT[%s:%d]", name, age);
        }
        

        }</code></pre>

        需要實現 Parcelable 序列化接口,AndroidStudio會自動生成靜態內部類 CREATORdescribeContents 方法,這些部分我們都不需要修改,用自動生成的就好。然后重寫 writeToParcel 方法,自定義 readFromParcel 方法,注意這兩個方法里面的 屬性順序必須一致 ,一個是寫入,一個是讀取。在構造方法Student(Parcel in)中調用readFromParcel(in)方法。

        接下來新建Student.aidl文件(也是在aidl包中):

        // Student.aidl
        package com.example.tee.testapplication.aidl;

        // Declare any non-default types here with import statements parcelable Student;</code></pre>

        注意這里Student前面的關鍵字parcelable首字母是 小寫 哦,再修改IMyAidlInterface.aidl文件如下:

        // IMyAidlInterface.aidl
        package com.example.tee.testapplication.aidl;

        // Declare any non-default types here with import statements import com.example.tee.testapplication.aidl.Student;

        interface IMyAidlInterface { Student getStudent(); void setStudent(in Student student); String getValue(); }</code></pre>

        定義了兩個方法,一個是設置Student,一個是獲取Student,在 setStudent 這個方法注意參數在類型前面有個 in 關鍵字,在 aidl 里參數分為 in 輸入, out 輸出

        現在在MAIDLService.java中重寫新加的兩個方法:

        private Student mStudent;
        public class MAIDLServiceImpl extends IMyAidlInterface.Stub{

        @Override
        public Student getStudent() throws RemoteException {
            return mStudent;
        }
        
        @Override
        public void setStudent(Student student) throws RemoteException {
            mStudent = student;
        }
        
        @Override
        public String getValue() throws RemoteException {
                return "get value : " + Thread.currentThread().getName() + Thread.currentThread().getId();
        }
        

        }</code></pre>

        服務端代碼修改完畢,來到客戶端工程,同樣要把剛才的aidl包下的文件拷貝覆蓋過來,保持兩邊一致,然后在MainActivity.java中修改如下:

        mValueTV = (TextView) findViewById(R.id.tv_test_value);
        mStudentTV = (TextView) findViewById(R.id.tv_test_student);
        mValueTV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    mValueTV.setText(mAidlInterface.getValue());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
        mStudentTV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    Student student = new Student();
                    student.age = 10;
                    student.name = "Tom";
                    mAidlInterface.setStudent(student);
                    mStudentTV.setText(mAidlInterface.getStudent().toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        現在編譯工程,會發現工程會報錯,找不到類Student,我們需要在app目錄下的build.gradle文件添加代碼如下:

        android {
            sourceSets {
                main {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                    java.srcDirs = ['src/main/java', 'src/main/aidl']
                    resources.srcDirs = ['src/main/java', 'src/main/aidl']
                    aidl.srcDirs = ['src/main/aidl']
                    res.srcDirs = ['src/main/res']
                    assets.srcDirs = ['src/main/assets']
                }
            }
        }

        也就是指定一下文件目錄,現在再編譯就沒有問題了

        總結

        Android的IPC使用起來還是挺簡單的,AIDL文件的語法也跟我們平時使用接口的時候很相似,但是它只支持基礎類型,只能引用AIDL文件,需要使用自定義類的時候要稍微麻煩一點。

         

        來自:http://www.jianshu.com/p/7eff275d1e0c

         

 本文由用戶 stnb5675 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!