Eclipse/tomcat 如何實現應用熱部署和熱啟動

天府雲創發表於2018-11-28

使用者希望應用程式能夠快速響應並載入。 一個啟動速度慢的應用程式不符合這個期望,可能會令使用者失望。 這種糟糕的體驗可能會導致使用者在應用商店中對您的應用進行糟糕的評價,甚至完全放棄您的應用。

        如果是對原來的類方法修改,那麼熱啟動非常好用;如果是新增了新的類或方法(非最上層的controller),那麼此方法也是好用的;但是如果是在controller上新增了新的介面,就不好用了,必須重啟Tomcat。

尤其是針對蘋果發版app或者下架上架應用,一般的解決方案:

1、“熱啟動”更新資料

2、發新版“迭代”修復bug

Eclipse/tomcat 如何實現熱部署和熱啟動

1、 熱部署:就是容器狀態在執行的情況下重新部署整個專案.在這種情況下一般整個記憶體會清空,重新載入.簡單來說就是Tomcat或者其他的web伺服器會幫我們重新載入專案.這種方式可能會造成session丟失等情況。

2、熱載入:就是容器狀態在執行的情況下重新載入改變編譯後的類.在這種情況下記憶體不會清空,sessin不會丟失,但容易造成記憶體溢位,或者找不到方法。因為記憶體無法轉變成對像. 一般改變類的結構和模型就會有異常,在已經有的變數和方法中改變是不會出問題的。

eclipse配置熱啟動:

在基於Java的實現熱部署、熱載入的過程中,類裝入器扮演著重要的角色。類裝入器不能重新裝入一個已經裝入的類,否則會報java.lang.LinkageError,但只要使用一個新的類裝入器例項,就可以將類再次裝入一個正在執行的應用程式。一般debug模式都支援熱載入。

但我在這裡遇到了eclipse使用debug啟動卻並沒有熱載入的狀況,這樣對於開發來說是非常耗時的,因為每更改一次class檔案都需要重新編譯。下面我就如何使用eclipse進行熱載入,做一個簡單介紹:

啟動eclicpse 找到下面這個目錄

 開啟server.xml,找到

程式碼如下:

<Context docBase="dreamlive" path="/ROOT" reloadable="true" crossContext="true" source="org.eclipse.jst.jee.server:dreamlive"/>

這行程式碼,在你部署新專案的時候,docBase和source會隨著改變,現在還是熱部署的狀態,這樣每次更改class檔案都會自動的去編譯,比較耗時,接下來我們更改一下配置:

程式碼如下:

<Context docBase="dreamlive" path="/ROOT" reloadable="false" crossContext="true" source="org.eclipse.jst.jee.server:dreamlive"/></Host>

 這裡將reloadable改為false,新增crossContext="true",這樣就能進行 熱啟動 了,注意:需要用debug啟動

不過我們需要在每次部署新專案的時候,重新去更改這個配置,我們可以觀察每次部署新專案的時候,server.xml這個檔案的動態變化,設定完成之後如果啟動專案熱載入並沒有生效,那麼勾選eclipse-->project-->Build Automatically,加上自動編譯。

下面附上每個屬性的含義   

  • path 指出你的訪問路
  • docBase指出你的存放路徑
  • debug 為設定debug的等級0提供最少的資訊,9提供最多的資訊
  • reloadable=true時 當web.xml或者class有改動的時候都會自動重新載入不需要從新啟動服務
  • crosscontext="true"表示配置的不同context共享一個session(可以不配置)

Tomcat配置熱啟動

具體方法如下:

第一步: Tomcat安裝目錄下,修改 conf/server.xml 中的 Host 配置,設定其reloadable屬性為true,即在Host標籤中新增reloadable="true"這一句,重啟Tomcat使配置檔案生效。

第二步:在conf資料夾中的web.xml檔案中新增

<init-param>
 <param-name>development</param-name>
 <param-value>true</param-value>
</init-param>

第三步:重啟tomcat伺服器,使修改生效。

專案在開發階段經常會修改後臺Java程式碼,但是每次make project後都需要重啟Tomcat才能是程式碼生效。

解決辦法是修改Tomcat的conf目錄下server.xml的配置檔案,使reloadable=true,這樣每次修改n個java檔案,make的時候把這些class檔案到Tomcat監聽目錄下,會自動提示有n個class reloaded。

以下是server.xml的修改:

<Context path="C:\Users\enweitech\Desktop\jms\out\artifacts\jms_Web_exploded" debug="1" reloadable="true"/>

