国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android實現(xiàn)App內(nèi)自動升級,適配了安卓7、8及以上版本

這篇具有很好參考價值的文章主要介紹了Android實現(xiàn)App內(nèi)自動升級,適配了安卓7、8及以上版本。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

? ? ? ? 應(yīng)用發(fā)布后,要實現(xiàn)灰度升級控制,如果只依賴各家應(yīng)用市場是不夠的,還需要自己在應(yīng)用中控制升級邏輯。并且每家應(yīng)用市場上新審核也是一件很麻煩的事情,尤其像至簡網(wǎng)格這樣的應(yīng)用,甚至沒在應(yīng)用市場上架,更不可能依賴它們了。所以必須要在應(yīng)用中實現(xiàn)自動升級功能。

????????網(wǎng)上有很多介紹,他們摸索的結(jié)果對我有很大幫助。可能是因為版本關(guān)系,或者關(guān)注點不同,照著做,會有很多過時的或錯誤的地方,所以我將摸索過程記錄在此,防止忘記。

????????下面幾個圖是在華為榮耀V9(安卓7.0、SDK 24)中的界面:

android app升級,android,至簡網(wǎng)格,java,android,自動升級

圖1、提醒有可升級的版本

android app升級,android,至簡網(wǎng)格,java,android,自動升級

圖2、下載版本

android app升級,android,至簡網(wǎng)格,java,android,自動升級

圖3、安卓7.0的安全檢測界面?

?????大致步驟如下:

  1. AndroidManifest及res設(shè)置;
  2. 申請外部存儲讀寫權(quán)限;
  3. 申請安裝應(yīng)用;
  4. 向服務(wù)端查詢是否有可升級版本,下載版本,執(zhí)行安裝;

? ? ? ?安卓各個版本差異較大,我的測試日期為(2023.5.29),測試環(huán)境為小米8(安卓10、SDK29)、華為榮耀v9(安卓7.0、SDK 24)兩種。因為不考慮兼容安卓7之前的版本,所以代碼中也無相關(guān)實現(xiàn)。

一、AndroidManifest及res設(shè)置

1、AndroidManifest設(shè)置

增加以下權(quán)限:

<!--  網(wǎng)絡(luò)權(quán)限,不用在程序中動態(tài)申請 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!--  外部存儲讀寫,需要在程序中動態(tài)申請,用于存儲運行日志,以及下載的升級版本-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 安裝APK權(quán)限,需要在程序中動態(tài)申請,并且不同于外部存儲讀寫權(quán)限申請 -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <application
        android:name="cn.net.zhijian.mesh.client.MeshClientApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:hardwareAccelerated="true"
        android:usesCleartextTraffic="true"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MeshClient"
        android:requestLegacyExternalStorage="true"
        android:windowSoftInputMode="stateHidden|adjustResize">

......
        <!-- fileprovider名稱在安裝時傳遞給系統(tǒng)安裝程序 -->
        <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/autoupdate" />
        </provider>
    </application>

有以下幾點需要注意:

  • application中需要增加屬性android:requestLegacyExternalStorage="true";
    
  • provider屬性android:authorities="${applicationId}.fileprovider",這個名稱可以自己定,但是在執(zhí)行安裝時必須保持一致,后面會再次提到;
    ?
  • provider中meta-data->android:resource="@xml/autoupdate"名稱可以自己定,但是需確保在res/xml/下有同名的xml文件,Android7.0及以上版本需要通過FileProvider方式進行安裝,文件內(nèi)容見下一節(jié);
    

    android app升級,android,至簡網(wǎng)格,java,android,自動升級

    2、res中的準備

  • 在res中新建一個xml目錄,創(chuàng)建autoupdate.xml,內(nèi)容如下,注意其中的注釋;
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 如果不設(shè)置root,將會發(fā)生“Failed to find configured root that contains...”錯誤 -->
    <root-path name="root_path" path="."/>
<!-- name與path,好像并無太多限制,請了解的同學(xué)指正以下 -->
    <external-path name="autoupdate" path="download/" />
</paths>
  • 下載安裝界面定義

