亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android實(shí)現(xiàn)Android?APP自動(dòng)更新功能

 更新時(shí)間:2025年04月18日 08:25:10   作者:Katie。  
在移動(dòng)應(yīng)用的全生命周期中,版本迭代和用戶更新體驗(yàn)至關(guān)重要,傳統(tǒng)的做法是依賴?Google?Play?商店強(qiáng)制推送更新,但在某些場景下,我們需要更即時(shí)地控制更新流程,所以本文給大家介紹了Android實(shí)現(xiàn)Android?APP自動(dòng)更新功能,需要的朋友可以參考下

一、項(xiàng)目介紹

在移動(dòng)應(yīng)用的全生命周期中,版本迭代和用戶更新體驗(yàn)至關(guān)重要。傳統(tǒng)的做法是依賴 Google Play 商店強(qiáng)制推送更新,但在某些場景下,我們需要:

  • 更即時(shí)地控制更新流程(如灰度、強(qiáng)制升級(jí)、提醒升級(jí)等);

  • 支持市場外分發(fā),比如企業(yè)內(nèi)部應(yīng)用分發(fā)、第三方應(yīng)用商店;

  • 自定義更新 UI,與應(yīng)用風(fēng)格保持一致。

本項(xiàng)目示例將展示兩種主流方案:

  1. Google Play In?App Updates(官方方案,適用于上架 Play 商店的應(yīng)用)

  2. 自建服務(wù) + APK 下載 & 安裝(適用于非 Play 分發(fā)場景)

通過本教程,你將學(xué)會(huì)如何在應(yīng)用內(nèi)檢測新版本、彈出升級(jí)對(duì)話框、后臺(tái)下載 APK、以及無縫觸發(fā)安裝流程,極大提升用戶體驗(yàn)。

二、相關(guān)知識(shí)

  1. Google Play Core Library

    • com.google.android.play:core:1.x.x 包含了 In?App Updates API,讓應(yīng)用可在運(yùn)行時(shí)檢查并觸發(fā)“靈活更新”或“立即更新”流程,無需用戶去 Play 商店界面。

  2. FileProvider & 安裝意圖

    • 對(duì)于自建更新方案,需要在 AndroidManifest.xml 配置 FileProvider,并通過 Intent.ACTION_VIEW 攜帶 APK 的 content:// URI,調(diào)用系統(tǒng)安裝界面。

  3. WorkManager / DownloadManager

    • 長任務(wù)(如后臺(tái)下載 APK)應(yīng)使用 WorkManager 或系統(tǒng) DownloadManager,保證下載可在后臺(tái)穩(wěn)定運(yùn)行,且重啟后可續(xù)傳。

  4. 運(yùn)行時(shí)權(quán)限 & 兼容性

    • Android 8.0+(API 26+)安裝需獲取 “允許安裝未知應(yīng)用” 權(quán)限 (REQUEST_INSTALL_PACKAGES)。

    • Android 7.0+(API 24+)文件 URI 必須走 FileProvider,否則會(huì)拋 FileUriExposedException。

三、項(xiàng)目實(shí)現(xiàn)思路

  1. 版本檢測

    • Play 方案:調(diào)用 Play Core 的 AppUpdateManager.getAppUpdateInfo() 檢查更新狀態(tài)。

    • 自建方案:向自有服務(wù)器發(fā)起網(wǎng)絡(luò)請(qǐng)求(如 GET /latest_version.json),獲取最新版本號(hào)、APK 下載地址、更新說明等。

  2. 彈窗交互

    • 根據(jù)策略選擇“立即更新”(強(qiáng)制)或“靈活更新”(允許后臺(tái)運(yùn)行時(shí)再重啟安裝),并展示更新日志。

  3. 下載 APK

    • Play 方案:由 Play Core 自動(dòng)下載。

    • 自建方案:用 DownloadManager 啟動(dòng)下載,并監(jiān)聽廣播獲取下載完成通知。

  4. 觸發(fā)安裝

    • 下載完成后,構(gòu)造 Intent.ACTION_VIEW,指定 MIME 類型 application/vnd.android.package-archive,使用 FileProvider 共享 APK URI,啟動(dòng)安裝流程。

四、完整代碼(All?in?One,含詳細(xì)注釋)

// =======================================
// 文件: AutoUpdateManager.java + MainActivity
// (本示例將 Manager 與 Activity 合寫于一處,注釋區(qū)分)
// =======================================
 
package com.example.autoupdate;
 
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
 
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
 
// —— Play Core 庫依賴(立即更新/靈活更新)
// implementation "com.google.android.play:core:1.10.3"
import com.google.android.play.core.appupdate.AppUpdateInfo;
import com.google.android.play.core.appupdate.AppUpdateManagerFactory;
import com.google.android.play.core.install.model.AppUpdateType;
import com.google.android.play.core.install.model.UpdateAvailability;
import com.google.android.play.core.tasks.Task;
 
import org.json.JSONObject;
 
import java.io.File;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
 
public class MainActivity extends AppCompatActivity {
 
