Android 热修复框架AndFix

导论:
上篇博客提到增量更新,这篇就要说说热修复,那么到底什么区别呢?通俗的理解:
1:你软件有新的功能或者升级一下一些更改,但是下载完整的APK又比较大,这时为了省流量才会用到增量更新,这个叫增量包,格式为.patch文件
2:用户使用你的应用某个地方老是出现bug,那么怎么办,不会还是发布完整APK下载使用吧,当然不会,这时就需要修补bug的补丁了,类似windows 漏洞更新,这个叫补丁包,格式为.apatch

为了学习更多的知识,我找到某个大牛的demo,里面介绍的是阿里百川的热修复框架Andfix,当然你也可以自己写框架,除非你强过阿里的技术大牛,在这里还会了解一点第三方服务器存储,友盟统计,极光消息推送Jpush,多线程下载等知识,也是为了让大家学到更多的知识,若有疑问,还请海涵.

原理:
1:启动应用获取友盟在线参数来判断当前应用是否有补丁下载,有则下载到SDcard里并且通过阿里热修复框架AndFix自动修补补丁
2:使用极光Jpush推送消息到应用,若收到消息,就有补丁需要下载,若没有收到消息,就通过友盟在线参数来判断有无补丁

步骤
1:在app/module build.gradle中添加依赖,有多线程下载,Jpush,友盟,阿里andfix

dependencies {
    ....
    compile 'com.mani:thindownloadmanager:1.0.0'
    compile files('libs/jpush-android-2.1.0.jar')
    compile files('libs/umeng-onlineconfig_v1.0.0.jar')
    compile 'com.alipay.euler:andfix:0.3.1@aar'
}

2:导入AndFix的so库文件以及极光推送的so库文件到jniLibs目录里,具体地址自己自行百度
这里写图片描述

3:配置友盟在线参数以及极光推送自定义消息的内容在这里忽略,官方有开发文档可以参考

4:制作补丁包放在第三方服务器上
A:下载apkpatch工具:
https://github.com/alibaba/AndFix/raw/master/tools/apkpatch-1.0.3.zip

B:将old.apk,new.apk,Jks签名文件拷贝到解压apkpatch工具的目录下,打开命令行输入命令生成补丁包,格式如下

生成差异文件命令 : apkpatch.bat -f new.apk -t old.apk -o output1 -k debug.keystore -p android -a androiddebugkey -e android  
-f <new.apk> :新版本  
-t <old.apk> : 旧版本  
-o <output> : 输出目录  
-k <keystore>: 打包所用的keystore  
-p <password>: keystore的密码  
-a <alias>: keystore 用户别名  
-e <alias password>: keystore 用户别名密码  

C:将生成的补丁.apach放到服务器上

D:根据服务器存储信息去配置友盟后台和定义极光推送消息路径

5:在自定义Application类进行初始化andfix PatchManager和Jpush JPushInterface

public class BaseApplication extends Application {
    public static String VERSION_NAME;
    public static PatchManager mPatchManager;

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            VERSION_NAME = this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName;
            mPatchManager = new PatchManager(this);
            mPatchManager.init(VERSION_NAME);
            mPatchManager.loadPatch();
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        JPushInterface.init(this);
    }
}

6:比对本地应用版本和远程服务器的版本及补丁版本,若有更新,就下载到本地SDcard,最后调用andfix PatchManager自动修补补丁

