成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

android基于mvp的熱修復(fù)方案構(gòu)思,不使用第三方

WrBug / 3398人閱讀

摘要:也是較容易出現(xiàn)問(wèn)題的一個(gè)模塊,對(duì)層實(shí)現(xiàn)熱修復(fù),對(duì)項(xiàng)目的穩(wěn)定性是十分有利的。需求在不修改層和層的基礎(chǔ)上,實(shí)現(xiàn)對(duì)層的熱修復(fù)。通過(guò)不停的更換所加載的文件,但調(diào)用方法一致,來(lái)達(dá)到熱修復(fù)的目的。綜上,本方案應(yīng)該是可以在項(xiàng)目中實(shí)際應(yīng)用的一個(gè)熱修復(fù)方案。

本文為作者原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處
由來(lái)

現(xiàn)在開(kāi)發(fā)android項(xiàng)目大部分都已經(jīng)由mvc轉(zhuǎn)移到了mvp,關(guān)于mvp是什么大致也不必多說(shuō)了,無(wú)非三個(gè)層:

m:model層,一般封裝對(duì)數(shù)據(jù)的操作,增刪改查,接口訪問(wèn)等等。

v:view層,也就是視圖層,視圖層不主動(dòng)做什么,只是根據(jù)某些事件作出對(duì)應(yīng)的視圖展示。

p:Presenter層,也就是邏輯層。

圖解(之前在別的博文看到的,覺(jué)得比較好就直接拿來(lái)用了):

在mvp模式中,model層和view層不再有直接交互,而把相應(yīng)的工作交給了“中間人”P(pán)resenter層來(lái)處理。
因此在我們的項(xiàng)目中,Presenter層可以說(shuō)是一個(gè)改動(dòng)比較頻繁,邏輯比較復(fù)雜的一個(gè)模塊。也是較容易出現(xiàn)問(wèn)題的一個(gè)模塊,對(duì)Presenter層實(shí)現(xiàn)熱修復(fù),對(duì)項(xiàng)目的穩(wěn)定性是十分有利的。
總結(jié)一下:

項(xiàng)目背景:mvp的項(xiàng)目。

需求:在不修改model層和view層的基礎(chǔ)上,實(shí)現(xiàn)對(duì)Presenter層的熱修復(fù)。

構(gòu)想

核心思想就是Presenter層只寫(xiě)接口,然后使用java的classloader機(jī)制加載Presenter層的實(shí)現(xiàn)類(lèi)來(lái)產(chǎn)生對(duì)象然后賦值給接口指針調(diào)用。通過(guò)不停的更換classloader所加載的文件,但調(diào)用方法一致,來(lái)達(dá)到熱修復(fù)的目的。
下面是我畫(huà)的一個(gè)整體的結(jié)構(gòu)圖。

嘗試實(shí)現(xiàn)

既然大體思路都有了,那么咱們就來(lái)嘗試一下能不能行得通吧。做一個(gè)案例,功能非常簡(jiǎn)單,界面上一個(gè)按鈕,點(diǎn)擊按鈕吐司一個(gè)字符串,這個(gè)字符串通過(guò)。

1.創(chuàng)建項(xiàng)目

除了一路next之外我這里想說(shuō)的實(shí)際上是項(xiàng)目module結(jié)構(gòu):

app里面主要寫(xiě)項(xiàng)目的相關(guān)界面。

motorlib里面主要寫(xiě)presenter接口和熱修復(fù)、classloadler等相關(guān)的代碼,另外model層也可以在里面寫(xiě),其實(shí)它們可以寫(xiě)在app里面,但這樣組件化的話,更有利于解耦。

motorhot這里就是寫(xiě)presenter的具體實(shí)現(xiàn)了,另外model層也可以在里面寫(xiě),那樣的話對(duì)于后臺(tái)接口返回的數(shù)據(jù)格式變更等也可以進(jìn)行修復(fù)了。

再設(shè)置一下這三個(gè)module之間的引用關(guān)系。

.appbuild.gradle

dependencies {
    ...
    //只關(guān)心接口,不關(guān)心實(shí)現(xiàn),因此只引用motorlib
    implementation project(":motorlib")
}

.motorhotbuild.gradle

dependencies {
    ...
    //需要集成motorlib定義的接口,因此需要引用
    implementation project(":motorlib")
}

.motorlibbuild.gradle

dependencies {
  ...
  //因?yàn)橹欢x接口,所以都不需要引用
}
2.persenter接口

