1. 程式人生 > >Android 版本更新(適配7.0 xml配置)

Android 版本更新(適配7.0 xml配置)

版本更新一般分兩種情況:

  1. 需要更新時跳轉到應用市場或者跳轉到瀏覽器處理
  2. 另一種情況則是在App內進行更新

第一種沒什麼好說的,本文主要是實現應用內進行更新

App內部更新分以下幾個步驟:

  1. 檢測App版本
  2. 下載Apk
  3. 安裝已下載完成的Apk

下面貼上例項:

1. 檢測App版本

/**
 * 登入時查詢是否有新版本應用
 */
public void queryVersion() {
    if (!Const.IS_CHECK_UPDATE) return; // 判斷是否進行請求更新 (場景:進入App自動檢測更新)

    HttpUtil.post(mContext, Const.URL_UPDATE, new TextResponseHandler() {
        @Override
        public void onSuccess(String result) {
        
            // 解析伺服器返回的版本資訊
            JSONObject jsonObject = JSON.parseObject(result);
            if (Const.JSON_SUCCESS.equals(jsonObject.getString("success")))
            {
                String strResult = jsonObject.getString("isupdate");
                if (TextUtils.isEmpty(strResult)) return;
                UpdateVersion update = JSON.parseObject(strResult, UpdateVersion.class);
                String edition = update.getEdition();    //    伺服器返回版本資訊
                String versionName = AppUtil.getVersionName(mContext);    //    當前應用版本號
                // 比較兩者 不一致提示更新
                if (!versionName.equals(edition))
                {
                    // 顯示更新提示
                    showNoticeDialog(update, versionName);
                }
            }
        }

        @Override
        public void onFailure(String message) // 視業務而定
        { 
            ...
        }
    });
}

2.有新版本時 提示更新

/**
 * 顯示軟體更新對話方塊
 *
 * @param update      更新介紹
 * @param versionName 當前版本名
 */
private void showNoticeDialog(final UpdateVersion update, String versionName) 
{
    // 構造對話方塊
    MaterialDialog.Builder builder = new MaterialDialog.Builder(mContext);
    builder.title("更新提示");
    if ("1".equals(update.getCode()))    //    強制更新
        builder.content("您當前使用版本會對您的使用帶來不便,請您更新!");
    else
        builder.content("當前版本:" + versionName + "\r\n新版本:" + update.getEdition());    //    非重要更新

    builder.positiveText("確定");
    builder.negativeText("取消");
    //點選事件新增 方式1
    builder.onAny(new MaterialDialog.SingleButtonCallback() 
    {
        @Override
        public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which)         
        {
            if (which == DialogAction.POSITIVE) 
            {
                dialog.dismiss();
                showDownloadProgress(update);    // 顯示更新進度 同時進行更新
            } else if (which == DialogAction.NEGATIVE) 
            {
                if ("2".equals(update.getCode()) || "3".equals(update.getCode()))
                {
                    dialog.dismiss();
                }else // 強制更新    如果不進行更新則無法使用當前應用
                {
                    dialog.dismiss();
                    mContext.finish();
                    System.exit(0);
                }
            }
        }
    });
    MaterialDialog materialDialog = builder.build();
    materialDialog.setCancelable(false);
    materialDialog.show();
}

3.顯示下載進度

/**
 * 顯示軟體下載提示框
 *
 * @param update 後臺返回更新的資訊
 */
private void showDownloadProgress(UpdateVersion update) {
    String url = update.getUrl();
    apkName = url.substring(url.lastIndexOf("/") + 1, url.length());
    String apkUrl = Const.URL_HOSTS + url;
    
    // 構造軟體下載對話方塊
    if (materialDialog == null) 
    {
        materialDialog = new MaterialDialog.Builder(mContext)
                .title("版本升級")
                .content("正在下載安裝包,請稍候")
                .progress(false, 100, false)
                .cancelable(false)
                .show();
    }
    downloadThread(apkUrl);    // 下載Apk
}

4.下載Apk

/**
 * 使用DownloadManager進行下載
 */
private void downloadThread(String apkUrl) 
{
    downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkName);
    //設定顯示在檔案下載Notification(通知欄)中顯示的文字。6.0的手機Description不顯示
    request.setTitle(mContext.getString(R.string.app_name));
    request.setDescription("新版本給您帶來更優質的體驗!");
    request.setMimeType("application/vnd.android.package-archive");
    request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
    request.allowScanningByMediaScanner();
    request.setVisibleInDownloadsUi(true);

    lastDownloadId = downloadManager.enqueue(request);
    downloadObserver = new DownloadChangeObserver(null);
    mContext.getContentResolver().registerContentObserver(CONTENT_URI, true, downloadObserver);
}

