Activity和Service的交互

1、前言

上次咱們從源碼的角度分析了Service的兩種狀態下的工做過程,具體分析可查看Service的工做過程。在文章的最後引出了兩個問題,其中一個是: Activity和Service的交互,Service是本地的或者遠程的,二者之間有什麼區別? 爲了分析上述二者的區別,咱們新建了一個工程,增長了兩個aidl文件,分別是Book.aidl、IBookManager.aidl。 Book.aidl:java

// IBook.aidl
package com.hwldzh.myapplication.aidl;

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

parcelable Book;
複製代碼

IBookManager.aidl:android

// IBookManager.aidl
package com.hwldzh.myapplication.aidl;
import com.hwldzh.myapplication.aidl.Book;

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

interface IBookManager {

    List<Book> getBookList();

    void addBookWithIn(in Book book);
}
複製代碼

而且增長了BookService.java和BookActivity.java。 BookService.java:bash

package com.hwldzh.myapplication.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by hwldzh on 2018/11/21
 */
public class BookService extends Service {
    private List<Book> bookList = new ArrayList<>();

    public BookService() {
        Book bookAndroid = new Book("Android", 10);
        bookList.add(bookAndroid);
    }

    IBookManager.Stub binder = new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            return bookList;
        }

        @Override
        public void addBookWithIn(Book book) throws RemoteException {
            
        }
    };


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}
複製代碼

BookActivity.java:app

package com.hwldzh.myapplication.aidl;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.mucfc.myapplication.R;

import java.util.List;

/**
 * Created by hwldzh on 2018/11/21
 */
public class BookActivity extends Activity {
    private IBookManager bookManager;

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

        Intent intent = new Intent(this, BookService.class);
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                bookManager = IBookManager.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                bookManager = null;
            }
        }, BIND_AUTO_CREATE);

        findViewById(R.id.add_book_in).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null) {
                    try {
                        Book book = new Book("JavaScript", 40);
                        bookManager.addBookWithIn(book);
                        Toast.makeText(BookActivity.this, "增長成功", Toast.LENGTH_SHORT).show();
                        Log.d("ABC", "book.Info=書名:" + book.getmBookName() + ", 價格:" + book.getmBookPrice());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        findViewById(R.id.get_books).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (bookManager != null) {
                    List<Book> bookList = null;
                    try {
                        bookList = bookManager.getBookList();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    if (bookList != null) {
                        StringBuilder builder = new StringBuilder();
                        for (int i = 0; i < bookList.size(); i++) {
                            Book book = bookList.get(i);
                            builder.append("第" + (i + 1) + "本書,書名爲:" + book.getmBookName() + ", 價格爲:" + book.getmBookPrice() + "\n");
                        }
                        ((TextView) findViewById(R.id.book_list)).setText(builder.toString());
                    }
                }
            }
        });
    }
}
複製代碼

2、本地Service

所謂本地Service,即Activity和Service處於同一個進程中。咱們直接看Activity和Service綁定成功後,Service的onBind方法返回的IBinder對象:ide

bindService(intent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        bookManager = IBookManager.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        bookManager = null;
    }
}, BIND_AUTO_CREATE);
複製代碼

在ServiceConnection的onServiceConnected方法中,調用了IBookManager.Stub對象的asInterface方法。 **IBookManager.Stub對象是什麼???**它就是上面的IBookManager.aidl通過build命令後生成的IBookManager的內部類,其中的asInterface方法以下:函數

public static com.mucfc.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) {
	if ((obj==null)) {
		return null;
	}
	android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
	if (((iin!=null)&&(iin instanceof com.mucfc.myapplication.aidl.IBookManager))) {
		return ((com.mucfc.myapplication.aidl.IBookManager)iin);
	}
	return new com.mucfc.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
複製代碼

上面的代碼中,調用了obj. queryLocalInterface方法,而obj就是Service傳進來的IBinder對象。因爲IBinder是一個接口,因此其中的queryLocalInterface方法由其實現類Binder來實現:ui

public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}
複製代碼

上面代碼傳進來的參數DESCRIPTOR是IBookManager的一個靜態不可變字符串,其內容爲IBookManager類的包路徑,保證了工程內的惟一性。this

從BookService中可知,當Service爲本地Service時,該Service的onBind方法返回的對象就是IBookManager.Stub對象,該對象的構造函數以下所示。spa

public Stub() {
  this.attachInterface(this, DESCRIPTOR);
}
複製代碼

IBookManager.Stub對象調用了Binder類的attachInterface方法,並將本身和DESCRIPTOR字符串傳遞進去,以下所示。3d

public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
}
複製代碼

上面的代碼邏輯將IBookManager的DESCRIPTOR字符串賦值給了mDescriptor對象,並將IBookManager.Stub對象賦值給mOwner對象。

這個時候,咱們再回頭看上面說到的queryLocalInterface方法,就知道了其返還的mOwer對象就是IBookManager.Stub對象,也就是在Service服務中建立的IBookManager.Stub對象。

