android应用更新框架AppUpdateLib(仿苹果极简风格、高度自定义)支持androidX

10 篇文章 0 订阅
1 篇文章 0 订阅

开发过程中,发现很多更新库定制样式比较费事(如自定义dialog功能不完善,更新dialog和下载dialog耦合高等),且功能不是特别契合需求(如使用dialogfragment封装难修改圆角、样式较难定制),为加快开发速度提升效率,空闲时间简单封装了一个应用更新库和dialog库,独立于okhttp和retrofit。

项目地址:https://github.com/ibshe/AppUpdateLib

或:jackshen/AppUpdateLib

功能:

轻量、易用、可定制的android更新和dialog库,xml自定义ui,默认ios极简风格,一行代码实现更新提示和下载安装。支持静默更新,强制更新等,支持本地json和直接读取服务端json,一个builder定制化各种dialog,个性化toast。

效果图:

使用方式:

1、在你项目的根目录 build.gradle中加入如下配置:

allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}

2、主app的build.gradle中添加依赖:

support版本:

github方式:

dependencies {
	        implementation 'com.github.ibshe:AppUpdateLib:1.0.7'
	}

androidx版本:

dependencies {
	        implementation 'com.github.ibshe:AppUpdateLibX:1.0.7'
	}

3、用法:

1. 一行代码检查更新、下载安装

new UpdateWrapper.Builder(this,mJsonUrl).build().start();

以上使用默认的更新dialog和下载dialog样式,以及默认toast提示,更多参数见下文。

也可自定义获取到服务端数据后解析成实体VersionModel传入,效果相同,优先级较高。

new UpdateWrapper.Builder(this,null)
                        .model(mModel)//本地实体,优先级 > url
                        .build()
                        .start();

2. 自定义更新dialog样式

布局文件完全自定义,布局内部元素在回调的view中自行处理,调用AbstractUpdateDialog的start()方法实现下载安装,调用cancel()取消更新等等,详见demo,下面举例:

custom_update_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/update_dialog_rootview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="30dp"
    android:layout_marginRight="30dp"
    android:layout_gravity="center"
    android:background="@drawable/update_bg_dark"
    xmlns:tools="http://schemas.android.com/tools">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minWidth="260dp"
        android:layout_centerInParent="true"
        android:orientation="vertical">
        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:layout_marginBottom="10dp"
            android:gravity="center"
            android:layout_marginTop="10dp"
            tools:text="@string/update_lib_dialog_title"
            android:visibility="visible"
            android:textColor="@color/dark_tx"
            android:textStyle="bold"
            android:textSize="20sp" />

        <RelativeLayout
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/title"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            >

            <TextView
                android:id="@+id/message"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center|left"
                android:layout_centerInParent="true"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:lineSpacingMultiplier="1.2"
                android:textSize="16sp"
                android:textColor="@color/dark_tx"
                tools:text="@string/update_lib_update_content" />

        </RelativeLayout>

        <View
            android:id="@+id/line"
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:layout_marginTop="10dp"
            android:layout_below="@id/content"
            android:background="@color/lineGray" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_below="@id/line">

            <TextView
                android:id="@+id/negtive"
                android:layout_width="0dp"
                android:layout_height="40dp"
                android:layout_weight="1"
                android:gravity="center"
                android:singleLine="true"
                tools:text="@string/update_lib_cancel"
                android:textColor="@color/orange"
                android:textSize="16sp" />

            <View
                android:id="@+id/column_line"
                android:layout_width="1px"
                android:layout_height="match_parent"
                android:background="@color/lineGray" />

            <TextView
                android:id="@+id/positive"
                android:layout_width="0dp"
                android:layout_height="40dp"
                android:layout_weight="1"
                android:gravity="center"
                android:singleLine="true"
                tools:text="@string/update_lib_update"
                android:textColor="@color/dark_tx"
                android:textSize="16sp" />
        </LinearLayout>
    </RelativeLayout>

</RelativeLayout>

继承自AbstractUpdateDialog类的CustomDialog

class CustomDialog extends AbstractUpdateDialog {
        public CustomDialog(Context context, final String title, final String negivteTx, final String positiveTx, int layoutId) {
            super(context, title, negivteTx, positiveTx, layoutId, false, RadiusEnum.UPDATE_RADIUS_10, new DownlaodCallback() {
                @Override
                public void callback(int code, String message) {
                    Log.i(TAG,message);
                }
            });
            this.customOnCreate(new BindingCallback() {
                @Override
                public void bindingVh(DialogViewHolder holder) {
                    View view = holder.getConvertView();
                    view.setBackgroundResource(ScreenUtils.getDrawableId(RadiusEnum.UPDATE_RADIUS_30.getType()));
                    titleTv = view.findViewById(R.id.title);
                    contentTv = view.findViewById(R.id.message);
                    negtive = view.findViewById(R.id.negtive);
                    positive = view.findViewById(R.id.positive);
                    titleTv.setText(title);
                    contentTv.setText(BaseConfig.UPDATE_CONTENT);
                    negtive.setText(negivteTx);
                    positive.setText(positiveTx);
                    negtive.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            cancel();
                        }
                    });
                    positive.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            start();
                        }
                    });
                }
            });
        }
    }