5.監聽下載進度

class DownloadChangeObserver extends ContentObserver {

    DownloadChangeObserver(Handler handler) 
    {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange) 
    {
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(lastDownloadId);
        DownloadManager dManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
        Cursor cursor = null;
        try 
        {
            cursor = dManager.query(query);
            if (cursor != null && cursor.moveToFirst())
            {
                final int totalColumn = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
                final int currentColumn = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
                int totalSize = cursor.getInt(totalColumn);
                int currentSize = cursor.getInt(currentColumn);
                float percent = (float) currentSize / (float) totalSize;
                int progress = Math.round(percent * 100);
                materialDialog.setProgress(progress);
                if (progress == 100)
                {
                    materialDialog.dismiss();
                    installAPK();
                }
            }
        } finally 
        {
            // 一定不要忘記關閉遊標!!!
            if (cursor != null) 
            {
                cursor.close();
            }
        }
    }

}

6.安裝新版Apk

private void installAPK() {
    File apkFile =new File
    (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), apkName);

    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
    {
        Uri apkUri = FileProvider.getUriForFile(mContext, "com.你的自定義路徑.update.fileprovider", apkFile);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
    } else
    {
        intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
    }
    mContext.startActivity(intent);
}

7.釋放資源

/**
 * 釋放當前資源
 */
public void onDestroy()
{

    if (Const.IS_CHECK_UPDATE && downloadManager != null) 
    {
        downloadManager = null;
        mContext.getContentResolver().unregisterContentObserver(downloadObserver);
    }
}

結束了?No No No ,由於Android系統的不斷完善,尤其是許可權方面,所以在7.0以上的系統需要在manifest中註冊provider

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.你的路徑.update.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
<!-- file_paths -->
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <paths>
        <external-path
            name="download"
            path=""/>
    </paths>
</resources>
​

ok,至此自動App更新功能完成

功能全部程式碼:

public class UpdateManager {
    private DownloadChangeObserver downloadObserver;
    private long lastDownloadId = 0;
    //"content://downloads/my_downloads"必須這樣寫不可更改
    private final Uri CONTENT_URI = Uri.parse("content://downloads/my_downloads");
    private AlertDialog materialDialog;

    private Activity mActivity;
    private String apkName;
    private DownloadManager downloadManager;


    public UpdateManager(Activity activity) 
    {
        this.mActivity = activity;
    }


    /**
     * 登入時查詢是否有新版本應用
     */
    public void queryVersion() {
        if (!Constants.IS_CHECK_UPDATE) return; 

        HttpUtil.get(mActivity, Constants.URL_UPDATE, new TextResponseHandler() {
            @Override
            public void onSuccess(String result) 
            {
                JSONObject jsonObject = JSON.parseObject(result);
                if (Constants.JSON_SUCCESS.equals(jsonObject.getString("success"))) 
                {
                    String strResult = jsonObject.getString("isupdate");
                    if (TextUtils.isEmpty(strResult)) return;
                    UpdateVersion update = JSON.parseObject(strResult, UpdateVersion.class);
                    String edition = update.getEdition();
                    String versionName = AppUtil.getVersionName(mContext);
                    String code = update.getCode();
                    if (!versionName.equals(edition))
                    {
                        showNoticeDialog(update, versionName);
                    }
                }
            }

            @Override
            public void onFailure(String message) 
            { 
                ...
            }
        });

    }