private static final int THREAD_COUNT = 3;  //下载的线程数
    private ThinDownloadManager mDownloadManager;
    private LocalPreferencesHelper mLocalPreferencesHelper;

    private static class SingletonHolder {
        public static final RepairBugUtil INSTANCE = new RepairBugUtil();
    }

    //单例模式
    public static RepairBugUtil getInstance() {
        return SingletonHolder.INSTANCE;
    }
    //使用ThinDownloadManager多线程下载和PatchManager修补补丁
    public void downloadAndLoad(Context context, final PatchBean bean, String downloadUrl) {
        if (mLocalPreferencesHelper == null) {
            mLocalPreferencesHelper = new LocalPreferencesHelper(context, SPConst.SP_NAME);
        }
        Uri downloadUri = Uri.parse(downloadUrl);
        Uri destinationUri = Uri.parse(Environment.getExternalStorageDirectory()
                .getAbsolutePath() + bean.url);
        //下载方法
        DownloadRequest downloadRequest = new DownloadRequest(downloadUri)
                .setDestinationURI(destinationUri)
                .setPriority(DownloadRequest.Priority.HIGH)
                .setDownloadListener(new DownloadStatusListener() {
                    public void onDownloadComplete(int id) {
                        try {
                            String patchFileString = Environment.getExternalStorageDirectory()
                                    .getAbsolutePath() + bean.url;
                            //修补补丁
                            BaseApplication.mPatchManager.addPatch(patchFileString);
                            File f = new File(patchFileString);
                            if (f.exists()) {
                                boolean result = new File(patchFileString).delete();
                            }
                        } catch (IOException e) {

                        } catch (Throwable throwable) {

                        }
                    }

                    public void onDownloadFailed(int id, int errorCode, String errorMessage) {
                    }

                    public void onProgress(int id, long totalBytes, int progress) {
                    }
                });
        //多线程下载
        mDownloadManager = new ThinDownloadManager(THREAD_COUNT);
        mDownloadManager.add(downloadRequest);
    }
    //比对远程服务器应用和补丁版本和本地的区别,若版本相同,但是补丁不一样,那么就下载新的补丁
    public void comparePath(Context context, PatchBean RemoteBean) throws Exception {
        String pathInfo = mLocalPreferencesHelper.getString(SPConst.PATH_INFO);
        final PatchBean localBean = GsonUtils.getInstance().parseIfNull(PatchBean.class, pathInfo);
        if (BaseApplication.VERSION_NAME.equals(RemoteBean.app_v)) {
            if (localBean == null && !TextUtils.isEmpty(RemoteBean.path_v)
                    || localBean.app_v.equals(RemoteBean.app_v) &&
                    !localBean.path_v.equals(RemoteBean.path_v)) {
                downloadAndLoad(context, RemoteBean,
                        SPConst.URL_PREFIX + RemoteBean.url);
                String json = GsonUtils.getInstance().parse(RemoteBean);
                mLocalPreferencesHelper.saveOrUpdate(SPConst.PATH_INFO, json);
            }
        }
    }
    //释放线程
    public void release() {
        if (mDownloadManager != null) {
            mDownloadManager.release();
        }
    }

7:在MainActivity中进行友盟补丁检测和极光推送的自定义消息的处理

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        registerMessageReceiver();  
        initUmeng(); 
        getUmengParamAndFix();
    }
    //获取友盟后台定义键对应的值并下载补丁后修补
    private void getUmengParamAndFix() {
        //获取友盟在线参数对应key的values
        String pathInfo = OnlineConfigAgent.getInstance().getConfigParams(this, UMENG_ONLINE_PARAM);
        if (!TextUtils.isEmpty(pathInfo)) {
            PatchBean onLineBean = GsonUtils.getInstance().parseIfNull(PatchBean.class, pathInfo);
            try {
                RepairBugUtil.getInstance().comparePath(this, onLineBean);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //初始化友盟
    private void initUmeng() {
        OnlineConfigAgent.getInstance().updateOnlineConfig(this);
        OnlineConfigAgent.getInstance().setDebugMode(true);
    }
    //注销Jpush广播
    protected void onDestroy() {
        unregisterReceiver(mMessageReceiver);
        RepairBugUtil.getInstance().release();
        super.onDestroy();
    }
    private WeakHandler mHandler = new WeakHandler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == MSG_WHAT_DOWNLOAD) {
                String message = (String) msg.obj;
                if (TextUtils.isEmpty(message)) return false;
                try {
                    //处理Jpush推送消息,下载补丁完成修补
                    PatchBean bean = GsonUtils.getInstance().parse(PatchBean.class, message);
                    RepairBugUtil.getInstance().comparePath(MainActivity.this, bean);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return false;
        }
    });
    private MessageReceiver mMessageReceiver;
    public static final String MESSAGE_RECEIVED_ACTION = "com.aile.andfix.MESSAGE_RECEIVED_ACTION";
    public static final String KEY_MESSAGE = "message";
    //注册Jpush广播
    public void registerMessageReceiver() {
        mMessageReceiver = new MessageReceiver();
        IntentFilter filter = new IntentFilter();
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        filter.addAction(MESSAGE_RECEIVED_ACTION);
        registerReceiver(mMessageReceiver, filter);
    }
    //创建Jpush广播处理器,接收Jpush推送消息内容
    public class MessageReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            if (MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) {
                String message = intent.getStringExtra(KEY_MESSAGE);
                Message msg = new Message();
                msg.what = MSG_WHAT_DOWNLOAD;
                msg.obj = message;
                mHandler.sendMessage(msg);
            }
        }
    }

8:关于极光Jpush广播的的创建及注册AndroidManifest.xml相关问题和友盟统计后台设置可以自行进入相关官网学习开发文档

好了,结束.

猜你喜欢

转载自blog.csdn.net/ware00/article/details/70849354