1. 程式人生 > >Android-動態載入外掛化的兩種實現方式(二):介面

Android-動態載入外掛化的兩種實現方式(二):介面

上一篇部落格中http://blog.csdn.net/lxping51/article/details/71480239,主要通過反射的方式來實現動態載入外掛化,今天我們以介面的方式來達到目的。介面的實現比反射更為簡單,而且直接呼叫對效能有很大的提高。但是這也意味著需要處理的麻煩也更多。此文章主要學習來自CSDN部落格大神尼古拉斯-趙四的這篇部落格內容講得很詳細。我的這一篇部落格是在此基礎上,根據自身遇到的問題,加以調整和延伸。介面實現動態載入,對我來說還是比較有挑戰性和成就感的,主要問題不在於介面的一個實現過程,而是其中我的專案中所遇到的要不斷需要改變類包名的挑戰。
接下來,就開始切入正題:
專案還是使用上一篇文章中的兩個:Host(宿主)、LibPlugin(外掛)
LibPlugin


1,首先要建立三個介面InterLibPluginActivitys,InterLibPluginReceivers,InterLibPluginServices,為了方便打包特意在每個檔案後多加了個”s”
InterLibPluginActivitys.java

public interface InterFloatActivitys {
    public void onCreate(Activity activity, Class<?> mActivityClass, Class<?> mServiceClass, Class<?> mReceiverClass);

    public
void onStart(); public void onResume(); public void onStop(); public void onDestroy(); public boolean onKeyDown(int keyCode, KeyEvent event); public boolean onTouchEvent(MotionEvent event); }

InterLibPluginReceivers.java

public interface InterLipPluginReceivers {
    void
onReceive(Context context, Intent intent, Class<?> mActivityClass, Class<?> mServiceClass, Class<?> mReceiverClass); }

InterLibPluginServices.java

public interface InterLipPluginServices {
    void onCreate(Context context, Class<?> mActivityClass, Class<?> mServiceClass, Class<?> mReceiverClass);

    int onStartCommand(Intent intent, int flags, int startId);

    void onDestroy();
}

2,LibPlugin中的對應實現,也就是上一篇中對應的檔案(例如Service)

public class LibPluginService implements InterLibPluginServices {
public void onCreate(Context context, Class<?> mActivityClass, Class<?> mServiceClass, Class<?> mReceiverClass) {
body...;
}
public int onStartCommand(Intent intent, int flags, int startId) {
body...;
}

public void onDestroy() {
body...;
}
}

3,打包過程依然使用ant打包,配置檔案中對名字處理;
這裡寫圖片描述
build檔案中不混淆;
這裡寫圖片描述
4,ant打包成LibPlugin.apk
這裡寫圖片描述
如果和第一篇中要實現的結果一樣,那麼接下來就更簡單了,有兩種方式:(1),可以直接在宿主Host中建立一個包名和類名相同的檔案(注意包名和類名相同)如下圖:
這裡寫圖片描述
然後在通過DexClassLoad動態載入獲取到類後,對類物件進行對應的強制轉換,例如 (Service)

    public class MyService extends Service {
    private InterLibPluginService service;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Class<?> servieclass = Utils.getClasses(this, CLASS_SERVICE, null);
        if (servieclass == null)
            return;
        try {
            service = (InterLibPluginService) servieclass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        service.onCreate(this, MyActivity.class, MyService.class, MyReceiver.class);
    }
    }

但這並不是我想要的,我希望的目的是做一個sdk來呼叫外掛,然後宿主再呼叫sdk,其實也就是多了一道程式。雖然只多了一個,但是整個打包過程,有會麻煩一些。核心問題還是不能混淆這幾個關鍵介面。接下來主要操作Host,如之前提到的更多的是對ant打包的學習。
Host:
1,在com.b.b檔案包下建立介面,內容和上面一樣。
這裡寫圖片描述
這裡其實有個疑惑:從編譯工具的角度出發,同一個專案中是不允許有兩個類名完全相同的檔案存在的,但是我們之前在編譯LibPlugin的時候其實已經編譯了com.b.b.InterLiaPluginActivity.java –等,此刻我們有再次建立相同介面,儘管專案中不會報錯(因為事實上沒處於同一個資料夾),但編譯一起的時候,應該處於同一個專案中。這沒有報錯而且很好的執行。這是一個疑點,也在研究中。不過話說回來,通過介面的方式來達到動態載入的目的,如果沒有引入介面,那麼轉化肯定是沒辦法進行的。所以它的可行是必須的。
2,轉化過程參考4-(1)
3,ant配置檔案local-dev.properties 申明三個介面不混淆

