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 阅读(1667) 评论(0)  编辑  收藏 所属分类: Android3D

只有注册用户登录后才能发表评论。


网站导航: