最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android實現Android?APP自動更新功能

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

一、項目介紹

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

  • 更即時地控制更新流程(如灰度、強制升級、提醒升級等);

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

  • 自定義更新 UI,與應用風格保持一致。

本項目示例將展示兩種主流方案:

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

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

通過本教程,你將學會如何在應用內檢測新版本、彈出升級對話框、后臺下載 APK、以及無縫觸發(fā)安裝流程,極大提升用戶體驗。

二、相關知識

  1. Google Play Core Library

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

  2. FileProvider & 安裝意圖

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

  3. WorkManager / DownloadManager

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

  4. 運行時權限 & 兼容性

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

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

三、項目實現思路

  1. 版本檢測

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

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

  2. 彈窗交互

    • 根據策略選擇“立即更新”(強制)或“靈活更新”(允許后臺運行時再重啟安裝),并展示更新日志。

  3. 下載 APK

    • Play 方案:由 Play Core 自動下載。

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

  4. 觸發(fā)安裝

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

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

// =======================================
// 文件: 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 更新請求碼
    private static final int REQUEST_INSTALL_PERMISSION = 101;        // 動態(tài)安裝權限
    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 商店內更新
            }
        });
 
        btnCustomUpdate.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                checkCustomUpdate(); // 自建服務器更新
            }
        });
 
        // 初始化 DownloadManager
        downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        // 注冊下載完成廣播
        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ā)起靈活更新請求
                    appUpdateManager.startUpdateFlowForResult(
                            info,
                            AppUpdateType.FLEXIBLE,
                            this,
                            REQUEST_CODE_UPDATE);
                } catch (Exception e) {
                    Log.e(TAG, "Play 更新啟動失敗", e);
                }
            } else {
                Toast.makeText(this, "無可用更新或不支持此更新類型", Toast.LENGTH_SHORT).show();
            }
        });
    }
 
    // ---------- 2. 自建服務器版本檢測 ----------
    private void checkCustomUpdate() {
        new Thread(() -> {
            try {
                // 1) 請求服務器 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) 獲取本地版本號
                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. 彈出更新對話框 ----------
    private void showUpdateDialog(String apkUrl, String changeLog) {
        new AlertDialog.Builder(this)
            .setTitle("發(fā)現新版本")
            .setMessage(changeLog)
            .setCancelable(false)
            .setPositiveButton("立即更新", (dialog, which) -> {
                startDownload(apkUrl);
            })
            .setNegativeButton("稍后再說", null)
            .show();
    }
 
    // ---------- 4. 啟動系統(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+ 需要請求安裝未知應用權限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                boolean canInstall = getPackageManager().canRequestPackageInstalls();
                if (!canInstall) {
                    // 請求“安裝未知應用”權限
                    ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES},
                        REQUEST_INSTALL_PERMISSION);
                    return;
                }
            }
            installApk(apkFile);
        }
    };
 
    // ---------- 6. 處理未知來源權限申請結果 ----------
    @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ā)安裝(假設 APK 仍在下載目錄)
                File apkFile = new File(Environment.getExternalStoragePublicDirectory(
                                Environment.DIRECTORY_DOWNLOADS), "update.apk");
                installApk(apkFile);
            } else {
                Toast.makeText(this, "安裝權限被拒絕,無法自動更新", 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, "無法啟動安裝程序", Toast.LENGTH_LONG).show();
        }
    }
 
    // ---------- 8. Activity 銷毀時注銷 Receiver ----------
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(onDownloadComplete);
    }
}
<!-- ======================================
文件: AndroidManifest.xml
注意:需要配置 FileProvider 與權限
====================================== -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.autoupdate">
 
    <!-- 安裝未知來源權限(Android 8.0+ 需動態(tài)申請) -->
    <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="自建服務更新"/>
</LinearLayout>

五、方法解讀

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

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

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

  • startDownload(String apkUrl)
    使用系統(tǒng) DownloadManager 發(fā)起后臺下載,保存至公開目錄,支持斷點續(xù)傳和系統(tǒng)下載通知。

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

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

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

六、項目總結

優(yōu)勢

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

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

注意與優(yōu)化

  1. 權限與兼容

    • Android 7.0+ 必須使用 FileProvider

    • Android 8.0+ 需動態(tài)申請 REQUEST_INSTALL_PACKAGES。

  2. 下載失敗重試

    • 可結合 WorkManager 增加重試與網絡斷線重連邏輯。

  3. 安全性

    • 建議對 APK 做簽名校驗(計算 SHA256 與服務器比對),防止被篡改。

  4. UI 體驗

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

    • 可顯示下載進度條、進度通知等。

  5. 灰度/強制升級

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