在motorlib中創(chuàng)建一個(gè)java接口,AppParsenter,里面只有一個(gè)方法:

public interface AppParsenter {
    String getStr();
}
3.實(shí)現(xiàn)接口persenter接口

在motorlib中創(chuàng)建一個(gè)java類(lèi),實(shí)現(xiàn)AppParsenter:

public class AppParsenterImpl implements AppParsenter {
    @Override
    public String getStr() {
        return "hello word !";
    }
}
4.熱修復(fù)文件打包

在android studio中的右側(cè),打開(kāi)Gradle一欄,然后點(diǎn)擊(也可以直接運(yùn)行g(shù)radlew命令gradlew motorhot:assembleRelease):


打包后的jar包文件存放在.motorhotuildintermediatesundles eleaseclasses.jar

拿到打包好的classes.jar,然后使用android sdk提供的dx.bat將jar包轉(zhuǎn)換為dex:

CD ..androidsdkuild-tools20.0.0
dx --dex --output=..hotfix.dex ..classes.jar

這樣,用于熱修復(fù)的.dex文件就打包完成了。

5.ObjectFactory

因?yàn)閐ex的加載離不開(kāi)DexClassLoader,因此我在這里先對(duì)DexClassLoader進(jìn)行了一下封裝:

public class Motor {
    private Context context;
    private MotorListener listener;
    private static volatile Motor motor;
    private DexClassLoader mClassLoader;

    private Motor() {
    }

    public static Motor get() {
        if (motor == null) {
            synchronized (Motor.class) {
                if (motor == null) {
                    motor = new Motor();
                }
            }
        }
        return motor;
    }

    public static void init(Context context, MotorListener listener) {
        get();
        motor.context = context;
        motor.listener = listener;
        //加載dex
        motor.initClassLoader();
        listener.initFnish();
        //todo 網(wǎng)絡(luò)檢查dex更新
    }

    private void initClassLoader() {
        String dexDir = context.getCacheDir().getAbsolutePath() + "/dex/";
        File dir = new File(dexDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File dexFile = new File(dir, "mydex.dex");
        if (!(dexFile.exists() && dexFile.isFile() && dexFile.length() > 0)) {
            copyFileFromAssets(context, "mydex.dex", dexFile.getAbsolutePath());
        }
        mClassLoader = new DexClassLoader(
                dexFile.getAbsolutePath(), context.getFilesDir().getAbsolutePath()
                , null, context.getClassLoader());

    }

    public boolean copyFileFromAssets(Context context, String assetName, String path) {
        boolean bRet = false;
        try {
            InputStream is = context.getAssets().open(assetName);

            File file = new File(path);
            file.createNewFile();
            FileOutputStream fos = new FileOutputStream(file);
            byte[] temp = new byte[64];
            int i = 0;
            while ((i = is.read(temp)) > 0) {
                fos.write(temp, 0, i);
            }
            fos.close();
            is.close();
            bRet = true;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bRet;
    }

    public DexClassLoader getClassLoader() {
        return mClassLoader;
    }

    public void setmClassLoader(DexClassLoader mClassLoader) {
        this.mClassLoader = mClassLoader;
    }

    public Context getContext() {
        return context;
    }

    public void setContext(Context context) {
        this.context = context;
    }

    public interface MotorListener {
        void initFnish();

        void initError(Throwable throwable);
    }
}

單例,首先從assets中把.dex文件拷貝到android的沙盒目錄(android加載dex的時(shí)候有限制,必須是在沙盒目錄中才能加載),然后構(gòu)建好了mClassLoader就完成了。

方便起見(jiàn),熱修復(fù)文件就不從網(wǎng)絡(luò)下載了,因此直接把打包好的.dex拷貝到項(xiàng)目的assets文件夾中。

在motorlib中創(chuàng)建ObjectFactory,用來(lái)通過(guò)classloader生產(chǎn)對(duì)象:

public class ObjectFactory {
    public static Object make(String type) {
        try {
            Class ap = (Class) Motor.get()
                    .getClassLoader().loadClass("com.example.motordex.AppParsenterImpl");
            AppParsenter o = ap.newInstance();
            return o;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}

到了這里就可以注意到了,反射構(gòu)建對(duì)象的時(shí)候ap.newInstance();沒(méi)法傳參數(shù),因此對(duì)于Parsenter的實(shí)現(xiàn)類(lèi)中,必須有一個(gè)無(wú)參的構(gòu)造方法。

6.運(yùn)行

app中新建一個(gè)activity,設(shè)置一個(gè)按鈕然后添加點(diǎn)擊事件:
layout/activity_main.xml

com.example.miqt.dexmvppdemo.MainActivity

public class MainActivity extends AppCompatActivity {
    AppParsenter ap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Motor.init(this, new Motor.MotorListener() {
            @Override
            public void initFnish() {
                ap = (AppParsenter) ObjectFactory.make(MainActivity.class.getName());
            }
            @Override
            public void initError(Throwable throwable) {
            }
        });

    }

    public void getStr(View view) {
        String str = ap.getStr();
        Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
    }
}

運(yùn)行結(jié)果:

修改motorhot中的AppParsenterImpl,模擬修復(fù)了一個(gè)bug:

public class AppParsenterImpl implements AppParsenter {
    @Override
    public String getStr() {
        return "fix a bug!";
    }
}

重復(fù)4步驟,打包運(yùn)行:

總結(jié)

事實(shí)證明這種構(gòu)想完全可以實(shí)現(xiàn),并且可以在不使用其他框架,達(dá)到熱修復(fù)的目的,并且不僅僅是presenter層,在其他的層應(yīng)用這種方式,也是可以的。

但實(shí)際上從上面的實(shí)踐,也可以發(fā)現(xiàn)一些問(wèn)題:

dex文件線上下載或拷貝過(guò)程中如果損壞,則可能引起程序崩潰。不過(guò)我們可以使用對(duì)dex文件取hash碼然后對(duì)比下載后的文件,如果不一致則證明出錯(cuò),重新下載拷貝。解決這個(gè)問(wèn)題。

dex暴露在用戶手機(jī)沙盒目錄中,而android的沙盒目錄在root之后是可以直接訪問(wèn)的,因此有可能被人拿到dex反編譯,修改邏輯搞破壞。并且因?yàn)榉瓷涞挠绊?,motorhot中的類(lèi)文件是不可以進(jìn)行混淆的,因?yàn)榛煜缶蜁?huì)報(bào)找不到類(lèi)的異常。不過(guò)關(guān)于這個(gè)也是有解決辦法的,我想到的那就是對(duì)熱修復(fù)文件加密,在loader之前再在內(nèi)存中解密。這樣別人沒(méi)法知道加密規(guī)則,也就沒(méi)法解密了。

