Android 热修复框架 Tinker ( 三 )

这篇博客是基于Android 热修复框架 Tinker ( 一)写的,在看这篇博客之前请先看Android 热修复框架 Tinker ( 一 )

1.方法替换

注意:下面是居于上一篇文章的项目(已经集成Tinker)

1.编写avtivity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="xmg.com.tinkertest.MainActivity">

      <Button
        android:id="@+id/btn_mothed"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="5dp"
        android:textAllCaps="false"
        />


</RelativeLayout>

2.编写MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //补丁所在的路劲
        String path=Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk";
        //添加补丁
        TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),path );
        Log.d("TAG","path="+path);

        //1.演示方法的替换:
        final Button mButton=(Button)findViewById(R.id.btn_mothed);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String name = getName();
                mButton.setText(name);
            }
        });


    }


    private String getName() {
        return  "登录";
    }


}

3.修改app/build.gradle:

tinkerBuildFlavorDirectory注释掉,因为暂时不用到多渠道打包。

ext {
    //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
    tinkerEnabled = true

    //for normal build
    //old apk file to build patch apk
    tinkerOldApkPath = "${bakPath}/app-debug-1218-16-32-05.apk"
    //proguard mapping file to build patch apk
    tinkerApplyMappingPath = "${bakPath}/mapping.txt"
    //resource R.txt to build patch apk, must input if there is resource changed
    tinkerApplyResourcePath = "${bakPath}/app-debug-1218-16-32-05-R.txt"

    //only use for build all flavor, if not, just ignore this field
//    tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
}

4.开始打包:

点击assembleRelease后,在app/build/apk下生成:下面三个文件1

5.将apk安装在模拟器中

安装:app-release-1220-10-49-26.apk

当点击button的时候出现:登录

6.修改代码,:

替换掉MainActivity中的getName() 方法

public class MainActivity extends AppCompatActivity {

    private TextView tv_show;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String path=Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk";
        TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),path );
        Log.d("TAG","path="+path);

         //1.演示方法的替换:
        final Button mButton=(Button)findViewById(R.id.btn_mothed);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                String name = getName(); // 该方法被下面的方法替换
                String name=getNameFromValuse();
                mButton.setText(name);

            }
        });

    }

    private String getName() {
        return  "登录";
    }

    /**
     * 从资源文件上获取文字
     * @return
     */
    private String getNameFromValuse(){
        String name=getResources().getString(R.string.mothed_name);
        return name;
    }

}

在vaulse包下的strings.xml中添加:

<string name="mothed_name">替换了getName的方法</string>

7.修改build.gradle的配置文件

启用并修改下面两个属性,并启用它们在该gradle被引用的地方,因为在第一打包的时候已把它们注释了

tinkerOldApkPath = "${bakPath}/app-release-1220-10-49-26.apk" //上面打包生成的.apk
tinkerApplyMappingPath = "${bakPath}/app-release-1220-10-49-26-mapping.txt"//上面生成的mapping.txt
tinkerApplyResourcePath = "${bakPath}/app-release-1220-10-49-26-R.txt" //上面打包生成的R.txt

8.一件制作补丁包:

调用tinkerPatchRelease, 补丁包与相关日志会保存在/build/outputs/tinkerPatch/2

9.打补丁:

然后我们将patch_signed_7zip.apk 补丁包 推送到手机的sdcard中

4

10.重新启动APP( 有时需要重新启动APP才能生效 )

再次点击chick me的时候就会冒出Toast:

现在我们就可以实现在没有重新安装apk的前提下动态发布代码,动态添加代码修复bugs。

11.总结:

1.Tinker可以实现方法的替换

2.Tinker可以实现对res资源下的valuse文件夹下的资源操作

2.类替换

1.编写avtivity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="xmg.com.tinkertest.MainActivity">

          <Button
            android:id="@+id/btn_class"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_marginTop="5dp"
            android:text="AUtils.Class"
            android:textAllCaps="false"
            />


</RelativeLayout>

