天天看點

Android AIDL跨程序通信

在Android系統中,一般的程序間的通信,資料傳遞,我們經常會使用,也很簡單。那麼,如果一個應用想通路另一個應用中的資料,或者互動我們又該如何做呢?

不用擔心,Android系統也已經給我們提供了解決的方法,那就是 AIDL(Android Interface Definition Language) Android接口定義語言。

内容:

  • 跨應用啟動Service
  • 與Service跨應用通信(一)
  • 與Service跨應用通信(二)

跨應用啟動Service

1. 同一個程序啟動Service

MainActivity :

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

        startService(new Intent(this,MyService.class));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopService(new Intent(this,MyService.class));
    }
           

MyService :

@Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();

        Log.e("MyService","Started Service");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService","Destroy Service");
    }
           

運作效果:

Android AIDL跨程式通信

可以看到我們的Log在啟動和退出MainActivity的時候 分别啟動了Service 和停止了Service。這很簡單,接下來我們看跨程序啟動。

2. 跨程序啟動Service

  • 重新建立一個module (anotherApp)
  • 在anotherApp中啟動上面建立的app中的service

我們先看第一個,建立module 之後

Android AIDL跨程式通信

修改AnotherApp中的MainActivity :

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                break;

            case R.id.btn_stopAppService:
                break;
        }
    } 
           

那麼問題來了,我們如何啟動App中的服務呢?很明顯,我們不可能像在同一個程序中,直接擷取到指定的類的定義

其實,我們可以使用Intent。在配置檔案中直接給service配置intent-filter。注意:(Android5.0之後的版本是不可以這樣的,從5.0開始(包括5.0),必須使用顯示Intent啟動。)

我們可以在AnotherApp代碼中這樣寫:

private Intent Serviceintent;

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

        Serviceintent = new Intent();
        Serviceintent.setComponent(new   ComponentName("com.example.mirgao.startservicefromanotherapp","com.example.mirgao.startservicefromanotherapp.MyService"));

        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                startService(Serviceintent);
                break;

            case R.id.btn_stopAppService:
                break;
        }
    }
           

我們new 了一個Intent,并且給他設定了一個setComponent,其中第一個參數傳遞的是要啟動的那個app的包名,第二個為要啟動的類名。

運作AnotherApp的結果:

Android AIDL跨程式通信

可以看到在AnotherApp中開啟和停止 App中的Service依然是成功的。

注意,劃紅線的地方要調整到你要啟動的應用包名,否則看不到對應的日志。

與Service跨應用通信(一)

上面是啟動service,現在我們說下綁定service。

我們知道一個程式B想綁定A中的service,我們是拿不到A中定義的IBind對象的,那麼這們該如何去做呢?不要着急,我們的問題Android系統以及給我們提供了方法,AIDL應運而生!

  • 在App中建立ADIL檔案
  • 重寫App中的Service中的onBind方法
  • 修改AnotherApp中的主函數邏輯

建立aidl檔案後,系統會自動生成相關的類。

