Activity如果想要调用Service中的方法就需要绑定服务,然后才能获取服务的代理对象。进一步调用服务中的方法。类似这样的代码:
首先去创建一个服务:
public class MyService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return new MyBinder(); } class MyBinder extends Binder implements IService { @Override public void showToast() { Show(); } } private void Show() { Toast.makeText(this, "我是服务中的方法", Toast.LENGTH_SHORT).show(); }}复制代码
这里Iservice是为了解耦去定义的接口:
interface IService { void showToast();}复制代码
我们在Activity中需要去获取binder对象,然后调用服务中的方法:
package remote.zz.remoteservice;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.annotation.Nullable;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import java.util.List;/** * Created by Administrator on 2018/6/27. */public class MainActivity extends AppCompatActivity { private MyServiceConnection conn; private IService ser; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //绑定服务 Intent service = new Intent(this, MyService.class); conn = new MyServiceConnection(); bindService(service, conn, Context.BIND_AUTO_CREATE); //点击对象 findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //调用服务中的方法 ser.showToast(); } }); } //内部类 class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { //获取服务中代理对象 ser = (IService) service; } @Override public void onServiceDisconnected(ComponentName name) { } } @Override protected void onDestroy() { //取消绑定,防止报错 unbindService(conn); super.onDestroy(); }}复制代码
上边的代码是同一个应用中Activity调用Service的基本代码。
假如有这样一个需求,A应用绑定B应用的服务,并互相之间传递数据,这样的需求呢?这个时候你首先会想到用intent的bundle,共享文件或者sp对象(并发会有问题,不建议使用),Messenger(它是封装aidl的,不好用)或者广播,当然这些手段是可以的。但是除了这些之外,我们还可以使用aidl实现跨应用之间的数据传递和调用。为了方便叙述,A应用称为本地应用,B应用成为远程服务应用。跨应用之间通讯其实内部也是使用Binder去做代理对象。只不过这个binder是系统帮我们生成的代码,但是我们需要按照步骤去创建需要的文件:
步骤:
1.先在远程服务应用B工程的main目录下建立一个名为aidl的文件夹,再右键该文件夹,在该文件夹下面新建一个名字与AndroidManifest.xml中的package相同的包,再右键该包,新建你所需的AIDL文件;
创建一个aidl文件,其实就是一个接口:
IRemoteService.aidl
package remote.zz.remoteservice;//注意这里需要自己导入包import remote.zz.remoteservice.Book;interface IRemoteService { ListgetData(); //注意这里的in必须写上,表示传递的方向 void setData(in Book book);}复制代码
还需要创建一个Book数据类型,并且实现序列化:
package remote.zz.remoteservice;import android.os.Parcel;import android.os.Parcelable;//这个写到java文件夹下即可public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } public int describeContents() { return 0; } public void writeToParcel(Parcel out, int flags) { out.writeInt(bookId); out.writeString(bookName); } public static final Parcelable.CreatorCREATOR = new Parcelable. Creator () { public Book createFromParcel(Parcel in) { return new Book(in); } public Book[] newArray(int size) { return new Book[size]; } }; private Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } @Override public String toString() { return "Book{" + "bookId=" + bookId + ", bookName='" + bookName + '\'' + '}'; }}复制代码
同时需要创建一个Book.aidl文件
package remote.zz.remoteservice;//这里的包名就是book的包名parcelable Book;//被序列化的数据类复制代码
2.当你进入你的AIDL文件并编写好了之后,点击AS上方菜单栏中的Build->Make Project,之后便可以在当前工程的app/build/generated/source/aidl/debug中找到系统为我们生成的.java文件了。这个对用生成的java文件就是系统为你创建binder类。
对应的服务我们应该这样写:
package remote.zz.remoteservice;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.support.annotation.Nullable;import android.util.Log;import java.util.ArrayList;import java.util.List;/** * Created by Administrator on 2018/6/27. */public class RemoveService extends Service { private ArrayList arr; @Override public void onCreate() { super.onCreate(); arr = new ArrayList(); arr.add(new Book(1001, "我与地坛")); arr.add(new Book(1002, "钢铁是怎样练成的")); } @Nullable @Override public IBinder onBind(Intent intent) { return new MyBinder(); } //系统为我们生成的binder,名字叫Stub, 可点进去查看源码 class MyBinder extends IRemoteService.Stub { @Override public List getData() throws RemoteException { return arr; } @Override public void setData(Book book) throws RemoteException { arr.add(book); } }}复制代码
在清单文件别忘了配置过滤器,让其他应用可以开启服务
复制代码
那么本地应用A怎么调用呢?
同样先复制远程应用aidl文件夹到本地应用的mian下,然后在mian下创建同远程应用Book类一样的文件夹,把Book类复制到这个文件夹下边,然后去Build->Make Project,同样也会生成一个binder类,这个时候我们就可以调用远程的应用获取服务中的数据:
package local.zz.localapp;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.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import java.util.List;import remote.zz.remoteservice.Book;import remote.zz.remoteservice.IRemoteService;public class MainActivity extends AppCompatActivity { private MyConnection conn; private IRemoteService remote; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //隐私启动服务,5.0之后需要设置包名,否则报错 Intent service = new Intent(); service.setAction("zz.remove.service"); service.setPackage("remote.zz.remoteservice"); conn = new MyConnection(); bindService(service, conn, BIND_AUTO_CREATE); findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { //获取远程服务数据 Listdata = remote.getData(); for (Book book : data) { } } catch (RemoteException e) { e.printStackTrace(); } } }); findViewById(R.id.tv2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { //调用远程方法传递数据 remote.setData(new Book(1003, "让子弹飞")); } catch (RemoteException e) { e.printStackTrace(); } } }); } class MyConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { //获取代理对象 remote = IRemoteService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } } @Override protected void onDestroy() { unbindService(conn); super.onDestroy(); }}复制代码
到此,aidl的使用完毕!
aidl可以传递aidl声明的接口,所以我们可以做一个观察者模式,动态去监听图书的变化,然后通知注册的观察者。
写这个demo时候遇到一个问题:如果本app的服务没有启动,另外app去绑定获取不到代理对象,还不知道原因是什么!