########## android sdk相關引數設定##############
#android-jar=E:/android-sdk-windows/platforms/android-7/android.jar
adtpath=D:/adt-bundle-windows-x86_64-20140702/sdk
android-jar=${adtpath}/platforms/android-8/android.jar
dynamic-jar=D:/workspace/svn_box_bar_new_sdk_use_interface/libs/Dynamic_inter.jar

############ 混淆相關引數設定 ############
#proguard-dir=E:/android-sdk-windows/tools/proguard
proguard-dir=${adtpath}/tools/proguard
############ ANT功能擴充套件引數設定 ############
ant-contrib=D:/AntToJar/ant-contrib-1.0b3.jar
######################################
target-root=dest

##############廣告型別###########
project-name=boxbar

############sdk版本 #######
sdk-version=1.2.1
#############舊包名##############
old-package-name=com.j.s

dynamic-package-name = com.b.b

###############新包名##########################
new-package-name=com.google.smy
#new-package-name=com.hzd.bdld
###############管理類名,不混淆##########################
manager-name=Em

a-name = InterLibPluginActivity
r-name = InterLibPluginReceiver
s-name = InterLibPluginService

#manager-name=itbl
###############舊類名, 多個時以【;】分割##########################
old-classes=Edt;MyActivity;MyReceiver;MyService
#old-classes=MyManager;MyActivity;MyReceiver;MyService
###############新類名, 多個時以【;】分割##########################
new-classes=Em;Ra;Tr;Ys
#new-classes=itbl;itblAc;itblR;itblS

4,build-dev-sdk.xml

<?xml version="1.0" ?>
<project name="ji_dev_sdk" default="release" basedir=".">

    <!-- Load the properties files -->
    <property file="local-dev.properties" />

    <!-- Input directories -->
    <property name="src-dir" value="src" />
    <!-- Output directories -->
    <property name="out-dir" value="${target-root}/tmp" />
    <property name="out-dir-classes" value="${out-dir}/classes" />

    <!-- package path -->
    <taskdef resource="net/sf/antcontrib/antcontrib.properties">
        <classpath>
            <pathelement location="${ant-contrib}"/>
        </classpath>
    </taskdef>

    <property name="new-package-path" value="${new-package-name}" />
    <propertyregex property="dynamic-package-path" input="${dynamic-package-name}" regexp='\.' replace="\/"/>
    <propertyregex property="old-package-path" input="${old-package-name}" regexp='\.' replace="\/"/>
    <propertyregex override="true" property="new-package-path" input="${new-package-name}" regexp='\.' replace="\/"/>
    <echo>${old-package-path}</echo>
    <echo>${new-package-path}</echo>
    <echo>${dynamic-package-path}</echo>

    <echo>${new-classes}</echo>
    <echo>${manager-name}</echo>

    <!-- Clean directories -->
    <target name="clean-dirs">
        <echo>clean-dirs..................</echo>
        <delete dir="${target-root}" />
    </target>

    <!-- Create directories if not exist -->
    <target name="mkdirs" depends="clean-dirs">
        <echo>mkdirs.................</echo>
        <delete dir="${target-root}" />
        <mkdir dir="${out-dir}" />
        <mkdir dir="${out-dir-classes}" />
        <mkdir dir="${target-root}/${dynamic-package-path}" />
        <mkdir dir="${target-root}/${old-package-path}" />
        <mkdir dir="${target-root}/${new-package-path}" />
    </target>

    <!-- copy the source files to target directory -->
    <target name="copy0" depends="mkdirs">
        <copydir dest="${target-root}/${old-package-path}" src="${src-dir}/${old-package-path}">
        </copydir>
    </target>

    <target name="copy1" depends="mkdirs">
         <copydir dest="${target-root}/${dynamic-package-path}" src="${src-dir}/${dynamic-package-path}"/>
    </target>

    <target name="update-classes" depends="copy0,copy1">
        <java jar="ClassRenameWithPackage.jar" fork="true" failonerror="true">
            <arg value="${target-root}" />
            <!-- <arg value="${dynamic-package-name}" /> -->
            <arg value="${old-package-name}" />
            <arg value="${old-classes}" />
            <arg value="${new-classes}" />
        </java>
    </target>

    <target name="copy" depends="update-classes">
        <rename dest="${target-root}/${new-package-path}" src="${target-root}/${old-package-path}"/>
    </target>

    <target name="update-package" depends="copy">
        <java jar="package-tool.jar" fork="true" failonerror="true">
            <arg value="${target-root}" />
            <arg value="${old-package-name}" />
            <arg value="${new-package-name}" />
        </java>
    </target>

    <!-- Compiling the java files -->
    <target name="compile" depends="update-package">
        <echo>Compiling the java files.................</echo>
        <javac encoding="utf-8" target="1.6" debug="true" srcdir="${target-root}" destdir="${out-dir-classes}" classpath="${out-dir-classes}" bootclasspath="${android-jar}" includeantruntime="false">
            <classpath>
                <!-- 
                <fileset dir="libs" includes="*.jar" />
                 -->
            </classpath>
        </javac>
        <delete>
            <fileset dir="${out-dir-classes}/${new-package-path}" includes="MainActivity*.class" />
        </delete>
        <!--  
        <copydir dest="${out-dir-classes}/assets" src="assets"></copydir>
        -->
        <jar basedir="${out-dir-classes}" destfile="temp.jar" />
        <!-- 
        <jar destfile="sdk/${project-name}-${new-package-name}-${sdk-version}-test.jar" basedir="${out-dir-classes}"/>
        -->
    </target>

    <target name="proguard" depends="compile">
        <echo>proguard..........................</echo>
        <java jar="${proguard-dir}/lib/proguard.jar" fork="true" failonerror="true">
            <jvmarg value="-Dmaximum.inlined.code.length=32" />
            <arg value="-injars temp.jar" />
            <arg value="-outjars sdk/temp-${sdk-version}.jar" />

            <arg value="-dontpreverify" />
            <arg value="-dontoptimize" />
            <arg value="-dontusemixedcaseclassnames" />
            <arg value="-allowaccessmodification" />
            <arg value="-dontskipnonpubliclibraryclassmembers" />
            <arg value="-dontskipnonpubliclibraryclasses" />
            <arg value="-libraryjars ${android-jar}" />
            <!-- <arg value="-libraryjars ${dynamic-jar}"/> -->

            <arg value="-optimizationpasses 7" />
            <arg value="-verbose" />

            <arg value="-keep public class * extends android.app.Activity" />
            <arg value="-keep public class * extends android.app.Service" />
            <arg value="-keep public class * extends android.content.BroadcastReceiver" />
            <arg value="-keep public class * extends android.content.ContentProvider" />
            <arg value="-keep public class * extends android.preference.Preference" />
            <arg value="-keep public class * extends android.app.Application" />

            <arg value="-keepclasseswithmembers class * {   native &lt;methods&gt;; }" />

            <arg value="-keepclasseswithmembernames class * {    public &lt;init&gt;(android.content.Context, android.util.AttributeSet);   }" />

            <arg value="-keepclasseswithmembernames class * {       public &lt;init&gt;(android.content.Context, android.util.AttributeSet, int);           }" />

            <arg value="-keepclassmembers enum * {                  public static **[] values();                    public static ** valueOf(java.lang.String);         }" />

            <arg value="-keep class ${new-package-name}.${manager-name} {                &lt;fields&gt; ;         &lt;methods&gt; ;    } " />
            <arg value="-keep interface ${dynamic-package-name}.${a-name} {                &lt;fields&gt; ;           &lt;methods&gt; ;    }" />
            <arg value="-keep interface ${dynamic-package-name}.${r-name} {                &lt;fields&gt; ;           &lt;methods&gt; ;    }" />
            <arg value="-keep interface ${dynamic-package-name}.${s-name} {                &lt;fields&gt; ;           &lt;methods&gt; ;    }" />

        </java>
        <delete file="temp.jar" />
        <delete dir="${out-dir-classes}"/>
        <mkdir dir="${out-dir-classes}" />

        <unjar src="sdk/temp-${sdk-version}.jar" dest="${out-dir-classes}"/>
        <jar destfile="sdk/${project-name}-${new-package-name}-${sdk-version}.jar" basedir="${out-dir-classes}"/>
        <delete file="sdk/temp-${sdk-version}.jar" />
        <delete dir="${target-root}" />

    </target>

    <target name="release" depends="proguard">
    </target>
