偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Android的多進(jìn)程通訊-深入了解

移動(dòng)開發(fā) Android
Android系統(tǒng)對(duì)每個(gè)應(yīng)用進(jìn)程的內(nèi)存占用是有限制的,而且占用內(nèi)存越大的進(jìn)程,通常被系統(tǒng)殺死的可能性越大。讓一個(gè)組件運(yùn)行在單獨(dú)的進(jìn)程中,可以減少主進(jìn)程所占用的內(nèi)存,避免OOM問題,降低被系統(tǒng)殺死的概率。

[[405775]]

一、多進(jìn)程是什么?

正常情況下,在Android系統(tǒng)中一個(gè)APP啟動(dòng)后只會(huì)運(yùn)行在一個(gè)進(jìn)程中,其進(jìn)程名為apk的包名,所有的組件都會(huì)在這個(gè)進(jìn)程中運(yùn)行;

但是如果需要將某些組件(如Service,Activity等)運(yùn)行在單獨(dú)的進(jìn)程中,就需要用到android:process屬性了。

我們可以給android的組件設(shè)置android:process屬性來使其運(yùn)行在指定的進(jìn)程中。

1、多進(jìn)程優(yōu)點(diǎn):

①使用更多的內(nèi)存

Android系統(tǒng)對(duì)每個(gè)應(yīng)用進(jìn)程的內(nèi)存占用是有限制的,而且占用內(nèi)存越大的進(jìn)程,通常被系統(tǒng)殺死的可能性越大。讓一個(gè)組件運(yùn)行在單獨(dú)的進(jìn)程中,可以減少主進(jìn)程所占用的內(nèi)存,避免OOM問題,降低被系統(tǒng)殺死的概率。

②實(shí)現(xiàn)多模塊

比如微信的小程序(一個(gè)住進(jìn)程,一個(gè)小程序進(jìn)程)、支付寶的小程序;

當(dāng)你啟動(dòng)一個(gè)小程序時(shí)候,就會(huì)啟動(dòng)小程序的進(jìn)程,不占用主進(jìn)程的內(nèi)存,使小程序進(jìn)程單獨(dú)出來,速度會(huì)快很多。

③子進(jìn)程奔潰,主進(jìn)程可以繼續(xù)工作

④主進(jìn)程退出,子進(jìn)程可以繼續(xù)工作

⑤實(shí)現(xiàn)守護(hù)進(jìn)程

通過JNI利用C/C++,調(diào)用fork()方法來生成子進(jìn)程,一般開發(fā)者會(huì)利用這種方法來做一些daemon(守護(hù)進(jìn)程)進(jìn)程,來實(shí)現(xiàn)防殺?;畹刃Ч?。

2、多進(jìn)程引起的問題

①靜態(tài)成員和單例模式失效

②線程同步機(jī)制失效

③SharedPreferences 可靠性降低

④Application 被多次創(chuàng)建

⑤多進(jìn)程交互麻煩

引起的原因:

Android中,系統(tǒng)會(huì)為每個(gè)應(yīng)用或進(jìn)程分配獨(dú)立的虛擬機(jī),不同的虛擬機(jī)自然占有不同的內(nèi)存地址空間,

所以同一個(gè)類的對(duì)象會(huì)產(chǎn)生不同的副本,導(dǎo)致共享數(shù)據(jù)失敗,必然也不能實(shí)現(xiàn)線程的同步。

二、多進(jìn)程通信方式

Android中支持的多進(jìn)程通信方式主要有以下幾種,它們之間各有優(yōu)缺點(diǎn),可根據(jù)使用場景選擇選擇:

  • AIDL:功能強(qiáng)大,支持進(jìn)程間一對(duì)多的實(shí)時(shí)并發(fā)通信,并可實(shí)現(xiàn) RPC (遠(yuǎn)程過程調(diào)用)。
  • Messenger:支持一對(duì)多的串行實(shí)時(shí)通信, AIDL 的簡化版本。
  • Bundle:四大組件的進(jìn)程通信方式,只能傳輸 Bundle 支持的數(shù)據(jù)類型。
  • ContentProvider:強(qiáng)大的數(shù)據(jù)源訪問支持,主要支持 CRUD 操作,一對(duì)多的進(jìn)程間數(shù)據(jù)共享,例如我們的應(yīng)用訪問系統(tǒng)的通訊錄數(shù)據(jù)。
  • BroadcastReceiver:即廣播

