<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 189,comments - 115,trackbacks - 0

    Android進程與線程

    當某個組件第一次運行的時候,Android啟動了一個進程。默認的,所有的組件和程序運行在這個進程和線程中。 
    也可以安排組件在其他的進程或者線程中運行 
    進程組件運行的進程由manifest file控制。組件的節點 — <activity>, <service>, <receiver>, 和 <provider> — 都包含一個 process 屬性。 這個屬性可以設置組件運行的進程:可以配置組件在一個獨立進程運行,或者多個組件在同一個進程運行。甚至可以多個程序在一個進程中運行——如果這些程序共享一個User ID并給定同樣的權限。<application> 節點也包含 process 屬性 ,用來設置程序中所有組件的默認進程。 
    所有的組件在此進程的主線程中實例 ,系統對這些組件的調用從主線程中分離。并非每個對象都會從主線程中分離。一般來說,響應例如View.onKeyDown()用戶操作的方法和通知的方法也在主線程中運行。這就表示,組件被系統調用的時候不應該長時間運行或者阻塞操作(如網絡操作或者計算大量數據),因為這樣會阻塞進程中的其他組件 。可以把這類操作從主線程中分離。 
    當更加常用的進程無法獲取足夠內存,Android可能會關閉不常用的進程。下次啟動程序的時候會重新啟動進程。 
    當決定哪個進程需要被關閉的時候, Android會考慮哪個對用戶更加有用。如Android會傾向于關閉一個長期不顯示在界面的進程來支持一個經常顯示在界面的進程。是否關閉一個進程決定于組件在進程中的狀態,參見后面的章節Component Lifecycles. 
    線程即使為組件分配了不同的進程,有時候也需要再分配線程。比如用戶界面需要很快對用戶進行響應,因此某些費時的操作,如網絡連接、下載或者非常占用服務器時間的操作應該放到其他線程。 
    線程通過java的標準對象Thread 創建. Android 提供了很多方便的管理線程的方法:— Looper 在線程中運行一個消息循環; Handler 傳遞一個消息; HandlerThread 創建一個帶有消息循環的線程。 
    遠程調用Remote procedure callsAndroid有一個遠程調用(RPCs) 的輕量級機制— 通過這個機制,方法可以在本地調用,在遠程執行(在其他進程執行),還可以返回一個值。要實現這個需求,必須分解方法調用,并且所有要傳遞的數據必須是操作系統可以訪問的級別。從本地的進程和內存地址傳送到遠程的進程和內存地址并在遠程處理和返回。返回值必須向相反的方向傳遞。Android提供了做以上操作的代碼,所以開發者可以專注于實現RPC的接口。 
    一個RPC接口只能包含方法。所有的方法都是同步執行的 (直到遠程方法返回,本地方法才結束阻塞),沒有返回值的時候也是如此。 
    簡單來說,這個機制是這樣的:使用IDL (interface definition language)定義你想要實現的接口, aidl 工具可以生成用于java的接口定義,本地和遠程都要使用這個定義 。它包含2個類,見下圖: 
     
    inner類包含了所有的管理遠程程序(符合IDL描述的接口)所需要的代碼。所有的inner類實現了IBinder 接口.其中一個在本地使用,可以不管它的代碼;另外一個叫做Stub繼承了 Binder 類。為了實現遠程調用,這個類包含RPC接口。開發者可以繼承Stub類來實現需要的方法 。 
    一般來說,遠程進程會被一個service管理(因為service可以通知操作系統這個進程的信息并和其他進程通信),它也會包含aidl 工具產生的接口文件,Stub類實現了遠處那個方法。服務的客戶端只需要aidl 工具產生的接口文件。 
    以下是如何連接服務和客戶端調用: 
    • ·服務的客戶端(本地)會實現onServiceConnected() 和onServiceDisconnected() 方法,這樣,當客戶端連接或者斷開連接的時候可以獲取到通知。通過 bindService() 獲取到服務的連接。
    • · 服務的 onBind() 方法中可以接收或者拒絕連接,取決它收到的intent (intent通過 bindService()方法連接到服務). 如果服務接收了連接,會返回一個Stub類的實例.
    • · 如果服務接受了連接,Android會調用客戶端的onServiceConnected() 方法,并傳遞一個Ibinder對象(系統管理的Stub類的代理),通過這個代理,客戶端可以連接遠程的服務。
    以上的描述省略很多RPC的機制。請參見Designing a Remote Interface Using AIDL 和 IBinder 類。 
    線程安全的方法在某些情況下,方法可能調用不止一個的線程,因此需要注意方法的線程安全。 
    對于可以遠程調用的方法,也要注意這點。當一個調用在Ibinder對象中的方法的程序啟動了和Ibinder對象相同的進程,方法就在 Ibinder的進程中執行。但是,如果調用者發起另外一個進程,方法在另外一個線程中運行,這個線程在和IBinder對象在一個線程池中;它不會在進程的主線程中運行。例如,一個service從主線程被調用onBind() 方法,onBind() 返回的對象(如實現了RPC的Stub子類)中的方法會被從線程池中調用。因為一個服務可能有多個客戶端請求,不止一個線程池會在同一時間調用 IBinder的方法。因此IBinder必須線程安全。 
    簡單來說,一個content provider 可以接收其他進程的數據請求。即使ContentResolver和ContentProvider類沒有隱藏了管理交互的細節,ContentProvider中響應這些請求的方法(query(), insert(), delete(), update(), and getType() )— 是在content provider的線程池中被調用的,而不是ContentProvider的本身進程。因為這些方法可能是同時從很多線程池運行的,所以這些方法必須要線程安全。

    Android移植: wifi設計原理(源碼分析)

    Android移植: wifi設計原理(源碼分析)

    相關搜索: Androidwifi源碼原理移植
    android_wifi.png 
    初始化
    在 SystemServer 啟動的時候,會生成一個 ConnectivityService 的實例,
                  try {
                        Log.i(TAG, "Starting Connectivity Service.");
                        ServiceManager.addService(Context.CONNECTIVITY_SERVICE, new
    ConnectivityService(context));
                  } catch (Throwable e) {
                        Log.e(TAG, "Failure starting Connectivity Service", e);
                  }
    ConnectivityService 的構造函數會創建 WifiService,
              if (DBG) Log.v(TAG, "Starting Wifi Service.");
              mWifiStateTracker = new WifiStateTracker(context, handler);
              WifiService wifiService = new WifiService(context, mWifiStateTracker);
              ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
    WifiStateTracker 會創建 WifiMonitor 接收來自底層的事件,WifiService 和 WifiMonitor 是整
    個模塊的核心。WifiService 負責啟動關閉 wpa_supplicant、啟動關閉 WifiMonitor 監視線程
    和把命令下發給 wpa_supplicant,而 WifiMonitor 則負責從 wpa_supplicant 接收事件通知。
    連接 AP
    1. 使能 WIFI
    WirelessSettings 在初始化的時候配置了由 WifiEnabler 來處理 Wifi 按鈕,
         private void initToggles() {
              mWifiEnabler = new WifiEnabler(
                           this,
                           (WifiManager) getSystemService(WIFI_SERVICE),
                           (CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI));
    當用戶按下 Wifi 按鈕后,               Android 會調用 WifiEnabler 的 onPreferenceChange,    再由 WifiEnabler
    調用 WifiManager 的 setWifiEnabled 接口函數,通過 AIDL,實際調用的是 WifiService 的
    setWifiEnabled 函數,WifiService 接著向自身發送一條 MESSAGE_ENABLE_WIFI 消息,在
    處理該消息的代碼中做真正的使能工作:首先裝載 WIFI 內核模塊(該模塊的位置硬編碼為
    "/system/lib/modules/wlan.ko" ), 然 后 啟 動 wpa_supplicant ( 配 置 文 件 硬 編 碼 為
    "/data/misc/wifi/wpa_supplicant.conf") 再通過 WifiStateTracker 來啟動 WifiMonitor 中的監視
                                               ,
    線程。
         private boolean setWifiEnabledBlocking(boolean enable) {
              final      int     eventualWifiState   =     enable    ?   WIFI_STATE_ENABLED     :
    WIFI_STATE_DISABLED;
              updateWifiState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING);
              if (enable) {
                    if (!WifiNative.loadDriver()) {
                           Log.e(TAG, "Failed to load Wi-Fi driver.");
                           updateWifiState(WIFI_STATE_UNKNOWN);
                           return false;
                    }
                    if (!WifiNative.startSupplicant()) {
                           WifiNative.unloadDriver();
                           Log.e(TAG, "Failed to start supplicant daemon.");
                           updateWifiState(WIFI_STATE_UNKNOWN);
                           return false;
                    }
                    mWifiStateTracker.startEventLoop();
              }
                 // Success!
                 persistWifiEnabled(enable);
                 updateWifiState(eventualWifiState);
                 return true;
          }
    當使能成功后,會廣播發送 WIFI_STATE_CHANGED_ACTION 這個 Intent 通知外界 WIFI
    已 經 成 功 使 能 了 。 WifiEnabler 創 建 的 時 候 就 會 向 Android 注 冊 接 收
    WIFI_STATE_CHANGED_ACTION,因此它會收到該 Intent,從而開始掃描。
              private void handleWifiStateChanged(int wifiState) {
                 if (wifiState == WIFI_STATE_ENABLED) {
                      loadConfiguredAccessPoints();
                      attemptScan();
                 }
    2. 查找 AP
    掃描的入口函數是 WifiService 的 startScan,它其實也就是往 wpa_supplicant 發送 SCAN 命
    令。
    static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz)
    {
          jboolean result;
          // Ignore any error from setting the scan mode.
          // The scan will still work.
          (void)doBooleanCommand("DRIVER SCAN-ACTIVE", "OK");
          result = doBooleanCommand("SCAN", "OK");
          (void)doBooleanCommand("DRIVER SCAN-PASSIVE", "OK");
          return result;
    }
    當 wpa_supplicant 處理完 SCAN 命令后,它會向控制通道發送事件通知掃描完成,從而
    wifi_wait_for_event 函數會接收到該事件,由此 WifiMonitor 中的 MonitorThread 會被執行來
    出來這個事件,
                 void handleEvent(int event, String remainder) {
                            case SCAN_RESULTS:
                                 mWifiStateTracker.notifyScanResultsAvailable();
                                 break;
    WifiStateTracker 則接著廣播發送 SCAN_RESULTS_AVAILABLE_ACTION 這個 Intent
                      case EVENT_SCAN_RESULTS_AVAILABLE:
                            mContext.sendBroadcast(new
    Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
    WifiLayer 注冊了接收 SCAN_RESULTS_AVAILABLE_ACTION 這個 Intent,所以它的相關
    處理函數 handleScanResultsAvailable 會被調用,在該函數中,先會去拿到 SCAN 的結果(最
    終是往 wpa_supplicant 發送 SCAN_RESULT 命令并讀取返回值來實現的)                             ,
                    List<ScanResult> list = mWifiManager.getScanResults();
    對每一個掃描返回的 AP,WifiLayer 會調用 WifiSettings 的 onAccessPointSetChanged 函數,
    從而最終把該 AP 加到 GUI 顯示列表中。
         public void onAccessPointSetChanged(AccessPointState ap, boolean added) {
              AccessPointPreference pref = mAps.get(ap);
              if (added) {
                    if (pref == null) {
                          pref = new AccessPointPreference(this, ap);
                          mAps.put(ap, pref);
                    } else {
                          pref.setEnabled(true);
                    }
                    mApCategory.addPreference(pref);
              }
         }
    3. 配置 AP 參數
    當用戶在 WifiSettings 界面上選擇了一個 AP 后,會顯示配置 AP 參數的一個對話框,
         public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference
    preference) {
              if (preference instanceof AccessPointPreference) {
                    AccessPointState             state           =         ((AccessPointPreference)
    preference).getAccessPointState();
                    showAccessPointDialog(state, AccessPointDialog.MODE_INFO);
              }
         }
    4. 連接
    當用戶在 AcessPointDialog 中選擇好加密方式和輸入密鑰之后,再點擊連接按鈕,Android
    就會去連接這個 AP。
         private void handleConnect() {
              String password = getEnteredPassword();
              if (!TextUtils.isEmpty(password)) {
                    mState.setPassword(password);
              }
              mWifiLayer.connectToNetwork(mState);
         }
    WifiLayer 會先檢測這個 AP 是不是之前被配置過,這個是通過向 wpa_supplicant 發送
    LIST_NETWORK 命令并且比較返回值來實現的,
              // Need WifiConfiguration for the AP
              WifiConfiguration config = findConfiguredNetwork(state);
    如果 wpa_supplicant 沒有這個 AP 的配置信息,                     則會向 wpa_supplicant 發送 ADD_NETWORK
    命令來添加該 AP,
              if (config == null) {
                    // Connecting for the first time, need to create it
                    config                             =                     addConfiguration(state,
    ADD_CONFIGURATION_ENABLE|ADD_CONFIGURATION_SAVE);
              }
    ADD_NETWORK 命 令 會 返 回 一 個 ID , WifiLayer 再 用 這 個 返 回 的 ID 作 為 參 數 向
    wpa_supplicant 發送 ENABLE_NETWORK 命令,從而讓 wpa_supplicant 去連接該 AP。
              // Make sure that network is enabled, and disable others
              mReenableApsOnNetworkStateChange = true;
              if (!mWifiManager.enableNetwork(state.networkId, true)) {
                    Log.e(TAG, "Could not enable network ID " + state.networkId);
                    error(R.string.error_connecting);
                    return false;
              }
    5. 配置 IP 地址
    當 wpa_supplicant 成功連接上 AP 之后,它會向控制通道發送事件通知連接上 AP 了,從而
    wifi_wait_for_event 函數會接收到該事件,由此 WifiMonitor 中的 MonitorThread 會被執行來
    出來這個事件,
              void handleEvent(int event, String remainder) {
                         case CONNECTED:
                               handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,
    remainder);
                               break;
    WifiMonitor 再調用 WifiStateTracker 的 notifyStateChange,WifiStateTracker 則接著會往自身
    發送 EVENT_DHCP_START 消息來啟動 DHCP 去獲取 IP 地址,
         private void handleConnectedState() {
              setPollTimer();
              mLastSignalLevel = -1;
              if (!mHaveIPAddress & !mObtainingIPAddress) {
                    mObtainingIPAddress = true;
                    mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget();
              }
         }
    然后再廣播發送 NETWORK_STATE_CHANGED_ACTION 這個 Intent
                    case EVENT_NETWORK_STATE_CHANGED:
                         if (result.state != DetailedState.DISCONNECTED || !mDisconnectPending) {
                               intent                                 =                        new
    Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
                               intent.putExtra(WifiManager.EXTRA_NETWORK_INFO,
    mNetworkInfo);
                              if (result.BSSID != null)
                                    intent.putExtra(WifiManager.EXTRA_BSSID, result.BSSID);
                              mContext.sendStickyBroadcast(intent);
                         }
                         break;
    WifiLayer 注冊了接收 NETWORK_STATE_CHANGED_ACTION 這個 Intent,所以它的相關
    處理函數 handleNetworkStateChanged 會被調用,
    當 DHCP 拿到 IP 地址之后,會再發送 EVENT_DHCP_SUCCEEDED 消息,
         private class DhcpHandler extends Handler {
              public void handleMessage(Message msg) {
                    switch (msg.what) {
                         case EVENT_DHCP_START:
                              if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
                                    event = EVENT_DHCP_SUCCEEDED;
                                                          }
    WifiLayer 處 理 EVENT_DHCP_SUCCEEDED 消 息 , 會 再 次 廣 播 發 送
    NETWORK_STATE_CHANGED_ACTION 這個 Intent,這次帶上完整的 IP 地址信息。
                    case EVENT_DHCP_SUCCEEDED:
                         mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
                         setDetailedState(DetailedState.CONNECTED);
                         intent                                 =                           new
    Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
                         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
                         mContext.sendStickyBroadcast(intent);
                         break;
    至此為止,整個連接過程完成。
    問題:
         目前的實現不支持 Ad-hoc 方式。

    開機啟動service,適合鬧鐘程序

    Android多媒體框架分析

    Android的Media Framework 處于Libraries這一層,這層的Library不是用Java實現,一般是C/C++實現,它們通過JavaJNI方式調用。      多媒體架構:基于第三方PacketVideo公司的OpenCORE platform 來實現,支持所有通用的音頻,視頻,靜態圖像格式。CODEC( 編解碼器 ) 使用 OpenMAX 1L interface 接口進行擴展,可以方便的支持hardware / software codec plug-ins。支持的格式包括: MPEG4 、H.264 、MP3 、AAC 、AMR 、JPG 、PNG 、GIF 等。 在實際開發中我們并不會過多的研究Open Core 的實現,Android 提供了上層的Media API 給開發人員使用,分別是MediaPlayer 和MediaRecorder。 
    (1)The Android platform is capable of playing both audio and video media. It is also capable of playing media contained in the resources for an application, or a standalone file in the filesystem, or even streaming media over a data connection. Playback is achieved through the android.media.MediaPlayer class. 
    (2)The Android platform can also record audio. Video recording capabilities are coming in the future. This is achieved through the android.media.MediaRecorder class. 分述:
          Media Player 提供的基本接口如下: 
    Public Methods 
    (1)static  MediaPlayer  create (Context context, Uri uri) 
    Convenience method to create a MediaPlayer for a given Uri.    
    (2)int getCurrentPosition () 
    Gets the current playback position.      
    (3)int getDuration () 
    Gets the duration of the file.      
    (4)int getVideoHeight () 
    Returns the height of the video.     
    (5)int getVideoWidth () 
    Returns the width of the video.     
    (6)boolean isPlaying () 
    Checks whether the MediaPlayer is playing.      
    (7)void pause () 
    Pauses playback.      
    (8)void prepare () 
    Prepares the player for playback, synchronously.      
    (9)void prepareAsync () 
    Prepares the player for playback, asynchronously.      
    (10)void release () 
    Releases resources associated with this MediaPlayer object.     
    (11)void reset () 
    Resets the MediaPlayer to its uninitialized state.      
    (12)void seekTo (int msec) 
    Seeks to specified time position.     
    (13) void setAudioStreamType (int streamtype) 
    Sets the audio stream type for this MediaPlayer.     
    (14)void setDataSource (String path) 
    Sets the data source (file-path or http/rtsp URL) to use.          
    (15)void setDisplay (SurfaceHolder sh) 
    Sets the SurfaceHolder to use for displaying the video portion of the media.      
    (16)void setVolume (float leftVolume, float rightVolume) 
    Sets the volume on this player.  
    (17)void start () 
    Starts or resumes playback.      
    (18)void stop () 
    Stops playback after playback has been stopped or paused. 
    我們可以看出 MediaPlayer 類提供了一個多媒體播放器的基本操作,播放,暫停,停止,設置音量等等。 
          簡單的例子: 

    Playing a File:


    MediaPlayer mp = new MediaPlayer();


    mp.setDataSource(PATH_TO_FILE);


    mp.prepare();


    mp.start();

    Playing a Raw Resource:

        MediaPlayer mp = MediaPlayer.create(context, R.raw.sound_file_1);

        mp.start();


        另Media Recorder 提供的基本接口如下: 
    Public Method: 
    (1)void prepare () 
    Prepares the recorder to begin capturing and encoding data.      
    (2)void release () 
    Releases resources associated with this MediaRecorder object.      
    (3)void reset () 
    Restarts the MediaRecorder to its idle state.      
    (4)void setAudioEncoder (int audio_encoder) 
    Sets the audio encoder to be used for recording.      
    (5)void setAudioSource (int audio_source) 
    Sets the audio source to be used for recording.      
    (6)void setOutputFile (String path) 
    Sets the path of the output file to be produced.      
    (7)void setOutputFormat (int output_format) 
    Sets the format of the output file produced during recording.      
    (8)void setPreviewDisplay (Surface sv) 
    Sets a Surface to show a preview of recorded media (video).     
    (9)void start () 
    Begins capturing and encoding data to the file specified with setOutputFile().      
    (10)void stop () 
    Stops recording. 
          簡單的例子: 
    Example: 
        MediaRecorder recorder = new MediaRecorder(); 
        recorder.setAudioSource(MediaRecorder.AudioSource.MIC); 
        recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 
        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 
        recorder.setOutputFile(PATH_NAME); 
        recorder.prepare(); 
        recorder.start(); // Recording is now started ... recorder.stop(); 
        recorder.reset(); // You can reuse the object by going back to  
                                  setAudioSource() step 
        recorder.release(); // Now the object cannot be reused 
        整體結構的核心文件的位置如下:
    A,MediaPlayer JNI                             代碼位置 /frameworks/base/media/jni 
    B, MediaPlayer (Native)                    代碼位置 /frameworks/base/media/libmedia 
    C, MediaPlayerService (Server)         代碼位置 /frameworks/base/media/libmediaplayerservice 
    D, MediaPlayerService Host Process 代碼位置 /frameworks/base/media/mediaserver/main_mediaserver.cpp 
    E, PVPlayer                                       代碼位置 /external/opencore/android/

    Android專用驅動和Binder

    Android中有些驅動程序提供輔助操作系統功能,這些驅動程序不是linux的標準驅動,它們一般并不操作實際的硬件,只是輔助系統的運行。主要要以下幾種:
    Ashmem:匿名共享內存驅動
    Logger:輕量級的Log驅動
    Binder:基于OpenBinder系統的驅動,為Android平臺提供IPC支持。
    Android Power Management:電源管理模塊
    Low Memory Killer:在缺少內存的情況下殺死進程
    Android PMEM:物理內存驅動      Binder:Android的Binder驅動程序為用戶層提供了IPC支持,Android的運行整個依賴Binder驅動。Binder設備節點名稱:/dev/binder。用ls -l /dev/binder可查看設備屬性。主設備號為10(misc driver),次設備號動態生成。
    Binder驅動程序在內核的路徑如下:
    \android_kernel_f301\include\linux\binder.h
    \android_kernel_f301\drivers\misc\binder.c
    Binder在Android用戶空間的調用主要表現在對libutil工具庫和service manager守護進程的支持。
    \frameworks\base\cmds\servicemanager\Binder.c
    \frameworks\base\cmds\servicemanager\Binder.h
    \frameworks\base\libs\utils\Binder.cpp
    \frameworks\base\libs\utils\Binder.h
          Binder是Android中主要的使用的IPC方式,通常只需要按照模板定義相關的類即可,不需要直接調用Binder驅動程序的設備節點。

    Android.mk的用法和基礎

    一個Android.mk file用來向編譯系統描述你的源代碼。具體來說:該文件是GNU Makefile的一小部分,會被編譯系統解析一次或多次。你可以在每一個Android.mk file中定義一個或多個模塊,你也可以在幾個模塊中使用同一個源代碼文件。編譯系統為你處理許多細節問題。例如,你不需要在你的Android.mk中列出頭文件和依賴文件。NDK編譯系統將會為你自動處理這些問題。這也意味著,在升級NDK后,你應該得到新的toolchain/platform支持,而且不需要改變你的Android.mk文件。
          先看一個簡單的例子:一個簡單的"hello world",比如下面的文件:
    sources/helloworld/helloworld.c 
    sources/helloworld/Android.mk
    相應的Android.mk文件會象下面這樣:
    ---------- cut here ------------------
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE
    := helloworld
    LOCAL_SRC_FILES := helloworld.c
    include $(BUILD_SHARED_LIBRARY)
    ---------- cut here ------------------
          我們來解釋一下這幾行代碼:
    LOCAL_PATH := $(call my-dir) 
    一個Android.mk file首先必須定義好LOCAL_PATH變量。它用于在開發樹中查找源文件。在這個例子中,宏函數’my-dir’, 由編譯系統提供,用于返回當前路徑(即包含Android.mk file文件的目錄)。
    include $( CLEAR_VARS)
    CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE為你清除許多LOCAL_XXX變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。這是必要的,因為所有的編譯控制文件都在同一個GNU MAKE執行環境中,所有的變量都是全局的。
    LOCAL_MODULE := helloworld
    LOCAL_MODULE變量必須定義,以標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。注意編譯系統會自動產生合適的前綴和后綴,換句話說,一個被命名為'foo'的共享庫模塊,將會生成'libfoo.so'文件。
    LOCAL_SRC_FILES := helloworld.c 
    LOCAL_SRC_FILES變量必須包含將要編譯打包進模塊中的C或C++源代碼文件。注意,你不用在這里列出頭文件和包含文件,因為編譯系統將會自動為你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好。      在Android中增加本地程序或者庫,這些程序和庫與其所載路徑沒有任何關系,只和它們的Android.mk文件有關系。Android.mk和普通的Makefile有所不同,它具有統一的寫法,主要包含一些系統公共的宏。
         在一個Android.mk中可以生成多個可執行程序、動態庫和靜態庫。
    1,編譯應用程序的模板:
         #Test Exe
         LOCAL_PATH := $(call my-dir)
         #include $(CLEAR_VARS)
         LOCAL_SRC_FILES:= main.c
         LOCAL_MODULE:= test_exe
         #LOCAL_C_INCLUDES :=
         #LOCAL_STATIC_LIBRARIES :=
         #LOCAL_SHARED_LIBRARIES :=
         include $(BUILD_EXECUTABLE)
    (菜鳥級別解釋::=是賦值的意思,$是引用某變量的值)LOCAL_SRC_FILES中加入源文件路徑,LOCAL_C_INCLUDES 中加入所需要包含的頭文件路徑,LOCAL_STATIC_LIBRARIES加入所需要鏈接的靜態庫(*.a)的名稱,LOCAL_SHARED_LIBRARIES中加入所需要鏈接的動態庫(*.so)的名稱,LOCAL_MODULE表示模塊最終的名稱,BUILD_EXECUTABLE表示以一個可執行程序的方式進行編譯。
    2,編譯靜態庫的模板:
         #Test Static Lib
         LOCAL_PATH := $(call my-dir)
         include $(CLEAR_VARS)
         LOCAL_SRC_FILES:= \
                   helloworld.c
         LOCAL_MODULE:= libtest_static
         #LOCAL_C_INCLUDES :=
         #LOCAL_STATIC_LIBRARIES :=
         #LOCAL_SHARED_LIBRARIES :=
         include $(BUILD_STATIC_LIBRARY)
    一般的和上面相似,BUILD_STATIC_LIBRARY表示編譯一個靜態庫。
    3,編譯動態庫的模板:
         #Test Shared Lib
         LOCAL_PATH := $(call my-dir)
         include $(CLEAR_VARS)
         LOCAL_SRC_FILES:= \
                   helloworld.c
         LOCAL_MODULE:= libtest_shared
         TARGET_PRELINK_MODULES := false
         #LOCAL_C_INCLUDES :=
         #LOCAL_STATIC_LIBRARIES :=
         #LOCAL_SHARED_LIBRARIES :=
          include $(BUILD_SHARED_LIBRARY)
    一般的和上面相似,BUILD_SHARED_LIBRARY表示編譯一個靜態庫。
          以上三者的生成結果分別在如下,generic依具體target會變:
    out/target/product/generic/obj/EXECUTABLE
    out/target/product/generic/obj/STATIC_LIBRARY
    out/target/product/generic/obj/SHARED_LIBRARY
          每個模塊的目標文件夾分別為:
    可執行程序:XXX_intermediates
    靜態庫:      XXX_static_intermediates
    動態庫:      XXX_shared_intermediates
          另外,在Android.mk文件中,還可以指定最后的目標安裝路徑,用LOCAL_MODULE_PATH和LOCAL_UNSTRIPPED_PATH來指定。不同的文件系統路徑用以下的宏進行選擇:
    TARGET_ROOT_OUT:表示根文件系統。
    TARGET_OUT:表示system文件系統。
    TARGET_OUT_DATA:表示data文件系統。
    用法如:
    CAL_MODULE_PATH:=$(TARGET_ROOT_OUT)
    posted on 2010-08-30 14:02 MEYE 閱讀(1671) 評論(0)  編輯  收藏 所屬分類: Android3D
    主站蜘蛛池模板: 久久经典免费视频| 亚洲不卡中文字幕| 国产三级电影免费观看| av无码国产在线看免费网站 | 国产乱码免费卡1卡二卡3卡| 四虎国产精品免费永久在线| 成人特级毛片69免费观看| 亚洲国产精品无码久久久秋霞1| 亚洲精品国产成人| 亚洲国产精品无码久久久秋霞2 | 亚洲毛片基地日韩毛片基地| 亚洲va中文字幕无码久久| 亚洲日韩涩涩成人午夜私人影院| 女人与禽交视频免费看| 欧洲乱码伦视频免费| 98精品全国免费观看视频| 久久久精品免费国产四虎| 国产一级a毛一级a看免费视频| 人妻无码中文字幕免费视频蜜桃| 亚洲国产欧美国产综合一区 | 亚洲国产成人久久综合一区| 亚洲精品无码Av人在线观看国产| 亚洲AV第一成肉网| 亚洲精品黄色视频在线观看免费资源 | 一区二区三区免费视频播放器| 亚洲人成网站在线播放2019 | 成人无码区免费视频观看 | 亚洲午夜无码久久久久软件 | 最近免费中文字幕视频高清在线看| 国产92成人精品视频免费| 久久久高清日本道免费观看| 18禁在线无遮挡免费观看网站| 中文字幕av免费专区| 中文字幕免费在线看| 免费无码H肉动漫在线观看麻豆| 国产一级婬片A视频免费观看| 最新亚洲成av人免费看| 免费精品99久久国产综合精品| 日本免费人成网ww555在线| 亚在线观看免费视频入口| 久久午夜伦鲁片免费无码|