You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为GitHub托管的非Google Play APK实现应用内更新(Android Studio Java)

实现GitHub托管APK的应用内更新检测(Java)

1. 前置准备

  • 将APK上传至GitHub仓库的Releases板块,Release标签建议使用带v前缀的版本号(如v1.0.1),并关联对应的APK文件。
  • 记下你的GitHub仓库最新版本API地址:格式为https://api.github.com/repos/你的用户名/你的仓库名/releases/latest

2. 添加权限与依赖

权限配置(AndroidManifest.xml)

<uses-permission android:name="android.permission.INTERNET" />
<!-- Android 8.0+需要申请安装未知来源应用权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- Android 12及以下需要存储权限,13+可省略 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

依赖添加(Module级build.gradle)

dependencies {
    // 网络请求库
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    // JSON解析库
    implementation 'com.google.code.gson:gson:2.10.1'
}

3. 版本检查工具类

创建UpdateChecker.java,负责请求GitHub最新版本并与本地版本对比:

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;

import com.google.gson.Gson;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

public class UpdateChecker {
    private static final String TAG = "UpdateChecker";
    // 替换为你的GitHub仓库最新版本API地址
    private static final String GITHUB_LATEST_RELEASE_URL = "https://api.github.com/repos/你的用户名/你的仓库名/releases/latest";

    public interface UpdateListener {
        void onUpdateAvailable(String latestVersion, String apkDownloadUrl);
        void onUpdateNotAvailable();
        void onCheckFailed(String error);
    }

    public static void checkForUpdates(Context context, UpdateListener listener) {
        String currentVersion = getCurrentAppVersion(context);
        if (currentVersion == null) {
            listener.onCheckFailed("无法获取当前应用版本");
            return;
        }

        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url(GITHUB_LATEST_RELEASE_URL)
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e(TAG, "更新检查失败: " + e.getMessage());
                listener.onCheckFailed(e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (!response.isSuccessful()) {
                    listener.onCheckFailed("请求失败,状态码: " + response.code());
                    return;
                }

                String responseBody = response.body().string();
                GitHubRelease release = new Gson().fromJson(responseBody, GitHubRelease.class);

                if (release == null || release.tag_name == null) {
                    listener.onCheckFailed版本信息解析失败");
                    return;
                }

                // 去除版本标签的v前缀(如v1.0.1 → 1.0.1)
                String latestVersion = release.tag_name.replaceFirst("^v", "");
                Log.d(TAG, "当前版本: " + currentVersion + " | 最新版本: " + latestVersion);

                if (isVersionOlder(currentVersion, latestVersion)) {
                    String apkUrl = getApkDownloadUrl(release);
                    if (apkUrl != null) {
                        listener.onUpdateAvailable(latestVersion, apkUrl);
                    } else {
                        listener.onCheckFailed("未找到APK下载链接");
                    }
                } else {
                    listener.onUpdateNotAvailable();
                }
            }
        });
    }

    // 获取当前应用版本名称
    private static String getCurrentAppVersion(Context context) {
        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return packageInfo.versionName;
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "获取当前版本失败: " + e.getMessage());
            return null;
        }
    }

    // 对比版本号,判断当前版本是否落后
    private static boolean isVersionOlder(String currentVersion, String latestVersion) {
        String[] currentParts = currentVersion.split("\\.");
        String[] latestParts = latestVersion.split("\\.");
        int maxLength = Math.max(currentParts.length, latestParts.length);

        for (int i = 0; i < maxLength; i++) {
            int currentPart = i < currentParts.length ? Integer.parseInt(currentParts[i]) : 0;
            int latestPart = i < latestParts.length ? Integer.parseInt(latestParts[i]) : 0;

            if (currentPart < latestPart) return true;
            if (currentPart > latestPart) return false;
        }
        return false; // 版本相同
    }

    // 从Release信息中提取APK下载链接
    private static String getApkDownloadUrl(GitHubRelease release) {
        if (release.assets == null || release.assets.length == 0) return null;
        for (GitHubAsset asset : release.assets) {
            if (asset.name != null && asset.name.endsWith(".apk")) {
                return asset.browser_download_url;
            }
        }
        return null;
    }

    // Gson解析实体类
    static class GitHubRelease {
        String tag_name;
        GitHubAsset[] assets;
    }

    static class GitHubAsset {
        String name;
        String browser_download_url;
    }
}

4. 在启动页调用更新检查

在MainActivity或启动页中触发检查,并处理更新提示:

import android.app.AlertDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 应用启动时检查更新
        checkForUpdates();
    }

    private void checkForUpdates() {
        UpdateChecker.checkForUpdates(this, new UpdateChecker.UpdateListener() {
            @Override
            public void onUpdateAvailable(String latestVersion, String apkDownloadUrl) {
                runOnUiThread(() -> showUpdateDialog(latestVersion, apkDownloadUrl));
            }

            @Override
            public void onUpdateNotAvailable() {
                runOnUiThread(() -> Toast.makeText(MainActivity.this, "当前已是最新版本", Toast.LENGTH_SHORT).show());
            }

            @Override
            public void onCheckFailed(String error) {
                runOnUiThread(() -> Toast.makeText(MainActivity.this, "更新检查失败: " + error, Toast.LENGTH_SHORT).show());
            }
        });
    }

    private void showUpdateDialog(String latestVersion, String apkDownloadUrl) {
        new AlertDialog.Builder(this)
                .setTitle("发现新版本 v" + latestVersion)
                .setMessage("更新后可获得更多功能与稳定性优化")
                .setPositiveButton("立即更新", (dialog, which) -> {
                    // 检查Android 8.0+的未知来源安装权限
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        if (!getPackageManager().canRequestPackageInstalls()) {
                            Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
                                    Uri.parse("package:" + getPackageName()));
                            startActivityForResult(intent, 1001);
                            return;
                        }
                    }
                    // 调用浏览器下载APK
                    Intent downloadIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkDownloadUrl));
                    startActivity(downloadIntent);
                })
                .setNegativeButton("稍后再说", null)
                .show();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1001) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                if (getPackageManager().canRequestPackageInstalls()) {
                    Toast.makeText(this, "已允许安装,可重新触发更新", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this, "未授予安装权限,无法更新", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
}

5. 注意事项

  • 版本号需统一使用x.y.z格式,确保版本对比逻辑正常工作。
  • GitHub API有请求限制:公开仓库未认证请求每小时最多60次,足够普通应用使用;私有仓库需在请求头添加Authorization: token 你的GitHub令牌。
  • 若需更优体验,可自行实现后台下载器(使用DownloadManager或OkHttp断点续传),替代浏览器下载。

内容的提问来源于stack exchange,提问作者King Grey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 03:32:08