</project>

不混淆的關鍵程式碼:
建立新的檔案路徑:com.b.b

<propertyregex property="dynamic-package-path" input="${dynamic-package-name}" regexp='\.' replace="\/"/>

<mkdir dir="${target-root}/${dynamic-package-path}" />

拷貝:

<target name="copy1" depends="mkdirs">
         <copydir dest="${target-root}/${dynamic-package-path}" src="${src-dir}/${dynamic-package-path}"/>
    </target>

    <target name="update-classes" depends="copy0,copy1">

不混淆:

<arg value="-keep interface ${dynamic-package-name}.${a-name} {                &lt;fields&gt; ;        &lt;methods&gt; ;    }" />
 <arg value="-keep interface ${dynamic-package-name}.${r-name} {                &lt;fields&gt; ;           &lt;methods&gt; ;    }" />
 <arg value="-keep interface ${dynamic-package-name}.${s-name} {                &lt;fields&gt; ;           &lt;methods&gt; ;    }" />

5,最終的目錄結構
這裡寫圖片描述
這樣的情況下,就可以在宿主中直接加入該jar包來使用。

整個過程看下來其實並不複雜,關鍵動態載入的內容集中在DexClassLoad的使用,介面這部分更多的是結合自身專案的特殊需求而稍加改變。技術部落格寫得還不是很成熟,鼓勵自己慢慢成長。

相關推薦

Android-動態載入外掛實現方式介面

上一篇部落格中http://blog.csdn.net/lxping51/article/details/71480239,主要通過反射的方式來實現動態載入外掛化,今天我們以介面的方式來達到目的。介面的實現比反射更為簡單,而且直接呼叫對效能有很大的提高。但是這也

圖片輪播的實現方式

專案中用到的第一種方法,學習借鑑了https://github.com/qingse/ImageSlideshow 自定義ImageSlideshow,繼承自FrameLayout,在構造方法中,初始化資料: public ImageSlideshow(Context c

二分查詢的實現方式JAVA

二分查詢又稱折半查詢,優點是比較次數少,查詢速度快,平均效能好;其缺點是要求待查表為有序表,且插入刪除困難。因此,折半查詢方法適用於不經常變動而查詢頻繁的有序列表。首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與查詢關鍵字比較,如果兩者相等,則查詢成功

JAVA處理Excel的三實現方式

awm96 2012-05-15 13:49 createExcel(excel6); //modifyExcel(excel2); } static void readExcel(String filePath) throws Exception{ //HSSFWorkb

Android開發——彈性滑動的實現方式

0. 前言我們在Android開發——View滑動的三種實現方式中學習瞭如何進行View滑動,在第一種方法,利用ScrollBy和ScrollTo進行滑動時,滑動效果是瞬間完成的,為了更好的使用者體驗,

動態代理及其實現方式JDK、CGLIB

什麼是代理模式 為某物件提供一個代理,從而通過代理來訪問這個物件。 代理模式的角色組成 代理模式有三種角色組成: 抽象角色:通過介面或抽象類宣告真實角色實現的業務方法。 代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象

android------引導頁實現方式原生和WebView網頁實現

有的App當你第一次開啟的是和常常會有引導頁來描述一些App資訊(功能,特點),當然也要做驗證,驗證第二次進入不進入引導頁,直接進入App,此部落格藉助ViewPager來實現引導頁, ViewPager類提供了多介面切換的新效果,是谷歌在3.0之後加入的新特性,所以需要引

