一.通过Binder绑定形式的通信
上一篇说道Binder机制的通信框架,也说过Messenger的底层实现自AIDL,因此对于跨进程通信中,Messenger是一种比较高级的框架,可以说对于一个app开发者来说重要性不言而喻
服务端模型
public class BackgroundService extends Service
{
private Messenger mMessenger = null;//监听数据
private Messenger replyMessenger = null //响应数据
@Override
public IBinder onBind(Intent intent)
{
if(mMessenger==null)
{
mMessenger = new Messenger(new ServerHandler());
}
return mMessenger.getBinder();
}
@Override
public boolean onUnbind(Intent intent)
{
return super.onUnbind(intent);
}
//注意,将ServerHandler静态化,以防止内存泄露
public static class ServerHandler extends Handler
{
@Override
public void handleMessage(Message msg)
{
if (msg.what>0)
{
try
{
replyMessenger = msg.replyTo;
replyMessenger.send(Message.obtain(null,0x000001, HttpStatus.SC_OK, 0));
LogUtils.e("[后台数据]=>报告地瓜,土豆收到 <MSGID[FlightID:"+msg.obj+",arg1:"+msg.arg1+"]>");
}
catch (RemoteException e)
{
e.printStackTrace();
}
}
else
{
super.handleMessage(msg);
}
}
}
}
客户端模型
public class MainActivity extends Activity
{
private Activity activity = null;
private final int RETRY_INTERVAL_TIME = 2 * 1000;
private final Messenger clientMessenger= new Messenger(new ClientHandler());
private Messenger replyMessenger = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
activity = this;
startBackgroundService(this);
//启动广播是异步的,因此必须通过不断轮询的方式来检测广播是否已启动
bindgroundService();
}
public static class ClientHandler extends Handler
{
@Override
public void handleMessage(Message msg)
{
if (msg.what == HttpStatus.SC_OK)
{
//msg.arg1就是remoteInt
Log.i("TAG", "服务端响应数据:"+msg.arg1);
} else {
super.handleMessage(msg);
}
}
}
/**绑定服务*/
private ServiceConnection serviceConnection = new ServiceConnection()
{
@Override
public void onServiceDisconnected(ComponentName name)
{
replyMessenger = null;
LogUtils.d("ServiceClient>>service onServiceDisconnected :连接失败");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
replyMessenger = new Messenger(service);
LogUtils.d("ServiceClient>>service onServiceConnected : 连接成功");
if(replyMessenger !=null)
{
LogUtils.e("[前台数据]==>土豆土豆,我是地瓜,收到请回应!");
Message msgTo = Message.obtain(null,200);
msgTo.obj = "[现在可以开始上传数据]";
msgTo.arg1 = 1024;
msgTo.replyTo = clientMessenger;
try
{
replyMessenger.send(msgTo);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
};
/**
* 启动后台服务
* @param context
*/
private void startBackgroundService(Context context)
{
if(!isServiceRunning(BackgroundService.class.getName(),context))
{
Intent targetIntent = new Intent(context,RCCBackgroundService.class);
//targetIntent.setAction(TARGET_SERVICE_ACTION);
context.startService(targetIntent);
}
}
/**
* 绑定后台服务
*/
private void bindgroundService()
{
if(replyMessenger ==null && isServiceRunning(BackgroundService.class.getName()))
{
Log.e("[前台数据]","[绑定服务]");
activity.getWindow().getDecorView().postDelayed(new Runnable()
{
@Override
public void run()
{
Intent intent = new Intent(activity, BackgroundService.class);
intent.setAction(WifiNetworkChangedReceiver.TARGET_SERVICE_ACTION);
activity.bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE);
}
}, RETRY_INTERVAL_TIME);
}else{
activity.getWindow().getDecorView().postDelayed(new Runnable() {
@Override
public void run() {
replyMessenger = null;
bindgroundService();
}
}, RETRY_INTERVAL_TIME);
}
}
/**
* 检测Service是否已经启动
* @param serviceClassName
* @return
*/
public boolean isServiceRunning(String serviceClassName)
{
final ActivityManager activityManager = (ActivityManager)activity.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (RunningServiceInfo runningServiceInfo : services)
{
if (runningServiceInfo.service.getClassName().equals(serviceClassName))
{
LogUtils.e("-ServiceClient->服务已经启动,开始绑定后台服务");
return true;
}
}
LogUtils.e("->ServiceClient->服务未启动,等待重新尝试绑定服务!");
return false;
}
}
二.非绑定形式的通信
原理是通过Parceable参数传递
客户端
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
public static final int MSG_UNCOLOR_START = 0;
public static final int MSG_UNCOLOR_STOP = 1;
public static final int MSG_COLOR_START = 2;
public static final int MSG_COLOR_STOP = 3;
public static final String MESSENGER_INTENT_KEY
= BuildConfig.APPLICATION_ID + ".MESSENGER_INTENT_KEY";
public static final String WORK_DURATION_KEY =
BuildConfig.APPLICATION_ID + ".WORK_DURATION_KEY";
private EditText mDelayEditText;
private EditText mDeadlineEditText;
private EditText mDurationTimeEditText;
private RadioButton mWiFiConnectivityRadioButton;
private RadioButton mAnyConnectivityRadioButton;
private CheckBox mRequiresChargingCheckBox;
private CheckBox mRequiresIdleCheckbox;
private ComponentName mServiceComponent;
private int mJobId = 0;
// Handler for incoming messages from the service.
private IncomingMessageHandler mHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_main);
// Set up UI.
mDelayEditText = (EditText) findViewById(R.id.delay_time);
mDurationTimeEditText = (EditText) findViewById(R.id.duration_time);
mDeadlineEditText = (EditText) findViewById(R.id.deadline_time);
mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered);
mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any);
mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging);
mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle);
mServiceComponent = new ComponentName(this, MyJobService.class);
mHandler = new IncomingMessageHandler(this);
}
@Override
protected void onStop() {
// A service can be "started" and/or "bound". In this case, it's "started" by this Activity
// and "bound" to the JobScheduler (also called "Scheduled" by the JobScheduler). This call
// to stopService() won't prevent scheduled jobs to be processed. However, failing
// to call stopService() would keep it alive indefinitely.
stopService(new Intent(this, MyJobService.class));
super.onStop();
}
@Override
protected void onStart() {
super.onStart();
// Start service and provide it a way to communicate with this class.
//主动发送Messenger,传递handler的IMessenger到Service
Intent startServiceIntent = new Intent(this, MyJobService.class);
Messenger messengerIncoming = new Messenger(mHandler);
startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming);
startService(startServiceIntent);
}
/**
* Executed when user clicks on SCHEDULE JOB.
*/
public void scheduleJob(View v) {
JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
String delay = mDelayEditText.getText().toString();
if (!TextUtils.isEmpty(delay)) {
builder.setMinimumLatency(Long.valueOf(delay) * 1000);
}
String deadline = mDeadlineEditText.getText().toString();
if (!TextUtils.isEmpty(deadline)) {
builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
}
boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
if (requiresUnmetered) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
} else if (requiresAnyConnectivity) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
}
builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());
builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());
// Extras, work duration.
PersistableBundle extras = new PersistableBundle();
String workDuration = mDurationTimeEditText.getText().toString();
if (TextUtils.isEmpty(workDuration)) {
workDuration = "1";
}
extras.putLong(WORK_DURATION_KEY, Long.valueOf(workDuration) * 1000);
builder.setExtras(extras);
// Schedule job
Log.d(TAG, "Scheduling job");
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.schedule(builder.build());
}
/**
* Executed when user clicks on CANCEL ALL.
*/
public void cancelAllJobs(View v) {
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.cancelAll();
Toast.makeText(MainActivity.this, R.string.all_jobs_cancelled, Toast.LENGTH_SHORT).show();
}
/**
* Executed when user clicks on FINISH LAST TASK.
*/
public void finishJob(View v) {
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
List<JobInfo> allPendingJobs = jobScheduler.getAllPendingJobs();
if (allPendingJobs.size() > 0) {
// Finish the last one
int jobId = allPendingJobs.get(0).getId();
jobScheduler.cancel(jobId);
Toast.makeText(
MainActivity.this, String.format(getString(R.string.cancelled_job), jobId),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(
MainActivity.this, getString(R.string.no_jobs_to_cancel),
Toast.LENGTH_SHORT).show();
}
}
/**
* A {@link Handler} allows you to send messages associated with a thread. A {@link Messenger}
* uses this handler to communicate from {@link MyJobService}. It's also used to make
* the start and stop views blink for a short period of time.
*/
private static class IncomingMessageHandler extends Handler {
// Prevent possible leaks with a weak reference.
private WeakReference<MainActivity> mActivity;
IncomingMessageHandler(MainActivity activity) {
super(/* default looper */);
this.mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = mActivity.get();
if (mainActivity == null) {
// Activity is no longer available, exit.
return;
}
View showStartView = mainActivity.findViewById(R.id.onstart_textview);
View showStopView = mainActivity.findViewById(R.id.onstop_textview);
Message m;
switch (msg.what) {
/*
* Receives callback from the service when a job has landed
* on the app. Turns on indicator and sends a message to turn it off after
* a second.
*/
case MSG_COLOR_START:
// Start received, turn on the indicator and show text.
showStartView.setBackgroundColor(getColor(R.color.start_received));
updateParamsTextView(msg.obj, "started");
// Send message to turn it off after a second.
m = Message.obtain(this, MSG_UNCOLOR_START);
sendMessageDelayed(m, 1000L);
break;
/*
* Receives callback from the service when a job that previously landed on the
* app must stop executing. Turns on indicator and sends a message to turn it
* off after two seconds.
*/
case MSG_COLOR_STOP:
// Stop received, turn on the indicator and show text.
showStopView.setBackgroundColor(getColor(R.color.stop_received));
updateParamsTextView(msg.obj, "stopped");
// Send message to turn it off after a second.
m = obtainMessage(MSG_UNCOLOR_STOP);
sendMessageDelayed(m, 2000L);
break;
case MSG_UNCOLOR_START:
showStartView.setBackgroundColor(getColor(R.color.none_received));
updateParamsTextView(null, "");
break;
case MSG_UNCOLOR_STOP:
showStopView.setBackgroundColor(getColor(R.color.none_received));
updateParamsTextView(null, "");
break;
}
}
private void updateParamsTextView(@Nullable Object jobId, String action) {
TextView paramsTextView = (TextView) mActivity.get().findViewById(R.id.task_params);
if (jobId == null) {
paramsTextView.setText("");
return;
}
String jobIdText = String.valueOf(jobId);
paramsTextView.setText(String.format("Job ID %s %s", jobIdText, action));
}
private int getColor(@ColorRes int color) {
return mActivity.get().getResources().getColor(color);
}
}
}
服务端
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
public static final int MSG_UNCOLOR_START = 0;
public static final int MSG_UNCOLOR_STOP = 1;
public static final int MSG_COLOR_START = 2;
public static final int MSG_COLOR_STOP = 3;
public static final String MESSENGER_INTENT_KEY
= BuildConfig.APPLICATION_ID + ".MESSENGER_INTENT_KEY";
public static final String WORK_DURATION_KEY =
BuildConfig.APPLICATION_ID + ".WORK_DURATION_KEY";
private EditText mDelayEditText;
private EditText mDeadlineEditText;
private EditText mDurationTimeEditText;
private RadioButton mWiFiConnectivityRadioButton;
private RadioButton mAnyConnectivityRadioButton;
private CheckBox mRequiresChargingCheckBox;
private CheckBox mRequiresIdleCheckbox;
private ComponentName mServiceComponent;
private int mJobId = 0;
// Handler for incoming messages from the service.
private IncomingMessageHandler mHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_main);
// Set up UI.
mDelayEditText = (EditText) findViewById(R.id.delay_time);
mDurationTimeEditText = (EditText) findViewById(R.id.duration_time);
mDeadlineEditText = (EditText) findViewById(R.id.deadline_time);
mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered);
mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any);
mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging);
mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle);
mServiceComponent = new ComponentName(this, MyJobService.class);
mHandler = new IncomingMessageHandler(this);
}
@Override
protected void onStop() {
// A service can be "started" and/or "bound". In this case, it's "started" by this Activity
// and "bound" to the JobScheduler (also called "Scheduled" by the JobScheduler). This call
// to stopService() won't prevent scheduled jobs to be processed. However, failing
// to call stopService() would keep it alive indefinitely.
stopService(new Intent(this, MyJobService.class));
super.onStop();
}
@Override
protected void onStart() {
super.onStart();
// Start service and provide it a way to communicate with this class.
Intent startServiceIntent = new Intent(this, MyJobService.class);
Messenger messengerIncoming = new Messenger(mHandler);
startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming);
startService(startServiceIntent);
}
/**
* Executed when user clicks on SCHEDULE JOB.
*/
public void scheduleJob(View v) {
JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
String delay = mDelayEditText.getText().toString();
if (!TextUtils.isEmpty(delay)) {
builder.setMinimumLatency(Long.valueOf(delay) * 1000);
}
String deadline = mDeadlineEditText.getText().toString();
if (!TextUtils.isEmpty(deadline)) {
builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
}
boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
if (requiresUnmetered) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
} else if (requiresAnyConnectivity) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
}
builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());
builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());
// Extras, work duration.
PersistableBundle extras = new PersistableBundle();
String workDuration = mDurationTimeEditText.getText().toString();
if (TextUtils.isEmpty(workDuration)) {
workDuration = "1";
}
extras.putLong(WORK_DURATION_KEY, Long.valueOf(workDuration) * 1000);
builder.setExtras(extras);
// Schedule job
Log.d(TAG, "Scheduling job");
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.schedule(builder.build());
}
/**
* Executed when user clicks on CANCEL ALL.
*/
public void cancelAllJobs(View v) {
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.cancelAll();
Toast.makeText(MainActivity.this, R.string.all_jobs_cancelled, Toast.LENGTH_SHORT).show();
}
/**
* Executed when user clicks on FINISH LAST TASK.
*/
public void finishJob(View v) {
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
List<JobInfo> allPendingJobs = jobScheduler.getAllPendingJobs();
if (allPendingJobs.size() > 0) {
// Finish the last one
int jobId = allPendingJobs.get(0).getId();
jobScheduler.cancel(jobId);
Toast.makeText(
MainActivity.this, String.format(getString(R.string.cancelled_job), jobId),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(
MainActivity.this, getString(R.string.no_jobs_to_cancel),
Toast.LENGTH_SHORT).show();
}
}
/**
* A {@link Handler} allows you to send messages associated with a thread. A {@link Messenger}
* uses this handler to communicate from {@link MyJobService}. It's also used to make
* the start and stop views blink for a short period of time.
*/
private static class IncomingMessageHandler extends Handler {
// Prevent possible leaks with a weak reference.
private WeakReference<MainActivity> mActivity;
IncomingMessageHandler(MainActivity activity) {
super(/* default looper */);
this.mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = mActivity.get();
if (mainActivity == null) {
// Activity is no longer available, exit.
return;
}
View showStartView = mainActivity.findViewById(R.id.onstart_textview);
View showStopView = mainActivity.findViewById(R.id.onstop_textview);
Message m;
switch (msg.what) {
/*
* Receives callback from the service when a job has landed
* on the app. Turns on indicator and sends a message to turn it off after
* a second.
*/
case MSG_COLOR_START:
// Start received, turn on the indicator and show text.
showStartView.setBackgroundColor(getColor(R.color.start_received));
updateParamsTextView(msg.obj, "started");
// Send message to turn it off after a second.
m = Message.obtain(this, MSG_UNCOLOR_START);
sendMessageDelayed(m, 1000L);
break;
/*
* Receives callback from the service when a job that previously landed on the
* app must stop executing. Turns on indicator and sends a message to turn it
* off after two seconds.
*/
case MSG_COLOR_STOP:
// Stop received, turn on the indicator and show text.
showStopView.setBackgroundColor(getColor(R.color.stop_received));
updateParamsTextView(msg.obj, "stopped");
// Send message to turn it off after a second.
m = obtainMessage(MSG_UNCOLOR_STOP);
sendMessageDelayed(m, 2000L);
break;
case MSG_UNCOLOR_START:
showStartView.setBackgroundColor(getColor(R.color.none_received));
updateParamsTextView(null, "");
break;
case MSG_UNCOLOR_STOP:
showStopView.setBackgroundColor(getColor(R.color.none_received));
updateParamsTextView(null, "");
break;
}
}
private void updateParamsTextView(@Nullable Object jobId, String action) {
TextView paramsTextView = (TextView) mActivity.get().findViewById(R.id.task_params);
if (jobId == null) {
paramsTextView.setText("");
return;
}
String jobIdText = String.valueOf(jobId);
paramsTextView.setText(String.format("Job ID %s %s", jobIdText, action));
}
private int getColor(@ColorRes int color) {
return mActivity.get().getResources().getColor(color);
}
}
}
对于有疑问说,使用Messenger没有AIDL方便,因为AIDL是IPC代理方式的,这个需要按照情况来决定,对于小型项目,没必要使用IPC代理,但对于大项目最好使用IPC代理,因为IPC代理这种方式是面向接口的服务。
三、Messenger、Binder、AIDL的区别
①Messenger和AIDL相比,都可以使用Parcelable传递复杂类型的数据,AIDL相对底层,灵活性比较好。
②Messenger底层封装自AIDL
③AIDL和Messenger都是通过Binder机制通信
④Messenger使用的是同一个线程的Handler和消息队列,因此无法处理多线程并发任务,而AIDL可以。
④Messenger可以实现在无绑定服务的情况下和Service交互
来源:oschina
链接:https://my.oschina.net/u/2256215/blog/352447