应用更新(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zsp_android_com/article/details/81937537

前言

更新分全量更新和增量更新(参考)。

UpdateAppDemo

FileDownloader

说明

下载可用系统自带DownloadManager。该类API level 9后出现,已处理下载失败、重新下载等。下载过程交系统负责,不需过多处理。国内某些机型阉割自带DownloadManager时可自实现下载类,也可如上引FileDownloader

使用

依赖

implementation 'com.liulishuo.filedownloader:library:1.7.5'

权限

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

update

AppDownload

package update;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;

import com.liulishuo.filedownloader.BaseDownloadTask;
import com.liulishuo.filedownloader.FileDownloadListener;
import com.liulishuo.filedownloader.FileDownloader;

import util.LogUtils;
import util.ToastUtils;

/**
 * @decs: AppDownload
 * @date: 2018/8/21 21:09
 * @version: v 1.0
 */
class AppDownload {
    /**
     * 地址
     */
    public static String path;

    /**
     * 浏览器下载
     *
     * @param context 上下文
     * @param url     url
     */
    public static void downloadByBrowser(Context context, String url) {
        Uri uri = Uri.parse(url);
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    /**
     * 应用内下载
     *
     * @param context     上下文
     * @param url         url
     * @param versionName 版本名
     */
    public static void downloadInApp(final Context context, String url, final String versionName) {
        path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/dfs/update/" + versionName + ".apk";
        // 注册定制组件
        FileDownloader.setup(context);
        // 执行
        FileDownloader.getImpl().create(url)
                .setPath(path)
                .setListener(new FileDownloadListener() {
                    @Override
                    protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) {
                    }

                    @Override
                    protected void connected(BaseDownloadTask task, String etag, boolean isContinue, int soFarBytes, int totalBytes) {
                    }

                    @Override
                    protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {
                        sendBroadcast(context, (int) (soFarBytes * 100.0 / totalBytes), versionName);
                        LogUtils.e((int) (soFarBytes * 100.0 / totalBytes));
                    }

                    @Override
                    protected void blockComplete(BaseDownloadTask task) {
                    }

                    @Override
                    protected void retry(final BaseDownloadTask task, final Throwable ex, final int retryingTimes, final int soFarBytes) {
                    }

                    @Override
                    protected void completed(BaseDownloadTask task) {
                        sendBroadcast(context, 100, versionName);
                        ToastUtils.shortShow("下载完成");
                    }

                    @Override
                    protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) {
                    }

                    @Override
                    protected void error(BaseDownloadTask task, Throwable e) {
                        ToastUtils.shortShow("下载出错");
                    }

                    @Override
                    protected void warn(BaseDownloadTask task) {
                    }
                }).start();
    }

    /**
     * 发送广播
     *
     * @param context     上下文
     * @param progress    进度
     * @param versionName 版本名
     */
    private static void sendBroadcast(Context context, int progress, String versionName) {
        Intent intent = new Intent("com.self.zsp.dfs.AppUpdate");
        intent.putExtra("progress", progress);
        intent.putExtra("title", versionName);
        context.sendBroadcast(intent);
    }
}

AppUpdate

package update;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;

import util.LogUtils;

/**
 * @decs: AppUpdate
 * @date: 2018/8/21 21:24
 * @version: v 1.0
 */
public class AppUpdate {
    public static final int CHECK_BY_VERSION_NAME = 1001;
    private static final int CHECK_BY_VERSION_CODE = 1002;
    public static final int DOWNLOAD_BY_APP = 1003;
    private static final int DOWNLOAD_BY_BROWSER = 1004;
    private Activity activity;
    private int checkBy = CHECK_BY_VERSION_CODE;
    private int downloadBy = DOWNLOAD_BY_APP;
    private int versionCode = 0;
    private String apkPath = "";
    private String versionName = "";
    /**
     * 强更否
     */
    private boolean isForce = false;
    private int localVersionCode = 0;
    private String localVersionName = "";
    /**
     * false不适配7.0(默true)
     */
    public static boolean needFitAndroidN = true;
    public static boolean showNotification = true;
    private String updateInfo = "";

    public AppUpdate fitAndroidN(boolean fitAndroidN) {
        AppUpdate.needFitAndroidN = fitAndroidN;
        return this;
    }

    private AppUpdate(Activity activity) {
        this.activity = activity;
        localVersionInfo(activity);
    }

    public static AppUpdate from(Activity activity) {
        return new AppUpdate(activity);
    }

    public AppUpdate checkBy(int checkBy) {
        this.checkBy = checkBy;
        return this;
    }

