上一篇說道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; } }
Android 5.0的調度做業JobScheduler併發
原理是經過Parceable參數傳遞app
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和AIDL相比,均可以使用Parcelable傳遞複雜類型的數據,AIDL相對底層,靈活性比較好。異步
②Messenger底層封裝自AIDLide
③AIDL和Messenger都是經過Binder機制通訊oop
④Messenger使用的是同一個線程的Handler和消息隊列,所以沒法處理多線程併發任務,而AIDL能夠。post
④Messenger能夠實如今無綁定服務的狀況下和Service交互ui