使用自定义更新dialog: 

new UpdateWrapper.Builder(this,mJsonUrl)
                        .customDialog(new CustomDialog(this,"发现新版本","取消","升级",R.layout.custom_update_dialog))
                        .checkEveryday(false)
                        .radius(RadiusEnum.UPDATE_RADIUS_30)
                        .build()
                        .start();

xml完全自定义,例如:

3、静默下载(无notification,后台下载完成直接跳转安装)

new DownloadWrapper(this,mDownloadUrl,true,RadiusEnum.UPDATE_RADIUS_30).start();

4、强制更新

new UpdateWrapper.Builder(this,mJsonUrl)
                        .isMustUpdate(true)
                        .build()
                        .start();

5、设置下载dialog样式

new DownloadDialog.Builder(this,mDownloadUrl,false)
                        .downloadCallback(new DownlaodCallback() {
                            @Override
                            public void callback(int code, String message) {
                            }
                        })
                        .build()
                        .setDialogStyle(R.drawable.update_bg_dark)
                        .setCancelColor(R.color.orange)
                        .setConfirmColor(R.color.dark_tx)
                        .setTitleColor(R.color.dark_tx)
                        .setLineColor(R.color.lineGray)
                        .setProgressStyle(getResources().getDrawable(R.drawable.custom_progressbar_bg))
                        .start();

6、不弹或自定义更新dialog,下面方式调用直接下载

new DownloadWrapper(this,mDownloadUrl,false,RadiusEnum.UPDATE_RADIUS_10)
                        .start();

或者:

new DownloadDialog.Builder(this,mDownloadUrl,false)
                        .radius(RadiusEnum.UPDATE_RADIUS_10)
                        .build().start();

7、主要参数

new UpdateWrapper.Builder(this,mJsonUrl)
                        .title("测试更新")//更新dialog标题
                        .negtiveText("取消")//更新dialog取消按钮
                        .radius(RadiusEnum.UPDATE_RADIUS_10)//更新和下载dialog圆角弧度同时生效
                        .positiveText("立即升级")//更新dialog确定按钮
                        .checkEveryday(false)//默认false 立即下载,true 每天最多检查一次。如今日已检查,则不再检查
                        .showNetworkErrorToast(true)//无网络提示
                        .showNoUpdateToast(true)//无更新提示
                        .isPost(false)//检查更新请求协议是否为POST,默认GET
                        .isMustUpdate(false)//是否强制更新
                        .backgroundDownload(false)//是否后台下载
                        .model(null)//本地实体,不传默认为null,优先级大于mJsonUrl
                        .downloadCallback(new DownlaodCallback() {//下载状态回调
                            @Override
                            public void callback(int code, String message) {
                                //code 1、后台下载;2、取消下载;3、下载完成;4、下载失败;
                                //Code 1. Background download; 2. Cancel the download; 3. Download completed; 4. Download failed;
                                Log.i(TAG,message);
                            }
                        })
                        .updateCallback(new UpdateWrapper.UpdateCallback() {//获取远端信息回调
                            @Override
                            public void res(VersionModel model, boolean hasNewVersion) {
                                Log.i(TAG,model.toString());
                            }
                        })
                        .build()
                        .start();

8、当做dialog使用,自定义dialog样式,自定义toast都OK

new BsDialog(this, R.layout.custom_update_dialog) {
            @Override
            public void onBindViewHolder(DialogViewHolder holder) {
                View view = holder.getConvertView();
                titleTv = view.findViewById(com.boge.update.R.id.title);
                contentTv = view.findViewById(com.boge.update.R.id.message);
                negtive = view.findViewById(com.boge.update.R.id.negtive);
                positive = view.findViewById(com.boge.update.R.id.positive);
                titleTv.setText(TextUtils.isEmpty(mTitle)?(TextUtils.isEmpty(BaseConfig.UPDATE_TITLE)?mContext.getString(R.string.update_lib_dialog_title):BaseConfig.UPDATE_TITLE):mTitle);
                contentTv.setText(BaseConfig.UPDATE_CONTENT);
                negtive.setText(TextUtils.isEmpty(mNegivteTx)?(TextUtils.isEmpty(BaseConfig.UPDATE_NEGITIVE)?mContext.getString(R.string.update_no_thanks):BaseConfig.UPDATE_NEGITIVE):mNegivteTx);
                positive.setText(TextUtils.isEmpty(mPositiveTx)?(TextUtils.isEmpty(BaseConfig.UPDATE_POSITIVE)?mContext.getString(R.string.update_sure):BaseConfig.UPDATE_POSITIVE):mPositiveTx);
                if(mRadius.getType() != 10){
                    view.setBackgroundResource(ScreenUtils.getDrawableId(mRadius.getType()));
                }
                negtive.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        cancel();
                    }
                });
                positive.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        start();
                    }
                });
            }
        }.backgroundLight(0.5)
                .setCancelAble(true)
                .setCanceledOnTouchOutside(true)
                .showDialog();