    // ---------- 常量區(qū) ----------
    private static final int REQUEST_CODE_UPDATE = 100;               // Play 更新請(qǐng)求碼
    private static final int REQUEST_INSTALL_PERMISSION = 101;        // 動(dòng)態(tài)安裝權(quán)限
    private static final String TAG = "AutoUpdate";
    private long downloadId;                                          // DownloadManager 返回 ID
    private DownloadManager downloadManager;
 
    // ---------- onCreate ----------
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);  // 布局見下文
 
        // 按鈕觸發(fā)兩種更新
        Button btnPlayUpdate    = findViewById(R.id.btnPlayUpdate);
        Button btnCustomUpdate  = findViewById(R.id.btnCustomUpdate);
 
        btnPlayUpdate.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                checkPlayUpdate();   // Play 商店內(nèi)更新
            }
        });
 
        btnCustomUpdate.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                checkCustomUpdate(); // 自建服務(wù)器更新
            }
        });
 
        // 初始化 DownloadManager
        downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        // 注冊(cè)下載完成廣播
        registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }
 
    // ---------- 1. Play In?App Updates 檢查 ----------
    private void checkPlayUpdate() {
        // 創(chuàng)建 AppUpdateManager
        com.google.android.play.core.appupdate.AppUpdateManager appUpdateManager =
                AppUpdateManagerFactory.create(this);
 
        // 異步獲取更新信息
        Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
        appUpdateInfoTask.addOnSuccessListener(info -> {
            // 判斷是否有更新且支持立即更新
            if (info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                    && info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
                try {
                    // 發(fā)起靈活更新請(qǐng)求
                    appUpdateManager.startUpdateFlowForResult(
                            info,
                            AppUpdateType.FLEXIBLE,
                            this,
                            REQUEST_CODE_UPDATE);
                } catch (Exception e) {
                    Log.e(TAG, "Play 更新啟動(dòng)失敗", e);
                }
            } else {
                Toast.makeText(this, "無可用更新或不支持此更新類型", Toast.LENGTH_SHORT).show();
            }
        });
    }
 
    // ---------- 2. 自建服務(wù)器版本檢測 ----------
    private void checkCustomUpdate() {
        new Thread(() -> {
            try {
                // 1) 請(qǐng)求服務(wù)器 JSON
                URL url = new URL("https://your.server.com/latest_version.json");
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(5000);
                conn.setRequestMethod("GET");
                InputStream in = conn.getInputStream();
                Scanner sc = new Scanner(in).useDelimiter("\\A");
                String json = sc.hasNext() ? sc.next() : "";
                JSONObject obj = new JSONObject(json);
                final int serverVersionCode = obj.getInt("versionCode");
                final String apkUrl       = obj.getString("apkUrl");
                final String changeLog   = obj.getString("changeLog");
 
                // 2) 獲取本地版本號(hào)
                int localVersionCode = getPackageManager()
                        .getPackageInfo(getPackageName(), 0).versionCode;
 
                if (serverVersionCode > localVersionCode) {
                    // 有新版,回到主線程彈窗提示
                    runOnUiThread(() ->
                        showUpdateDialog(apkUrl, changeLog)
                    );
                } else {
                    runOnUiThread(() ->
                        Toast.makeText(this, "已是最新版本", Toast.LENGTH_SHORT).show()
                    );
                }
            } catch (Exception e) {
                Log.e(TAG, "檢查更新失敗", e);
            }
        }).start();
    }
 
    // ---------- 3. 彈出更新對(duì)話框 ----------
    private void showUpdateDialog(String apkUrl, String changeLog) {
        new AlertDialog.Builder(this)
            .setTitle("發(fā)現(xiàn)新版本")
            .setMessage(changeLog)
            .setCancelable(false)
            .setPositiveButton("立即更新", (dialog, which) -> {
                startDownload(apkUrl);
            })
            .setNegativeButton("稍后再說", null)
            .show();
    }
 
    // ---------- 4. 啟動(dòng)系統(tǒng) DownloadManager 下載 APK ----------
    private void startDownload(String apkUrl) {
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI
                                      | DownloadManager.Request.NETWORK_MOBILE);
        request.setTitle("正在下載更新包");
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "update.apk");
        // 開始下載
        downloadId = downloadManager.enqueue(request);
    }
 
    // ---------- 5. 監(jiān)聽下載完成,觸發(fā)安裝 ----------
    private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
        @Override public void onReceive(Context context, Intent intent) {
            long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
            if (id != downloadId) return;
 
            // 下載完成,安裝 APK
            File apkFile = new File(Environment.getExternalStoragePublicDirectory(
                            Environment.DIRECTORY_DOWNLOADS), "update.apk");
 
            // Android 8.0+ 需要請(qǐng)求安裝未知應(yīng)用權(quán)限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                boolean canInstall = getPackageManager().canRequestPackageInstalls();
                if (!canInstall) {
                    // 請(qǐng)求“安裝未知應(yīng)用”權(quán)限
                    ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES},
                        REQUEST_INSTALL_PERMISSION);
                    return;
                }
            }
            installApk(apkFile);
        }
    };
 
    // ---------- 6. 處理未知來源權(quán)限申請(qǐng)結(jié)果 ----------
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        if (requestCode == REQUEST_INSTALL_PERMISSION) {
            if (grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) {
                // 再次觸發(fā)安裝(假設(shè) APK 仍在下載目錄)
                File apkFile = new File(Environment.getExternalStoragePublicDirectory(
                                Environment.DIRECTORY_DOWNLOADS), "update.apk");
                installApk(apkFile);
            } else {
                Toast.makeText(this, "安裝權(quán)限被拒絕,無法自動(dòng)更新", Toast.LENGTH_LONG).show();
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
 
    // ---------- 7. 安裝 APK 輔助方法 ----------
    private void installApk(File apkFile) {
        Uri apkUri = FileProvider.getUriForFile(this,
                getPackageName() + ".fileprovider", apkFile);
 
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, "無法啟動(dòng)安裝程序", Toast.LENGTH_LONG).show();
        }
    }
 
    // ---------- 8. Activity 銷毀時(shí)注銷 Receiver ----------
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(onDownloadComplete);
    }
}
<!-- ======================================
文件: AndroidManifest.xml
注意:需要配置 FileProvider 與權(quán)限
====================================== -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.autoupdate">
 
    <!-- 安裝未知來源權(quán)限(Android 8.0+ 需動(dòng)態(tài)申請(qǐng)) -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
 
        <!-- FileProvider 聲明 -->
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
 
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
<!-- ======================================
文件: res/xml/file_paths.xml
FileProvider 路徑配置
====================================== -->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="download" path="Download/"/>
</paths>
<!-- ======================================
文件: res/layout/activity_main.xml
簡單示例界面
====================================== -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:gravity="center"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:padding="24dp">
 
    <Button
        android:id="@+id/btnPlayUpdate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Play In?App 更新"/>
 
    <View android:layout_height="16dp" android:layout_width="match_parent"/>
 
    <Button
        android:id="@+id/btnCustomUpdate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="自建服務(wù)更新"/>
