編輯:關於Android編程
最近有一個需求就是往程序中加入大數據的采集點,但是因為我們的Android程序包含兩個進程,所以涉及到跨進程通信的問題。現將Android中的跨進程通信方式總結如下。
Android中有4種跨進程通信方式,分別是利用AIDL Service、ContentProvider、Broadcast、Activity實現。
1.利用AIDL Service實現跨進程通信
這是我個人比較推崇的方式,因為它相比Broadcast而言,雖然實現上稍微麻煩了一點,但是它的優勢就是不會像廣播那樣在手機中的廣播較多時會有明顯的時延,甚至有廣播發送不成功的情況出現。
注意普通的Service並不能實現跨進程操作,實際上普通的Service和它所在的應用處於同一個進程中,而且它也不會專門開一條新的線程,因此如果在普通的Service中實現在耗時的任務,需要新開線程。
要實現跨進程通信,需要借助AIDL(Android Interface Definition Language)。Android中的跨進程服務其實是采用C/S的架構,因而AIDL的目的就是實現通信接口。
首先舉一個簡單的栗子。
服務端代碼如下:
首先是aidl的代碼:
package com.android.service; interface IData { int getRoomNum(); }
RoomService的代碼如下:
package com.android.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class RoomService extends Service{ private IData.Stub mBinder=new IData.Stub() { @Override public int getRoomNum() throws RemoteException { return 3008; } }; @Override public IBinder onBind(Intent intent) { return mBinder; } }AndroidManifest如下:
然後運行該Service所在的Project即可。
客戶端代碼如下:
注意客戶端也要有aidl文件,所以最簡單的辦法就是將Service端中aidl所在的包直接復制過去。另外要注意的是在onDestroy中要解除和Service的綁定。
MainActivity.java的代碼如下:
package com.example.aidlsampleclient; import com.android.service.IData; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import android.view.Menu; import android.widget.Button; import android.widget.Toast; import android.view.View; public class MainActivity extends Activity implements View.OnClickListener{ private static final String TAG=MainActivity; private static final String ROOM_SERVICE_ACTION=com.aidl.service.room; private Button bindServiceButton; private Button getServiceButton; IData mData; private ServiceConnection conn=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG,----------------onServiceConnected--------); mData=IData.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG,----------------onServiceDisconnected-------------); mData=null; } }; private void initView() { bindServiceButton=(Button)findViewById(R.id.bindServiceButton); getServiceButton=(Button)findViewById(R.id.getServiceButton); bindServiceButton.setOnClickListener(this); getServiceButton.setOnClickListener(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()) { case R.id.bindServiceButton: bindService(); break; case R.id.getServiceButton: getService(); break; default: break; } } private void bindService() { Intent intent=new Intent(); intent.setAction(ROOM_SERVICE_ACTION); bindService(intent,conn,BIND_AUTO_CREATE); } private void getService() { try { if(mData!=null) { int roomNum=mData.getRoomNum(); showLongToast(RoomNum:+roomNum); } } catch(RemoteException ex) { ex.printStackTrace(); } } private void showLongToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); unbindService(conn); } }activity_main.xml的代碼如下:
運行結果如下:
然後舉一個稍微復雜一點的栗子,注意如果*.aidl文件中含有自定義的對象,那麼該對象的類要實現Parcelable接口,並且要新建一個該類的aidl文件,否則會出現could not find import for class com.android.service.XX的錯誤,其中XX為類名。還是上面的栗子,但是aidl文件中添加了一些新的方法。仍以上面的RoomService為例,
Service端的代碼如下:
Room類的代碼為:
package com.android.service; import android.os.Parcel; import android.os.Parcelable; public class Room implements Parcelable{ //房間號 private int roomNum; //房間大小 private float roomSpace; //是否有空調 private boolean hasAirConditioner; //是否有Wifi private boolean hasWifi; //房間內的裝飾風格 private String decorativeStyle; public static final Parcelable.CreatorCREATOR=new Parcelable.Creator () { @Override public Room createFromParcel(Parcel source) { return new Room(source); } @Override public Room[] newArray(int size) { // TODO Auto-generated method stub return null; } }; public Room(int roomNum,float roomSpace,boolean hasAirConditioner,boolean hasWifi,String decorativeStyle) { this.roomNum=roomNum; this.roomSpace=roomSpace; this.hasAirConditioner=hasAirConditioner; this.hasWifi=hasWifi; this.decorativeStyle=decorativeStyle; } private Room(Parcel source) { roomNum=source.readInt(); roomSpace=source.readFloat(); boolean[]tempArray=new boolean[2]; source.readBooleanArray(tempArray); hasAirConditioner=tempArray[0]; hasWifi=tempArray[1]; decorativeStyle=source.readString(); } @Override public String toString() { StringBuilder sb=new StringBuilder(); sb.append(Basic info of room is as follows: ); sb.append(RoomNum:+roomNum+ ); sb.append(RoomSpace:+roomSpace+ ); sb.append(HasAirConditioner:+hasAirConditioner+ ); sb.append(HasWifi:+hasWifi+ ); sb.append(Decorative Style:+decorativeStyle); return sb.toString(); } @Override public int describeContents() { // TODO Auto-generated method stub return 0; } @Override public void writeToParcel(Parcel dest,int flags) { dest.writeInt(roomNum); dest.writeFloat(roomSpace); dest.writeBooleanArray(new boolean[]{hasAirConditioner,hasWifi}); dest.writeString(decorativeStyle); } }
package com.android.service; parcelable Room;
package com.android.service; import com.android.service.Room; interface IRoom { Room getRoom(); }RoomService的代碼為:
package com.android.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class RoomService extends Service{ private IRoom.Stub mBinder=new IRoom.Stub() { @Override public Room getRoom() throws RemoteException { Room room=new Room(3008,23.5f,true,true,IKEA); return room; } }; @Override public IBinder onBind(Intent intent) { return mBinder; } }由於AndroidManifest.xml的代碼不變,因而此處不再貼出。下面是客戶端的代碼:
package com.example.aidlsampleclient; import com.android.service.IRoom; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import android.view.Menu; import android.widget.Button; import android.widget.Toast; import android.view.View; public class MainActivity extends Activity implements View.OnClickListener{ private static final String TAG=MainActivity; //private static final String SERVICE_ACTION=com.aidl.service.data; private static final String ROOM_SERVICE_ACTION=com.aidl.service.room; private Button bindServiceButton; private Button getServiceButton; IRoom mRoom; private ServiceConnection conn=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG,----------------onServiceConnected--------); showLongToast(onServiceConnected); mRoom=IRoom.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG,----------------onServiceDisconnected-------------); mRoom=null; } }; private void initView() { bindServiceButton=(Button)findViewById(R.id.bindServiceButton); getServiceButton=(Button)findViewById(R.id.getServiceButton); bindServiceButton.setOnClickListener(this); getServiceButton.setOnClickListener(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()) { case R.id.bindServiceButton: bindService(); break; case R.id.getServiceButton: getService(); break; default: break; } } private void bindService() { Intent intent=new Intent(); intent.setAction(ROOM_SERVICE_ACTION); bindService(intent,conn,BIND_AUTO_CREATE); } private void getService() { if(mRoom!=null) { try { showLongToast(mRoom.getRoom().toString()); } catch (RemoteException e) { e.printStackTrace(); } } } private void showLongToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); unbindService(conn); } }
運行結果如下:
顯然,客戶端已經成功讀取到服務信息。
注意,上面的所舉的栗子其實不只是跨進程,還是跨應用。要注意的是,跨應用一定跨進程,但是跨進程不一定是跨應用。對於跨應用的情況,利用AIDL基本上是較好的解決了問題,但也只是“較好”而已,實際上並不完美,比如,如果要增加一個服務,如果利用AIDL的話,那麼又要改寫aidl文件,如果是涉及自定義對象,則還要增加自定義對象的聲明,而且這種改變不只是Service端的改變,客戶端也要跟著改變,顯然這種解決方案不夠優雅。
那麼,有沒有更優雅的方法呢?
當然有,那就是利用Service的onStartCommand(Intent intent, int flags, int startId)方法。
服務端代碼如下:
package com.android.service; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.widget.Toast; public class RoomService extends Service{ private static final String TAG=RoomService; private static final int CLEAN_SERVICE=0x1; private static final int ORDER_SERVICE=0x2; private static final int PACKAGE_SERVICE=0x3; private static final String SERVICE_KEY=ServiceName; @Override public void onStart(Intent intent, int startId) { showLog(onStart); } @Override public int onStartCommand(Intent intent, int flags, int startId) { //String action=intent.getAction(); Log.i(TAG,onStartCommand); int actionFlag=intent.getIntExtra(SERVICE_KEY, -1); switch(actionFlag) { case CLEAN_SERVICE: showShortToast(Start Clean Service Right Now); break; case ORDER_SERVICE: showShortToast(Start Order Service Right Now); break; case PACKAGE_SERVICE: showShortToast(Start Package Service Right Now); break; default: break; } return super.onStartCommand(intent, flags, startId); } private void showLog(String info) { Log.i(TAG,info); } private void showShortToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_SHORT).show(); } @Override public void onDestroy() { // TODO Auto-generated method stub showLog(onDestroy); super.onDestroy(); } @Override public void onCreate() { // TODO Auto-generated method stub showLog(onCreate); super.onCreate(); } @Override public IBinder onBind(Intent intent) { showLog(onBind); return null; } @Override public boolean onUnbind(Intent intent) { showLog(onUnbind); return super.onUnbind(intent); } }客戶端代碼如下:
package com.example.aidlsampleclient; import com.android.service.IRoom; import com.android.service.RoomService; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import android.view.Menu; import android.widget.Button; import android.widget.Toast; import android.view.View; public class MainActivity extends Activity implements View.OnClickListener{ private static final String TAG=MainActivity; private static final String ROOM_SERVICE_ACTION=com.aidl.service.room; private static final int CLEAN_SERVICE=0x1; private static final int ORDER_SERVICE=0x2; private static final int PACKAGE_SERVICE=0x3; private static final String SERVICE_KEY=ServiceName; private Button cleanButton; private Button orderButton; private Button packageButton; private void initView() { cleanButton=(Button)findViewById(R.id.cleanButton); orderButton=(Button)findViewById(R.id.orderButton); packageButton=(Button)findViewById(R.id.packageButton); cleanButton.setOnClickListener(this); orderButton.setOnClickListener(this); packageButton.setOnClickListener(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()) { case R.id.cleanButton: cleanAction(); break; case R.id.orderButton: orderAction(); break; case R.id.packageButton: packageAction(); break; default: break; } } private void cleanAction() { startAction(ROOM_SERVICE_ACTION,CLEAN_SERVICE); } private void orderAction() { startAction(ROOM_SERVICE_ACTION,ORDER_SERVICE); } private void packageAction() { startAction(ROOM_SERVICE_ACTION,PACKAGE_SERVICE); } private void startAction(String actionName,int serviceFlag) { //Intent intent=new Intent(this,RoomService.class); Intent intent=new Intent(); intent.setAction(actionName); intent.putExtra(SERVICE_KEY, serviceFlag); this.startService(intent); } private void showLongToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); } }運行結果如下:
顯然,此時客戶端順利獲取了服務。
上面舉的是跨應用的例子,如果是在同一個應用的不同進程的話,則有更簡單的實現方法。
RoomService的代碼如下:
package com.android.service; import com.android.actions.Actions; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.widget.Toast; public class RoomService extends Service{ private static final String TAG=RoomService; @Override public void onStart(Intent intent, int startId) { showLog(onStart); } @Override public int onStartCommand(Intent intent, int flags, int startId) { //String action=intent.getAction(); Log.i(TAG,onStartCommand); String action=intent.getAction(); if(Actions.CLEAN_ACTION.equals(action)) { showShortToast(Start Clean Service Right Now); } else if(Actions.ORDER_ACTION.equals(action)) { showShortToast(Start Order Service Right Now); } else if(Actions.PACKAGE_ACTION.equals(action)) { showShortToast(Start Package Service Right Now); } else { showShortToast(Wrong action); } return super.onStartCommand(intent, flags, startId); } private void showLog(String info) { Log.i(TAG,info); } private void showShortToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_SHORT).show(); } @Override public void onDestroy() { // TODO Auto-generated method stub showLog(onDestroy); super.onDestroy(); } @Override public void onCreate() { // TODO Auto-generated method stub showLog(onCreate); super.onCreate(); } @Override public IBinder onBind(Intent intent) { showLog(onBind); return null; } @Override public boolean onUnbind(Intent intent) { showLog(onUnbind); return super.onUnbind(intent); } }
MainActivity的代碼如下:
package com.android.activity; import com.android.activity.R; import com.android.service.RoomService; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.widget.Button; import android.widget.Toast; import android.view.View; import com.android.actions.Actions; public class MainActivity extends Activity implements View.OnClickListener{ private static final String TAG=MainActivity; private static final String SERVICE_KEY=ServiceName; private Button cleanButton; private Button orderButton; private Button packageButton; private void initView() { cleanButton=(Button)findViewById(R.id.cleanButton); orderButton=(Button)findViewById(R.id.orderButton); packageButton=(Button)findViewById(R.id.packageButton); cleanButton.setOnClickListener(this); orderButton.setOnClickListener(this); packageButton.setOnClickListener(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()) { case R.id.cleanButton: cleanAction(); break; case R.id.orderButton: orderAction(); break; case R.id.packageButton: packageAction(); break; default: break; } } private void cleanAction() { startAction(Actions.CLEAN_ACTION); } private void orderAction() { startAction(Actions.ORDER_ACTION); } private void packageAction() { startAction(Actions.PACKAGE_ACTION); } private void startAction(String actionName) { Intent intent=new Intent(this,RoomService.class); intent.setAction(actionName); this.startService(intent); } private void showLongToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); } }運行結果同上,此處不再貼圖。
那AIDL還有存在的必要嗎?當然有!最重要的一點是代價問題,從打印的log可看出,客戶端每調用一次Context.startService(Intent),Service就會重新執行一次onStartCommand---->onStart,而使用AIDL的話,綁定服務之後,不會重復執行onStart,顯然後者的代價更小。
最後再講一個Service的重點應用:前台Service,像我們經常用的天氣、音樂其實都利用了前台Service來實現。 實例到明天再補上吧。
轉載請注明出處:http://blog.csdn.net/bettarwang/article/details/40947481
仿微信圖片選擇器源碼下載連接:先上圖,後上代碼:打開app後點擊添加圖片按鈕:獲取選擇的圖片 並返回在gridview中:private ImageButton imag
步驟:分兩步一、usb連接:在Ubuntu掛載使用MTP設備步驟如下:1.將MTP設備連接至PC機2.如果是第一次使用MTP設備需要安裝以下軟件,否則可以跳過此步驟:復制
接觸了百度地圖開發平台半個月了,這2天試著模仿了微信給好友發送位置功能,對百度地圖的操作能力又上了一個台階(如果需要完整demo,請評論留下郵箱) 我在實現這個
Android實現帶圖標的ListView已經成為每一個android應用開發初學者的必學知識,熟練掌握此知識點,對我們以後的開發工作會大有好處,今天我們就來一步一步實現