到此這篇關于Android實現Android APP自動更新功能的文章就介紹到這了,更多相關Android APP自動更新內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Android SharedPreferences數據存儲詳解

    Android SharedPreferences數據存儲詳解

    SharedPreferences是安卓平臺上一個輕量級的存儲類,用來保存應用的一些常用配置,比如Activity狀態(tài),Activity暫停時,將此activity的狀態(tài)保存到SharedPereferences中;當Activity重載,系統(tǒng)回調方法onSaveInstanceState時,再從SharedPreferences中將值取出
    2022-11-11
  • Android自定義View實現隨機數驗證碼

    Android自定義View實現隨機數驗證碼

    這篇文章主要為大家詳細介紹了Android如何利用自定義View實現隨機數驗證碼效果,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2022-06-06
  • Android中微信搶紅包助手的實現詳解

    Android中微信搶紅包助手的實現詳解

    本篇文章主要介紹了Android中微信搶紅包助手的實現詳解,通過利用AccessibilityService輔助服務,監(jiān)測屏幕內容,如監(jiān)聽狀態(tài)欄的信息,屏幕跳轉等,以此來實現自動拆紅包的功能,有興趣的可以了解一下。
    2017-02-02
  • Android自定義控件仿QQ抽屜效果

    Android自定義控件仿QQ抽屜效果

    這篇文章主要為大家詳細介紹了Android自定義控件仿QQ抽屜效果的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • Android中控件GridView實現設置行列分割線的方法示例

    Android中控件GridView實現設置行列分割線的方法示例

    這篇文章主要介紹了利用Android中控件GridView實現設置行列分割線的方法,文中給出了詳細的介紹與示例代碼,相信對大家具有一定的參考價值,有需要的朋友們下面來一起看看吧。
    2017-01-01
  • android 類似微信的搖一搖功能實現思路及代碼

    android 類似微信的搖一搖功能實現思路及代碼

    微信的搖一搖功能的出現,讓彼此之間的距離有近了一步,本文也想實現以下微信的搖一搖功能,感興趣的朋友可以了解下啊,希望本人對你有所幫助
    2013-01-01
  • Android?BottomSheetBehavior使用方法及常見問題詳解

    Android?BottomSheetBehavior使用方法及常見問題詳解

    這篇文章主要介紹了Android?BottomSheetBehavior使用方法及常見問題的相關資料,BottomSheetBehavior是AndroidX中用于實現底部彈出式面板(底部抽屜)的行為類,支持拖拽、展開/收起、狀態(tài)監(jiān)聽等核心能力,需要的朋友可以參考下
    2025-12-12
  • Android WebView自定義長按選擇實現收藏/分享選中文本功能

    Android WebView自定義長按選擇實現收藏/分享選中文本功能

    這篇文章主要介紹了Android WebView自定義長按選擇實現收藏/分享選中文本功能,需要的朋友可以參考下
    2017-06-06
  • Android觸屏事件和MotionEvent詳解

    Android觸屏事件和MotionEvent詳解

    這篇文章主要介紹了Android觸屏事件和MotionEvent詳解的相關資料,希望通過本文能幫助到大家,讓大家理解掌握這部分內容,需要的朋友可以參考下
    2017-10-10
  • Flutter實現頁面切換后保持原頁面狀態(tài)的3種方法

    Flutter實現頁面切換后保持原頁面狀態(tài)的3種方法

    這篇文章主要給大家介紹了關于Flutter實現頁面切換后保持原頁面狀態(tài)的3種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者使用Flutter具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-03-03

最新評論

尚义县| 海伦市| 镇康县| 惠州市| 阿拉尔市| 福海县| 乌兰察布市| 高雄市| 寿光市| 郴州市| 红安县| 峡江县| 吴川市| 封丘县| 南召县| 巩义市| 上饶市| 东台市| 东山县| 界首市| 龙游县| 吉安县| 深州市| 扶风县| 久治县| 康乐县| 马关县| 望江县| 且末县| 吉隆县| 叙永县| 同江市| 闽清县| 比如县| 孝昌县| 泰兴市| 伊春市| 赤城县| 荥经县| 陵川县| 固始县|