廣播是一種被動(dòng)跨進(jìn)程通訊的方式。當(dāng)某個(gè)程序向系統(tǒng)發(fā)送廣播時(shí),其他的應(yīng)用程序只能被動(dòng)地接收廣播數(shù)據(jù)。

這就象電臺(tái)進(jìn)行廣播一樣,聽眾只能被動(dòng)地收聽,而不能主動(dòng)與電臺(tái)進(jìn)行溝通,在應(yīng)用程序中發(fā)送廣播比較簡單。

只需要調(diào)用sendBroadcast方法即可。該方法需要一個(gè)Intent對(duì)象。通過Intent對(duì)象可以發(fā)送需要廣播的數(shù)據(jù)

  • 文件共享:在非高并發(fā)情況下共享簡單的數(shù)據(jù)。
  • Socket:通過網(wǎng)絡(luò)傳輸數(shù)據(jù)。

這次介紹日常開發(fā)APP中常用的交互方式AIDL和Messenger

如圖

1、多進(jìn)程AIDL通信

AIDL是Android接口定義語言是用于定義服務(wù)器和客戶端通信接口的一種描述語言,可以使用它定義客戶端與服務(wù)端進(jìn)程間通信(IPC)的編程接口,

在 Android 中,進(jìn)程之間無法共享內(nèi)存(用戶空間),不同進(jìn)程之間的通信一般使用 AIDL 來處理。

使用簡單步驟:

創(chuàng)建一個(gè)AIDL文件(擴(kuò)展名為.aidl);

暴露一個(gè)接口給客戶端(通過建立一個(gè)Service,在onBind()方法中返回一個(gè)Stub類的實(shí)例);

服務(wù)端實(shí)現(xiàn)該AIDL文件生成的Java接口(系統(tǒng)會(huì)自動(dòng)生成對(duì)應(yīng)的Java接口);

客戶端連接綁定該遠(yuǎn)程服務(wù)。

①創(chuàng)建AIDL文件

創(chuàng)建一個(gè) AIDL 文件,聲明服務(wù)端要暴露給客戶端的接口,然后創(chuàng)建一個(gè) Service 監(jiān)聽客戶端的連接請(qǐng)求,并在其中實(shí)現(xiàn) AIDL 文件中的接口

注意,為了方便開發(fā),我們一般把 AIDL 相關(guān)的文件放在同一包中,這樣當(dāng)客戶端是另一個(gè)應(yīng)用時(shí)可方便的把整個(gè)包復(fù)制到客戶端工程中

首先了解下 AIDL 文件支持的幾種數(shù)據(jù)類型:

  • 基本數(shù)據(jù)類型
  • String、CharSequence
  • ArrayList、HashMap,其內(nèi)部元素也需要被AIDL支持
  • 實(shí)現(xiàn)了 Parcelable 接口的對(duì)象
  • AIDL 類型的接口,非普通接口

Book是實(shí)現(xiàn)了Parcelable的類,只定義了name字段,按照規(guī)定如果 AIDL 文件用到了自定義Parcelable對(duì)象,同時(shí)需要提供一個(gè)Book.aidl文件

  1. package com.json; 
  2.  
  3.  
  4. parcelable Book; 

