Android 之動態加載代碼

 

1、前言

在項目研發中會遇到部分功能常常變動,常常升級app會對用戶產生反感,形成體驗不好。html

項目中有這樣一個功能:下載到本地的視頻須要在播放時加載字幕,可是某些視頻咱們的服務器中不存在字幕,通過調研發現字幕庫網站能經過影片名查詢到相應的字幕,並下載下來。可是問題是字幕庫沒有公開的字幕查詢接口,只能經過一些逆向分析後,對頁面進行解析捕獲到了字幕的下載路徑,字幕下載路徑是嵌套在html代碼中,這樣只有經過JSoup技術(不懂得能夠查百度,這兒就不細說)對html頁面進行動態解析,拿到字幕的下載地址,以後再下載到咱們的服務器上面,開發的都知道使用第三方的老是不穩定,都說吃人嘴軟,拿人手短,仍是不如本身的,第三方的網頁佈局變化了,那麼使用jsoup解析的地址就所有出錯了,可是不能由於這個就去對app進行從新打包發佈新版本,這樣對用戶的體驗很差,那麼咱們就要使用動態加載技術去改變這樣的頻繁打包工做。java

解決思路:把常常變化的放在服務器上面,每次啓動app的時候就從服務器上面下載下來邏輯,再動態的加載到app的包裏面,動態打包咱們的app的,實例化對象,若是字幕庫發生變化,咱們就只須要更新服務器上面的解析代碼,從新下載相應的邏輯加載到app。android

2、使用方式

如何實現動態加載的流程?bash

第一:製做dex文件。服務器

第二:把製做的dex文件發佈到服務器上面,從服務器上面下載dex文件以後動態打包到app中app

製做的工具類:ide

public class JsoupUtils {

    public static String html2Url(String html) {
        return "url" + html;
    }
}複製代碼

①、製做dex文件函數

而後編譯以後再androidstudio的build/intermediates/classes/debug/ 下面會看到你的包名生成的字節碼,以後使用Java打包命令:jar -cvf把指定的字節碼打包成jar文件,以下:工具


出現這個表示打包成功,佈局



而後再把jar文件打包成dex文件,如今就要使用dex命令,dx.bat文件,在build目錄下,或者配置環境變量:


出現如下表示打包成功:


至此dex文件打包成功

②、把製做的dex文件發佈到服務器上面,從服務器上面下載dex文件以後動態打包到app中

在此演示則不去服務器下載,省略下載的步驟,直接放在assets目錄下面:


先把assets目錄下的utils_dex.jar拷貝到sd卡上面


3、加載器

如下就是類加載器:

使用反射與類加載器

android中的類加載器主要有三個:

(1)、URLClassLoader

只能用於加載jar文件,可是因爲dalvik不能直接識別jar文件,因此android中沒法使用這個類加載器

(2)、PathClassLoader

它只能加載已經安裝的apk,由於PathClassLoader只會讀取/data/dalvik-cache/目錄下的dex文件,


例如安裝一個apk的時候,就會在這個目錄下面的x86目錄下生成每一個apk對應的dex文件:


使用PathClassLoader加載apk時,它就會在這個目錄下面去查找對應的DEX文件,若是apk沒有安裝,則會報錯,ClassNotFoundException

(3)、DexClassLoader

是最理想的加載器,它的構造函數包括四個參數

一、dexPath:目標類所在的APK或jar文件的路徑,類加載器將從該路徑中尋找指定的目標類,該類必須是apk或者jar的全路徑,若是包含多個路徑,路徑之間必須使用特定的分隔符分隔,特定的分隔符可使用System.getProperty("path.separtor")得到;

二、dexOutputDir:因爲dex文件被包含在apk或者jar文件中,所以在裝載目標類以前須要先從apk或jar文件中解壓出dex文件,該參數就是定製解壓出的dex文件存放的路徑,在Android系統中,一個應用程序通常對應一個Linux用戶,應用程序僅對屬於本身的數據目錄路徑有寫的權限,所以,該參數可使用該程序的數據路徑

三、libPath:指目標類中使用的C/C++庫存放的路徑

四、classload是指該裝載器的父裝載器,通常爲當前執行類的裝載器

直接上代碼:

package com.parse.dex;

import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

import java.io.File;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initLoadDex();
    }

    private void initLoadDex() {
        FileUtils.copyAssetsJarToFile(this, "utils_dex.jar", "utils_dex.jar");
        File file = new File(Environment.getExternalStorageDirectory().toString() + File.separator + "utils_dex.jar");
        File optimizedDexOutputPath = getDir("dex", Context.MODE_PRIVATE);
        DexClassLoader dexClassLoader = new DexClassLoader(file.getAbsolutePath(), optimizedDexOutputPath.getAbsolutePath(), null, getClassLoader());
        try {
            Class loadClass = dexClassLoader.loadClass("com.parse.dex.JsoupUtils");
            Method html2Url = loadClass.getMethod("html2Url", String.class);
            String s = (String) html2Url.invoke(loadClass, "解析html文件");
            Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}複製代碼

整個動態加載類的流程就是這樣的

相關文章
相關標籤/搜索