Activity和Service的交互

一、前言

上次我们从源码的角度分析了Service的两种状态下的工作过程,具体分析可查看Service的工作过程。在文章的最后引出了两个问题,其中一个是: Activity和Service的交互,Service是本地的或者远程的,两者之间有什么区别? 为了分析上述两者的区别,我们新建了一个工程,增加了两个aidl文件,分别是Book.aidl、IBookManager.aidl。 Book.aidl:

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

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

parcelable Book;

IBookManager.aidl:

// 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:

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

    publicBookService() {
        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:

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());
                    }
                }
            }
        });
    }
}

二、本地Service

所谓本地Service,即Activity和Service处于同一个进程中。我们直接看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方法。 **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来实现:

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

上面代码传进来的参数DESCRIPTOR是IBookManager的一个静态不可变字符串,其内容为IBookManager类的包路径,保证了工程内的唯一性。

从BookService中可知,当Service为本地Service时,该Service的onBind方法返回的对象就是IBookManager.Stub对象,该对象的构造函数如下所示。

publicStub() {
  this.attachInterface(this, DESCRIPTOR);
}

IBookManager.Stub对象调用了Binder类的attachInterface方法,并将自己和DESCRIPTOR字符串传递进去,如下所示。

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进行交互。

三、远程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数据的过程是相类似的,所以就不再作分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值