目錄
一叉钥、使用 Intent
Activity,Service篙贸,Receiver 都支持在 Intent 中傳遞 Bundle 數(shù)據(jù)投队,而 Bundle 實(shí)現(xiàn)了 Parcelable 接口,可以在不同的進(jìn)程間進(jìn)行傳輸爵川。
在一個(gè)進(jìn)程中啟動(dòng)了另一個(gè)進(jìn)程的 Activity敷鸦,Service 和 Receiver ,可以在 Bundle 中附加要傳遞的數(shù)據(jù)通過(guò) Intent 發(fā)送出去寝贡。
二扒披、使用文件共享
Windows 上,一個(gè)文件如果被加了排斥鎖會(huì)導(dǎo)致其他線程無(wú)法對(duì)其進(jìn)行訪問(wèn)圃泡,包括讀和寫碟案;而 Android 系統(tǒng)基于 Linux ,使得其并發(fā)讀取文件沒(méi)有限制地進(jìn)行洞焙,甚至允許兩個(gè)線程同時(shí)對(duì)一個(gè)文件進(jìn)行讀寫操作蟆淀,盡管這樣可能會(huì)出問(wèn)題。
可以在一個(gè)進(jìn)程中序列化一個(gè)對(duì)象到文件系統(tǒng)中澡匪,在另一個(gè)進(jìn)程中反序列化恢復(fù)這個(gè)對(duì)象(注意:并不是同一個(gè)對(duì)象熔任,只是內(nèi)容相同。)唁情。
SharedPreferences 是個(gè)特例疑苔,系統(tǒng)對(duì)它的讀 / 寫有一定的緩存策略,即內(nèi)存中會(huì)有一份 ShardPreferences 文件的緩存甸鸟,系統(tǒng)對(duì)他的讀 / 寫就變得不可靠惦费,當(dāng)面對(duì)高并發(fā)的讀寫訪問(wèn),SharedPreferences 有很多大的幾率丟失數(shù)據(jù)抢韭。因此薪贫,IPC 不建議采用 SharedPreferences。
三刻恭、使用 Messenger
Messenger 是一種輕量級(jí)的 IPC 方案瞧省,它的底層實(shí)現(xiàn)是 AIDL ,可以在不同進(jìn)程中傳遞 Message 對(duì)象鳍贾,它一次只處理一個(gè)請(qǐng)求鞍匾,在服務(wù)端不需要考慮線程同步的問(wèn)題,服務(wù)端不存在并發(fā)執(zhí)行的情形骑科。
服務(wù)端進(jìn)程:服務(wù)端創(chuàng)建一個(gè) Service 來(lái)處理客戶端請(qǐng)求橡淑,同時(shí)通過(guò)一個(gè) Handler 對(duì)象來(lái)實(shí)例化一個(gè) Messenger 對(duì)象,然后在 Service 的 onBind 中返回這個(gè) Messenger 對(duì)象底層的 Binder 即可咆爽。
publicclassMessengerServiceextendsService {privatestaticfinalStringTAG=MessengerService.class.getSimpleName();privateclassMessengerHandlerextendsHandler {/***@param msg*/@OverridepublicvoidhandleMessage(Messagemsg) {switch (msg.what) {caseConstants.MSG_FROM_CLIENT:Log.d(TAG,"receive msg from client: msg = ["+ msg.getData().getString(Constants.MSG_KEY)+"]");Toast.makeText(MessengerService.this,"receive msg from client: msg = ["+ msg.getData().getString(Constants.MSG_KEY)+"]",Toast.LENGTH_SHORT).show();Messenger client= msg.replyTo;Message replyMsg=Message.obtain(null,Constants.MSG_FROM_SERVICE);Bundle bundle=newBundle(); bundle.putString(Constants.MSG_KEY,"我已經(jīng)收到你的消息梁棠,稍后回復(fù)你置森!"); replyMsg.setData(bundle);try { client.send(replyMsg); }catch (RemoteException e) { e.printStackTrace(); }break;default:super.handleMessage(msg); } } }privateMessenger mMessenger=newMessenger(newMessengerHandler());@Nullable@OverridepublicIBinderonBind(Intentintent) {return mMessenger.getBinder();
? ? }
}
客戶端進(jìn)程:首先綁定服務(wù)端 Service ,綁定成功之后用服務(wù)端的 IBinder 對(duì)象創(chuàng)建一個(gè) Messenger 掰茶,通過(guò)這個(gè) Messenger 就可以向服務(wù)端發(fā)送消息了暇藏,消息類型是 Message 蜜笤。如果需要服務(wù)端響應(yīng)濒蒋,則需要?jiǎng)?chuàng)建一個(gè) Handler 并通過(guò)它來(lái)創(chuàng)建一個(gè) Messenger(和服務(wù)端一樣),并通過(guò) Message 的 replyTo 參數(shù)傳遞給服務(wù)端把兔。服務(wù)端通過(guò) Message 的 replyTo 參數(shù)就可以回應(yīng)客戶端了沪伙。
publicclassMainActivityextendsAppCompatActivity {privatestaticfinalStringTAG=MainActivity.class.getSimpleName();privateMessenger mGetReplyMessenger=newMessenger(newMessageHandler());privateMessenger mService;privateclassMessageHandlerextendsHandler {@OverridepublicvoidhandleMessage(Messagemsg) {switch (msg.what) {caseConstants.MSG_FROM_SERVICE:Log.d(TAG,"received msg form service: msg = ["+ msg.getData().getString(Constants.MSG_KEY)+"]");Toast.makeText(MainActivity.this,"received msg form service: msg = ["+ msg.getData().getString(Constants.MSG_KEY)+"]",Toast.LENGTH_SHORT).show();break;default:super.handleMessage(msg);? ? ? ? ? ? }? ? ? ? }? ? }@OverrideprotectedvoidonCreate(BundlesavedInstanceState) {super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.activity_main);? ? }publicvoidbindService(Viewv) {Intent mIntent=newIntent(this,MessengerService.class);? ? ? ? bindService(mIntent, mServiceConnection,Context.BIND_AUTO_CREATE);? ? }publicvoidsendMessage(Viewv) {Message msg=Message.obtain(null,Constants.MSG_FROM_CLIENT);Bundle data=newBundle();? ? ? ? data.putString(Constants.MSG_KEY,"Hello! This is client.");? ? ? ? msg.setData(data);? ? ? ? msg.replyTo= mGetReplyMessenger;try {? ? ? ? ? ? mService.send(msg);? ? ? ? }catch (RemoteException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }@OverrideprotectedvoidonDestroy() {? ? ? ? unbindService(mServiceConnection);super.onDestroy();? ? }privateServiceConnection mServiceConnection=newServiceConnection() {/***@param name*@param service*/@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice) {? ? ? ? ? ? mService=newMessenger(service);Message msg=Message.obtain(null,Constants.MSG_FROM_CLIENT);Bundle data=newBundle();? ? ? ? ? ? data.putString(Constants.MSG_KEY,"Hello! This is client.");? ? ? ? ? ? msg.setData(data);//? ? ? ? ? ? msg.replyTo= mGetReplyMessenger;try {? ? ? ? ? ? ? ? mService.send(msg);? ? ? ? ? ? }catch (RemoteException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }? ? ? ? }/***@param name*/@OverridepublicvoidonServiceDisconnected(ComponentNamename) {
? ? ? ? }
? ? };
}
**注意:**客戶端和服務(wù)端是通過(guò)拿到對(duì)方的 Messenger 來(lái)發(fā)送 Message 的。只不過(guò)客戶端通過(guò) bindService onServiceConnected 而服務(wù)端通過(guò) message.replyTo 來(lái)獲得對(duì)方的 Messenger 县好。Messenger 中有一個(gè) Hanlder 以串行的方式處理隊(duì)列中的消息围橡。不存在并發(fā)執(zhí)行,因此我們不用考慮線程同步的問(wèn)題缕贡。
四翁授、使用 AIDL
Messenger 是以串行的方式處理客戶端發(fā)來(lái)的消息,如果大量消息同時(shí)發(fā)送到服務(wù)端晾咪,服務(wù)端只能一個(gè)一個(gè)處理收擦,所以大量并發(fā)請(qǐng)求就不適合用 Messenger ,而且 Messenger 只適合傳遞消息谍倦,不能跨進(jìn)程調(diào)用服務(wù)端的方法塞赂。AIDL 可以解決并發(fā)和跨進(jìn)程調(diào)用方法的問(wèn)題,要知道 Messenger 本質(zhì)上也是 AIDL 昼蛀,只不過(guò)系統(tǒng)做了封裝方便上層的調(diào)用而已宴猾。
基本數(shù)據(jù)類型;
String和CharSequence
ArrayList叼旋,里面的元素必須能夠被 AIDL 支持仇哆;
HashMap,里面的元素必須能夠被 AIDL 支持夫植;
Parcelable讹剔,實(shí)現(xiàn) Parcelable 接口的對(duì)象;?注意:如果 AIDL 文件中用到了自定義的 Parcelable 對(duì)象偷崩,必須新建一個(gè)和它同名的 AIDL 文件辟拷。
AIDL,AIDL 接口本身也可以在 AIDL 文件中使用阐斜。
服務(wù)端創(chuàng)建一個(gè) Service 用來(lái)監(jiān)聽客戶端的連接請(qǐng)求衫冻,然后創(chuàng)建一個(gè) AIDL 文件,將暴露給客戶端的接口在這個(gè) AIDL 文件中聲明谒出,最后在 Service 中實(shí)現(xiàn)這個(gè) AIDL 接口即可隅俘。
綁定服務(wù)端的 Service 邻奠,綁定成功后,將服務(wù)端返回的 Binder 對(duì)象轉(zhuǎn)成 AIDL 接口所屬的類型为居,然后就可以調(diào)用 AIDL 中的方法了碌宴。客戶端調(diào)用遠(yuǎn)程服務(wù)的方法蒙畴,被調(diào)用的方法運(yùn)行在服務(wù)端的 Binder 線程池中贰镣,同時(shí)客戶端的線程會(huì)被掛起,如果服務(wù)端方法執(zhí)行比較耗時(shí)膳凝,就會(huì)導(dǎo)致客戶端線程長(zhǎng)時(shí)間阻塞碑隆,導(dǎo)致 ANR 〉乓簦客戶端的 onServiceConnected 和 onServiceDisconnected 方法都在 UI 線程中上煤。
使用 Permission 驗(yàn)證,在 manifest 中聲明
服務(wù)端 onBinder 方法中
publicIBinder onBind(Intent intent) {//Permission 權(quán)限驗(yàn)證int check= checkCallingOrSelfPermission("com.jc.ipc.ACCESS_BOOK_SERVICE");if (check==PackageManager.PERMISSION_DENIED) {returnnull;? ? }return mBinder;
}
Pid Uid 驗(yàn)證
詳細(xì)代碼:
// Book.aidlpackagecom.jc.ipc.aidl;parcelableBook;
// IBookManager.aidlpackage com.jc.ipc.aidl;import com.jc.ipc.aidl.Book;import com.jc.ipc.aidl.INewBookArrivedListener;// AIDL 接口中只支持方法著淆,不支持靜態(tài)常量劫狠,區(qū)別于傳統(tǒng)的接口interfaceIBookManager {ListgetBookList();// AIDL 中除了基本數(shù)據(jù)類型,其他數(shù)據(jù)類型必須標(biāo)上方向,in,out 或者 inout// in 表示輸入型參數(shù)// out 表示輸出型參數(shù)// inout 表示輸入輸出型參數(shù)voidaddBook(inBookbook);voidregisterListener(INewBookArrivedListenerlistener);voidunregisterListener(INewBookArrivedListenerlistener);
}
// INewBookArrivedListener.aidlpackagecom.jc.ipc.aidl;importcom.jc.ipc.aidl.Book;// 提醒客戶端新書到來(lái)interfaceINewBookArrivedListener {voidonNewBookArrived(inBooknewBook);
}
publicclassBookManagerActivityextendsAppCompatActivity {privatestaticfinalStringTAG=BookManagerActivity.class.getSimpleName();privatestaticfinalintMSG_NEW_BOOK_ARRIVED=0x10;privateButton getBookListBtn,addBookBtn;privateTextView displayTextView;privateIBookManager bookManager;privateHandler mHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg) {switch (msg.what) {caseMSG_NEW_BOOK_ARRIVED:Log.d(TAG,"handleMessage: new book arrived"+ msg.obj);Toast.makeText(BookManagerActivity.this,"new book arrived"+ msg.obj,Toast.LENGTH_SHORT).show();break;default:super.handleMessage(msg);? ? ? ? ? ? }? ? ? ? }? ? };privateServiceConnection mServiceConn=newServiceConnection() {@OverridepublicvoidonServiceConnected(ComponentNamename,IBinderservice) {? ? ? ? ? ? bookManager=IBookManager.Stub.asInterface(service);try {? ? ? ? ? ? ? ? bookManager.registerListener(listener);? ? ? ? ? ? }catch (RemoteException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }? ? ? ? }@OverridepublicvoidonServiceDisconnected(ComponentNamename) {? ? ? ? }? ? };privateINewBookArrivedListener listener=newINewBookArrivedListener.Stub() {@OverridepublicvoidonNewBookArrived(BooknewBook)throwsRemoteException {? ? ? ? ? ? mHandler.obtainMessage(MSG_NEW_BOOK_ARRIVED, newBook).sendToTarget();? ? ? ? }? ? };@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState) {super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.book_manager);? ? ? ? displayTextView= (TextView) findViewById(R.id.displayTextView);Intent intent=newIntent(this,BookManagerService.class);? ? ? ? bindService(intent, mServiceConn,BIND_AUTO_CREATE);? ? }publicvoidgetBookList(Viewview) {try {List list= bookManager.getBookList();Log.d(TAG,"getBookList:"+ list.toString());? ? ? ? ? ? displayTextView.setText(list.toString());? ? ? ? }catch (RemoteException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }publicvoidaddBook(Viewview) {try {? ? ? ? ? ? bookManager.addBook(newBook(3,"天龍八部"));? ? ? ? }catch (RemoteException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }@OverrideprotectedvoidonDestroy() {if (bookManager!=null&& bookManager.asBinder().isBinderAlive()) {Log.d(TAG,"unregister listener"+ listener);try {? ? ? ? ? ? ? ? bookManager.unregisterListener(listener);? ? ? ? ? ? }catch (RemoteException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }? ? ? ? }? ? ? ? unbindService(mServiceConn);super.onDestroy();
? ? }
}
publicclassBookManagerServiceextendsService {privatestaticfinalStringTAG=BookManagerService.class.getSimpleName();// CopyOnWriteArrayList 支持并發(fā)讀寫永部,實(shí)現(xiàn)自動(dòng)線程同步独泞,他不是繼承自 ArrayListprivateCopyOnWriteArrayList mBookList=newCopyOnWriteArrayList();//對(duì)象是不能跨進(jìn)程傳輸?shù)模瑢?duì)象的跨進(jìn)程傳輸本質(zhì)都是反序列化的過(guò)程扬舒,Binder 會(huì)把客戶端傳遞過(guò)來(lái)的對(duì)象重新轉(zhuǎn)化生成一個(gè)新的對(duì)象//RemoteCallbackList 是系統(tǒng)專門提供的用于刪除系統(tǒng)跨進(jìn)程 listener 的接口阐肤,利用底層的 Binder 對(duì)象是同一個(gè)//RemoteCallbackList 會(huì)在客戶端進(jìn)程終止后,自動(dòng)溢出客戶端注冊(cè)的 listener 讲坎,內(nèi)部自動(dòng)實(shí)現(xiàn)了線程同步功能孕惜。privateRemoteCallbackList mListeners=newRemoteCallbackList<>();privateAtomicBoolean isServiceDestroied=newAtomicBoolean(false);privateBinder mBinder=newIBookManager.Stub() {@OverridepublicListgetBookList()throwsRemoteException {return mBookList;? ? ? ? }@OverridepublicvoidaddBook(Bookbook)throwsRemoteException {Log.d(TAG,"addBook:"+ book.toString());? ? ? ? ? ? mBookList.add(book);? ? ? ? }@OverridepublicvoidregisterListener(INewBookArrivedListenerlistener)throwsRemoteException {? ? ? ? ? ? mListeners.register(listener);? ? ? ? }@OverridepublicvoidunregisterListener(INewBookArrivedListenerlistener)throwsRemoteException {? ? ? ? ? ? mListeners.unregister(listener);? ? ? ? }? ? };@OverridepublicvoidonCreate() {super.onCreate();? ? ? ? mBookList.add(newBook(1,"老人與海"));? ? ? ? mBookList.add(newBook(2,"哈姆雷特"));newThread(newServiceWorker()).start();? ? }privatevoidonNewBookArrived(Bookbook)throwsRemoteException {? ? ? ? mBookList.add(book);int count= mListeners.beginBroadcast();for (int i=0; i< count; i++) {INewBookArrivedListener listener= mListeners.getBroadcastItem(i);if (listener!=null) {? ? ? ? ? ? ? ? listener.onNewBookArrived(book);? ? ? ? ? ? }? ? ? ? }? ? ? ? mListeners.finishBroadcast();? ? }privateclassServiceWorkerimplementsRunnable {@Overridepublicvoidrun() {while (!isServiceDestroied.get()) {try {Thread.sleep(5000);? ? ? ? ? ? ? ? }catch (InterruptedException e) {? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }int bookId= mBookList.size()+1;Book newBook=newBook(bookId,"new book #"+ bookId);try {? ? ? ? ? ? ? ? ? ? onNewBookArrived(newBook);? ? ? ? ? ? ? ? }catch (RemoteException e) {? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }? ? }@Nullable@OverridepublicIBinderonBind(Intentintent) {//Permission 權(quán)限驗(yàn)證int check= checkCallingOrSelfPermission("com.jc.ipc.ACCESS_BOOK_SERVICE");if (check==PackageManager.PERMISSION_DENIED) {returnnull;? ? ? ? }return mBinder;? ? }@OverridepublicvoidonDestroy() {? ? ? ? isServiceDestroied.set(true);super.onDestroy();? ? }}
五、使用 ContentProvider
用于不同應(yīng)用間數(shù)據(jù)共享晨炕,和 Messenger 底層實(shí)現(xiàn)同樣是 Binder 和 AIDL衫画,系統(tǒng)做了封裝,使用簡(jiǎn)單瓮栗。 系統(tǒng)預(yù)置了許多 ContentProvider 削罩,如通訊錄、日程表费奸,需要跨進(jìn)程訪問(wèn)弥激。 使用方法:繼承 ContentProvider 類實(shí)現(xiàn) 6 個(gè)抽象方法,這六個(gè)方法均運(yùn)行在 ContentProvider 進(jìn)程中愿阐,除 onCreate 運(yùn)行在主線程里微服,其他五個(gè)方法均由外界回調(diào)運(yùn)行在 Binder 線程池中。
ContentProvider 的底層數(shù)據(jù)缨历,可以是 SQLite 數(shù)據(jù)庫(kù)以蕴,可以是文件糙麦,也可以是內(nèi)存中的數(shù)據(jù)。
詳見代碼:
publicclassBookProviderextends ContentProvider {
? ? privatestaticfinalString TAG = "BookProvider";
? ? publicstaticfinalString AUTHORITY = "com.jc.ipc.Book.Provider";
? ? publicstaticfinalUri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
? ? publicstaticfinalUri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
? ? publicstaticfinalintBOOK_URI_CODE = 0;
? ? publicstaticfinalintUSER_URI_CODE = 1;
? ? privatestaticfinalUriMatcher sUriMatcher =new UriMatcher(UriMatcher.NO_MATCH);
? ? static {
? ? ? ? sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
? ? ? ? sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
? ? }
? ? private Context mContext;
? ? private SQLiteDatabase mDB;
? ? @Override
? ? publicboolean onCreate() {
? ? ? ? mContext = getContext();
? ? ? ? initProviderData();
? ? ? ? returntrue;
? ? }
? ? privatevoid initProviderData() {
? ? ? ? //不建議在 UI 線程中執(zhí)行耗時(shí)操作mDB =new DBOpenHelper(mContext).getWritableDatabase();
? ? ? ? mDB.execSQL("delete from " + DBOpenHelper.BOOK_TABLE_NAME);
? ? ? ? mDB.execSQL("delete from " + DBOpenHelper.USER_TABLE_NAME);
? ? ? ? mDB.execSQL("insert into book values(3,'Android');");
? ? ? ? mDB.execSQL("insert into book values(4,'iOS');");
? ? ? ? mDB.execSQL("insert into book values(5,'Html5');");
? ? ? ? mDB.execSQL("insert into user values(1,'haohao',1);");
? ? ? ? mDB.execSQL("insert into user values(2,'nannan',0);");
? ? }
? ? @Nullable
? ? @Override
? ? public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
? ? ? ? Log.d(TAG, "query, current thread"+ Thread.currentThread());
? ? ? ? String table = getTableName(uri);
? ? ? ? if(table ==null) {
? ? ? ? ? ? thrownewIllegalArgumentException("Unsupported URI" + uri);
? ? ? ? }
? ? ? ? returnmDB.query(table, projection, selection, selectionArgs,null,null, sortOrder,null);
? ? }
? ? @Nullable
? ? @Override
? ? public String getType(Uri uri) {
? ? ? ? Log.d(TAG, "getType");
? ? ? ? returnnull;
? ? }
? ? @Nullable
? ? @Override
? ? public Uri insert(Uri uri, ContentValues values) {
? ? ? ? Log.d(TAG, "insert");
? ? ? ? String table = getTableName(uri);
? ? ? ? if(table ==null) {
? ? ? ? ? ? thrownewIllegalArgumentException("Unsupported URI" + uri);
? ? ? ? }
? ? ? ? mDB.insert(table, null, values);
? ? ? ? // 通知外界 ContentProvider 中的數(shù)據(jù)發(fā)生變化mContext.getContentResolver().notifyChange(uri,null);
? ? ? ? return uri;
? ? }
? ? @Override
? ? publicint delete(Uri uri, String selection, String[] selectionArgs) {
? ? ? ? Log.d(TAG, "delete");
? ? ? ? String table = getTableName(uri);
? ? ? ? if(table ==null) {
? ? ? ? ? ? thrownewIllegalArgumentException("Unsupported URI" + uri);
? ? ? ? }
? ? ? ? intcount = mDB.delete(table, selection, selectionArgs);
? ? ? ? if(count > 0) {
? ? ? ? ? ? mContext.getContentResolver().notifyChange(uri, null);
? ? ? ? }
? ? ? ? return count;
? ? }
? ? @Override
? ? publicint update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
? ? ? ? Log.d(TAG, "update");
? ? ? ? String table = getTableName(uri);
? ? ? ? if(table ==null) {
? ? ? ? ? ? thrownewIllegalArgumentException("Unsupported URI" + uri);
? ? ? ? }
? ? ? ? introw = mDB.update(table, values, selection, selectionArgs);
? ? ? ? if(row > 0) {
? ? ? ? ? ? getContext().getContentResolver().notifyChange(uri, null);
? ? ? ? }
? ? ? ? return row;
? ? }
? ? private String getTableName(Uri uri) {
? ? ? ? String tableName =null;
? ? ? ? switch (sUriMatcher.match(uri)) {
? ? ? ? ? ? case BOOK_URI_CODE:
? ? ? ? ? ? ? ? tableName = DBOpenHelper.BOOK_TABLE_NAME;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case USER_URI_CODE:
? ? ? ? ? ? ? ? tableName = DBOpenHelper.USER_TABLE_NAME;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? return tableName;
? ? }
}
publicclassDBOpenHelperextends SQLiteOpenHelper {
? ? privatestaticfinalString DB_NAME = "book_provider.db";
? ? publicstaticfinalString BOOK_TABLE_NAME = "book";
? ? publicstaticfinalString USER_TABLE_NAME = "user";
? ? privatestaticfinalintDB_VERSION = 1;
? ? privateString CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "? ? ? ? ? ? + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
? ? privateString CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS "? ? ? ? ? ? + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,"? ? ? ? ? ? + "sex INT)";
? ? public DBOpenHelper(Context context) {
? ? ? ? super(context, DB_NAME,null, DB_VERSION);
? ? }
? ? @Override
? ? publicvoid onCreate(SQLiteDatabase db) {
? ? ? ? db.execSQL(CREATE_BOOK_TABLE);
? ? ? ? db.execSQL(CREATE_USER_TABLE);
? ? }
? ? @Override
? ? publicvoidonUpgrade(SQLiteDatabase db,intoldVersion,int newVersion) {
? ? }
}
publicclassProviderActivityextends AppCompatActivity {
? ? privatestaticfinalString TAG = ProviderActivity.class.getSimpleName();
? ? private TextView displayTextView;
? ? private Handler mHandler;
? ? @Override
? ? protectedvoid onCreate(@Nullable Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_provider);
? ? ? ? displayTextView = (TextView) findViewById(R.id.displayTextView);
? ? ? ? mHandler =new Handler();
? ? ? ? getContentResolver().registerContentObserver(BookProvider.BOOK_CONTENT_URI, true,new ContentObserver(mHandler) {
? ? ? ? ? ? @Override
? ? ? ? ? ? publicboolean deliverSelfNotifications() {
? ? ? ? ? ? ? ? returnsuper.deliverSelfNotifications();
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? publicvoidonChange(boolean selfChange) {
? ? ? ? ? ? ? ? super.onChange(selfChange);
? ? ? ? ? ? }
? ? ? ? ? ? @Override
? ? ? ? ? ? publicvoidonChange(boolean selfChange, Uri uri) {
? ? ? ? ? ? ? ? Toast.makeText(ProviderActivity.this, uri.toString(), Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? super.onChange(selfChange, uri);
? ? ? ? ? ? }
? ? ? ? });
? ? }
? ? publicvoid insert(View v) {
? ? ? ? ContentValues values =new ContentValues();
? ? ? ? values.put("_id",1123);
? ? ? ? values.put("name", "三國(guó)演義");
? ? ? ? getContentResolver().insert(BookProvider.BOOK_CONTENT_URI, values);
? ? }
? ? publicvoid delete(View v) {
? ? ? ? getContentResolver().delete(BookProvider.BOOK_CONTENT_URI, "_id = 4",null);
? ? }
? ? publicvoid update(View v) {
? ? ? ? ContentValues values =new ContentValues();
? ? ? ? values.put("_id",1123);
? ? ? ? values.put("name", "三國(guó)演義新版");
? ? ? ? getContentResolver().update(BookProvider.BOOK_CONTENT_URI, values , "_id = 1123",null);
? ? }
? ? publicvoid query(View v) {
? ? ? ? Cursor bookCursor = getContentResolver().query(BookProvider.BOOK_CONTENT_URI,newString[]{"_id", "name"},null,null,null);
? ? ? ? StringBuilder sb =new StringBuilder();
? ? ? ? while (bookCursor.moveToNext()) {
? ? ? ? ? ? Book book =newBook(bookCursor.getInt(0),bookCursor.getString(1));
? ? ? ? ? ? sb.append(book.toString()).append("\n");
? ? ? ? }
? ? ? ? sb.append("--------------------------------").append("\n");
? ? ? ? bookCursor.close();
? ? ? ? Cursor userCursor = getContentResolver().query(BookProvider.USER_CONTENT_URI,newString[]{"_id", "name", "sex"},null,null,null);
? ? ? ? while (userCursor.moveToNext()) {
? ? ? ? ? ? sb.append(userCursor.getInt(0))
? ? ? ? ? ? ? ? ? ? .append(userCursor.getString(1)).append(" ,")
? ? ? ? ? ? ? ? ? ? .append(userCursor.getInt(2)).append(" ,")
? ? ? ? ? ? ? ? ? ? .append("\n");
? ? ? ? }
? ? ? ? sb.append("--------------------------------");
? ? ? ? userCursor.close();
? ? ? ? displayTextView.setText(sb.toString());
? ? }
}
六丛肮、使用 Socket
Socket起源于 Unix赡磅,而 Unix 基本哲學(xué)之一就是“一切皆文件”,都可以用“打開 open –讀寫 write/read –關(guān)閉 close ”模式來(lái)操作宝与。Socket 就是該模式的一個(gè)實(shí)現(xiàn)焚廊,網(wǎng)絡(luò)的 Socket 數(shù)據(jù)傳輸是一種特殊的 I/O,Socket 也是一種文件描述符伴鳖。Socket 也具有一個(gè)類似于打開文件的函數(shù)調(diào)用: Socket()节值,該函數(shù)返回一個(gè)整型的Socket 描述符,隨后的連接建立榜聂、數(shù)據(jù)傳輸?shù)炔僮鞫际峭ㄟ^(guò)該 Socket 實(shí)現(xiàn)的。
常用的 Socket 類型有兩種:流式 Socket(SOCK_STREAM)和數(shù)據(jù)報(bào)式 Socket(SOCK_DGRAM)嗓蘑。流式是一種面向連接的 Socket须肆,針對(duì)于面向連接的 TCP 服務(wù)應(yīng)用;數(shù)據(jù)報(bào)式 Socket 是一種無(wú)連接的 Socket 桩皿,對(duì)應(yīng)于無(wú)連接的 UDP 服務(wù)應(yīng)用豌汇。
Socket 本身可以傳輸任意字節(jié)流。
談到Socket泄隔,就必須要說(shuō)一說(shuō) TCP/IP 五層網(wǎng)絡(luò)模型:
應(yīng)用層:規(guī)定應(yīng)用程序的數(shù)據(jù)格式拒贱,主要的協(xié)議 HTTP,F(xiàn)TP佛嬉,WebSocket逻澳,POP3 等;
傳輸層:建立“端口到端口” 的通信暖呕,主要的協(xié)議:TCP斜做,UDP;
網(wǎng)絡(luò)層:建立”主機(jī)到主機(jī)”的通信湾揽,主要的協(xié)議:IP瓤逼,ARP ,IP 協(xié)議的主要作用:一個(gè)是為每一臺(tái)計(jì)算機(jī)分配 IP 地址库物,另一個(gè)是確定哪些地址在同一子網(wǎng)霸旗;
數(shù)據(jù)鏈路層:確定電信號(hào)的分組方式,主要的協(xié)議:以太網(wǎng)協(xié)議戚揭;
物理層:負(fù)責(zé)電信號(hào)的傳輸诱告。
Socket 是連接應(yīng)用層與傳輸層之間接口(API)。
只實(shí)現(xiàn) TCP Socket 毫目。
Client 端代碼:
publicclassTCPClientActivityextendsAppCompatActivityimplements View.OnClickListener{
? ? privatestaticfinalString TAG = "TCPClientActivity";
? ? publicstaticfinalintMSG_RECEIVED = 0x10;
? ? publicstaticfinalintMSG_READY = 0x11;
? ? private EditText editText;
? ? private TextView textView;
? ? private PrintWriter mPrintWriter;
? ? private Socket mClientSocket;
? ? private Button sendBtn;
? ? private StringBuilder stringBuilder;
? ? privateHandler mHandler =new Handler(){
? ? ? ? @Override
? ? ? ? publicvoid handleMessage(Message msg) {
? ? ? ? ? ? switch (msg.what) {
? ? ? ? ? ? ? ? case MSG_READY:
? ? ? ? ? ? ? ? ? ? sendBtn.setEnabled(true);
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? case MSG_RECEIVED:
? ? ? ? ? ? ? ? ? ? stringBuilder.append(msg.obj).append("\n");
? ? ? ? ? ? ? ? ? ? textView.setText(stringBuilder.toString());
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? ? ? super.handleMessage(msg);
? ? ? ? ? ? }
? ? }
? ? };
? ? @Override
? ? protectedvoid onCreate(@Nullable Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.tcp_client_activity);
? ? ? ? editText = (EditText) findViewById(R.id.editText);
? ? ? ? textView = (TextView) findViewById(R.id.displayTextView);
? ? ? ? sendBtn = (Button) findViewById(R.id.sendBtn);
? ? ? ? sendBtn.setOnClickListener(this);
? ? ? ? sendBtn.setEnabled(false);
? ? ? ? stringBuilder =new StringBuilder();
? ? ? ? Intent intent =newIntent(TCPClientActivity.this, TCPServerService.class);
? ? ? ? startService(intent);
? ? ? ? new Thread(){
? ? ? ? ? ? @Override
? ? ? ? ? ? publicvoid run() {
? ? ? ? ? ? ? ? connectTcpServer();
? ? ? ? ? ? }
? ? ? ? }.start();
? ? }
? ? privateString formatDateTime(long time) {
? ? ? ? returnnewSimpleDateFormat("(HH:mm:ss)").format(new Date(time));
? ? }
? ? privatevoid connectTcpServer() {
? ? ? ? Socket socket =null;
? ? ? ? while(socket ==null) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? socket =newSocket("localhost", 8888);
? ? ? ? ? ? ? ? mClientSocket = socket;
? ? ? ? ? ? ? ? mPrintWriter =newPrintWriter(new BufferedWriter(
? ? ? ? ? ? ? ? ? ? ? ? new OutputStreamWriter(socket.getOutputStream())
? ? ? ? ? ? ? ? ), true);
? ? ? ? ? ? ? ? mHandler.sendEmptyMessage(MSG_READY);
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // receive messageBufferedReader bufferedReader =null;
? ? ? ? try {
? ? ? ? ? ? bufferedReader =newBufferedReader(new InputStreamReader(socket.getInputStream()));
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? while(!isFinishing()) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? String msg = bufferedReader.readLine();
? ? ? ? ? ? ? ? if(msg !=null) {
? ? ? ? ? ? ? ? ? ? String time = formatDateTime(System.currentTimeMillis());
? ? ? ? ? ? ? ? ? ? String showedMsg = "server " + time + ":" + msg
? ? ? ? ? ? ? ? ? ? ? ? ? ? + "\n";
? ? ? ? ? ? ? ? ? ? mHandler.obtainMessage(MSG_RECEIVED, showedMsg).sendToTarget();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? @Override
? ? publicvoid onClick(View v) {
? ? ? ? if(mPrintWriter !=null) {
? ? ? ? ? ? String msg = editText.getText().toString();
? ? ? ? ? ? mPrintWriter.println(msg);
? ? ? ? ? ? editText.setText("");
? ? ? ? ? ? String time = formatDateTime(System.currentTimeMillis());
? ? ? ? ? ? String showedMsg = "self " + time + ":" + msg + "\n";
? ? ? ? ? ? stringBuilder.append(showedMsg);
? ? ? ? }
? ? }
? ? @Override
? ? protectedvoid onDestroy() {
? ? ? ? if(mClientSocket !=null) {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? mClientSocket.shutdownInput();
? ? ? ? ? ? ? ? mClientSocket.close();
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? super.onDestroy();
? ? }
}
Server端代碼:
publicclassTCPServerServiceextends Service {
? ? privatestaticfinalString TAG = "TCPServerService";
? ? privatebooleanisServiceDestroyed =false;
? ? privateString[] mMessages =new String[]{
? ? ? ? ? ? "Hello! Body!",
? ? ? ? ? ? "用戶不在線蔬啡!請(qǐng)稍后再聯(lián)系诲侮!",
? ? ? ? ? ? "請(qǐng)問(wèn)你叫什么名字呀?",
? ? ? ? ? ? "厲害了箱蟆,我的哥沟绪!",
? ? ? ? ? ? "Google 不需要***是真的嗎?",
? ? ? ? ? ? "扎心了空猜,老鐵U来取!辈毯!"? ? };
? ? @Override
? ? publicvoid onCreate() {
? ? ? ? newThread(new TCPServer()).start();
? ? ? ? super.onCreate();
? ? }
? ? @Override
? ? publicvoid onDestroy() {
? ? ? ? isServiceDestroyed =true;
? ? ? ? super.onDestroy();
? ? }
? ? @Nullable
? ? @Override
? ? public IBinder onBind(Intent intent) {
? ? ? ? returnnull;
? ? }
? ? privateclassTCPServerimplements Runnable {
? ? ? ? @Override
? ? ? ? publicvoid run() {
? ? ? ? ? ? ServerSocket serverSocket =null;
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? serverSocket =newServerSocket(8888);
? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? while(!isServiceDestroyed) {
? ? ? ? ? ? ? ? // receive request from clienttry {
? ? ? ? ? ? ? ? ? ? finalSocket client = serverSocket.accept();
? ? ? ? ? ? ? ? ? ? Log.d(TAG, "=============== accept ==================");
? ? ? ? ? ? ? ? ? ? new Thread(){
? ? ? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ? ? ? publicvoid run() {
? ? ? ? ? ? ? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? responseClient(client);
? ? ? ? ? ? ? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }.start();
? ? ? ? ? ? ? ? } catch (IOException e) {
? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? privatevoidresponseClient(Socket client)throws IOException {
? ? ? ? //receive messageBufferedReader in =new BufferedReader(
? ? ? ? ? ? ? ? new InputStreamReader(client.getInputStream()));
? ? ? ? //send messagePrintWriter out =new PrintWriter(
? ? ? ? ? ? ? ? new BufferedWriter(
? ? ? ? ? ? ? ? ? ? ? ? new OutputStreamWriter(
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? client.getOutputStream())),true);
? ? ? ? out.println("歡迎來(lái)到聊天室坝疼!");
? ? ? ? while(!isServiceDestroyed) {
? ? ? ? ? ? String str = in.readLine();
? ? ? ? ? ? Log.d(TAG, "message from client: " + str);
? ? ? ? ? ? if(str ==null) {
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? Random random =new Random();
? ? ? ? ? ? intindex = random.nextInt(mMessages.length);
? ? ? ? ? ? String msg = mMessages[index];
? ? ? ? ? ? out.println(msg);
? ? ? ? ? ? Log.d(TAG, "send Message: " + msg);
? ? ? ? }
? ? ? ? out.close();
? ? ? ? in.close();
? ? ? ? client.close();
? ? }
}