②ILibraryManager.aidl定義了服務(wù)端要暴露給客戶端的接口:

  1. package com.json; 
  2.  
  3. import com.json.Book; 
  4.  
  5. interface ILibraryManager{ 
  6.     // 近期新書查詢 
  7.     List<Book> getNewBookList(); 
  8.     // 圖書捐贈(zèng) 
  9.     void donateBook(in Book book); 

③接下來就是AIDL文件生成的Java接口,在編寫服務(wù)類前要先編譯項(xiàng)目,這樣在服務(wù)類里使用 AIDL 生成的java類

首先通過ILibraryManager.Stub()創(chuàng)建一個(gè)mBinder對(duì)象,并實(shí)現(xiàn)了ILibraryManager.aidl中定義的接口方法,在onBind()方法中返回創(chuàng)建的mBinder,并在服務(wù)onCreate()添加數(shù)據(jù)。

最后在 AndroidManifest.xml 注冊(cè)服務(wù):

  1. <service 
  2.     android:name=".ManagerService" 
  3.     android:process=":remote"
  4. </service> 

  1. public class ManagerService extends Service { 
  2.  
  3.     private static final String TAG = "ManagerService"
  4.  
  5.     // CopyOnWriteArrayList 支持并發(fā)讀寫 
  6.     private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); 
  7.  
  8.     private Binder mBinder = new ILibraryManager.Stub() { 
  9.  
  10.         @Override 
  11.         public List<Book> getNewBookList() throws RemoteException { 
  12.             return mBookList; 
  13.         } 
  14.  
  15.         @Override 
  16.         public void donateBook(Book book) throws RemoteException { 
  17.             mBookList.add(book); 
  18.         } 
  19.     }; 
  20.  
  21.     public LibraryManagerService() { 
  22.     } 
  23.  
  24.     @Override 
  25.     public IBinder onBind(Intent intent) { 
  26.         return mBinder; 
  27.     } 
  28.  
  29.     @Override 
  30.     public void onCreate() { 
  31.         super.onCreate(); 
  32.         mBookList.add(new Book("book0")); 
  33.         mBookList.add(new Book("book1")); 
  34.     } 

④客戶端連接綁定該遠(yuǎn)程服務(wù)

先實(shí)現(xiàn)ServiceConnection接口,在onServiceConnected()方法中將IBinder對(duì)象轉(zhuǎn)換成ILibraryManager對(duì)象,通過該對(duì)象就能調(diào)用ILibraryManager.aidl中聲明的方法了。

接下來綁定服務(wù):

  1. public class AIDLActivity extends AppCompatActivity { 
  2.     private static final String TAG = "AIDLActivity"
  3.    ILibraryManager libraryManager=null
  4.     private ServiceConnection mServiceConnection = new ServiceConnection() { 
  5.         @Override 
  6.         public void onServiceConnected(ComponentName name, IBinder service) { 
  7.              libraryManager = ILibraryManager.Stub.asInterface(service); 
  8.             try { 
  9.                 // test1 
  10.                 List<Book> books = libraryManager.getNewBookList(); 
  11.                 Log.e(TAG, "books:" + books.toString()); 
  12.                 // test2 
  13.                 libraryManager.donateBook(new Book("book" + books.size())); 
  14.                 List<Book> books2 = libraryManager.getNewBookList(); 
  15.                 Log.e(TAG, "books:" + books2.toString()); 
  16.             } catch (RemoteException e) { 
  17.                 e.printStackTrace(); 
  18.             } 
  19.         } 
  20.  
  21.         @Override 
  22.         public void onServiceDisconnected(ComponentName name) { 
  23.  
  24.         } 
  25.     }; 
  26.  
  27.      void bindAidlData(){ 
  28.       if(null==libraryManager){ 
  29.     return
  30.     } 
  31.         try { 
  32.                 // test1 
  33.                 List<Book> books = libraryManager.getNewBookList(); 
  34.                 Log.e(TAG, "books:" + books.toString()); 
  35.                 // test2 
  36.                 libraryManager.donateBook(new Book("book" + books.size())); 
  37.                 List<Book> books2 = libraryManager.getNewBookList(); 
  38.                 Log.e(TAG, "books:" + books2.toString()); 
  39.             } catch (RemoteException e) { 
  40.                 e.printStackTrace(); 
  41.             } 
  42.    } 
  43.     @Override 
  44.     protected void onCreate(Bundle savedInstanceState) { 
  45.         super.onCreate(savedInstanceState); 
  46.         setContentView(R.layout.activity_aidl); 
  47.         bindNewService(); 
  48.     } 
  49.  
  50.     private void bindNewService() { 
  51.         Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class); 
  52.         bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); 
  53.     } 
  54.  
  55.     @Override 
  56.     protected void onDestroy() { 
  57.         unbindService(mServiceConnection); 
  58.         super.onDestroy(); 
  59.     } 

2、aidl的通知功能