????????在res/layout中增加download_dlg.xml,用以顯示下載進度及安裝中碰到的問題,怎樣顯示請看后面的Updater類實現(xiàn)。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="2mm"
    android:orientation="vertical">
    <ProgressBar
        android:id="@+id/progress"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/txtMsg"
        android:layout_margin="2mm"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text=""
        android:textSize="16sp"
        android:textStyle="normal" />
</LinearLayout>

??????

二、申請外部存儲讀寫權(quán)限與安裝權(quán)限

????????安卓5.0之后,申請權(quán)限的操作變化很大,使用起來比老版本好一些,同一個邏輯不會被打散到多個地方實現(xiàn)。以下實現(xiàn)在MainActivity.onCreate中調(diào)用,沒有考慮兼容老版本。里面有個updater.checkVersion調(diào)用,后面會講到,用來查詢服務(wù)端是否有新版本,可以根據(jù)自己的需要做不同的實現(xiàn)。因為只有在具備外部存儲讀寫權(quán)限后才可以執(zhí)行升級操作,所以申請成功后,才會檢查是否有可升級版本。如果沒有新版本,是不會出現(xiàn)申請安裝應(yīng)用權(quán)限的界面,否則系統(tǒng)的提示會嚇退一部分用戶。

?????????申請安裝權(quán)限的實現(xiàn)與申請外部存儲讀寫權(quán)限不同,在安卓8.0(SDK26)后有一次大變動。在后面的Updater類中,如果是8.0之前的版本則直接安裝,否則要申請權(quán)限。

        //申請必要的權(quán)限
        Updater updater = new Updater(this);
        /*
         * 申請安裝應(yīng)用的權(quán)限。
         * registerForActivityResult必須在onCreate中調(diào)用,
         * 否則會報錯:LifecycleOwners must call register before they are STARTED.
         */
        ActivityResultLauncher<Intent> installApkLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
        (result) -> {//安裝申請確認完畢后的回調(diào)
            if (result.getResultCode() == Activity.RESULT_OK) {
                updater.showDownloadDialog();
            }
        });

        /*
         * 申請外部存儲卡讀寫權(quán)限。
         * 調(diào)用Environment.getExternalStoragePublicDirectory等函數(shù),必須具備外部存儲讀寫權(quán)限,
         * 除了在manifest中要聲明權(quán)限,同時在application中設(shè)置android:requestLegacyExternalStorage="true"
         * 并且,還需要在代碼中動態(tài)申請。
         * 申請成功后才能確定應(yīng)用升級可以執(zhí)行下去,所以才會查詢新版本。
         */
        registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),
            result -> {//權(quán)限申請執(zhí)行完畢后的回調(diào)
                String permissions = "";
                boolean allPassed = true;
                for (Map.Entry<String, Boolean> p : result.entrySet()) {
                    permissions += p.getKey() + ':' + p.getValue() + '\n';
                    if(!p.getValue()) {
                        allPassed = false;
                    }
                }
                LOG.debug("STORAGE_PERMISSION grantResults:\n{}", permissions);
                if(allPassed) { //有了外部存儲讀寫權(quán)限之后再判斷是否有升級版本
                    updater.checkVersion(installApkLauncher);
                }
            }
        ).launch(new String[] {
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE
        });

? ? ??

三、升級安裝

? ? ? ? 檢查版本、下載、安裝,都在Updater類中實現(xiàn),在申請外部存儲讀寫權(quán)限、申請安裝權(quán)限時,會調(diào)用到Updater中的函數(shù)。

? ? ? ? 代碼中出現(xiàn)的cn.net.zhijian包下的類都是我的公共類,看的時候可以忽略,根據(jù)函數(shù)名稱應(yīng)該能大致猜出它的功能。

????????注意其中的String authority = BuildConfig.APPLICATION_ID + ".fileprovider";前面提到過,必須與provider定義中保持一致。否則會提示Couldn't find meta-data for provider with authority...錯誤。

? ? ?installApk(File apkFile, String digest, AbsHttpCallback.IDownloadProgress progress)

  1. apkFile:已下載的安裝文件,我指定的路徑是context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)+"/app.apk",似乎autoupdate.xml中的設(shè)置在此并未起什么作用;
  2. digest:從我的服務(wù)器上查到的文件md5值,安裝前比較校驗碼,不同則拒絕安裝;
  3. progress:用以提示下載進度、安裝錯誤信息等;
??showUpdateDialog(ActivityResultLauncher<Intent> installPermApply)

??????installPermApply是在MainActivity.onCreate中初始化安裝權(quán)限申請加載器時傳遞進來的。安卓8.0及以上版本才會調(diào)用它,其他情況則直接顯示下載安裝界面。

import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.activity.result.ActivityResultLauncher;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.FileProvider;

import org.slf4j.Logger;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.net.zhijian.mesh.client.abs.AbsHttpCallback;
import cn.net.zhijian.mesh.client.abs.IConst;
import cn.net.zhijian.mesh.client.abs.IThreadPool;
import cn.net.zhijian.mesh.client.bean.Company;
import cn.net.zhijian.mesh.client.bean.RequestOptions;
import cn.net.zhijian.mesh.client.util.HttpClient;
import cn.net.zhijian.meshclient.BuildConfig;
import cn.net.zhijian.meshclient.R;
import cn.net.zhijian.util.FileUtil;
import cn.net.zhijian.util.HttpUtil;
import cn.net.zhijian.util.LogUtil;
import cn.net.zhijian.util.StringUtil;
import cn.net.zhijian.util.UrlPathInfo;
import cn.net.zhijian.util.ValParser;

class Updater {
    private static final Logger LOG = LogUtil.getInstance();

    private final Activity context;

    private String verFromSrv; //服務(wù)端返回的應(yīng)用版本號
    private String cdnUrl; //服務(wù)端返回的CDN頭部地址,后面加上/app_id/version/app.apk
    private String digest; //服務(wù)端返回的應(yīng)用apk校驗碼
    private int size; //服務(wù)端返回的應(yīng)用apk大小
    private List<String> features; //服務(wù)端返回的新版本的特性列表

    public Updater(Activity context) {
        this.context = context;
    }

    private void showUpdateDialog(ActivityResultLauncher<Intent> installPermApply) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle(R.string.there_is_new_ver);
        builder.setIcon(R.drawable.download);
        StringBuilder sb = new StringBuilder();
        sb.append(context.getString(R.string.ver_no)).append(this.verFromSrv).append('\n');
        for(String f : features) {
            sb.append(f).append('\n');
        }

        builder.setMessage(sb.toString());
        builder.setPositiveButton(R.string.update_rightnow, (DialogInterface dialog, int which) -> {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                boolean haveInstallPermission = context.getPackageManager().canRequestPackageInstalls();
                if (!haveInstallPermission) { //如果已經(jīng)有權(quán)限,不必再申請
                    Uri packageURI = Uri.parse("package:" + BuildConfig.APPLICATION_ID);
                    Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
                    installPermApply.launch(intent); //權(quán)限申請通過后執(zhí)行showDownloadDialog
                    return;
                }
            }
            showDownloadDialog();// 版本<26(Android 8)或已申請了權(quán)限,則直接顯示下載安裝
        });
        builder.setNegativeButton(R.string.do_it_later, (DialogInterface dialog, int which) -> {
            dialog.dismiss();
        });

        builder.create().show();
    }

    /**
     * 顯示下載對話框,在其中顯示下載、安裝的進度,
     * 如果發(fā)生錯誤,也會顯示錯誤信息
     */
    public void showDownloadDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle(R.string.update_apk);
        LayoutInflater inflater = LayoutInflater.from(context);
        View v = inflater.inflate(R.layout.download_dlg, null);
        ProgressBar progressBar = (ProgressBar) v.findViewById(R.id.progress);
        TextView txtMsg = v.findViewById(R.id.txtMsg);

        builder.setView(v);
        builder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
            //canceled = true;
        });

        //用于顯示當前的進度,請參照download_dlg.xml中的UI定義
        AbsHttpCallback.IDownloadProgress progress = new AbsHttpCallback.IDownloadProgress() {
            String header = "";

            @Override
            public void progress(int curSize) {
                int percent = (int) (((float) curSize / size) * 100);
                context.runOnUiThread(() -> {
                    txtMsg.setText(header + percent + "%");
                    progressBar.setProgress(percent);
                });
            }

            @Override
            public void message(String msg) {
                this.header = msg;
                context.runOnUiThread(() -> {
                    txtMsg.setText(header);
                });
            }
        };

        builder.create().show();
        String url = cdnUrl;
        if(!url.endsWith("/")) {
            url += '/';
        }
        url += BuildConfig.APPLICATION_ID + '/' + this.verFromSrv + "/app.apk";

        String saveAs = FileUtil.addPath(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app.apk");
        File f = new File(saveAs);
        if(f.exists()) { //如果文件存在,并且校驗碼相同,則不必再次下載
            String localDigest = FileUtil.digest(f);
            if(digest.equals(localDigest)) {
                LOG.info("Reinstall apk {}, size:{}", saveAs, size);
                context.runOnUiThread(() -> {
                    progress.progress(size);
                    installAPK(new File(saveAs), digest, progress);
                });
                return;
            }
        }

        progress.message(context.getString(R.string.downloading));
        HttpClient.download(url, saveAs, progress).whenCompleteAsync((hr, e) -> {
            if(e != null) {
                LOG.error("Fail to download {}", cdnUrl, e);
                return;
            }

            if(hr.code != RetCode.OK || hr.data == null || hr.data.size() == 0) {
                LOG.error("Fail to download {}, result:{}", cdnUrl, hr.brief());
                return;
            }
            int appSize = ValParser.getAsInt(hr.data, "size");
            if(appSize != size) {
                LOG.error("Fail to download {}, invalid size({}!={}}", cdnUrl, size, appSize);
                return;
            }
            LOG.info("Reinstall apk {}, size:{}", ValParser.getAsStr(hr.data, "saveAs"), size);
            context.runOnUiThread(() -> {
                installAPK(new File(saveAs), digest, progress);
            });
        });
    }

    /**
     * 安裝apk
     * @param apkFile apk文件完整路徑
     * @param digest 校驗碼
     * @param progress 打印消息的回調(diào)
     */
    private void installAPK(File apkFile, String digest, AbsHttpCallback.IDownloadProgress progress) {
        progress.message(context.getString(R.string.installing));
        try {
            if (!apkFile.exists()) {
                LOG.error("Update apk file `{}` not exists", apkFile);
                progress.message(context.getString(R.string.apk_not_exists));
                return;
            }

            String localDigest = FileUtil.digest(apkFile);
            if(!localDigest.equals(digest)) {
                LOG.error("Invalid apk file `{}` digest({}!={})", apkFile, localDigest, digest);
                progress.message(context.getString(R.string.wrong_digest));
                return;
            }

            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//安裝完成后打開新版本
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 給目標應(yīng)用一個臨時授權(quán)
            //Build.VERSION.SDK_INT >= 24,使用FileProvider兼容安裝apk
            //packageName也可以通過context.getApplicationContext().getPackageName()獲取
            String authority = BuildConfig.APPLICATION_ID + ".fileprovider";
            Uri apkUri = FileProvider.getUriForFile(context, authority, apkFile);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            context.startActivity(intent);
            //安裝完之后會提示”完成” “打開”。
            android.os.Process.killProcess(android.os.Process.myPid());
        } catch (Exception e) {
            LOG.error("Fail to install apk {}", apkFile, e);
            progress.message(context.getString(R.string.fail_to_install));
        }
    }

    public void checkVersion(ActivityResultLauncher<Intent> installPermApply) {
        Company company = RequestOptions.getCompany(Company.PERSONAL_COMPANY_ID);
        int localVer = StringUtil.verToInt(IConst.VERSION);
        UrlPathInfo url = new UrlPathInfo("/checkAppVer")
                .appendPara("service", BuildConfig.APPLICATION_ID, false)
                .appendPara("ver", localVer, false)
                .appendPara("evm", "Android_" + Build.VERSION.SDK_INT, false);
        Map<String, Object> req = new HashMap<>();
        req.put("url", url.toString());
        req.put("method", HttpUtil.METHOD_GET);
        req.put("private", false);
        RequestOptions.parse(company, req, IConst.SERVICE_APPSTORE).thenComposeAsync(opts -> {
            return HttpClient.get(opts.url.node, opts.url.url(), opts.headers);
        }, IThreadPool.Pool).whenCompleteAsync((hr, e) -> {
            if(e != null) {
                LOG.error("Fail to get service info from cloud", e);
                return;
            }

            if(hr.code == RetCode.NOT_EXISTS) {
                LOG.info("No update version for {}", url);
                return;
            }

            if(hr.code != RetCode.OK || hr.data == null || hr.data.size() == 0) {
                LOG.error("Fail to get service info from cloud, result:{}", hr.brief());
                return;
            }

            LOG.debug("checkVersion:{}", hr.data);
            int serverVer = ValParser.getAsInt(hr.data, "ver");
            if(localVer < serverVer) {
                this.verFromSrv = StringUtil.intToVer(serverVer);
                this.cdnUrl = ValParser.getAsStr(hr.data, "url");
                this.digest = ValParser.getAsStr(hr.data, "digest");
                this.size = ValParser.getAsInt(hr.data, "size");
                this.features = ValParser.getAsStrList(hr.data, "features");
                context.runOnUiThread(() -> {
                    showUpdateDialog(installPermApply);
                });
            }
        }, IThreadPool.Pool);
    }
}

希望以上內(nèi)容對你有點幫助,如果有什么問題,歡迎留言評論,我盡量完善它。

此文只在CSDN上編輯修改過,有網(wǎng)站轉(zhuǎn)載了老版本的,里面存在錯誤,請注意。文章來源地址http://www.zghlxwxcb.cn/news/detail-690365.html

到了這里,關(guān)于Android實現(xiàn)App內(nèi)自動升級,適配了安卓7、8及以上版本的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Android APP開機啟動,安卓APP開發(fā)自啟動,安卓啟動后APP自動啟動 Android讓程序開機自動運行APP

    Android APP開機啟動,安卓APP開發(fā)自啟動,安卓啟動后APP自動啟動 Android讓程序開機自動運行APP

    第一步設(shè)置獲取廣播后的業(yè)務(wù) 第二查權(quán)限給APP 理論以上兩步做完就可以了。APP也能收到廣播信息了, 但是APP沒有在桌面啟動。 經(jīng)過再研究,發(fā)現(xiàn)要在手機再設(shè)置自動開啟等業(yè)務(wù),以下是小米、魅族的系統(tǒng)設(shè)置的一些內(nèi)容,其它平臺自己研究。 這里已經(jīng)顯示收到廣播信息 ?

    2024年02月06日
    瀏覽(38)
  • Android存儲權(quán)限完美適配(Android11及以上適配)

    Android存儲權(quán)限完美適配(Android11及以上適配)

    一、Bug簡述 一個很普通的需求,需要下載圖片到本地,我的三個測試機(榮耀Android10,紅米 11 和小米Android 13都沒有問題)。 然后,主角登場了,測試的三星Android 13 死活拉不起存儲權(quán)限彈窗。 想了下,三星的系統(tǒng)可能和小米的系統(tǒng)做了些區(qū)別。于是就是看了下存儲權(quán)限的版

    2024年02月06日
    瀏覽(14)
  • Android app的暗黑模式適配實現(xiàn)

    Android app的暗黑模式適配實現(xiàn)

    原文地址: Android app的暗黑模式適配實現(xiàn) - Stars-One的雜貨小窩 很久之前放在草稿箱的一篇簡單筆記,是之前藍奏云批量下載工具Android版本實現(xiàn)暗黑主題的適配記錄 本文所說的這里的暗黑主題,應(yīng)該只支持Android10系統(tǒng),不過我手頭的Flyme系統(tǒng)(Android9)上測試也有效果,其他低版本則沒

    2024年02月05日
    瀏覽(22)
  • burpsuite無法抓取安卓9以上的app數(shù)據(jù)包問題

    burpsuite無法抓取安卓9以上的app數(shù)據(jù)包問題

    當對app進行滲透測試時發(fā)現(xiàn),android9的系統(tǒng)上burpsuite無法抓取app應(yīng)用數(shù)據(jù)包,后經(jīng)一番搜尋得知扔是證書問題,android9不信任用戶安裝的證書,那么需要使用adb命令的方式將burpsuite證書導(dǎo)入系統(tǒng)證書中。安卓7的系統(tǒng)好像也是無法抓取的。 首先需要轉(zhuǎn)換證書格式。 需要安裝op

    2024年02月05日
    瀏覽(18)
  • Android打開系統(tǒng)文件管理器,并獲取選中文件的路徑,適配Android10及以上無法獲取路徑

    1.進入文件管理器 2.在onActivityResult中獲取返回結(jié)果 3.FileHelper完整工具類

    2024年02月02日
    瀏覽(19)
  • 安卓12(高版本9+以上)安裝Charles證書到系統(tǒng)證書安裝目錄

    (1) 安卓手機開啟root并安裝Magisk (2) 先安裝Chalers證書到用戶證書安裝目錄 (3) 下載并安裝magisk的adguardcert模塊 adguardcert模塊下載:https://download.csdn.net/download/weixin_51111267/87929688 (4) 把剛剛安裝到用戶目錄的證書 06c57dd5.0 移動到以下目錄 /data/adb/modules/aguardcert/system/etc/security/cacert

    2024年01月17日
    瀏覽(25)
  • 記錄一次uniapp實現(xiàn)APP自動升級

    記錄一次uniapp實現(xiàn)APP自動升級

    app的版本管理和升級,是一個不可或缺的功能,而uniapp則是提供了一整套的流程,由于官方文檔過于復(fù)雜,而且寫的云里霧里的,所以個人記錄一次我的操作,直到配置成功。 一共分為2個部分,官方提供的兩個插件( uni-upgrade-center - Admin 和 uni-upgrade-center - App )配套使用,

    2024年01月23日
    瀏覽(17)
  • Jupyter-notebook升級內(nèi)核至Python3.9版本以上

    Jupyter-notebook升級內(nèi)核至Python3.9版本以上

    ? 目錄 1、用管理員身份打開Anaconda Prompt 2、Install 3、Activate 4、pywin32_postinstall.py -install ?5、打開 6、問題 6.1、Jupyter-notebook默認文件夾位置(Home) 6.2、再次打開3.9版本需要重復(fù)操作5、打開 因為調(diào)用matplotlib庫時出現(xiàn)版本不兼容的問題,我想將Python版本升級到3.9以上,搞了一下午

    2024年02月05日
    瀏覽(27)
  • 入門級帶你實現(xiàn)一個安卓智能家居APP(1)java版本

    入門級帶你實現(xiàn)一個安卓智能家居APP(1)java版本

    ?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"? 以上是比較重要的UI控件,大家了解一下,其他的一些屬性我就不多說了,你們可以自己嘗試修改一下參數(shù),就會發(fā)現(xiàn)具體的對應(yīng)的作用是什么了。 我們的ui已經(jīng)做好了,現(xiàn)在就去寫主活動類的邏輯代碼?。。?我主要說說主要邏輯吧。 首先,當然是

    2024年04月27日
    瀏覽(22)
  • 【教程】安卓設(shè)備使用AidLux部署高版本HomeAssistant(2023.2及以上)及安裝HACS

    【教程】安卓設(shè)備使用AidLux部署高版本HomeAssistant(2023.2及以上)及安裝HACS

    本文發(fā)布于:2023年7月1日 備注:Python3.11裝起來問題比較多不建議使用 ? ? ? ? 由于AidLux應(yīng)用商店提供的Python版本最高只支持到3.9,對HomeAssistant的支持只到2023.1版本,而且問題比較多。 ? ? ? ? 本文先安裝一個Python3.10。為了能跟系統(tǒng)自帶的Python3.7共存,使用源碼編譯。 ?

    2024年02月12日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包