</LinearLayout>

五、方法解讀

  • checkPlayUpdate()
    檢查 Google Play 上的更新可用性,并以“靈活更新”方式啟動(dòng)下載和安裝流程。

  • checkCustomUpdate()
    通過 HttpURLConnection 請(qǐng)求服務(wù)器 JSON,解析最新 versionCode 與 apkUrl,對(duì)比本地版本,決定是否彈窗。

  • showUpdateDialog(...)
    基于服務(wù)器返回的 changeLog 構(gòu)建 AlertDialog,提供“立即更新”與“稍后再說”兩種交互。

  • startDownload(String apkUrl)
    使用系統(tǒng) DownloadManager 發(fā)起后臺(tái)下載,保存至公開目錄,支持?jǐn)帱c(diǎn)續(xù)傳和系統(tǒng)下載通知。

  • BroadcastReceiver onDownloadComplete
    監(jiān)聽 DownloadManager.ACTION_DOWNLOAD_COMPLETE 廣播,確認(rèn)是本次下載后觸發(fā)安裝流程。

  • onRequestPermissionsResult(...)
    處理 Android 8.0+ “安裝未知來源”權(quán)限授權(quán)結(jié)果,授權(quán)后繼續(xù)調(diào)用 installApk()。

  • installApk(File apkFile)
    通過 FileProvider 獲取 APK 的 content URI,并以 Intent.ACTION_VIEW 調(diào)用系統(tǒng)安裝器。

六、項(xiàng)目總結(jié)

優(yōu)勢(shì)

  • Play Core In?App 更新:官方支持,體驗(yàn)與 Play 商店一致,無需手工管理下載邏輯。

  • 自建方案:靈活可控,支持任意分發(fā)渠道,自定義 UI 與灰度策略。

注意與優(yōu)化

  1. 權(quán)限與兼容

    • Android 7.0+ 必須使用 FileProvider。

    • Android 8.0+ 需動(dòng)態(tài)申請(qǐng) REQUEST_INSTALL_PACKAGES。

  2. 下載失敗重試

    • 可結(jié)合 WorkManager 增加重試與網(wǎng)絡(luò)斷線重連邏輯。

  3. 安全性

    • 建議對(duì) APK 做簽名校驗(yàn)(計(jì)算 SHA256 與服務(wù)器比對(duì)),防止被篡改。

  4. UI 體驗(yàn)

    • 對(duì)“立即更新”與“后臺(tái)更新”作更多狀態(tài)提示。

    • 可顯示下載進(jìn)度條、進(jìn)度通知等。

  5. 灰度/強(qiáng)制升級(jí)

    • 可在服務(wù)器 JSON 中添加策略字段,如 forceUpdate,在對(duì)話框中禁止“稍后再說”。

到此這篇關(guān)于Android實(shí)現(xiàn)Android APP自動(dòng)更新功能的文章就介紹到這了,更多相關(guān)Android APP自動(dòng)更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論