9、服务端json格式(文件)

{
  "versionCode":24,
  "versionName":"1.7.8",
  "content":"增加报表导出功能",
  "minSupport":4,	
  "url":"http://103.45.138.168/apps/music_pj.apk",
  "updateTitle":"发现新版本",
  "mustUpdate":false,
  "date":"2021-06-01 09:02:10"
}

10、代码混淆

-keep class com.boge.update.** {*;}

更多参数查看demo,欢迎提出问题或参与开源,ibshen@aliyun.com,如对你有帮助请star支持,谢谢

demo下载(非csdn)

或 apk看效果

框架内部支持中/英文(其他语言只需要在对应的string.xml中取相同的名字即可)内部对话框背景图片、按钮支持自定义了查看版本中的Log只需要过滤AppUpdate开头的Tag重点: 如果没有设置downloadPath则默认为getExternalCacheDir()目录,同时不会申请[存储]权限!目录编译问题效果图功能介绍DownloadManagerUpdateConfiguration使用步骤Demo下载体验版本更新记录结语编译问题因为适配了Android O的通知栏,所以依赖的v7包版本比较高appcompat-v7:26.1.0使用的gradle版本为gradle-4.1-all,所以建议使用Android Studio 3.0及以上的版本打开此项目效果图     功能介绍 支持断点下载 支持后台下载 支持自定义下载过程 支持 设备 >= Android M 动态权限的申请 支持通知栏进度条展示(或者自定义显示进度) 支持Android支持Android支持中/英文双语 支持自定内置对话框的样式 使用HttpURLConnection下载,未集成其他第三方框架更加详细的文档参阅此处《AppUpdate API文档》DownloadManager:配置文档初始化使用DownloadManager.getInstance(this)属性描述默认值是否必须设置context上下文nulltrueapkUrlapk的下载地址nulltrueapkNameapk下载好的名字nulltruedownloadPathapk下载的位置getExternalCacheDir()falseshowNewerToast是否提示用户 "当前已是最新版本"falsefalsesmallIcon通知栏的图标(资源id)-1trueconfiguration这个库的额外配置nullfalseapkVersionCode更新apk的versionCode (如果设置了那么库中将会进行版本判断下面的属性也就需要设置了)1falseapkVersionName更新apk的versionNamenullfalseapkDescription更新描述nullfalseapkSize新版本的安装包大小(单位M)nullfalseauthorities兼容Android N uri授权应用包名falseUpdateConfiguration:配置文档属性描述默认值notifyId通知栏消息id1011notificationChannel适配Android O的渠道通知详情查阅源码httpManager设置自己的下载过程nullbreakpointDownload是否需要支持断点下载trueenableLog是否需要日志输出trueonDownloadListener下载过程的回调nulljumpInstallPage下载完成是否自动弹出安装页面trueshowNotification是否显示通知栏进度(后台下载提示)trueforcedUpgrade是否强制升级falseonButtonClickListener按钮点击事件回调nulldialogImage对话框背景图片资源(图片规范参考demo)-1dialogButtonColor对话框按钮的颜色-1dialogButtonTextColor对话框按钮的文字颜色-1所有版本:点击查看使用步骤第一步: app/build.gradle进行依赖implementation 'com.azhon:appupdate:1.7.3'第二步:创建DownloadManager,更多用法请查看这里示例代码DownloadManager manager = DownloadManager.getInstance(this); manager.setApkName("appupdate.apk")         .setApkUrl("https://raw.githubusercontent.com/azhon/AppUpdate/master/apk/appupdate.apk")         .setSmallIcon(R.mipmap.ic_launcher)         //可设置,可不设置         .setConfiguration(configuration)         .download();第三步:兼容Android N 及以上版本,在你应用的Manifest.xml添加如下代码<--! android:authorities="${applicationId}"  这个值必须与DownloadManager中的authorities一致(不设置则为应用包名)--> <provider     android:name="android.support.v4.content.FileProvider"     android:authorities="${applicationId}"     android:exported="false"     android:grantUriPermissions="true">     <meta-data         android:name="android.support.FILE_PROVIDER_PATHS"         android:resource="@xml/file_paths_public" /> </provider>第四步:资源文件res/xml/file_paths_public.xml内容<?xml version="1.0" encoding="utf-8"?> <paths>     <external-path         name="app_update_external"         path="/" />     <external-cache-path         name="app_update_cache"         path="/" /> </paths>兼容Android O及以上版本,需要设置NotificationChannel(通知渠道);库中已经写好可以前往查阅NotificationUtil.java温馨提示:升级对话框中的内容是可以上下滑动的哦!如果需要实现自己一套下载过程,只需要继承BaseHttpDownloadManager 并使用listener更新进度public class MyDownload extends BaseHttpDownloadManager {}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值