Android AIDL跨程式通信
interface AppServiceBinder {
    /**
     * 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);
}
           

那麼此時,我們就可以直接在MyService中的onBind方法中直接傳回 并且實作aidl的方法:(注意,在使用之前,請先Build——Rebuild Project)

public class MyService extends Service {
    public MyService() {

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return new AppServiceBinder.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }
        };
    }

    @Override
    public void onCreate() {
        super.onCreate();

        Log.e("MyService","Started Service");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService","Destroy Service");
    }
}
           

最後,我們去修改AnotherApp中的邏輯:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {

    private Intent Serviceintent;

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

        Serviceintent = new Intent();
        Serviceintent.setComponent(new ComponentName("com.example.mirgao.startservicefromanotherapp","com.example.mirgao.startservicefromanotherapp.MyService"));

        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
        findViewById(R.id.btn_bindAppService).setOnClickListener(this);
        findViewById(R.id.btn_unbindAppService).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                startService(Serviceintent);
                break;

            case R.id.btn_stopAppService:
                stopService(Serviceintent);
                break;

            case R.id.btn_bindAppService:
                bindService(Serviceintent,this, Context.BIND_AUTO_CREATE);
                break;

            case R.id.btn_unbindAppService:
                unbindService(this);
                break;
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        Log.e("anotherApp","Bind Service");
        Log.e("anotherApp", String.valueOf(iBinder));
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }
}
           

我們運作:

運作的時候需要注意幾個問題:

  • App要重新啟動
  • 啟動AnotherApp

App的日志:

我們點選AnotherApp中的啟動,停止,綁定,接觸綁定,都會調用到App中的MyService服務

Android AIDL跨程式通信

這時候,我們調換一下包名,看看AnotherApp的日志:

Android AIDL跨程式通信

可以看到,此時AnotherApp已經知道了綁定App中的服務成功了,并且也擷取到了IBind。

到此,我們綁定了服務,那麼我們如何去和他進行通信呢?因為不通信,我們綁定服務是沒有意義的。好,别着急,請看下面:

與Service跨應用通信(二)

目的:

修改App中的資料。

步驟:

  • 在aidl檔案中添加修改資料方法
  • 在App中的MyService重寫新增的方法
  • 在App中的MyService的OnCreate方法中啟動一個循環資料線程
  • 在AnotherApp中操作進行資料同步到App中

AIDL中添加方法 setData() 并重寫MyService中的OnBind方法:

interface AppServiceBinder {
    /**
     * 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);

    void setData(String data);
}
           
@Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return new AppServiceBinder.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }

            @Override
            public void setData(String data) throws RemoteException {
                MyService.this.data = data;
            }
        };
    }
           

在App中的MyService的OnCreate方法中啟動一個循環資料線程,每個1s輸出一遍資料,整個MyService代碼:

public class MyService extends Service {
    public MyService() {

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return new AppServiceBinder.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }

            @Override
            public void setData(String data) throws RemoteException {
                MyService.this.data = data;
            }
        };
    }

    @Override
    public void onCreate() {
        super.onCreate();

        new Thread(){
            @Override
            public void run() {
                super.run();

                running = true;

                while (running) {

                    Log.e("MyService",data);

                    try {
                        sleep();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService","Destroy Service");

        running = false;
    }

    private String data = "AIDL學習";
    private boolean running = false;
}
           

接着我們去修改AnotherApp:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {

    private Intent Serviceintent;

    private EditText et;

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

        Serviceintent = new Intent();
        Serviceintent.setComponent(new ComponentName("com.example.mirgao.startservicefromanotherapp","com.example.mirgao.startservicefromanotherapp.MyService"));

        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
        findViewById(R.id.btn_bindAppService).setOnClickListener(this);
        findViewById(R.id.btn_unbindAppService).setOnClickListener(this);
        findViewById(R.id.btn_Sync).setOnClickListener(this);

        et = (EditText) findViewById(R.id.et);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                startService(Serviceintent);
                break;

            case R.id.btn_stopAppService:
                stopService(Serviceintent);
                break;

            case R.id.btn_bindAppService:
                bindService(Serviceintent,this, Context.BIND_AUTO_CREATE);
                break;

            case R.id.btn_unbindAppService:
                unbindService(this);
                break;

            case R.id.btn_Sync:

                break;
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        Log.e("anotherApp","Bind Service");
        Log.e("anotherApp", String.valueOf(iBinder));
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }
}
           

到這裡,問題來了,我們同步的時候該如何去使用呢。我們需要有一個IBinder,然後我們如何去通過IBinder很友善的去執行一個遠端的函數呢?

我們需要給AnotherApp也建立一個AIDL檔案。并且一定要保證同樣的包名:

如何在AnotherApp中建立包名:

Android AIDL跨程式通信

然後在這個檔案中建立一個包,注意,包名要和App的AIDL包名一樣,然後将那個AIDL檔案拷貝過來。

此時,AnotherApp有了一個同樣的AIDL檔案,意味着可以在裡面生成同樣的AIDL相關定義。(重新Rebuild)

這樣,我們修改AnotherApp的MainActivity:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {

    private Intent Serviceintent;

    private EditText et;

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

        Serviceintent = new Intent();
        Serviceintent.setComponent(new ComponentName("com.example.mirgao.startservicefromanotherapp","com.example.mirgao.startservicefromanotherapp.MyService"));

        findViewById(R.id.btn_startAppService).setOnClickListener(this);
        findViewById(R.id.btn_stopAppService).setOnClickListener(this);
        findViewById(R.id.btn_bindAppService).setOnClickListener(this);
        findViewById(R.id.btn_unbindAppService).setOnClickListener(this);
        findViewById(R.id.btn_Sync).setOnClickListener(this);

        et = (EditText) findViewById(R.id.et);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_startAppService:
                startService(Serviceintent);
                break;

            case R.id.btn_stopAppService:
                stopService(Serviceintent);
                break;

            case R.id.btn_bindAppService:
                bindService(Serviceintent,this, Context.BIND_AUTO_CREATE);
                break;

            case R.id.btn_unbindAppService:
                unbindService(this);
                break;

            case R.id.btn_Sync:
                if (binder != null){
                    try {
                        binder.setData(et.getText().toString());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

        Log.e("anotherApp","Bind Service");
        Log.e("anotherApp", String.valueOf(iBinder));

        binder = AppServiceBinder.Stub.asInterface(iBinder);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }

    private AppServiceBinder binder = null;
}
           

注意::!!

在onServiceConnected,不可以直接這樣寫binder = (AppServiceBinder) iBinder;,因為binder 和iBnder是在兩個應用程式中,在記憶體中也是所占用兩個不同的位址。

注意:運作前把App中的啟動和停止服務代碼注釋掉

我們觀察下結果:

Android AIDL跨程式通信

在App中,每個1S就會輸出一條資料,然後我們點選同步資料,就會發現,資料已經改變了

Android AIDL跨程式通信

到此,我們已經成功的在另一個應用中通過遠端方法将資料傳遞給了目前應用中的服務!

源碼有需要的私信我