完全host節點:

 1       <Host name="localhost"  appBase="webapps"
 2             unpackWARs="true" autoDeploy="true">
 3 
 4         <Context path="C:\Users\enweitech\Desktop\jms\out\artifacts\jms_Web_exploded" debug="1" reloadable="true"/>
 5         
 6         <!-- SingleSignOn valve, share authentication between web applications
 7              Documentation at: /docs/config/valve.html -->
 8         <!--
 9         <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
10         -->
11 
12         <!-- Access log processes all example.
13              Documentation at: /docs/config/valve.html
14              Note: The pattern used is equivalent to using pattern="common" -->
15         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
16                prefix="localhost_access_log." suffix=".txt"
17                pattern="%h %l %u %t &quot;%r&quot; %s %b" />
18 
19       </Host>

 經測試有效,使用的Tomcat7.0,但是對於新新增的class不起作用,只有對已經存在的Java類修改了,會立馬reload。

冷啟動、熱啟動時間效能優化

Launch Internals 
應用程式啟動可以發生在三種狀態之一,每種狀態都會影響您的應用程式對使用者可見的時間長度:冷啟動,熱啟動和溫熱啟動。 冷啟動,你的應用程式從頭開始。 在其他的狀態,系統需要將應用程式從後臺引向前臺。 我們建議您始終根據冷啟動的情況進行優化。 這樣做可以改善熱啟動和溫熱啟動的效能。

為了優化您的應用程式以實現快速啟動,常常需要了解系統和應用程式級別發生了什麼以及它們如何互動

冷啟動

冷啟動是指應用程式從頭開始:系統的程式在開始之前還沒有建立應用程式的程式。 冷啟動發生在您的應用程式自啟動裝置以來第一次啟動的情況下,或由於系統殺死應用程式。 這種型別的啟動在縮短啟動時間方面提出了最大的挑戰,因為系統和應用比其他啟動狀態有更多的工作要做。

在冷啟動開始時,系統有三項任務。 這些任務是:

  1. 載入並啟動應用。
  2. 啟動後立即顯示應用程式的空白開始視窗。
  3. 建立應用程式。

只要系統建立應用程式程式,應用程式程式就負責下一個階段。 這些階段是:

  1. 建立該應用物件
  2. 啟動主執行緒
  3. 建立主activity
  4. 填充views
  5. 鋪設螢幕
  6. 執行初始繪製  一旦應用程式完成第一次繪製,系統程式就會將當前顯示的背景視窗替換為主要活動。 此時,使用者可以開始使用該應用程式。 
  7. 這裡寫圖片描述

效能問題可能在建立應用程式和建立activity時出現

Application creation

當您的應用程式啟動時,空白的起始視窗將保留在螢幕上,直到系統第一次完成繪製應用程式。 此時,系統程式為應用程式換掉了啟動視窗,允許使用者開始與應用程式進行互動

如果您在自己的應用程式中過載了Application.oncreate(),系統將呼叫您的應用程式物件上的onCreate()方法。 之後,應用程式會生成主執行緒,也稱為UI執行緒,並執行建立主Activity的任務。

從這一點來看,系統和應用程式級別的程式按照應用程式生命週期階段進行。

Activity的建立

應用程式程式建立您的活動後,活動執行以下操作:

  1. 初始化值
  2. 呼叫構造方法
  3. 呼叫回撥方法,按照activity的生命週期

通常情況下,onCreate()方法對載入時間影響最大,因為它執行的開銷最高:載入和擴充檢視,並初始化活動執行所需的物件

熱啟動 
應用程式的熱啟動比冷啟動更簡單,開銷更低。 在一個溫暖的開始,所有的系統都會把你的activity帶到前臺。 如果您的所有應用程式的活動仍駐留在記憶體中,則應用程式可以避免重複物件初始化,佈局填充和渲染

但是,如果某些記憶體已被清除以響應記憶體調整事件(如onTrimMemory()),則將響應熱啟動事件重新建立這些物件。

熱啟動顯示與冷啟動場景相同的螢幕行為:系統程式顯示空白螢幕,直到應用完成activity的渲染。

Lukewarm start

一個lukewarm start包含了在冷啟動期間發生的一些操作子集; 與此同時,它代表的不是一個熱啟動的開始。 有許多潛在的狀態可以被認為是溫和的開始。 例如:

  • 使用者退出應用程式,但重新啟動它。 該程式可能會繼續執行,但應用程式必須通過呼叫onCreate()從頭開始重新建立活動
  • 系統從記憶體中清除您的應用程式,然後使用者重新啟動它。 程式和Activity需要重新啟動,但是任務可以從saved instance state bundle 傳遞給onCreate()

