1. 程式人生 > >快速入門android AIDL(開啟多程序並進行通訊)

快速入門android AIDL(開啟多程序並進行通訊)

為何要開啟多程序?主要有兩種情況:

一、一個應用由於自身需要採用多程序模式來實現。比如播放器之類,如果僅僅在service中執行會影響主執行緒的響應速度,很可能會造成ANR,一般情況下不會這麼寫;如果僅僅在子執行緒中執行,一旦開啟該執行緒的Activity被殺死後,執行緒也被殺死,無法實現後臺執行效果,更加不合理。而如果在另外一個程序中使用service後臺執行,就顯得十分恰當了。

二、由於android對單個應用所使用的最大記憶體做了限制,為了加大一個應用可使用的記憶體,所以通過多程序來獲取多份記憶體空間。

本篇文章demo重點:(demo原始碼在文章結尾)

1、開啟多程序

2、兩個程序之間使用AIDL進行通訊

開啟多程序:

在Android中常用的使用多程序只有一種辦法,那就是在AndroidManifest中為四大元件(Activity、Service、Broadcast Receiver、ContentProvier)指定android:process屬性。筆者demo中的遠端service如下圖:


最終繫結該service後在DDMS中程序的顯示情況如下圖:


可以看到最後的兩個程序都是同一個包名,只是第二個是“:remote”。這樣就非常簡單的開啟了多程序。

講到此處,很多好奇的讀者定然有疑問了,“android:process”中的引數到底代表了什麼?簡單來講就是代表了新開的這個程序的id。如果兩個應用要共享同一個程序就需要用到這個了。

那麼筆者此處寫的“:remote”又是什麼意思呢?“remote”不是關鍵,這個完全可以自己隨意取名字,“:”冒號才是關鍵

程序名以“:”開頭的程序屬於當前應用的私有程序,其他應用的元件不可以和它跑在同一個程序中。而程序名不以“:”開頭的程序屬於全域性程序,其他應用可以通過某些方式和它跑在同一個程序中。

兩個程序之間使用AIDL進行通訊:

筆者此篇文章實現的主要效果:

能夠在當前程序中MainActivity,執行另一個程序中開啟的Service中實現的方法testMethod(),方法與最終效果如下:



介面上主要有兩個按鈕,第一個是開啟遠端程序中的Service,另一個為執行該方法。


主要實現步驟:(主要有三條,分別為AIDL、Service、和呼叫處(demo中為MainActivity))

1、建立一個AIDL介面,並寫入自己要在程序間通訊用的抽象方法。

myAIDL.aidl:

package com.example.double2.myaidltest;

interface myAIDL {

    void testMethod();
}

建立AIDL檔案與建立java檔案等類似,直接右擊建立即可。android studio中就十分方便,會自動在main檔案下建立一個aidl資料夾,並在該資料夾創建於你專案名相同的包名。

可能遇到的小問題:

筆者第一次建立AIDL,在Service中發現找不到該AIDL的包。遇到相同問題的讀者可以在建立AIDL並寫完抽象方法之後使用build->make project重新構建一下專案。


2、建立一個遠端Service,在Service中建立一個類繼承AIDL介面中的Stub類並實現Stub中的抽象方法,最後不要忘記在onBind中返回這個類的物件。

public class AIDLRemoteService extends Service {
    private static final String TAG = "AIDLRemoteService";

    private final myAIDL.Stub mBinder=new myAIDL.Stub(){
        @Override
        public void testMethod() throws RemoteException {
            Log.d(TAG, "testMethod: "+"this is myAIDLTest");
        }
    };

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

3、在要呼叫的地方(筆者demo中就為MainActivity中)繫結該Service,將Service返回的Binder物件轉換成AIDL介面所屬的型別,接著直接呼叫AIDL的方法。

在成功連線之後,將Service返回的Binder物件轉換成AIDL介面所屬的型別:

private myAIDL mMyAIDL;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "onServiceConnected");
            mMyAIDL = myAIDL.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "onServiceDisconnected");
            mMyAIDL = null;
        }
    };

在呼叫處直接使用:

btnStartMethod.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                try {
                    mMyAIDL.testMethod();
                } catch (RemoteException e) {
                    Toast.makeText(MainActivity.this, "服務被異常殺死,請重新開啟。", Toast.LENGTH_SHORT).show();
                }

            }
        });


demo專案結構:


myAIDL.aidl:

package com.example.double2.myaidltest;

interface myAIDL {

    void testMethod();
}
AIDLRemoteService:
package com.example.double2.myaidltest;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

/**
 * 專案名稱:MyAIDLTest
 * 建立人:Double2號
 * 建立時間:2016/6/10 8:13
 * 修改備註:
 */
public class AIDLRemoteService extends Service {
    private static final String TAG = "AIDLRemoteService";

    private final myAIDL.Stub mBinder=new myAIDL.Stub(){
        @Override
        public void testMethod() throws RemoteException {
            Log.d(TAG, "testMethod: "+"this is myAIDLTest");
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
MainActivity:
package com.example.double2.myaidltest;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private Button btnBindService;
    private Button btnStartMethod;

    private myAIDL mMyAIDL;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "onServiceConnected");
            mMyAIDL = myAIDL.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "onServiceDisconnected");
            mMyAIDL = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView() {
        btnBindService = (Button) findViewById(R.id.btn_bind_service);
        btnStartMethod = (Button) findViewById(R.id.btn_start_method);
        btnBindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, AIDLRemoteService.class);
                bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
            }
        });

        btnStartMethod.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                try {
                    mMyAIDL.testMethod();
                } catch (RemoteException e) {
                    Toast.makeText(MainActivity.this, "服務被異常殺死,請重新開啟。", Toast.LENGTH_SHORT).show();
                }

            }
        });
    }

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

activity_main:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <Button
        android:id="@+id/btn_bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="bindService"/>

    <Button
        android:id="@+id/btn_start_method"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="startMethod"/>

</LinearLayout>

AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.double2.myaidltest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name=".AIDLRemoteService"
            android:process=":remote"/>
    </application>

</manifest>