[轉]Web APi之認證Authentication實現方式十三

用戶數 ted das 客戶 元素 基礎 目標 開始 net 本文轉自:http://www.cnblogs.com/CreateMyself/p/4857799.html 前言 上一節我們詳細講解了認證及其基本信息,這一節我們通過兩種不同方式來實現認證,並且分析如

Web APi之認證Authentication實現方式十三

基於web 推薦 zed {0} scheme sage https 函數 ges 原文:Web APi之認證(Authentication)兩種實現方式【二】(十三)前言 上一節我們詳細講解了認證及其基本信息,這一節我們通過兩種不同方式來實現認證,並且分析如何合理的利用

快速排序的實現方法js

while 交換 splice rt+ dex 進行 return ont mat 快速排序的基本思想:通過一趟排序,將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另外一部分記錄的關鍵字小,則可分別對著兩部分記錄繼續進行排序,以達到整個序列有序的目的。------

、C++迭代器的實現方式 Range for和C#、Java中的foreach

一、迭代器概述   這個標題其實有點“標題黨”的含義,因為C++在標準庫中的實現迭代器的方式只有一種,也就是為類定義begin()和end()函式,C++11增加了range for語句,可以用來遍歷迭代器中的元素。實現迭代器的第二種方式,就是用C++模擬C#和Java中的

自動補全、自動提示的實現方式前端實現與後端實現

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link rel="style

資料庫連線的實現方式讀取配置檔案——DBCP&C3P0;DBCP實現連線程式碼,C3P0實現連線程式碼——包含完整程式碼

兩種資料庫連線實現方式 第一種方式:DBCP DBCP使用流程 導jar包使用DBCP建立資料庫連線物件 DataSource ds=BasicDataSourceFactory.createDatasource("一個儲存連線資訊的properties集合");使

android開發中監聽器的三實現方法OnClickListener

宣告:本寶寶的畢業設計是基於Android開發的********    所以對Android開發有用的文章就先轉載過來    對9月份寫論文起一定幫助作用 標籤: Android開發中監聽器的實現有三種方法,對於初學者來說,能夠很好地理解這三種方法,將能更好地增進自己對a

RxJS的另外四實現方式——性能最高的庫

如何 www table fas set export llb const events 接上篇 RxJS的另外四種實現方式(二)——代碼最小的庫(續) 代碼最小的庫rx4rx-lite雖然在性能測試中超過了callbag,但和most庫較量的時候卻落敗了,於是我下載了

hibernate 級聯刪除時候的情況 之 刪除從表,無法刪除關係表

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

RxJS的另外四實現方式——效能最高的庫

程式碼最小的庫rx4rx-lite雖然在效能測試中超過了callbag,但和most庫較量的時候卻落敗了,於是我下載了most庫,要解開most庫效能高的原因。 我們先上一組測試資料,這是在我的windows10 上面跑的 dataflow for 10000

RxJS的另外四實現方式——使用Stream類實現

該實現方式與之前幾種不同的,該實現方式僅針對Nodejs環境。在Nodejs環境中,提供了Stream類,包括Readable、Transform、Writeable等子類都是可擴充套件的。從字面上看,正好對應Rx中的生產者、傳遞者、消費者。 實現該庫的起因是

Unity中UGUI人物血條跟隨的幾實現方式

昨天在群裡有人在做遊戲的時候遇到了一個坑,就是用UGUI做人物血條跟隨遇到了大坑,今天就來說說如何用UGUI來做人物血條跟隨。 第一種: 把Canvas畫布作為Player的子物體。 首先:佈置一下場

第一次使用Android Studio時你應該知道的一切配置新建一個屬於自己的工程並安裝Genymotion模擬器

人性 pro net 參考 json irb 一個地方 vid 調試 【聲明】 歡迎轉載,但請保留文章原始出處→_→ 生命壹號:http://www.cnblogs.com/smyhvae/ 文章來源:http://www.cnblogs.com/smyhvae/p/439