    public AppUpdate apkPath(String apkPath) {
        this.apkPath = apkPath;
        return this;
    }

    public AppUpdate downloadBy(int downloadBy) {
        this.downloadBy = downloadBy;
        return this;
    }

    public AppUpdate showNotification(boolean showNotification) {
        AppUpdate.showNotification = showNotification;
        return this;
    }

    public AppUpdate updateInfo(String updateInfo) {
        this.updateInfo = updateInfo;
        return this;
    }

    public AppUpdate versionCode(int versionCode) {
        this.versionCode = versionCode;
        return this;
    }

    public AppUpdate versionName(String versionName) {
        this.versionName = versionName;
        return this;
    }

    public AppUpdate isForce(boolean isForce) {
        this.isForce = isForce;
        return this;
    }

    /**
     * 本地版信息
     *
     * @param context 上下文
     */
    private void localVersionInfo(Context context) {
        PackageManager manager = context.getPackageManager();
        try {
            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
            // 版本名
            localVersionName = info.versionName;
            // 版本号
            localVersionCode = info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void update() {
        switch (checkBy) {
            case CHECK_BY_VERSION_CODE:
                if (versionCode > localVersionCode) {
                    updateExecute();
                } else {
                    LogUtils.e("当前为最新版" + versionCode + "/" + versionName);
                }
                break;
            case CHECK_BY_VERSION_NAME:
                if (!versionName.equals(localVersionName)) {
                    updateExecute();
                } else {
                    LogUtils.e("当前版本最新" + versionCode + "/" + versionName);
                }
                break;
            default:
                break;
        }
    }

    private void updateExecute() {
        AppUpdateDialog dialog = new AppUpdateDialog(activity, new Callback() {
            @Override
            public void callback(int position) {
                switch (position) {
                    // cancel
                    case 0:
                        if (isForce) {
                            System.exit(0);
                        }
                        break;
                    // sure
                    case 1:
                        if (downloadBy == DOWNLOAD_BY_APP) {
                            if (isWifiConnect(activity)) {
                                AppDownload.downloadInApp(activity, apkPath, versionName);
                            } else {
                                new AppUpdateDialog(activity, new Callback() {
                                    @Override
                                    public void callback(int position) {
                                        if (position == 1) {
                                            AppDownload.downloadByBrowser(activity, apkPath);
                                        } else {
                                            if (isForce) {
                                                activity.finish();
                                            }
                                        }
                                    }
                                }).setContent("目前手机非WIFI\n继续下载更新吗?").show();
                            }
                        } else if (downloadBy == DOWNLOAD_BY_BROWSER) {
                            AppDownload.downloadByBrowser(activity, apkPath);
                        }
                        break;
                    default:
                        break;
                }
            }
        });
        String content = "发现新版本" + versionName + "\n下载更新吗?";
        if (!TextUtils.isEmpty(updateInfo)) {
            content = "发现新版本" + versionName + "下载更新吗?\n\n" + updateInfo;
        }
        dialog.setContent(content);
        dialog.setCancelable(false);
        dialog.show();
    }

    /**
     * WIFI连状
     */
    private static boolean isWifiConnect(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm != null) {
            NetworkInfo networkInfo = cm.getActiveNetworkInfo();
            return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
        }
        return false;
    }
}

AppUpdateDialog

package update;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import com.self.zsp.dfs.R;

/**
 * @decs: ConfirmDialog
 * @date: 2018/8/21 21:44
 * @version: v 1.0
 */
public class AppUpdateDialog extends Dialog {
    private TextView content;
    private Callback callback;

    /**
     * constructor
     *
     * @param context  上下文
     * @param callback 回调
     */
    AppUpdateDialog(Context context, Callback callback) {
        super(context, R.style.AppUpdateDialog);
        this.callback = callback;
        setCustomDialog();
    }

    private void setCustomDialog() {
        @SuppressLint("InflateParams") View view = LayoutInflater.from(getContext()).inflate(R.layout.app_update_dialog, null);
        TextView tvSure = view.findViewById(R.id.tvAppUpdateDialogSure);
        TextView tvCancel = view.findViewById(R.id.tvAppUpdateDialogCancel);
        content = view.findViewById(R.id.tvAppUpdateDialogTitle);
        tvSure.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                callback.callback(1);
                AppUpdateDialog.this.cancel();
            }
        });
        tvCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                callback.callback(0);
                AppUpdateDialog.this.cancel();
            }
        });
        super.setContentView(view);
    }

    public AppUpdateDialog setContent(String s) {
        content.setText(s);
        return this;
    }
}