2.编写MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //补丁所在的路劲
        String path=Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk";
        //添加补丁
        TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),path );
        Log.d("TAG","path="+path);

        //2.演示类的替换
       final Button btn_class = (Button) findViewById(R.id.btn_class);
        btn_class.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //新建了一个A类的对象
                A a = new A();
                btn_class.setText(a.getName());
            }
        });

    }



}

3.新添加一个A.java类

public class A {

   public  String getName(){

       return  "Name is form A Class ";

    }
}

4.修改app/build.gradle:

tinkerBuildFlavorDirectory注释掉,因为暂时不用到多渠道打包。

ext {
    //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
    tinkerEnabled = true

    //for normal build
    //old apk file to build patch apk
    tinkerOldApkPath = "${bakPath}/app-debug-1218-16-32-05.apk"
    //proguard mapping file to build patch apk
    tinkerApplyMappingPath = "${bakPath}/mapping.txt"
    //resource R.txt to build patch apk, must input if there is resource changed
    tinkerApplyResourcePath = "${bakPath}/app-debug-1218-16-32-05-R.txt"

    //only use for build all flavor, if not, just ignore this field
//    tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
}

5.开始打包:

点击assembleRelease后,在app/build/apk下生成:下面三个文件1

6.将apk安装在模拟器中

安装:app-release-1220-10-49-26.apk

当点击button的时候出现:Name is form A Class

7.修改代码,:

添加一个B.java类

public class B {

    public String getName(Context context){
        try {
            InputStream in = context.getAssets().open("config.txt");
            BufferedReader reader=new BufferedReader(new InputStreamReader(in));
            String s = reader.readLine();
            return s;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return  " IOException is from B class";
    }
}

在Assets文件夹中添加config.txt文件, 并输入

Name if from B Class

在MainActivity中用B类替换A类

public class MainActivity extends AppCompatActivity {

    private TextView tv_show;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String path=Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk";
        TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),path );
        Log.d("TAG","path="+path);

        //2.演示类的替换
       final Button btn_class = (Button) findViewById(R.id.btn_class);
        btn_class.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                A a = new A();
//                btn_class.setText(a.getName());
                //B类替换了A类
                B b=new B();
                String name = b.getName(MainActivity.this);
                btn_class.setText(name);
            }
        });

    }

}

8.修改build.gradle的配置文件

启用并修改下面两个属性,并启用它们在该gradle被引用的地方,因为在第一打包的时候已把它们注释了

tinkerOldApkPath = "${bakPath}/app-release-1220-10-49-26.apk" //上面打包生成的.apk
tinkerApplyMappingPath = "${bakPath}/app-release-1220-10-49-26-mapping.txt"//上面生成的mapping.txt
tinkerApplyResourcePath = "${bakPath}/app-release-1220-10-49-26-R.txt" //上面打包生成的R.txt

9.一件制作补丁包:

调用tinkerPatchRelease, 补丁包与相关日志会保存在/build/outputs/tinkerPatch/2

10.打补丁:

然后我们将patch_signed_7zip.apk 补丁包 推送到手机的sdcard中

4

11.重新启动APP( 有时需要重新启动APP才能生效 )

再次点击button的时候就会冒出:name if from B Class

现在我们就可以实现在没有重新安装apk的前提下动态发布代码,动态添加代码修复bugs。

12.总结:

1.Tinker可以实现添加类,并实现类的替换

2.Tinker可以实现对Assets资源下的文件进行操作

3.资源替换

1.编写avtivity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="xmg.com.tinkertest.MainActivity">


    <ImageView
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:src="@mipmap/ic_launcher"
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_margin="5dp"
        android:hint="输入账号"
        android:paddingLeft="10dp"
        />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_margin="5dp"
        android:hint="输入密码"
        android:paddingLeft="10dp"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_margin="5dp"
        android:text="登录"
        />

    <Button
        android:id="@+id/btn_Theme"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_margin="5dp"
        android:text="切换主题"
        />


</RelativeLayout>

2.编写MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //补丁所在的路劲
        String path=Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk";
        //添加补丁
        TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),path );
        Log.d("TAG","path="+path);

        //3.演示资源的替换
        final Button btn_class =(Button)findViewById(R.id.btn_Theme);
        btn_class.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "资源替换", Toast.LENGTH_SHORT).show();
            }
        });


    }


}

