1. 程式人生 > >Android含資原始檔引用的專案打包成jar包

Android含資原始檔引用的專案打包成jar包

一、發現問題

       最近一個專案臨到收尾,就差最後一步打包了~~

       但卻遇到了一些小問題。這個專案裡面用到Notification,用過notification的人都知道,notification必須設定小圖示setSmallIcon(int icon),引數icon就是資原始檔的Id。於是很簡單,我把需要的資原始檔一起打包成jar包就可以了;可是老大說,我們是產出SDK給第三方用的,而第三方可以把notification資原始檔換成第三方自己的應用圖示,意思就是我不能把資原始檔一起打包;還是很簡單,我只打包jar檔案,不包含資原始檔,到時候第三方在自己專案中新增對應名稱的資原始檔就OK啦 ~~

      可是,測試執行jar包,發現找不能資原始檔。為什麼??

二、分析問題

      原來,問題出現在下面這句簡單的程式碼:

	builder.setSmallIcon(R.id.small_icon);
      這個程式碼我們常寫,會有什麼問題呢? 問題就出現在R這個類。熟悉Eclipse專案結構的人都知道,會在 " /gen/包名/"  下面自動生成一個R.java類,例如:/gen/com.mobisummer.ads.banner/R.java 。而這個R類有點像資料庫的索引表,把資原始檔和ID對應起來,開發者可以通過id直接找到資原始檔。所以,上面的程式碼是省略的,實際可以看成下面這樣:
	builder.setSmallIcon(com.mobisummer.ads.banner.R.id.small_icon);

        所以,當在第三方專案中引用jar包,把資原始檔直接放在第三方專案中,那麼上面的程式碼當然找不到對應的資源了,因為jar包的包名和第三方專案的包名不一樣,即是索引ID的包名和資源實際的包名不一致。
       那麼,就沒有辦法了嗎? 有,當然有。。。

三、方法一

       這種方法的原理也很簡單:上面說到出錯原因是ID和資源的包名不一致,所以我們就婉轉一點,不引用絕對的ID地址,而把ID改成相對的,即  “getPackageName().R.id.small_icon”  的形式,這樣,不管第三方的包名是什麼,都能索引到正確的包下面的資源了。

       當然,上面的形式是不符合程式碼格式的,所以android提供了專門的介面:

       builder.setSmallIcon(getResources().getIdentifier("small_icon", "drawable", getPackageName()));

     其中,第一個引數是資源的名字,第二個引數是資源的型別,例如drawable/ layout/ string等,第三個引數是目標專案的包名。這個實際就是,利用反射根據資源名字獲取資源ID。

      當然,為了專案的模組化和結構優化,我們可以搞一個專門的類實現該功能,程式碼很簡單,應該都能讀懂,不多做分析,至於引數,程式碼裡面有說明:

package com.mobisummer.ads.banner.util;

import android.content.Context;
import android.util.Log;

public class MResource {

	/**
	 * 根據資源的名字獲取其ID值
	 * 
	 * @param context
	 *            目標應用程式,一般通過getApplication()取得
	 * @param className
	 *            資源型別,如layout/id/drawable等
	 * @param name
	 *            資源的名稱
	 * @return
	 */
	public static int getIdByName(Context context, String className, String name) {
		String packageName = context.getPackageName();
		Class r = null;
		int id = 0;
		try {
			r = Class.forName(packageName + ".R");

			Class[] classes = r.getClasses();
			Class desireClass = null;

			for (int i = 0; i < classes.length; ++i) {
				if (classes[i].getName().split("\\$")[1].equals(className)) {
					desireClass = classes[i];
					break;
				}
			}

			if (desireClass != null)
				id = desireClass.getField(name).getInt(desireClass);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}

		return id;
	}
}

       呼叫程式碼如下:
		builder.setSmallIcon(MResource.getIdByName(
				mContext.getApplicationContext(), "drawable", "small_icon"));

       很簡單吧~~~

四、方法二

       這個方法的原理也很簡單: 當自己的專案是為自己所用,或者程式碼可以公開給第三方時,將專案設定為引用庫,在其他專案中新增這個庫的引用即可。

       具體的操作就不自己寫了, 直接從網上摘抄過來的,摘抄連結:

       http://blog.csdn.net/jia635/article/details/44004079

根據Android的官方文件,將其中一個專案設定為引用庫,在另一個專案中新增這個庫的引用。

簡單的做法是

在被引用專案TestJar中的project.properties中新增一行

 android.library=true

在引用的專案TestMain的project.properties中新增

 android.library=false
 android.library.reference.1=../TestJar

其中1表示引用包的序號,“../TestJar”表示引用專案的路徑

在Eclipse中操作具體做法如下:

  1. 把普通的android project設定成庫專案
    庫專案也是一個標準的Android專案,因此你先建立一個普通的Android專案,這個專案可以起任何的名稱,任何的包名,設定其他需要設定的欄位等。
    接著把專案設定成庫專案,步驟如下:
    1)在Package Explorer中,滑鼠右鍵專案資料夾(TestJar),點選Properties。
    2)在Properties視窗選擇”Android“,Library屬性顯示在右下邊。
    3)把”is Library“單選框選上,在點選Apply。
    4)點選OK關閉Properties。
    這時,這個專案就變成庫專案了,當然純的java專案也可以變成庫專案,非常簡單,執行上面四步就OK了。其他程式專案就可以引用這個庫專案了。

引用庫專案
如果你開發的應用程式想要呼叫庫專案中的程式碼和資源,也easy哦,引用步驟如下:
1)在Package Explorer中,滑鼠右鍵專案資料夾(TestMain),點選Properties。
2)在Properties視窗選擇”Android“,Library屬性顯示在右下邊。
3)點選Add,打開了Project Selection對話方塊。
4)從可用的庫專案列表中選擇要新增的庫專案,點選OK。
5)對話方塊關閉之後點選Apply,(在Properties視窗)。
6)點選OK,關閉Properties視窗。
完成以上六步,Eclipse會重建專案,把庫專案中的內同包含進去。


  1. 如果你想增加多個庫專案的引用,使用up和down可以設定他們的相對的優先順序和合並順序。工具在合併引用的庫的時候順序是從低優先順序(列表的下面)到高優先順序(列表的上面)。 如果不只一個庫定義了相同的資源ID,這個工具選擇資源時會選擇高優先順序的資源。應用程式自身擁有最高的優先順序。