AppUpdateReceiver

package update;

import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.FileProvider;

import java.io.File;
/**
 * @decs: AppUpdateReceiver
 * @date: 2018/8/21 21:21
 * @version: v 1.0
 */
public class AppUpdateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        int notifyId = 1;
        int progress = intent.getIntExtra("progress", 0);
        String title = intent.getStringExtra("title");
        NotificationManager nm = null;
        if (AppUpdate.showNotification) {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
            builder.setContentTitle("正在下载" + title);
            builder.setSmallIcon(android.R.mipmap.sym_def_app_icon);
            builder.setProgress(100, progress, false);
            Notification notification = builder.build();
            nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            nm.notify(notifyId, notification);
        }
        if (progress == 100) {
            if (nm != null) {
                nm.cancel(notifyId);
            }
            if (AppDownload.path != null) {
                Intent i = new Intent(Intent.ACTION_VIEW);
                File file = new File(AppDownload.path);
                if (AppUpdate.needFitAndroidN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    Uri contentUri = FileProvider.getUriForFile(context, "com.self.zsp.dfs.fileprovider", file);
                    i.setDataAndType(contentUri, "application/vnd.android.package-archive");
                } else {
                    i.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
                }
                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(i);
            }
        }
    }
}

Callback

package update;

/**
 * @decs: 回调
 * @date: 2018/8/21 21:15
 * @version: v 1.0
 */
public interface Callback {
    /**
     * 回调
     *
     * @param position 位
     */
    void callback(int position);
}

drawable

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <size
        android:width="40dp"
        android:height="20dp" />
    <solid android:color="#EFEFEF" />
    <corners android:radius="12dp" />
</shape>

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="270dp"
    android:layout_height="wrap_content"
    android:background="@drawable/app_update_dialog"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="15dp"
        android:gravity="center_horizontal"
        android:text="@string/hint"
        android:textColor="@color/fontHint"
        android:textSize="19sp" />

    <TextView
        android:id="@+id/tvAppUpdateDialogTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="10dp"
        android:gravity="center_horizontal"
        android:lineSpacingExtra="5dp"
        android:textColor="@color/fontInput"
        android:textSize="17sp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginTop="13dp"
        android:background="#DFDFDF" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tvAppUpdateDialogCancel"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:clickable="true"
            android:focusable="true"
            android:gravity="center"
            android:text="@string/cancel"
            android:textColor="@color/fontInput"
            android:textSize="17sp" />

        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="#DFDFDF" />

        <TextView
            android:id="@+id/tvAppUpdateDialogSure"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:clickable="true"
            android:focusable="true"
            android:gravity="center"
            android:text="@string/sure"
            android:textColor="@color/fontInput"
            android:textSize="17sp" />
    </LinearLayout>
</LinearLayout>

styles

<style name="AppUpdateDialog" parent="@android:style/Theme.Dialog">
    <item name="android:windowFrame">@null</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:windowIsTranslucent">false</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowBackground">@drawable/app_update_dialog</item>
    <item name="android:backgroundDimEnabled">true</item>
</style>

主代码

private AppUpdateReceiver appUpdateReceiver;

/**
 * 初始应用更新接收器
 */
private void stepAppUpdateReceiver() {
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.self.zsp.dfs.AppUpdate");
    appUpdateReceiver = new AppUpdateReceiver();
    registerReceiver(appUpdateReceiver, intentFilter);
}

AppUpdate.from(this)
        // 更检方式(默VersionCode)
        .checkBy(AppUpdate.CHECK_BY_VERSION_NAME)
        .versionCode(2)
        .versionName("2.0")
        .apkPath("http://yapkwww.cdn.anzhi.com/data4/apk/201808/09/8e0274d7643e6898fab820bc1cf7eba6_73867900.apk")
        // 下载进度至通知栏(默true)
        .showNotification(true)
        // 更日志
        .updateInfo("测试")
        // 下载方式(应用内下载、手机浏览器下载。默应用内下载)
        .downloadBy(AppUpdate.DOWNLOAD_BY_APP)
        // 强更(默false,强更用户不同意不可用)
        .isForce(true)
        .update();

@Override
protected void onDestroy() {
    super.onDestroy();      
    unregisterReceiver(appUpdateReceiver);
}

注意

  • 注意6.0运行时权限、7.0FileProvider
  • 8.0限制静态注册广播,改动态注册。参考
  • 通知需适配8.0

猜你喜欢

转载自blog.csdn.net/zsp_android_com/article/details/81937537