這樣,咱們就可以在Activity中獲取到Service建立的IBookManager.Stub對象,經過該對象就能夠和Service進行交互。

3、遠程Service

所謂遠程Service,即該Service和Activity不在同一個進程中。這個時候Activity和Service的交互通訊就是進程間的通訊(IPC)。 Activity和Service綁定成功後,Service的onBind方法給咱們返回了一個IBinder對象:

bindService(intent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        bookManager = IBookManager.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        bookManager = null;
    }
}, BIND_AUTO_CREATE);
複製代碼

在ServiceConnection的onServiceConnected方法中,調用了IBookManager.Stub對象的asInterface方法。這一步和上面本地Service的分析是一致的,其中IBookManager.Stub對象的asInterface方法以下:

public static com.mucfc.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) {
    if ((obj==null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.mucfc.myapplication.aidl.IBookManager))) {
        return ((com.mucfc.myapplication.aidl.IBookManager)iin);
    }
    return new com.mucfc.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
複製代碼

從本地Service的分析可得知,obj因爲是一個遠程進程傳遞過來的IBinder對象,因此其中的mDescriptor和本地的IBookManager的DESCRIPTOR是不相等的,即asInterface方法會走到最後一步,也就是new了一個IBookManager.Stub.Proxy對象,並將obj傳遞了過去,IBookManager.Stub.Proxy對象的構造函數以下:

private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
	mRemote = remote;
}
複製代碼

很簡單的賦值操做,將傳進來的IBinder對象進行了存儲。 這樣就完了嗎???所謂的交互呢???

咱們接着往下看,BookActivity中有一個獲取書單的按鈕,其邏輯爲:

findViewById(R.id.get_books).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (bookManager != null) {
            List<Book> bookList = null;
            try {
                bookList = bookManager.getBookList();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            if (bookList != null) {
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < bookList.size(); i++) {
                    Book book = bookList.get(i);
                    builder.append("第" + (i + 1) + "本書,書名爲:" + book.getmBookName() + ", 價格爲:" + book.getmBookPrice() + "\n");
                }
                ((TextView) findViewById(R.id.book_list)).setText(builder.toString());
            }
        }
    }
});
複製代碼

onClick方法中的bookManager對象是什麼?就是上面分析的IBookManager.Stub.Proxy對象。好,它接着調用了getBookList方法,也就是IBookManager.Stub.Proxy對象的getBookList方法,以下所示。

@Override 
public java.util.List<com.hwldzh.myapplication.aidl.Book> getBookList() throws android.os.RemoteException {
	android.os.Parcel _data = android.os.Parcel.obtain();
	android.os.Parcel _reply = android.os.Parcel.obtain();
	java.util.List<com.hwldzh.myapplication.aidl.Book> _result;
	try {
		_data.writeInterfaceToken(DESCRIPTOR);
		mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
		_reply.readException();
		_result = _reply.createTypedArrayList(com.hwldzh.myapplication.aidl.Book.CREATOR);
	}
	finally {
		_reply.recycle();
		_data.recycle();
	}
	return _result;
}
複製代碼

上面的代碼中建立了兩個Parcel對象,分別是_data、_reply,建立了一個List<com.hwldzh.myapplication.aidl.Book>對象——_result,而後調用了mRemote對象的transact方法,並將_data、_reply對象傳遞過去。上面分析到mRemote對象就是遠程的IBinder對象,這裏調用transact方法,會回調到遠程IBinder對象的onTransact方法:

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
	switch (code)
	{
		case INTERFACE_TRANSACTION:
		{
			reply.writeString(DESCRIPTOR);
			return true;
		}
		case TRANSACTION_getBookList:
		{
			data.enforceInterface(DESCRIPTOR);
			java.util.List<com.mucfc.myapplication.aidl.Book> _result = this.getBookList();
			reply.writeNoException();
			reply.writeTypedList(_result);
			return true;
		}
		case TRANSACTION_addBookWithIn:
		{
			data.enforceInterface(DESCRIPTOR);
			com.mucfc.myapplication.aidl.Book _arg0;
			if ((0!=data.readInt())) {
			_arg0 = com.mucfc.myapplication.aidl.Book.CREATOR.createFromParcel(data);
		} else {
			_arg0 = null;
		}
			this.addBookWithIn(_arg0);
			reply.writeNoException();
			return true;
		}
	}
	return super.onTransact(code, data, reply, flags);
}
複製代碼

而onTransact方法會根據不一樣方法的code值,將計算結果計算出來後,並回寫到reply(Parcel類型)對象中。因此上面的getBookList方法經過遠程Service返回的_reply參數就能獲取到遠程Service的BookList,從而實現了本地Activity和遠程Service的交互。

上面咱們只分析了Activity從遠程Service獲取數據的過程,而Activity傳遞數據給遠程Service數據的過程是相相似的,因此就再也不做分析。

相關文章
相關標籤/搜索