3.修改app/build.gradle:

tinkerBuildFlavorDirectory注释掉,因为暂时不用到多渠道打包。

ext {
    //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
    tinkerEnabled = true

    //for normal build
    //old apk file to build patch apk
    tinkerOldApkPath = "${bakPath}/app-debug-1218-16-32-05.apk"
    //proguard mapping file to build patch apk
    tinkerApplyMappingPath = "${bakPath}/mapping.txt"
    //resource R.txt to build patch apk, must input if there is resource changed
    tinkerApplyResourcePath = "${bakPath}/app-debug-1218-16-32-05-R.txt"

    //only use for build all flavor, if not, just ignore this field
//    tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
}

4.开始打包:

点击assembleRelease后,在app/build/apk下生成:下面三个文件1

5.将apk安装在模拟器中

安装:app-release-1220-10-49-26.apk

当点击切换主题的时候出现:资源替换

6.修改代码,:

修改MainActivity.java

public class MainActivity extends AppCompatActivity {

    private TextView tv_show;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //切换主题
        sp= getSharedPreferences("config", 0);
        boolean theme = sp.getBoolean("theme",true);
        if(theme){
            this.setTheme(R.style.DayTheme);
        }else{
            this.setTheme(R.style.NightTheme);
        }

        setContentView(R.layout.activity_main);
        String path=Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk";
        TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),path );
        Log.d("TAG","path="+path);

        //3.演示资源的替换:切换主题
        final Button btn_class =(Button)findViewById(R.id.btn_Theme);
        btn_class.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                Toast.makeText(MainActivity.this, "资源替换", Toast.LENGTH_SHORT).show();
                boolean theme = sp.getBoolean("theme",true);
                sp.edit().putBoolean("theme",!theme).commit();
                MainActivity.this.recreate();//从新Create Activity
            }
        });

    }


}

在vaulse包下的styles.xml中添加:

    <!--白天主题-->
    <style name="DayTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">#03A9F4</item>
        <item name="android:textColorPrimary">#ffffff</item>
        <item 

name="android:windowBackground">@color/background_material_light</item>
        <item name="colorAccent">#00BCD4</item>
        <item name="colorControlNormal">#00BCD4</item>

        <item name="android:textColor">#9C27B0</item>
        <item name="android:textSize">16sp</item>
    </style>


    <!--夜晚主题-->
    <style name="NightTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">#00796B</item>
        <item name="android:textColorPrimary">#212121</item>
        <item 

name="android:windowBackground">@color/background_material_dark</item>
        <item name="colorAccent">#00796B</item>
        <item name="colorControlNormal">#212121</item>

        <item name="android:textColor">#212121</item>
        <item name="android:textSize">20sp</item>
    </style>

7.修改build.gradle的配置文件

启用并修改下面两个属性,并启用它们在该gradle被引用的地方,因为在第一打包的时候已把它们注释了

tinkerOldApkPath = "${bakPath}/app-release-1220-10-49-26.apk" //上面打包生成的.apk
tinkerApplyMappingPath = "${bakPath}/app-release-1220-10-49-26-mapping.txt"//上面生成的mapping.txt
tinkerApplyResourcePath = "${bakPath}/app-release-1220-10-49-26-R.txt" //上面打包生成的R.txt

8.一件制作补丁包:

调用tinkerPatchRelease, 补丁包与相关日志会保存在/build/outputs/tinkerPatch/2

9.打补丁:

然后我们将patch_signed_7zip.apk 补丁包 推送到手机的sdcard中

4

10.重新启动APP( 有时需要重新启动APP才能生效 )

再次点击切换主题的时候就会:切换主题

现在我们就可以实现在没有重新安装apk的前提下动态发布代码,动态添加代码修复bugs。

11.总结:

1.Tinker可以实现资源的替换

2.Tinker可以实现对res资源下的valuse文件夹下的资源操作

4.So替换

