編輯:關於Android編程
這是一篇半技術類文章。眾所周知現在Google主推Android Studio開發工具,而Eclipse已經被閒置一陣子了,但是Eclipse項目卻還有很多沒有遷移到AS中;而現在一些新的庫都是采用AS Gradle打包並發布到公共倉庫中,而這些庫Eclipse 並不能直接使用,在本篇文章中將講解如何導入一個或者多個庫到Eclipse中使用。
Eclipse基本算是荒廢的狀態了,這並不是說Eclipse就不好,畢竟都是Eclipse過來的;只能說一個個是新歡一個舊情人了。舊情人肯定是有她特殊的韻味的,所以現在並不是所有的都遷移到AS了,還有很大一部分處於Eclipse上的項目。
這裡就有問題了,許多的開發者在AS上的庫一般來說都不是打包為Jar包,而是直接生成AAR庫,隨後發布到公共倉庫中,AS的使用者呢就使用一句簡單的代碼就能使用這個庫了,其下載-加載-編譯-運行都用Gradle搞定了,而Eclipse就不樂意了~~
Eclipse 就面臨著尴尬的局面想要用新的庫,但是卻沒法用~~
在本篇文章中,將會講解如何在Eclipse中使用一個公共倉庫中的庫。
在AS中發布庫都是采用Gradle發布到對應的公共倉庫中進行使用,倉庫有很多,你也可以自己使用服務器搭建倉庫;而公共倉庫中有兩個特別OK的:
jcenter mavencentral在最初google框架中默認使用的倉庫是:mavencentral ,但是因為mavencentral 倉庫的發布流程較為嚴格並且操作上不夠人性化所以後來改為了:jcenter。
所以推薦使用jcenter,當然如果你在mavencentral中發布了你的庫,那麼這個庫在發布成功後的24小時內會被同步到jcenter中,而jcenter中發布的庫也可以通過一鍵式操作發布到mavencentral倉庫中。
因為有上面的倉庫區別,所以如果你使用的是AS將會在配置中看見諸如這樣的配置:
repositories {
jcenter()
}
repositories {
jcenter {
url "http://jcenter.bintray.com/"
}
}
repositories {
mavenLocal()
}
這就是在告訴你的項目中使用的庫的來源,AS將會更加庫的標識去尋找需要的文件,當然兩個倉庫可以同時寫入到項目配置中。
在我的文章中有一篇關於發布到mavencentral倉庫的文章: [Publish AAR To Maven] 使用 Gradle 發布 AAR 到 Maven 倉庫
上面所介紹的是發布到mavencentral倉庫的流程,而jcenter的流程也基本類似,只不過把文件加密的過程放到了服務器上去完成。
整體來說:
首先我們需要有一份庫的代碼 編譯源碼得到Jar, R, JNI, xml等源碼文件。 打包所有文件為一個aar的文件 簽名 發布到倉庫 校驗並發布而使用這個庫,則也很簡單下載對應版本的AAR文件,加入到項目中,並編譯進項目。
從這裡就可以看出一點,實際的android使用中就是使用的aar文件,只不過此時的aar文件會被解包編譯。
在AS的項目配置中我們加上這樣一句:
dependencies {
compile 'net.qiujuer.genius:ui:1.3.0'
}
在編譯後,我們查看一下項目的Build文件夾:
在這裡我們可以看見實際的使用中的確如我們所想的一般。
當然這裡之所以會出現“res“包,是因為“genius-ui“包中默認引用了“genius-res“包,所以當我們使用“UI“包的時候就會同步下載“res“包。詳細可以見開源項目:Genius-Android<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxibG9ja3F1b3RlPg0KCTxwPk9LLCDSu7j2v+K1xLzytaXB97PMztLDx8u1usPBy6O7xMfDtMC0v7S/tMjnus7U2kVjbGlwc2XW0Mq508OhozwvcD4NCjwvYmxvY2txdW90ZT4NCjxoMSBpZD0="問題">問題
從上面的庫的流程中可以看出一個AAR庫中可以包括:jar, aidl, assets, res, R 文件等數據;而Eclipse中默認引用庫的時候只能引用jar文件。
如果我們只是把jar文件放到eclipse中的話將會無法正常使用對應的資源文件。那麼是不是只要同時把資源文件放到eclipse中也就解決了這個問題?並不是的!
眾所周知,一個資源文件在編譯的時候會在R文件中生成一個int 值與之相對應,而實際代碼中全部使用這個int值代替;那麼上面添加的資源文件全部會生成一份新的值與之對應,那麼在庫的jar中依然無法找到與之對應的資源,最終將會導致APP崩潰。
此時有一個辦法可以解決,那就是固化資源對應的INT值,讓改資源在編譯的時候每次都生成我們指定的INT值。在這裡我們使用一個android中特有的文件:“public.xml“
public.xml: 用於固化資源所對應的INT值。
首先我們需要准備一份對應的aar文件,在這裡我使用 genius-android 庫的 UI 包來完成該操作。
下載點有:
jcenter mavencentral我們進入mavencentral,並搜索 genius ,找到 ui 包:
因為UI包中引用了res包,所以我們同時下載ui和res包。
更改後綴為zip,並解壓:
AndroidManifest.xml :含有權限-服務聲明-廣播注冊-activity聲明等信息,直接將其中的數據拷貝到自己的項目中就OK。 aidl:這個中一般都是獨立服務的東西,在項目中按照對應的地方建立好文件就好。 assets:這個中一般是字體,html等數據 classes.jar 這個就是我們需要直接使用的jar包了 R.txt 這個就是資源對應的int值了,我們需要做的主要工作就是把其轉換為 public.xml 文件 Res 文件夾中的內容可以原封不動的拷貝到項目的res文件夾中。為了避免該項目與自己的項目相沖突,我們首先需要建立項目:
在這裡起名為:GeniusUI,並指定包名為:”net.qiujuer.genius.ui”
為什麼呢?因為上面解壓後的AndroidManifest.xml中有指定包名:
如果這裡未指定包名,那麼就算後面我們寫好了 public.xml文件,其雖然能固化INT值,但是卻沒法對應到我們的包名上,換句話說我們的 jar 文件中依然無法找到對應的資源文件。
進入項目屬性(Properties)-Android-勾選上(Is Library) 使其成為庫項目。
請記住在這裡我們並不為RES包單獨創建項目;因為UI庫中引用了RES包中的資源,那麼在UI庫生成的時候將會重新計算一份新的Res庫的資源INT值。
在這裡之所以要下載Res包,僅僅是只需要其中的資源文件而已。
同時打開 Res包中的 R文件(左邊)與UI包中的R文件(右邊):
可以看出UI包中同樣含有顏色對應的INT值,但是其值與原Res包中並不相同。
我們按照圖片所示把現有的文件全部拷貝進項目中:
此時編譯一次項目,一般來說只要文件全部拷貝正確了,那麼是不應該報錯了,並且此時應該生成了一份R文件,只不過此時的R文件與原來庫中的值並不完全相同(有可能有部分相同,這也就導致有部分資源可以正常使用)。
此時我們對比項目生成的R文件與庫中的R文件:
可以發現不盡相同,當然你的也或許完全相同,比如:
這意味著什麼?意味著這個項目中的資源可以完全使用,但是什麼情況下會不同?
我們在 Res 項目中的 values.xml 文件中加上一個顏色:
#ffecb3
這句話將會帶來什麼?帶來後續的ID全部對應失敗的問題:
這也就是我說的資源無法正常對應的問題。
這個相同和不相同都是隨機的,也許原來的庫剛剛巧合了那麼也就相同了。
此時我們在 項目的 values 文件夾中創建 public.xml 文件,並加上:
編譯項目,此時我們再次查看 R 文件:
在這裡可以看出我們寫了的部分已經OK了,從第5個開始又出現問題了,所以啊,我們需要把全部的R對應的INT值都固化到Public中。
好了,現在知道Public文件的魅力了,但是想要固化一整個文件並不是那麼的如意;我一打開UI庫的R文件的時候是崩潰的。
我擦,500多汗~~
坑爹呢不是!!
而且大概看了一下有顏色-數字-style-TM 還有 styleable 這是什麼> 鬼?如果你寫過自定義控件那麼應該知道這個是:自定義屬性。但是,但是這個怎麼固化?500多行,等弄好菜都涼了。
我們是程序員,我們都很懶,所以我們用程序來解決。
在這裡我們寫一個從 R.txt 轉換到 public.xml 的小程序就好。
首先來看看R.txt 文件中轉換到 XML 時對應的格式。
顏色、數字、數組、ID、String、drawable可以直接轉換
int color black 0x7f04000e
int dimen font_10 0x7f050000
int array loading 0x7f030000
int id all 0x7f080009
int string g_font_file 0x7f060000
int drawable background 0x7f020000
style 需要替換“_“為“.“
int style Genius_Widget_BalloonMarker 0x7f070000
attr屬性需要拋棄styleable部分
int attr gAllowTrackClickToDrag 0x7f010022
...
int[] styleable AbsSeekBar { 0x7f01000c, 0x7f010019, 0x7f01001a, 0x7f01001b, 0x7f01001c, 0x7f01001d, 0x7f01001e, 0x7f01001f, 0x7f010020, 0x7f010021, 0x7f010022, 0x7f010023, 0x7f010024, 0x7f010025, 0x7f010026, 0x7f010027, 0x7f010028, 0x7f010029, 0x7f01002a, 0x7f01002b, 0x7f01002c }
int styleable AbsSeekBar_gAllowTrackClickToDrag 10
要得到 gAllowTrackClickToDrag
所代表的INT值,兩種辦法,直接從第一句中轉換得到。
第二個辦法,讀取到 styleable AbsSeekBar
時存儲後面的數組,然後讀取到 int styleable AbsSeekBar_gAllowTrackClickToDrag 10
時,按照 10 這個下標去取得值 0x7f010022
。
static class PublicLine implements Comparable {
public String type;
public String name;
public String id;
public PublicLine() {
}
public PublicLine(String type, String name, String id) {
this.type = type.trim();
this.name = name.trim();
this.id = id.trim();
}
public String getKey() {
return type + "_" + name;
}
@Override
public String toString() {
return "";
}
@Override
public int compareTo(PublicLine o) {
int i = this.type.compareTo(o.type);
if (i == 0)
return this.name.compareTo(o.name);
else
return i;
}
}
實現 compareTo 是用於後面排序,避免輸出的內容亂糟糟的。 “getKey()“ 方法是為了存儲到Map中的主鍵值而准備,避免重復。 “toString()“這是為了輸出日志而准備。
public static void main(String[] args) throws IOException {
File in = new File("R.txt");
File out = new File("public.xml");
if (!in.exists()) {
throw new NullPointerException("R.txt is not null.");
}
try {
out.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return;
}
System.out.println(in.getAbsolutePath());
System.out.println(out.getAbsolutePath());
InputStreamReader read = new InputStreamReader(new FileInputStream(in));
BufferedReader bufferedReader = new BufferedReader(read);
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(out));
BufferedWriter bufferedWriter = new BufferedWriter(writer);
Map xml = new HashMap<>();
buildXml(bufferedReader, xml);
List lines = new ArrayList<>();
lines.addAll(xml.values());
Collections.sort(lines);
saveFile(lines, bufferedWriter);
close(bufferedReader);
close(bufferedWriter);
System.out.println("End.");
}
整個流程非常簡單,直接使用當前目錄生成兩個文件,R.txt 為輸入源,public.xml 則是輸出文件,該文件每次運行都重新生成新文件。
然後輸出一次兩個文件的目錄信息到控制台。
然後初始化Buffer,在這裡經過2層的分裝,最終得到的是:BufferedReader 與 BufferedWriter,才有這兩個的目的主要是為了一行行的讀取,最終也一行行的輸出。
然後聲明一個 Map 變量,用於存儲讀取並轉換為實體的集合。之所以采用Map是為了實現避免重復內容的出現。
再後面我們又聲明了一個 List 變量,並把Map的值存儲到List中,這一步主要是為了排序整個集合;Map本身是無序的,而且如果直接對Map排序就相當復雜,所以這裡我們填充到 List 後再進行排序。
然後則是輸出到Public文件中,然後關閉流的操作。
public static void buildXml(BufferedReader reader, Map xml) {
while (true) {
String line;
try {
line = reader.readLine();
if (line == null || line.trim().length() == 0)
return;
} catch (IOException e) {
e.printStackTrace();
continue;
}
if (line.contains("styleable")) {
// skip styleable array
continue;
} else {
// convert other xml
String[] split = line.split(" ");
if (split.length == 0)
continue;
String type = split[1];
String name = split[2];
String id = split[3];
if (type.contains("style"))
name = name.replace("_", ".");
saveToMap(xml, new PublicLine(type, name, id));
}
}
}
循環讀取每一行,當讀取到某行空的情況下則退出循環 在這裡我們對attr的轉換采用過濾的方式,所以凡事具有“styleable“ 標示的行都可以直接拋棄掉。 正常情況下,我們讀取一行,並按空格劃分開;然後分別讀取到 type 、name、id 的值。 如果當前類型為 style 類型,那麼我們的名稱需要把 “_” 替換為 “.” 。 把當前識別到的信息存儲到 Map 中。
如果這裡對 attr 的識別采取的是讀取 styleable 數組中的信息,然後再按索引找尋對應INT值的話,代碼就應該是這樣:
public static void buildXml(BufferedReader reader, Map xml) {
while (true) {
String line;
try {
line = reader.readLine();
if (line == null || line.trim().length() == 0)
return;
} catch (IOException e) {
e.printStackTrace();
continue;
}
String[] split = line.split(" ");
if (split.length == 0)
continue;
if (line.contains("int[]")) {
// convert attr xml
String name = split[2].trim();
line = line.substring(line.indexOf("{") + 1, line.lastIndexOf("}"));
System.out.println(line);
String[] ids = line.split(",");
if (ids.length > 0) {
readStyleableXml(reader, xml, ids, name);
}
} else {
// convert other xml
String type = split[1];
String name = split[2];
String id = split[3];
if (type.contains("style"))
name = name.replace("_", ".");
saveToMap(xml, new PublicLine(type, name, id));
}
}
}
@SuppressWarnings("unused")
public static void readStyleableXml(BufferedReader reader, Map xml, String[] ids, String name) {
for (String id : ids) {
String line;
try {
line = reader.readLine();
if (line == null)
continue;
} catch (IOException e) {
e.printStackTrace();
continue;
}
String[] split = line.split(" ");
String lName = split[2].substring(split[2].indexOf(name) + name.length() + 1);
String lId = ids[Integer.parseInt(split[3].trim())];
saveToMap(xml, new PublicLine("attr", lName, lId));
}
}
在這裡我們就需要多一個方法,在這個方法中我們按照當前控件的數組長度進行循環讀取其後對應數組長度的行,然後再解析並存儲。
public static void saveToMap(Map xml, PublicLine line) {
try {
xml.putIfAbsent(line.getKey(), line);
System.out.println(">>>: " + line.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
存儲方法相對簡單,我們采用 Map 的 “putIfAbsent“ 方法,該方法有一個作用,就是會判斷當前 KEY 是否存在,如果不存在則存儲。
public static void saveFile(List lines, BufferedWriter writer) throws IOException {
// write head
writer.append("");
writer.append("\n");
writer.append("");
writer.append("\n");
for (PublicLine line : lines) {
try {
writer.append(" ");
writer.append(line.toString());
writer.append("\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
// write footer
writer.append(" ");
writer.flush();
}
存儲過程分為存儲頭部,存儲集合數據,以及存儲底部來完成。
public static void close(Closeable closeable) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
關閉流的方法就更加簡單了,無非就是單獨出來了而已。
此時我們把R文件放到根目錄,運行一次代碼,控制台將會輸出:
此時已經生成了一份 public 文件,與R文件對應看一下:
可以看出效果是相當不錯,把public文件拷貝到庫中直接投入使用。
為了測試是否成功,我按照開源庫 Genius-Android 中的 sample 項目代碼在 Eclipse 中完成了一份界面布局。
所有控件與資源都能正常使用。
在這裡我把代碼生成了一份 jar 文件,你可以下載該文件,並把 R.txt 文件放到同一目錄,運行該 jar 文件既可得到 public 文件。
同時我把所有的源文件與代碼都上傳到了 GtiHub 的 BeFoot 項目中;以後的所有例子也都會更新到該項目中。
BeFoot import-aar-eclipse ConverToXml.jar如果有哪裡不對,或者說的不夠清楚還請多多指教。
今天github 排行榜上突然出現了 谷歌最新推出的Android 最新控件FlexboxLayout 。 FlexboxLayout究竟是什麼東西
Volley簡介我們平時在開發Android應用的時候不可避免地都需要用到網絡技術,而多數情況下應用程序都會使用HTTP協議來發送和接收網絡數據。Androi
對於從Eclipse遷移項目到Android Studio中添加.jar文件和.so文件無疑是一件很重要也是很頭疼的問題!在最新版本中,默認是自動打包libs下面的所有.
本片博客的事例是根據我自己項目中的部分需求來的,所以有些和這個不相關的內容和源碼,大家可以忽略不計。這種發說說的功能,我也是折騰了很久,今日才得知道,大神請不要見笑,,l