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数据的过程是相相似的,因此就再也不做分析。

相关文章
相关标签/搜索