1.编写avtivity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="xmg.com.tinkertest.MainActivity">

    <Button
    android:id="@+id/btn_so"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:layout_margin="5dp"
    android:text="So的替换"
    android:textAllCaps="false"
    />

</RelativeLayout>

2.编写MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String path= Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk";
        TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),path );
        Log.d("TAG","path="+path);

        //4.演示So的替换
        final Button btn_so=(Button)findViewById(R.id.btn_so);
        btn_so.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                 //for lib/armeabi, just use TinkerInstaller.loadLibrary
                TinkerInstaller.loadArmLibrary(this,"testShare");//加载testShare.so
                String version1 =TestShare.getVersionStatic1();
                Log.d("TAG","version1="+version1);
                btn_so.setText(version1);
            }
        });

    }



}

3.添加testShare.so库

12

4.添加TestShare.java

新建一个包:xmg.com.ndktest.ndk

然后把TestShare.java 类添加到该包中

public class TestShare {
    static {
        System.loadLibrary("testShare");
    }

    public  native  String getVersion1();
    public static native  String getVersionStatic1();
}

5.修改app/build.gradle:

tinkerBuildFlavorDirectory也注释掉,因为暂时不用到多渠道打包。

ext {
    //for some reason, you may want to ignore tinkerBuild, such as instant run debug build?
    tinkerEnabled = true

    //for normal build
    //old apk file to build patch apk
    tinkerOldApkPath = "${bakPath}/app-debug-1218-16-32-05.apk"
    //proguard mapping file to build patch apk
    tinkerApplyMappingPath = "${bakPath}/mapping.txt"
    //resource R.txt to build patch apk, must input if there is resource changed
    tinkerApplyResourcePath = "${bakPath}/app-debug-1218-16-32-05-R.txt"

    //only use for build all flavor, if not, just ignore this field
    tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
}

6.开始打包:

点击assembleRelease后,在app/build/apk下生成:下面三个文件1

7.将apk安装在模拟器中

安装:app-release-1220-10-49-26.apk

当点击button的时候出现:Hello World from jni 1.0.0!

8.修改代码,:

修改MainActivity中的代码

public class MainActivity extends AppCompatActivity {

    private TextView tv_show;

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

          //4.演示So的替换
        //for lib/armeabi, just use TinkerInstaller.loadLibrary
        TinkerInstaller.loadArmLibrary(this,"testShare");//加载testShare
        final Button btn_so=(Button)findViewById(R.id.btn_so);
        btn_so.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               //替换换成so的第二版本库
                String version2 =TestShare.getVersionStatic2();
                Log.d("TAG","version2="+version2);
                btn_so.setText(version2);
            }
        });

    }


}

修改TestShare.java

public class TestShare {
    static {
        System.loadLibrary("testShare");
    }

    public  native  String getVersion1();
    public static native  String getVersionStatic1();
    //添加下面两个本地方法
    public  native  String getVersion2();
    public static native  String getVersionStatic2();
}

把lib下所有的so库全部替换成第二版本的so库:

9.修改build.gradle的配置文件

启用并修改下面两个属性,并启用它们在该gradle被引用的地方,因为在第一打包的时候已把它们注释了

tinkerOldApkPath = "${bakPath}/app-release-1220-10-49-26.apk" //上面打包生成的.apk
tinkerApplyMappingPath = "${bakPath}/app-release-1220-10-49-26-mapping.txt"//上面生成的mapping.txt
tinkerApplyResourcePath = "${bakPath}/app-release-1220-10-49-26-R.txt" //上面打包生成的R.txt

8.一件制作补丁包:

调用tinkerPatchRelease, 补丁包与相关日志会保存在/build/outputs/tinkerPatch/2

9.打补丁:

然后我们将patch_signed_7zip.apk 补丁包 推送到手机的sdcard中

4

10.重新启动APP( 有时需要重新启动APP才能生效 )

再次点击chick me的时候就会冒出Toast:

现在我们就可以实现在没有重新安装apk的前提下动态发布代码,动态更新so库。

11.总结:

1.Tinker可以实现so库动态的更新

该项目下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值