雙親委托機(jī)制,在classloader加載外部dex之前會(huì)先檢查本地是否已經(jīng)存在同名的類(lèi),如果有則優(yōu)先加載本地已經(jīng)存在的類(lèi),因此在實(shí)際使用中我們最好還要禁用雙親委托機(jī)制。

綜上,本方案應(yīng)該是可以在項(xiàng)目中實(shí)際應(yīng)用的一個(gè)熱修復(fù)方案。

完整代碼:https://github.com/miqt/MVPHo...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/76756.html

相關(guān)文章

  • Android 進(jìn)階

    摘要:理解內(nèi)存模型對(duì)多線程編程無(wú)疑是有好處的。干貨高級(jí)動(dòng)畫(huà)高級(jí)動(dòng)畫(huà)進(jìn)階,矢量動(dòng)畫(huà)。 這是最好的Android相關(guān)原創(chuàng)知識(shí)體系(100+篇) 知識(shí)體系從2016年開(kāi)始構(gòu)建,所有的文章都是圍繞著這個(gè)知識(shí)體系來(lái)寫(xiě),目前共收入了100多篇原創(chuàng)文章,其中有一部分未收入的文章在我的新書(shū)《Android進(jìn)階之光》中。最重要的是,這個(gè)知識(shí)體系仍舊在成長(zhǎng)中。 Android 下拉刷新庫(kù),這一個(gè)就夠了! 新鮮出...

    DoINsiSt 評(píng)論0 收藏0
  • 在 2016 年學(xué) Android 是一種什么樣的體驗(yàn)?

    摘要:當(dāng)然,目前看來(lái),的勢(shì)頭是蓋過(guò)的。平臺(tái)的插件化框架也是存在多種方案,各有優(yōu)劣。常見(jiàn)的攜程的,的,的以及等。另外,插件化也是解決問(wèn)題的一大利器。 在 2016 年學(xué) Android 是一種什么樣的體驗(yàn)? @author ASCE1885的 Github 簡(jiǎn)書(shū) 微博 CSDN 知乎本文由于潛在的商業(yè)目的,不開(kāi)放全文轉(zhuǎn)載許可,謝謝! showImg(/img/remote/146000000...

    MonoLog 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<