本篇文章,作者分享了他做程式碼整理的心得,我非常推薦大家參考作者的做法,擁有自己的程式碼管理倉庫、筆記記錄。
包括我也會思考下,如何做程式碼管理,也會借鑒作者的一些做法。
很慚愧,自己寫Android好幾年,寫了很多的測試demo,等哪天忽然想找的時候找不到了,又重新新建個專案,從頭開始,非常浪費時間。
剛好github也開放了個人私有的倉庫,配合一波,非常nice。
借本篇文章,希望大家養成好習慣有所幫助~
概述
為什麼會有這篇文章呢,是因為我發現我以前在學習開發知識時,程式碼整理得太不好了。經常是會發生以前學習過的知識,現在卻找不到程式碼在哪裡的情況,於是又要重新開始。
因為Android開發涉及的內容很多,如果想要系統的去整理筆記的話,就必須要分檔案夾,分專案進行管理。每個專案對應著不同方向的知識,而我們很多時候,每個練習專案都有著很多需要重覆配置的東西。
比如常用的依賴啊,工具類,分包啊,還有基本的測試按鈕,比如你想學習資料庫操作,可能就會在xml佈局中書寫4個Button,再寫上id,點選事件…基本上這些弄完,10分鐘過去了。
而我接下來分享的小技巧,可能讓你把原本半小時才能做完的事情,在5分鐘內完成,因為我們會編寫一個練習專案的模板,把重覆的東西抽取出來。
文章不僅僅是為了介紹如何節約時間,更是因為在專案開發中經常要對各種Api進行封裝處理,這也算是一個封裝示例了。
專案採用Java語言,同時提供kotlin語言版本作為小彩蛋,需要說明的是,專案的開發環境是3.1.1,在3.0以下的軟體執行起來需要調整,具體怎麼調整我不告訴你,因為推薦升級軟體。
專案原始碼已經上傳的Github倉庫的template檔案夾內了
https://github.com/13531982270/Blog4
現在,我們建立我們的template專案
建立專案結構
我們建立兩個module,一個是預設的app,一個是appk,一個庫common
-
app用來編寫Java測試程式碼
-
appk用來編寫kotlin測試程式碼
-
common庫用於存放一些公共的依賴,工具類
當然,這裡的app和appk並不要求百分百嚴格區分Java和kotlin程式碼,看實際情況,比如你比較多的時間是在使用Java,那麼就在app裡面寫,偶爾也可以在裡面寫些kotlin,這都無所謂。
現在開啟右上角的File-Prokect Structure,我們把common庫的依賴新增到app和appk中,這樣兩個module就能使用Common裡面的東西啦。
App執行庫
完成分包,配置主題,建立Menu選單,drawable-hdpi,color,assets檔案夾操作
1)建立專案的包結構
我們在module下建立以下包
然後,我來具體解釋一下每個包的含義(其實看名字也挺清晰的了)
-
adapter 配接器包,用來存放RecyclerView等的配接器
-
app 建立自定義Application,因為後面可能配置很多第三方,而第三方的初始化建議是每個第三方單獨的建立一個單例樣式的類,提供一個init方法去初始化,不然全都寫在Application裡面,Application程式碼會太多,太凌亂,後面會提供一個AppConfig類作為示範,可以使用外掛去生成單例樣式的類,參考我的另一篇文章 https://www.jianshu.com/p/6a3b0ae4aeb4
-
module 業務包,用來存放我們的各種實際練習程式碼,這裡視你的專案是用來做什麼來定,假如你是實際開發,就是一個登入包,一個主頁包,如圖,假如該專案你是用來學習Android原生控制元件,那麼下麵就是imageview,textview這樣子;
-
還有一個refrence包,這是用來存放你的練習草稿的,怎麼說呢,你在練習的時候如果有什麼需要單獨提取出來測試的,你都可以在裡面建立一個Activity,那個draft.txt也很好理解,就是草稿,你可以把在網上複製的程式碼先放到裡面,後面寫時可以去參考;
-
net包用來存放網路請求相關的類;
-
util包用來存放工具類;
-
view包用來存放用到的自定義控制元件,可能你會有疑問了,實際開發中,view包不是都是放置在Common通用庫裡面的嗎?是的,因為我們實際開發可能會使用元件化,多Module的開發,把view提取到Common通用庫確確實實是為了讓多個module來使用,但是這裡我們只是練習模板專案,如果還要使用元件化那這個模板專案也就太複雜了,不符合我們當初節約時間的初衷,所以這裡直接是,哪個Module的view就放在哪個module。
你可能還會有疑問,你App的包分得都那麼詳細了,那麼還要Common通用庫來做什麼呢?
我是這麼打算的,像下麵舉例子的L工具類,Glide工具類封裝,都屬於專案通用,必備的工具類,那就放在Common庫,這樣後面我們App模組的類不會太多。
而像一些只有在特定業務場景下才會使用達到的util工具類,你就直接放在App模組的util包下就可以了,畢竟工具類實在是太多了。我們不可能將每種都先考慮好放在Common庫,而如果不在App下也設定一個util包,特定業務場景的工具類需要在Common庫來回跳轉就太麻煩了
比如下麵這篇教程https://www.jianshu.com/p/8b7186624ea5 中修改底部導航切換效果的工具類BottomNavigationViewHelper ,Common庫還有的作用就是把經常用到的依賴,不至於使Module的程式碼太多。
2)配置主題和顏色
以前自己學習東西建立專案時,很喜歡把主題改成沒有標題的,然後再到建立的Activity裡面去建立幾個Button,寫點選事件去測試,這是多浪費時間的事情啊!
其實我們可以使用預設的主題來解決這個問題(為什麼能解決後面會說),其實預設的主題並不醜,醜的是它的顏色,所以我們到res-values包下的colors檔案,修改一下它的配色
<resources>
<color name="colorPrimary">#008577color>
<color name=“colorPrimaryDark”>#00574Bcolor>
<color name=“colorAccent”>#D81B60color>
resources>
執行程式
或者更加青春一點的藍色
<color name="colorAccent">#cc0094ffcolor>
<color name=“colorPrimary”>#33aaffcolor>
<color name=“colorPrimaryDark”>#2299eecolor>
你看,這樣就好看多了吧
3)建立Menu檔案,實現點選事件
上面已經解決了標題欄醜的問題,可能你還會覺得,預設的帶標題的主題,未免有點佔用我們的螢幕。是的,但是它的標題,卻帶給我們一個平時用的少,但是測試起來用的爽的東西,Menu
我們在res包下建立Menu檔案夾,再建立menu.xml檔案.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/one"
android:title="one" />
<item
android:id="@+id/two"
android:title="two" />
<item
android:id="@+id/three"
android:title="three" />
<item
android:id="@+id/four"
android:title="four" />
<item
android:id="@+id/five"
android:title="five" />
<item
android:id="@+id/six"
android:title="six" />
<item
android:id="@+id/seven"
android:title="seven" />
<item
android:id="@+id/eight"
android:title="eight" />
menu>
8個按鈕,足夠你一個Activity測試了吧,畢竟比如練習資料庫操作也就是增刪改查,再多的程式碼也不適合放在同一個Activity裡了,後面我們希望看到的,就是點選右邊的..,就可以彈出我們的Menu測試按鈕
之後我們在module-refrence包下建立JavaActiivty,將Activity作為第一啟動項,編寫以下程式碼來實現選單的點選事件。
public class JavaActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java);
setTitle("tonjies的頁面");//設定標題名稱
}
//建立選單
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
//選單選項
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int i = item.getItemId();
switch (i) {
//
case R.id.one:
Toast.makeText(this, "one", Toast.LENGTH_SHORT).show();
break;
//
case R.id.two:
Toast.makeText(this, "two", Toast.LENGTH_SHORT).show();
break;
//
case R.id.three:
Toast.makeText(this, "three", Toast.LENGTH_SHORT).show();
break;
//
case R.id.four:
Toast.makeText(this, "four", Toast.LENGTH_SHORT).show();
break;
//
case R.id.five:
Toast.makeText(this, "five", Toast.LENGTH_SHORT).show();
break;
//
case R.id.six:
Toast.makeText(this, "six", Toast.LENGTH_SHORT).show();
break;
//
case R.id.seven:
Toast.makeText(this, "seven", Toast.LENGTH_SHORT).show();
break;
//
case R.id.eight:
Toast.makeText(this, "eight", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
}
執行效果如下,註意到我們onCreate方法裡面的 setTitle(“tonjies的頁面”);
如果我們不設定特定的標題,預設的標題名稱就全都是專案名稱,這樣所有的頁面標題都是一樣,不利於區分。所以我們可以透過setTitle設定標題來區分是哪一個介面
ok,我們已經完美的完成了,可能你會覺得,這樣程式碼量也不少啊,是的,但是它在所有Activity裡面的程式碼都是一樣的啊!
我們需要在哪個Activity用,就直接複製貼上就可以了,其實也不推薦複製貼上的方法,我們有更高效的,使用軟體自己的程式碼模板功能,接下來演示一下該方法
使用程式碼模板製作選單.gif
程式碼模板具體的製作方法可以檢視慕課網的教程的第三章,第二節
https://www.imooc.com/learn/924
不過實際上我們一般也用不了8個測試按鈕這麼多,我們快捷鍵一般設定4個就夠了,當同時也把8個按鈕的設定了,以防萬一,比圖4個按鈕的快捷鍵是btnMenu,8個按鈕的快捷鍵是btnMenu8這樣。
另外如果你是使用Java程式碼開發,強烈推薦新增一個ButterKnife來節約我們findId控制元件的時間。
4)建立drawable-hdpi,color,asstes檔案夾
可以將一些大圖放置到比較高解析度的檔案夾下,因為放置在該檔案夾裡面的圖片,多數情況下,如果螢幕解析度較低,系統會幫助你進行一些壓縮處理,避免當你不使用圖片載入框架的時候,ImageView有可能因為圖片過大而導致記憶體上限溢位,程式崩潰。
還有一個原因就是保持程式碼的整潔,我通常是.xml結尾的,例如選擇器,使用shape製作的圖片,vector圖片就放置在drawable,而其它能直接看到的格式的,如,png,jpg,就會放置在drawable-hdpi。
我們可以事先防止幾張圖片在模板裡,以備測試的不時之需。
有時候需要用網路圖片來測試,所以也可以在Common的string.xml檔案夾下新增幾條網路圖片連結
<resources>
<string name="app_name">templatestring>
<string name=“meizi_01”>https://ww1.sinaimg.cn/large/0065oQSqly1fu7xueh1gbj30hs0uwtgb.jpgstring>
resources>
而上面的顏色選擇color包可能會在某些場景下用,比如使用BottomNavigationView的時候。
最後是我們的assets檔案夾,用來存放如字型,本地除錯html頁面等,在原始碼裡的Refrence包下會有呼叫字型檔案的參考程式碼。
Common通用庫
該庫用來存放我們練習中幾乎必備的工具類和相關的依賴,我們把這些東西提取在Common包裡面,讓app去新增它,這樣app就不用顯示過多的依賴,顯得比較簡潔,而且我們的appk同樣可以使用它。
1)Log工具類
我們在common庫中建立util檔案夾,然後建立L日誌列印工具類,簡化我們後面的日誌操作:
/**
* Created by 舍長 on 2018/12/14
* describe: 日誌工具類
*/
public class L {
public void d(String msg) {
Log.d("helloWorld", msg);
}
}
後面就可以直接L.d使用啦。
不過實際專案開發中要再加一個過濾操作,因為我們的App上線之後,如果Log不關閉,有洩漏資料的風險,涉及到安全問題。所以我們可以這樣做。
public class L {
private static final Boolean isOpen=true;
//普通版
public static void d(String msg) {
//開啟日誌
if(isOpen){
Log.d("helloWorld", msg);
}else {
//關閉日誌
}
}
//進行String型別轉換,所以我們可以隨意傳入任意型別的引數
public static void d(Object msg) {
String string = msg.toString();
Log.d("helloWorld", string);
}
}
設定一個變數,如何我們想要列印的話,就設定為true,不想列印的話,就將它設定為Fasle就可以啦,我們還多載了一個引數Object的d方法,該方法的作用是將傳入的任意型別的字串轉換成String型別
這樣做是為了方便我們可以傳入任何型別的資料,這樣我們就可以省去將資料轉成String型別這一步了,
相信這難不倒你啦,接著我們來看第二個封裝類,SharedPreferences封裝類。
2)SharedPreferences封裝類ACache
這裡我們需要考慮一個問題,就是SharedPreferences和Log列印不一樣,它是需要獲取到當前的Context物件的,如下麵所示在Activity中使用的getSharedPreferences,其實它隱藏了context的使用,實際程式碼是context.getSharedPreferences。
存資料
SharedPreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","tonjies");
editor.putInt("age",20);
editor.apply();
取資料
SharedPreferences preferences = getSharedPreferences("data", MODE_PRIVATE);
String name = preferences.getString("name", "");
int age = preferences.getInt("age", 18);
L.d("name:" + name);
L.d("age" + age);
你第一個想到的,可能是可以透過引數,將當前Activity的Context物件傳進來進行封裝。
可是這樣每次都要寫個Activity.this,一點都不酷,所以我們使用另外一種方法,封裝統一的全域性Context物件,這在元件化的開發中也很常見,實際操作並不複雜,我們來看一下.
我們的Common庫中建立app包,在該包下建立BaseApp類,讓該類繼承於Application,作為我們的基礎Application,然後我們提供一個getContext()方法來傳回Context物件.
3)AGide
封裝思想在開發中應該是很重要的,其中不僅僅是因為我們為了節約時間,更是為了穩妥起見。
舉個例子,我們如果使用圖片載入框架glide,我們並不會直接的去使用它,而是會把它的程式碼寫在一個包裝類裡面,使用包裝類的方法去載入圖片,這樣實際上還是使用glide,那麼這樣有什麼意義呢?
/**
* Created by 舍長 on 2019/1/2
* describe:Glide封裝類
*/
public class AGlide {
//獲取Url地址
public static void getUrl(Context context, String url, ImageView imageView) {
Glide.with(context).load(url).into(imageView);
}
}
試想這麼一個場景,某一天你因為某些原因,不得不放棄glide,使用另外一個圖片載入框架,那簡直就是災難,因為你程式碼中使用的地方實在是太多了,你要一個個的去註釋,然後改成新的Api,而如果你是使用包裝類,你只要把包裝類裡面的glide程式碼改了就行了,對整個專案並沒有太大的影響:
public class AGlide {
//獲取Url地址
public static void getUrl(Context context, String url, ImageView imageView) {
// Glide.with(context).load(url).into(imageView);
//偽程式碼,這裡假裝使用Picasso的Api
}
}
工具類部分舉例子結束,除了這些常用的,我們還可以在工具類中封裝Toast工具類啊,字型工具類啊,在原始碼中我會把它兩補上。
3)新增常用依賴
這裡需要註意的是,3.0以上後,AndroidStudio新增依賴推薦使用api和implementation和替代compile,不然build時就會報紅(雖然還是可以執行)。
implementation和api的區別在於,implementation用於執行Module,api用於庫新增依賴,什麼意思呢,就是如果你在Common庫裡使用implementation來新增依賴,那麼你的AppModule是檢測不到你新增的依賴的。
換句話來說,implementation只能是當它自己的範圍使用,不管你是庫Module還是執行Module,所以如果我們想讓在Common通用庫裡面載入的框架到AppModule執行Module裡能使用的話,得使用api才行。
接下來我們在Common的budile.gradle下新增這幾個依賴:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//Anko框架,用於kotlin開發擴充套件
api "org.jetbrains.anko:anko-commons:0.10.7"
//圓形處理框架
api 'de.hdodenhof:circleimageview:2.2.0'
// 材料設計
api 'com.android.support:design:28.0.0'
// Retrofit庫
api 'com.squareup.retrofit2:retrofit:2.4.0'
api 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' // 此處一定要註意使用RxJava2的版本
api 'com.squareup.retrofit2:converter-gson:2.4.0' // 支援Gson解析
// Okhttp庫
api 'com.squareup.okhttp3:okhttp:3.11.0'
api 'com.squareup.okhttp3:logging-interceptor:3.11.0'
// 支援Gson解析
api 'com.squareup.retrofit2:converter-gson:2.4.0'
// RxJava
api 'io.reactivex.rxjava2:rxjava:2.1.7'
api 'io.reactivex.rxjava2:rxandroid:2.0.1'
// 圖片載入框架
api 'com.github.bumptech.glide:glide:4.7.1'
// 卡片佈局
api 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
mavenCentral()
}
新增常用第三方
我們可以選擇使用騰訊bugly進行程式的異常檢測,整合步驟比較簡單,官網檔案非常詳細。
因為也不需要新增程式特定包名,所以我們註冊一個應用,後面就可以一直用同一個ID了。
為什麼要使用它呢,如果是上線應用,就必須使用它來進行異常檢測,不然別的使用者使用出現適配問題了,鬼知道發生了什麼。
但是如果是對於我們學習來說的話,有時候透過AndroidStudio你檢測不出error在哪裡,雖然場景比較少,比如使用RxJava進行封裝的時候,如果是獨立於Actiivty之外的封裝,log檢測不出來。
實際練習程式碼的分類
1)筆記庫的搭建
有了上面的專案模板,後面我們需要的話,就可以直接複製,貼上,改個名字就可以直接上手學習了,但是我們的Android開發的內容很多,顯然不是一個專案能放得下的。
所以根據學習內容的不同,我大致將它們分為以下內容,進而我們可以為不同的練習內容設定不同的專案檔案名
開發內容 | 專案英文名 | 解釋 |
---|---|---|
Java基礎 | java | 這裡用IDEA |
kotlin基礎 | kotlin | 這裡用IDEA |
Android架構,開發規範 | framework | 如Mvp,mvc,程式碼抽取 |
Android控制元件 | materialDesign | Android原生控制元件,和原生有關的介面操作 |
四大元件 | unit | 包括Fragment也放在這裡 |
資料庫儲存 | data | 資料庫框框架,資料庫操作 |
Android混合開發 | mixed | 與H5的互動,ndk開發 |
開源庫 | library | 比較小的UI庫,比如PickerView |
主流框架 | frame | 如RxJava,Dagger等 |
第三方服務 | third | 友盟,騰訊bugly |
業務程式碼 | profession | 例如多語言切換 |
系統互動 | system | 如藍芽,相機,通知欄 |
Android安全與適配 | safety | 系統適配方案 |
你自己的練習專案1 | ||
你自己的練習專案2 |
大致就是這樣,我們複製以上數量的專案檔案夾,更改完名字後,我們的筆記庫到這裡就結束了,也許整個過程是稍顯複雜,但是我們只要製作一次。
後面我們需要找程式碼就方便很多,還有的好處就是如果你上次在某個方面學習到一半,有其他的事情,你下次可以很快的找到,繼續學習。
上面可能你會有個疑問,開源庫和主流框架的區別,其實本質上是沒有區別,但是我去區分它們的標準是,它們是否極其重要,不可替代的,極其重要的,如RxJava,就把它歸納到框架裡面。
而其它的,像提供某個UI控制元件的庫,使用的少,我們可以找到替代的,甚至我們可以自己寫一個的,就會被歸納到庫library裡面。
2)配合筆記軟體
上面製作的筆記目錄,不一定適合你,實際上你可以使用筆記軟體,石墨檔案製作屬於你自己的練習專案名稱目錄。
而有一些筆記,也是使用筆記軟體記錄比較合適。簡單來說就是,你的文字筆記目錄和你的練習程式碼倉庫的目錄是一致的。
不過需要註意的是,文字筆記和我們的練習程式碼倉庫一樣,不同類別的筆記是重新建立新的筆記來寫的
這是因為石墨檔案如果你這是同一份檔案寫的內容太多就會卡,這裡我把我的總目錄檔案發現出來作為參考:
點進去後就是每一類知識的具體筆記。
不過不同類別的檔案都會被放置在Android筆記的檔案夾中:
好了,本篇文章就到這裡了,老實說,這是我寫的30篇文章中感覺寫得最累的一次,因為涉及的東西雖然不難,但是很多,所以很累。但是本篇文章基本都是乾貨,實際運用中,能節約你大量配置專案的時間。
文章並沒有對RxJava和Retrofit的封裝進行詳細的講解,因為這一塊講起來是得花很多的篇幅,不過如果真要說封裝專案模組,它又幾乎是必需的。
再叨一下,例如元件化方案 https://blog.csdn.net/guiying712/article/details/55213884,路由框架 https://blog.csdn.net/zhaoyanjun6/article/details/76165252的學習,雖然作為練習專案模組我不想用,但是實際開發用的還是蠻多的,安利,安利。