分析啟動效能

為了正確地診斷開始時間效能,您可以跟蹤顯示啟動應用程式所需時間 
要重現使用者體驗,請確保以非可除錯模式對應用進行配置。 可除錯模式啟用除錯功能,導致啟動時間非典型的使用者體驗。

初始時間顯示

從Android 4.4(API級別19),logcat包含一個輸出行,其中包含一個名為Displayed的值。 該值表示啟動過程和完成在螢幕上繪製相應活動之間所經過的時間量。 經過的時間包括以下一系列事件:

  1. 啟動程式
  2. 初始物件
  3. 建立並初始化activity
  4. 填充佈局
  5. 在第一時間繪製你的應用

報告的日誌行與以下示例類似:

ActivityManager: Displayed   com.android.myexample/.StartupTiming: +3s534ms

如果您正在從命令列或終端跟蹤logcat輸出,則查詢已用時間很簡單。 要在Android Studio中查詢已用時間,您必須在logcat檢視中禁用篩選器。 禁用過濾器是必要的,因為系統伺服器,而不是應用程式本身,服務於這個日誌。

一旦你做了適當的設定,你可以輕鬆地搜尋正確的術語來檢視時間。 圖2顯示瞭如何禁用過濾器,並在底部輸出的第二行顯示了Displayed時間的logcat輸出示例。

Logcat輸出中的“顯示”度量不一定會捕獲所有資源載入和顯示之前的時間量:它會遺漏佈局檔案中未引用的資源或應用程式作為物件初始化的一部分建立的資源。 它排除了這些資源,因為載入它們是一個內嵌的過程,並且不會阻止應用程式的初始顯示

您也可以使用ADB Shell活動管理器命令執行您的應用程式來測量初始顯示的時間。 這是一個例子:

adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN

顯示的度量像以前一樣出現在logcat輸出中。 您的終端視窗還應顯示以下內容:

Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete

-c和-a引數是可選的,並讓您為intent指定和

完全呈現的時間

您可以使用reportFullyDrawn()方法來測量應用程式啟動和完成顯示所有資源和檢視層次之間的已用時間。 在應用執行延遲載入的情況下,這可能很有價值。 在延遲載入中,應用程式不會阻止視窗的初始繪製,而是非同步載入資源並更新檢視層次結構。

如果由於延遲載入,應用程式的初始顯示不包含所有資源,則可以將所有資源和檢視的載入和顯示視為一個單獨的度量標準:例如,您的UI可能已完全載入,繪製了一些文字, 但還沒有顯示應用程式必須從網路中獲取的影象。

為了解決這個問題,您可以手動呼叫reportFullyDrawn()讓系統知道您的活動已經完成了延遲載入。 使用此方法時,logcat顯示的值是從建立應用程式物件到呼叫reportFullyDrawn()的時間。 這裡是一個logcat輸出的例子:

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

logcat輸出有時會包含一個總時間,正如在“初始顯示時間”中所述 
如果你知道你的顯示時間比你想要的慢,你可以繼續嘗試確定啟動過程中的瓶頸

查詢瓶頸的兩個好方法是Android Studio方法跟蹤工具和內聯跟蹤。 要了解Method Tracer,請參閱工具文件。 
如果您無法訪問Method Tracer工具,或無法在正確的時間啟動該工具以獲取日誌資訊,則可以通過在應用程式和活動的onCreate()方法中進行內嵌跟蹤來獲得類似的洞察。 要了解內聯跟蹤,請參閱跟蹤功能參考文件以及Systrace工具

常見問題 
本節討論經常影響應用程式啟動效能的幾個問題。 這些問題主要涉及初始化應用程式和活動物件,以及載入螢幕

沉重的應用初始化 
當您的程式碼覆蓋Application物件時,啟動效能會受到影響,並在初始化該物件時執行繁重的工作或複雜的邏輯。 如果您的應用程式子類執行不需要完成的初始化,您的應用程式可能會浪費時間在啟動過程中。 一些初始化可能是完全不必要的:例如,當應用程式實際啟動以響應意圖時,初始化主要活動的狀態資訊。 意圖是,應用程式只使用以前初始化的狀態資料的一個子集。

應用程式初始化過程中的其他問題包括垃圾收集事件的影響或數量眾多,或磁碟I / O與初始化同時發生,進一步阻止初始化過程。 垃圾收集尤其是Dalvik執行時的一個考慮因素; Art執行時同時執行垃圾收集,最大限度地減少操作的影響