    /**
     * 顯示軟體更新對話方塊
     *
     * @param update      更新介紹
     * @param versionName 當前版本名
     */
    private void showNoticeDialog(final UpdateVersion update, String versionName) 
    {
        // 構造對話方塊
        MaterialDialog.Builder builder = new MaterialDialog.Builder(mContext);
        builder.title("系統提示");
        if ("1".equals(update.getCode()))    // 強制更新
            builder.content("您當前使用版本會對您的使用帶來不便,請您更新!");
        else    // 正常更新
            builder.content("當前版本:" + versionName + "\r\n新版本:" + update.getEdition());

        builder.positiveText("確定");
        builder.negativeText("取消");

        builder.onAny(new MaterialDialog.SingleButtonCallback() 
        {
            @Override
            public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) 
            {
                if (which == DialogAction.POSITIVE)
                {
                    dialog.dismiss();
                    showDownloadProgress(update);
                } else if (which == DialogAction.NEGATIVE) 
                {
                    if ("2".equals(update.getCode()) || "3".equals(update.getCode()))
                    {
                        dialog.dismiss();
                    }else     // 強制更新
                    {
                        dialog.dismiss();
                        mContext.finish();
                        System.exit(0);
                    }
                }
            }
        });
        MaterialDialog materialDialog = builder.build();
        materialDialog.setCancelable(false);
        materialDialog.show();
    }

    /**
     * 顯示軟體下載對話方塊
     *
     * @param update 返回更新資訊
     */
  private void showDownloadProgress(UpdateVersion update) 
  {
        String url = update.getUrl();
        apkName = url.substring(url.lastIndexOf("/") + 1, url.length());
        String apkUrl = Constants.URL_HOSTS + url;
        // 構造軟體下載對話方塊
        if (materialDialog == null) 
        {
            materialDialog = new MaterialDialog.Builder(mActivity)
                    .title("版本升級")
                    .content("正在下載安裝包,請稍候")
                    .progress(false, 100, false)
                    .cancelable(false)
                    .show();
        }
         
        // 螢幕常亮
        mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        downloadThread(apkUrl);    // 下載
    }

    private void downloadThread(String apkUrl)
    {
        String directoryDownloads = Environment.DIRECTORY_DOWNLOADS;
        String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
//        ToolUtil.deleteFile(path + "/" + apkName);    // 刪除下載的安裝包
        downloadManager = (DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE);
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
        request.setDestinationInExternalPublicDir(directoryDownloads, apkName);
        //設定顯示在檔案下載Notification(通知欄)中顯示的文字。6.0的手機Description不顯示
        request.setTitle(mActivity.getString(R.string.app_name));
        request.setDescription("新版本給您帶來更優質的體驗!");
        request.setMimeType("application/vnd.android.package-archive");
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
        request.allowScanningByMediaScanner();
        request.setVisibleInDownloadsUi(true);

        lastDownloadId = downloadManager.enqueue(request);
        downloadObserver = new DownloadChangeObserver(null);
        mActivity.getContentResolver().registerContentObserver(CONTENT_URI, true, downloadObserver);
    }

    //用於顯示下載進度
    class DownloadChangeObserver extends ContentObserver {

        DownloadChangeObserver(Handler handler) 
        {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) 
        {
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(lastDownloadId);
            DownloadManager dManager = (DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE);
            Cursor cursor = null;
            try
            {
                assert dManager != null;
                cursor = dManager.query(query);
                if (cursor != null && cursor.moveToFirst()) 
                {
                    //下載的檔案到本地的目錄
                    String addressDir = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                    final int totalColumn = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
                    final int currentColumn = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
                    int totalSize = cursor.getInt(totalColumn);
                    int currentSize = cursor.getInt(currentColumn);
                    float percent = (float) currentSize / (float) totalSize;
                    int progress = Math.round(percent * 100);
                    materialDialog.setProgress(progress);
                    if (progress == 100)
                    {
                        materialDialog.dismiss();
                        installAPK();
                    }
                }
            } finally 
            {
                if (cursor != null)
                {
                    cursor.close();
                }
            }
        }

    }
/*
    // 使用瀏覽器下載
    private void openBrowserDown(UpdateVersion update) 
    {
        String apkUrl = Constants.URL_HOSTS + update.getUrl();
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.addCategory(Intent.CATEGORY_BROWSABLE);
        intent.setData(Uri.parse(apkUrl));
        mActivity.startActivity(intent);
        SystemClock.sleep(1500L);
        System.exit(0);
    }
*/
    private void installAPK() 
    {
        File apkFile =
                new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), apkName);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) 
        {
            Uri apkUri = FileProvider.getUriForFile(mActivity, "com.wzy.buytickets.update.fileprovider", apkFile);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else 
        {
            intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
        }
        mActivity.startActivity(intent);
    }


    /**
     * 釋放當前資源
     */
    public void onDestroy() 
    {
        if (!Constants.IS_CHECK_UPDATE && downloadManager == null) return;

        downloadManager = null;
        mActivity.getContentResolver().unregisterContentObserver(downloadObserver);
    }

}

使用:

private UpdateManager updateManager;    //  更新類

if (updateManager == null)   // 初始化
{
     updateManager = new UpdateManager(this);
}

updateManager.queryVersion();    // 呼叫(直接呼叫或button點選呼叫)