首先創(chuàng)建一個(gè)服務(wù)端通知客戶端的 IOnNewBookArrivedListener.aidl 接口:

  1. package com.json; 
  2.  
  3.  
  4. import com.json.Book; 
  5.  
  6.  
  7. interface IOnNewBookArrivedListener { 
  8.     void onNewBookArrived(in Book book); 

服務(wù)端要先注冊(cè)后才能收到通知,同時(shí)也可以取消注冊(cè),所以要給之前的ILibraryManager.aidl添加連個(gè)方法了:

  1. interface ILibraryManager{ 
  2.     ...... 
  3.     // 注冊(cè)通知 
  4.     void register(IOnNewBookArrivedListener listener); 
  5.     // 取消注冊(cè) 
  6.     void unregister(IOnNewBookArrivedListener listener); 

修改ManagerService:

  1. public class ManagerService extends Service { 
  2.     ...... 
  3.   ...... 
  4.   ...... 
  5.     // 系統(tǒng)提供的專門用于保存、刪除跨進(jìn)程 listener 的類 
  6.     private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>(); 
  7.     // AtomicBoolean 支持并發(fā)讀寫 
  8.     private AtomicBoolean mIsServiceDestroy = new AtomicBoolean(false); 
  9.  
  10.     private Binder mBinder = new ILibraryManager.Stub() { 
  11.         ...... 
  12.         @Override 
  13.         public void register(IOnNewBookArrivedListener listener) throws RemoteException { 
  14.             mListenerList.register(listener); 
  15.             Log.e(TAG, "register success"); 
  16.         } 
  17.  
  18.         @Override 
  19.         public void unregister(IOnNewBookArrivedListener listener) throws RemoteException { 
  20.             mListenerList.unregister(listener); 
  21.             Log.e(TAG, "unregister success"); 
  22.         } 
  23.     }; 
  24.  
  25.    ....... 
  26.  
  27.     @Override 
  28.     public void onCreate() { 
  29.         super.onCreate(); 
  30.         ...... 
  31.        
  32.         new Thread(new Runnable() { 
  33.             @Override 
  34.             public void run() { 
  35.                 // 如果服務(wù)還沒終止 
  36.                 while (!mIsServiceDestroy.get()) { 
  37.                     try { 
  38.                         Thread.sleep(10 * 1000); 
  39.                     } catch (InterruptedException e) { 
  40.                         e.printStackTrace(); 
  41.                     } 
  42.  
  43.                     Book book = new Book("book" + mBookList.size()); 
  44.                     mBookList.add(book); 
  45.                     bookArrivedNotify(book); 
  46.                 } 
  47.             } 
  48.         }).start(); 
  49.     } 
  50.      
  51.     private void bookArrivedNotify(Book book) { 
  52.         int n = mListenerList.beginBroadcast(); 
  53.         for (int i = 0; i < n; i++) { 
  54.             IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i); 
  55.             if (listener != null) { 
  56.                 try { 
  57.                     listener.onNewBookArrived(book); 
  58.                 } catch (RemoteException e) { 
  59.                     e.printStackTrace(); 
  60.                 } 
  61.             } 
  62.         } 
  63.         mListenerList.finishBroadcast(); 
  64.     } 
  65.  
  66.     @Override 
  67.     public void onDestroy() { 
  68.         super.onDestroy(); 
  69.         mIsServiceDestroy.set(true); 
  70.     } 

這里用到了RemoteCallbackList類,它是系統(tǒng)提供的專門用于刪除跨進(jìn)程 listener 的類,用普通的集合難以保證客戶端注冊(cè)的 listener 和服務(wù)端存儲(chǔ)的 listener 是同一個(gè),會(huì)取消注冊(cè)失敗

客戶端代碼修改

  1. public class AIDLActivity extends AppCompatActivity { 
  2.     ...... 
  3.     private ILibraryManager mLibraryManager; 
  4.  
  5.     private Handler mHandler = new Handler(new Handler.Callback() { 
  6.         @Override 
  7.         public boolean handleMessage(Message msg) { 
  8.             switch (msg.what) { 
  9.                 case MESSAGE_NEW_BOOK_ARRIVED: 
  10.                     Log.e(TAG, "new book:" + msg.obj); 
  11.                     break; 
  12.             } 
  13.             return true
  14.         } 
  15.     }); 
  16.  
  17.     private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() { 
  18.         @Override 
  19.         public void onNewBookArrived(Book book) throws RemoteException { 
  20.             // 由于 onNewBookArrived 方法在子線程被調(diào)用,所以通過Handler切換到UI線程,方便UI操作 
  21.             mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget(); 
  22.         } 
  23.     }; 
  24.  
  25.     private ServiceConnection mServiceConnection = new ServiceConnection() { 
  26.         @Override 
  27.         public void onServiceConnected(ComponentName name, IBinder service) { 
  28.             ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service); 
  29.             mLibraryManager = libraryManager; 
  30.             try { 
  31.                 ...... 
  32.                 // 注冊(cè)通知 
  33.                 libraryManager.register(listener); 
  34.             } catch (RemoteException e) { 
  35.                 e.printStackTrace(); 
  36.             } 
  37.         } 
  38.         ...... 
  39.     }; 
  40.  
  41.     @Override 
  42.     protected void onCreate(Bundle savedInstanceState) { 
  43.         super.onCreate(savedInstanceState); 
  44.         setContentView(R.layout.activity_aidl); 
  45.         bindNewService(); 
  46.     } 
  47.  
  48.     @Override 
  49.     protected void onDestroy() { 
  50.         unbindService(mServiceConnection); 
  51.         if (mLibraryManager != null && mLibraryManager.asBinder().isBinderAlive()) { 
  52.             try { 
  53.                 // 取消注冊(cè) 
  54.                 mLibraryManager.unregister(listener); 
  55.             } catch (RemoteException e) { 
  56.                 e.printStackTrace(); 
  57.             } 
  58.         } 
  59.         super.onDestroy(); 
  60.     } 

3、Messenger通訊

Messenger 是一種輕量級(jí)的多進(jìn)程通信方式,它是在 AIDL 的基礎(chǔ)上封裝而成的,可以看做是 AIDL 的簡化版,支持一對(duì)多的串行實(shí)時(shí)通信,

一次只處理一個(gè)請(qǐng)求,不存在并發(fā)的問題。和 AIDL 的使用類似,但要簡單的多,同樣需要實(shí)現(xiàn)服務(wù)端和客戶端。

首先來看服務(wù)端

  1. public class MessengerService extends Service { 
  2.     private static final String TAG = "MessengerService"
  3.     // 將Messenger和Handler關(guān)聯(lián)起來 
  4.     private Messenger mServiceMessenger = new Messenger(new MessengerHandler()); 
  5.  
  6.     public MessengerService() { 
  7.     } 
  8.  
  9.     @Override 
  10.     public IBinder onBind(Intent intent) { 
  11.         return mServiceMessenger.getBinder(); 
  12.     } 
  13.  
  14.     private static class MessengerHandler extends Handler { 
  15.         @Override 
  16.         public void handleMessage(Message msg) { 
  17.             super.handleMessage(msg); 
  18.             switch (msg.what) { 
  19.                 case MessengerActivity.MESSAGE_FROM_CLIENT: 
  20.                     // 打印接收到的客戶端消息 
  21.                     Log.e(TAG, "receive message from client:" + msg.getData().getString("msg")); 
  22.                     // 給客戶端回復(fù)一條消息 
  23.                     Messenger clientMessenger = msg.replyTo; 
  24.                     Message message = Message.obtain(); 
  25.                     message.what = MessengerActivity.MESSAGE_FROM_SERVICE; 
  26.                     Bundle bundle = new Bundle(); 
  27.                     bundle.putString("msg""I am fine,thank you!"); 
  28.                     message.setData(bundle); 
  29.                     try { 
  30.                         clientMessenger.send(message); 
  31.                     } catch (RemoteException e) { 
  32.                         e.printStackTrace(); 
  33.                     } 
  34.                     break; 
  35.             } 
  36.         } 
  37.     } 

看客戶端的實(shí)現(xiàn)

  1. public class MessengerActivity extends AppCompatActivity { 
  2.     private static final String TAG = "MessengerActivity"
  3.  
  4.     public static final int MESSAGE_FROM_CLIENT = 1; 
  5.     public static final int MESSAGE_FROM_SERVICE = 2; 
  6.  
  7.     private Messenger mServiceMessenger; 
  8.  
  9.     private Messenger mClientMessenger = new Messenger(new MessengerHandler()); 
  10.  
  11.     private ServiceConnection mServiceConnection = new ServiceConnection() { 
  12.         @Override 
  13.         public void onServiceConnected(ComponentName name, IBinder service) { 
  14.             mServiceMessenger = new Messenger(service); 
  15.             Message message = Message.obtain(); 
  16.             message.what = MESSAGE_FROM_CLIENT; 
  17.             Bundle bundle = new Bundle(); 
  18.             bundle.putString("msg""how are you?"); 
  19.             message.setData(bundle); 
  20.             // 傳遞服務(wù)端回復(fù)客戶端時(shí)需要使用的Messenger 
  21.             message.replyTo = mClientMessenger; 
  22.             try { 
  23.                 mServiceMessenger.send(message); 
  24.             } catch (RemoteException e) { 
  25.                 e.printStackTrace(); 
  26.             } 
  27.         } 
  28.  
  29.         @Override 
  30.         public void onServiceDisconnected(ComponentName name) { 
  31.  
  32.         } 
  33.     }; 
  34.  
  35.     @Override 
  36.     protected void onCreate(Bundle savedInstanceState) { 
  37.         super.onCreate(savedInstanceState); 
  38.         setContentView(R.layout.activity_messenger); 
  39.  
  40.         Intent intent = new Intent(MessengerActivity.this, MessengerService.class); 
  41.         bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); 
  42.     } 
  43.  
  44.     @Override 
  45.     protected void onDestroy() { 
  46.         unbindService(mServiceConnection); 
  47.         super.onDestroy(); 
  48.     } 
  49.  
  50.     private static class MessengerHandler extends Handler { 
  51.         @Override 
  52.         public void handleMessage(Message msg) { 
  53.             super.handleMessage(msg); 
  54.             switch (msg.what) { 
  55.                 case MessengerActivity.MESSAGE_FROM_SERVICE: 
  56.                     Log.e(TAG, "receive message from service:" + msg.getData().getString("msg")); 
  57.                     break; 
  58.             } 
  59.         } 
  60.     } 

在onServiceConnected()中,將服務(wù)端的Binder轉(zhuǎn)換成服務(wù)端的Messenger對(duì)象,然后發(fā)送消息,由于服務(wù)端還需要給客服端回復(fù)消息,所以需要在客戶端創(chuàng)建一個(gè)Messenger對(duì)象附加在消息上發(fā)送給服務(wù)端使用

多進(jìn)程總結(jié):

1、多進(jìn)程app可以在系統(tǒng)中申請(qǐng)多份內(nèi)存,但應(yīng)合理使用,建議把一些高消耗但不常用的模塊放到獨(dú)立的進(jìn)程,不使用的進(jìn)程可及時(shí)手動(dòng)關(guān)閉;

2、多進(jìn)程占用內(nèi)存多,耗電量會(huì)增加,這個(gè)要注意;

3、每個(gè)進(jìn)程在創(chuàng)建的時(shí)候,都會(huì)執(zhí)行Application的onCreate進(jìn)行初始化,如果這時(shí)候沒有針對(duì)不同進(jìn)程處理,onCreate的初始化業(yè)務(wù)會(huì)被多次執(zhí)行,這是沒有必要的而且多次初始化容易引起問題,所以需根據(jù)不同進(jìn)程初始化相應(yīng)的業(yè)務(wù)。

本文轉(zhuǎn)載自微信公眾號(hào)「 Android開發(fā)編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 Android開發(fā)編程公眾號(hào)。

 

責(zé)任編輯:姜華 來源: Android開發(fā)編程
相關(guān)推薦

2010-06-23 20:31:54

2010-07-13 09:36:25

2010-11-19 16:22:14

Oracle事務(wù)

2020-09-21 09:53:04

FlexCSS開發(fā)

2009-08-25 16:27:10

Mscomm控件

2022-08-26 13:48:40

EPUBLinux

2020-07-20 06:35:55

BashLinux

2019-08-02 08:59:21

Token認(rèn)證服務(wù)器

2019-11-29 16:21:22

Spring框架集成

2017-01-20 08:30:19

JavaScriptfor循環(huán)

2018-02-24 13:21:02

2018-09-04 16:20:46

MySQ索引數(shù)據(jù)結(jié)構(gòu)

2013-04-10 11:16:19

iPad的MouseE

2016-10-20 08:46:17

2021-09-03 08:27:47

FortinetSASE平臺(tái)安全

2011-07-18 15:08:34

2022-06-03 10:09:32

威脅檢測(cè)軟件

2010-11-15 11:40:44

Oracle表空間

2018-06-22 13:05:02

前端JavaScript引擎

2021-01-19 12:00:39

前端監(jiān)控代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)