診斷問題 
您可以使用方法跟蹤或內聯跟蹤來嘗試診斷問題。 
方法追蹤 
執行Method Tracer工具顯示callApplicationOnCreate()方法最終呼叫com.example.customApplication.onCreate方法。 如果該工具顯示這些方法需要很長時間才能完成執行,那麼您應該進一步研究以檢視正在進行的工作。 
內聯追蹤 
使用內聯追蹤來調查可能的罪魁禍首,包括:

  • 你的應用初始onCrate() 方法
  • 全域性的單例物件初始
  • 任何磁碟I / O,反序列化,或瓶頸期間可能發生的緊密迴圈

    解決問題 
    無論問題出在不必要的初始化還是磁碟I / O,解決方案都會呼叫延遲初始化物件:只初始化那些立即需要的物件。 例如,不是建立全域性靜態物件,而是移動到單例模式,應用程式只在第一次訪問物件時建立物件。 另外,考慮使用像Dagger這樣的依賴注入框架來建立物件,並且依賴關係是當它們被首次注入時

    繁重的activity 初始化 
    activity建立通常需要大量的高開銷工作。 通常,有機會優化這項工作來實現效能改進。 這些常見問題包括:

  • 填充龐大或者複雜的佈局

  • 阻塞磁碟的螢幕繪圖或網路I / O
  • 載入並解碼圖片
  • 柵格化VectorDrawable物件
  • 在activity初始化其他子系統 
    解決問題重點內容 
    在這種情況下,方法跟蹤和內聯跟蹤也是有用的

Method tracing 
當執行Method Tracer工具時,特定的區域將關注於您的應用程式的Application子類的建構函式和com.example.custom的Application.onCreate()方法。

如果該工具顯示這些方法需要很長時間才能完成執行,那麼您應該進一步研究以檢視正在進行的工作。 
Inline tracing 
使用內聯追蹤來調查可能的罪魁禍首,包括:

  • 你的應用初始onCreate() 方法
  • 全域性的單例物件初始
  • 任何磁碟I / O,反序列化,或瓶頸期間可能發生的緊密迴圈 
    解決問題 
    有很多潛在的瓶頸,但是兩個常見的問題和解決方法如下

    檢視層次越大,應用所需的時間就越多。 你可以採取兩個步驟來解決這個問題

  • 通過減少冗餘或巢狀佈局來平坦您的檢視層次結構
  • 不要在啟動時填充不需要顯示的部分UI。使用ViewStub物件作為應用程式可以在更合適的時間填充的子層次結構的佔位符來代替。

在主執行緒上完成所有的資源初始化操作也會減慢啟動速度。 你可以解決這個問題如下:

  • 把所有可以通過懶載入的初始資源放到不同的執行緒去執行
  • 允許應用載入展示你的檢視,並且可以稍後跟新視覺屬性通過bitmaps 和其他資源。

主題啟動螢幕 
您可能希望主題化您的應用的載入體驗,以便應用的啟動螢幕與應用的其餘部分在主題上保持一致,而不是與系統主題一致。 這樣做可以隱藏一個緩慢的activity 啟動。

實現主題啟動螢幕的常用方法是使用windowDisablePreview主題屬性來關閉啟動應用程式時系統程式繪製的初始空白螢幕。 但是,這種方法會導致比不抑制預覽視窗的應用程式更長的啟動時間。 此外,它強制使用者在活動啟動時等待而沒有任何反饋,使他們不知道該應用程式是否執行正常。

診斷問題 
您可以通過觀察使用者啟動應用程式時的慢速響應來經常診斷此問題。 在這種情況下,螢幕可能會被凍結,或者停止響應輸入 
解決問題 
我們建議您不要禁用預覽視窗,而要遵循常見的Material Design模式。 您可以使用該活動的windowBackground主題屬性為開始活動提供一個簡單的自定義繪圖。

例如,您可以建立一個新的可繪製檔案,並從佈局XML和應用程式清單檔案中引用它,如下所示: 
佈局檔案:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
  <!-- The background color, preferably the same as your normal theme -->
  <item android:drawable="@android:color/white"/>
  <!-- Your product logo - 144dp color version of your app icon -->
  <item>
    <bitmap
      android:src="@drawable/product_logo_144dp"
      android:gravity="center"/>
  </item>
</layer-list>

Manifest file:

<activity ...
android:theme="@style/AppTheme.Launcher" />

轉換回正常主題最簡單的方法是在呼叫super.onCreate()和setContentView()之前呼叫setTheme(R.style.AppTheme):

public class MyMainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    // Make sure this is before calling super.onCreate
    setTheme(R.style.Theme_MyApp);
    super.onCreate(savedInstanceState);
    // ...
  }
}

相關文章