Binder机制是android系统中跨进程通信的重要手段。其中,Service与Activity的交互通信使用到了这一机制。为此,我写了service的小案例,以此来方便更好地理解binder通信机制。
Service代码:
转存失败重新上传取消
public class MyService extends Service {
public boolean flag=true;
int count;
//实例化一个binder
ServiceBinder serviceBinder=new ServiceBinder();
private static final String TAG = "MyService";
@Override
public void onCreate() {
Log.i("Winer","MyService is onCreate!");
//创建一个线程不断输出变化的count变量
new Thread(new Runnable() {
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
//flag为true则count自加
if (flag)
count++;
//flag为flase则count自减
else
count--;
//输出count
Log.i("MyService", "Count is" + count);
}
}
}).start();
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("Winer","MyService is onUnbind!");
return super.onUnbind(intent);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("Winer","MyService is onBind!");
//返回binder
return serviceBinder;
}
@Override
public void onDestroy() {
Log.i("Winer","MyService is onDestroy!");
super.onDestroy();
}
//binder结构体,用于通信
class ServiceBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Winer","MyService is onStartCommand!");
return super.onStartCommand(intent, flags, startId);
}
}
View Code
MainActivity代码:
转存失败重新上传取消
public class MainActivity extends AppCompatActivity {
Button startBtn,changeBtn;
MyService myService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建启动服务需要的intent
final Intent i = new Intent(this,MyService.class);
changeBtn= (Button) findViewById(R.id.change_btn);
startBtn = (Button) findViewById(R.id.start_btn);
startBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//创建并且绑定一个Service
bindService(i, new ServiceConnection() {
@Override
//service连接上后所做的事情
public void onServiceConnected(ComponentName name, IBinder service) {
//通过binder获取到service用来访问service的flag变量
myService = ((MyService.ServiceBinder) service).getService();
//利用按钮来改变flag
changeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!myService.flag)
myService.flag=true;
else
myService.flag=false;
}
});
}
@Override
//意外断开连接(如果程序正常退出不会执行该代码)
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
}
});
}
}
View Code
最后别忘了在manifest里面注册service。
在了解binder之前,我们需要了解一下什么是Service。Service是安卓服务组件,类似于一个没有界面的Activity,常用于耗时服务进程,例如音乐播放器,下载器等。我们知道,activity与activity之间没有办法直接进行数据的交流。我们需要利用intent(binder机制的封装)来传数据。具体的方式为调用intent.putExtra(...)方法来放入intent中来启动activity。启动后,新的activity再重新获取这个数据。当然这样的方法也适用于启动Service。与activity不同的是,实际应用service与activity的交流更加频繁。例如音乐播放器,可能需要给音乐播放服务频繁发送播放、暂停、切换、快进等操作,服务也要及时返回播放进度等信息。因此,这种利用intent传递数据的方式显然是不合适的。因此,安卓直接使用了binder机制来传递信息。例如上方的代码,MainActivity是这样MyService的flag变量的:
1.通过bindService方法的参数ServiceConnection实例中onServiceConnected回调函数获取到了binder变量。
2.通过binder类中的返回值来获取MyService实例。
binder就好像一个service和activity之间的桥梁。activity先获取了binder实例,再利用binder实例获取到了Service实例。
下面我们一起来看下其内部逻辑的实现:
首先我们从bindService函数入手,其具体实现位于 /frameworks/base/core/java/android/app/ContextImpl.java 处如下:
转存失败重新上传取消
1753 @Override
1754 public boolean bindService(Intent service, ServiceConnection conn,
1755 int flags) {
1756 warnIfCallingFromSystemProcess();
1757 return bindServiceCommon(service, conn, flags, Process.myUserHandle());
1758 }
1767 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
1768 UserHandle user) {
1769 IServiceConnection sd;
1770 if (conn == null) {
1771 throw new IllegalArgumentException("connection is null");
1772 }
1773 if (mPackageInfo != null) {
1774 sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
1775 mMainThread.getHandler(), flags);
1776 } else {
1777 throw new RuntimeException("Not supported in system context");
1778 }
1779 validateServiceIntent(service);
1780 try {
1781 IBinder token = getActivityToken();
1782 if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
1783 && mPackageInfo.getApplicationInfo().targetSdkVersion
1784 < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
1785 flags |= BIND_WAIVE_PRIORITY;
1786 }
1787 service.prepareToLeaveProcess();
1788 int res = ActivityManagerNative.getDefault().bindService(
1789 mMainThread.getApplicationThread(), getActivityToken(),
1790 service, service.resolveTypeIfNeeded(getContentResolver()),
1791 sd, flags, user.getIdentifier());
1792 if (res < 0) {
1793 throw new SecurityException(
1794 "Not allowed to bind to service " + service);
1795 }
1796 return res != 0;
1797 } catch (RemoteException e) {
1798 return false;
1799 }
1800 }