Whatever is worth doing is worth doing well.
概述:
隨著應用的不斷迭代,應用的體積不斷增大,專案越來越臃腫,冗餘增加.專案新功能的新增,無法確定與使用者匹配性,發生嚴重異常往往牽一髮而動全身,只能緊急釋出補丁版本,強制使用者進行更新.結果頻繁的更新,反而容易降低使用者使用黏性.或者是公司業務的不斷髮展,同系的應用越來越多,傳統方式需要通過使用者量最大的主專案進行引導下載並安裝.
怎麼辦?參考瀏覽器-外掛開發模式:
一. 來可以將自己的應用分拆,某些功能可以在外掛中實現,用到時再進行下載,而且不用安裝. 如果有新功能的新增,不需要更新應用,只要預留外掛管理,我們就可以通過新增外掛的方式,動態更新自己的應用,該功能需要改進或擴充套件,更新外掛即可,無需頻繁安裝或解除安裝(容易造成使用者反感).
二. 對應同系應用,正常的引流方式只能引導使用者進行新應用的下載和安裝,如果使用外掛化開發,則無需安裝應用,關閉外掛功能也十分方便,省去應用安裝和解除安裝的過程,可以實現無縫引流.
這裡要向大家推薦一個開源的動態載入框架DL, 該專案由singwhatiwanna發起,目前一共有三個人開發,我有幸成為了其中的contributor.
如果你對DL動態載入框架還不熟悉,建議先看一下這篇文章:
如果你看過之後還會不太清晰,請看下DL外掛化框架的全景圖,如下
這裡我主要向大家介紹一下利用DL框架進行開發的具體步驟:
1. 首先我們需要從github上獲取專案程式碼:
這裡我們可以看到下載後的目錄如下
lib目錄就是我們的公共外掛庫
sample目錄是對應的demo, 具體的工程可以參照上面的DL全景圖中的三種模式:
由於一般專案中,程式碼管理和開發團隊相對獨立, 一般外掛工程的團隊是很難接觸主專案團隊的程式碼.因此,這裡主要以第一種,也是我們最為推薦的方式進行, 採用外掛不依賴宿主的方式進行開發. 不需要兩個團隊過多的互動,開發效率相對較高.2 匯入lib工程,如下所示:
可以看到bin目錄下的dl-lib.jar, 如果我們需要對lib工程進行修改,重新build獲取對應的dl-lib.jar即可
3. 外掛工程的開發, 匯入demo中的main-plugin工程
首先還是要強調外掛開發的注意事項,以免出現不必要的錯誤
外掛也需要引用DL的jar包,但是不能放入到外掛工程的libs目錄下面,換句話說,就是外掛編譯的時候依賴jar包但是打包成apk的時候不要把jar包打進去,這是因為,dl-lib.jar已經在宿主工程中存在了,如果外掛中也有這個jar包,就會發生類連結錯誤,原因很簡單,記憶體中有兩份一樣的類,重複了。至於support-v4也是同樣的道理。對於eclipse很簡單,只需要在外掛工程中建立一個目錄,比如external-jars,然後把dl-lib.jar和support-v4.jar放進去,同時在.classpath中追加如下兩句即可:
<classpathentry kind="lib" path="external-jars/dl-lib.jar"/>
<classpathentry kind="lib" path="external-jars/android-support-v4.jar"/>
然後是外掛開發中的具體步驟(1) 如果原有的為Activity,這裡需要改為繼承DLBasePluginActivity,如果原來為FragmentActivity,那麼需要繼承DLBasePluginFragmentActivity, for example:
public class MainActivity extends DLBasePluginActivity
TestFragmentActivity extends DLBasePluginFragmentActivity
(2) 如果需要外掛獨立安裝執行, 只要將jar放到libs下面即可,若支援動態載入,仍需按上述注意事項加入 exteral-jars中
(3)外掛所需要許可權需要在宿主工程中宣告
如果是實際開發,一般為從伺服器獲取外掛,這裡我們方便自己除錯演示,將執行生成對應的外掛apk,放入sd卡上的DynamicLoadHost目錄中
4. 宿主工程中將上述生成的dl-lib.jar加入libs即可,如下所示為demo中提供的宿主工程
在宿主工程中,首先我們需要獲取要呼叫的外掛apk對應的MainActivity,DL的demo中外掛路徑為 sd卡上的DynamicLoadHost目錄,沒有的話需要建立,或者根據自己需求進行修改.
String pluginFolder = Environment.getExternalStorageDirectory() + "/DynamicLoadHost";
File file = new File(pluginFolder);
File[] plugins = file.listFiles();
if (plugins == null || plugins.length == 0) {
mNoPluginTextView.setVisibility(View.VISIBLE);
return;
}
for (File plugin : plugins) {
PluginItem item = new PluginItem();
item.pluginPath = plugin.getAbsolutePath();
item.packageInfo = DLUtils.getPackageInfo(this, item.pluginPath);
if (item.packageInfo.activities != null && item.packageInfo.activities.length > 0) {
item.launcherActivityName = item.packageInfo.activities[0].name;
}
mPluginItems.add(item);
}
接著是調起響應的apk,這時需要使用dl-lib.jar:
(1)通過Class.forName的方式獲取我們需要呼叫的外掛apk中MainActivity的class物件
(2) 就上面提到的,我們需要判斷該物件繼承自DLBasePluginActivity還是DLBasePluginFragmentActivity,得到對應的代理class物件
(3)使用對應的代理class物件調起外掛apk
PluginItem item = mPluginItems.get(position);
Class<?> proxyCls = null;
try {
Class<?> cls = Class.forName(item.launcherActivityName, false,
DLClassLoader.getClassLoader(item.pluginPath, getApplicationContext(), getClassLoader()));
if (cls.asSubclass(DLBasePluginActivity.class) != null) {
proxyCls = DLProxyActivity.class;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
Toast.makeText(this,
"load plugin apk failed, load class " + item.launcherActivityName + " failed.",
Toast.LENGTH_SHORT).show();
} catch (ClassCastException e) {
// ignored
} finally {
if (proxyCls == null) {
proxyCls = DLProxyFragmentActivity.class;
}
Intent intent = new Intent(this, proxyCls);
intent.putExtra(DLConstants.EXTRA_DEX_PATH,
mPluginItems.get(position).pluginPath);
startActivity(intent);
}
最後執行宿主工程main-host,就可以看到最終的效果:
相信大家經過如上步驟,可以對DL動態載入框架進行開發有了一定了解.目前DL框架仍在不斷擴充套件中,歡迎對我們的專案進行star,fork,或者提出寶貴的建議.,如有問題請及時反饋,我們會在後續版本中進行修復或者改進: