1. 程式人生 > >Android App升級非常好用的工具類(應用內升級),及相關可能踩到的坑

Android App升級非常好用的工具類(應用內升級),及相關可能踩到的坑

App升級一般有兩種方式: 第一種,是在App內部升級,自己寫下載程式碼,一種是調到第三方瀏覽器中,讓瀏覽器下載本應用升級(之前文章有講過,連結地址:https://blog.csdn.net/wolfking0608/article/details/79619472)

下面重點介紹應用內部升級

  升級工具類如下:

public class UpdateService extends Service {
    public static final String TAG =  "UpdateService";
    public static final String ACTION = "me.shenfan.UPDATE_APP";
    public static final String STATUS = "status";
    public static final String PROGRESS = "progress";
    public static boolean DEBUG = true;

    //下載大小通知頻率
    public static final int UPDATE_NUMBER_SIZE = 1;
    public static final int DEFAULT_RES_ID = -1;

    public static final int UPDATE_PROGRESS_STATUS = 0;
    public static final int UPDATE_ERROR_STATUS = -1;
    public static final int UPDATE_SUCCESS_STATUS = 1;

    //params
    private static final String URL = "downloadUrl";
    private static final String ICO_RES_ID = "icoResId";
    private static final String ICO_SMALL_RES_ID = "icoSmallResId";
    private static final String UPDATE_PROGRESS = "updateProgress";
    private static final String STORE_DIR = "storeDir";
    private static final String DOWNLOAD_NOTIFICATION_FLAG = "downloadNotificationFlag";
    private static final String DOWNLOAD_SUCCESS_NOTIFICATION_FLAG = "downloadSuccessNotificationFlag";
    private static final String DOWNLOAD_ERROR_NOTIFICATION_FLAG = "downloadErrorNotificationFlag";
    private static final String IS_SEND_BROADCAST = "isSendBroadcast";


    private String downloadUrl;
    private int icoResId;             //default app ico
    private int icoSmallResId;
    private int updateProgress;   //update notification progress when it add number
    private String storeDir;          //default sdcard/Android/package/update
    private int downloadNotificationFlag;
    private int downloadSuccessNotificationFlag;
    private int downloadErrorNotificationFlag;
    private boolean isSendBroadcast;

    private UpdateProgressListener updateProgressListener;
    private LocalBinder localBinder = new LocalBinder();

    /**
     * Class used for the client Binder.
     */
    public class LocalBinder extends Binder {
        /**
         * set update progress call back
         * @param listener
         */
        public void setUpdateProgressListener(UpdateProgressListener listener){
            UpdateService.this.setUpdateProgressListener(listener);
        }
    }


    private boolean startDownload;//開始下載
    private int lastProgressNumber;
    private NotificationCompat.Builder builder;
    private NotificationManager manager;
    private int notifyId;
    private String appName;
    private LocalBroadcastManager localBroadcastManager;
    private Intent localIntent;
    private DownloadApk downloadApkTask;

    /**
     * whether debug
     */
    public static void debug(){
        DEBUG = true;
    }

    private static Intent installIntent(String path){
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            //特別特別 注意這裡的包名,必須和你應用的包名保持一致,不然下載了也會安裝不了,程式閃退!!!
Uri contentUri = FileProvider.getUriForFile(BaseApplication.app, "com.wcyq.gangrong.FileProvider", new File(path)); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); }else { Uri uri = Uri.fromFile(new File(path)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(uri, "application/vnd.android.package-archive"); } return intent; } private static Intent webLauncher(String downloadUrl){ Uri download = Uri.parse(downloadUrl); Intent intent = new Intent(Intent.ACTION_VIEW, download); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return intent; } private static String getSaveFileName(String downloadUrl) { if (downloadUrl == null || TextUtils.isEmpty(downloadUrl)) { return "noName.apk"; } return downloadUrl.substring(downloadUrl.lastIndexOf("/")); } private static File getDownloadDir(UpdateService service){ File downloadDir = null; if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { if (service.storeDir != null){ downloadDir = new File(Environment.getExternalStorageDirectory(), service.storeDir); }else { downloadDir = new File(service.getExternalCacheDir(), "update"); } } else { downloadDir = new File(service.getCacheDir(), "update"); } if (!downloadDir.exists()) { downloadDir.mkdirs(); } return downloadDir; } @Override public void onCreate() { super.onCreate(); appName = getApplicationName(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (!startDownload && intent != null){ startDownload = true; downloadUrl = intent.getStringExtra(URL); icoResId = intent.getIntExtra(ICO_RES_ID, DEFAULT_RES_ID); icoSmallResId = intent.getIntExtra(ICO_SMALL_RES_ID, DEFAULT_RES_ID); storeDir = intent.getStringExtra(STORE_DIR); updateProgress = intent.getIntExtra(UPDATE_PROGRESS, UPDATE_NUMBER_SIZE); downloadNotificationFlag = intent.getIntExtra(DOWNLOAD_NOTIFICATION_FLAG, 0); downloadErrorNotificationFlag = intent.getIntExtra(DOWNLOAD_ERROR_NOTIFICATION_FLAG, 0); downloadSuccessNotificationFlag = intent.getIntExtra(DOWNLOAD_SUCCESS_NOTIFICATION_FLAG, 0); isSendBroadcast = intent.getBooleanExtra(IS_SEND_BROADCAST, false); if (DEBUG){ Log.d(TAG, "downloadUrl: " + downloadUrl); Log.d(TAG, "icoResId: " + icoResId); Log.d(TAG, "icoSmallResId: " + icoSmallResId); Log.d(TAG, "storeDir: " + storeDir); Log.d(TAG, "updateProgress: " + updateProgress); Log.d(TAG, "downloadNotificationFlag: " + downloadNotificationFlag); Log.d(TAG, "downloadErrorNotificationFlag: " + downloadErrorNotificationFlag); Log.d(TAG, "downloadSuccessNotificationFlag: " + downloadSuccessNotificationFlag); Log.d(TAG, "isSendBroadcast: " + isSendBroadcast); } notifyId = startId; buildNotification(); buildBroadcast(); downloadApkTask = new DownloadApk(this); downloadApkTask.execute(downloadUrl); } return super.onStartCommand(intent, flags, startId); } @Nullable @Override public IBinder onBind(Intent intent) { return localBinder; } @Override public boolean onUnbind(Intent intent) { return true; } public void setUpdateProgressListener(UpdateProgressListener updateProgressListener) { this.updateProgressListener = updateProgressListener; } @Override public void onDestroy() { if (downloadApkTask != null){ downloadApkTask.cancel(true); } if (updateProgressListener != null){ updateProgressListener = null; } localIntent = null; builder = null; super.onDestroy(); } public String getApplicationName() { PackageManager packageManager = null; ApplicationInfo applicationInfo = null; try { packageManager = getApplicationContext().getPackageManager(); applicationInfo = packageManager.getApplicationInfo(getPackageName(), 0); } catch (PackageManager.NameNotFoundException e) { applicationInfo = null; } String applicationName = (String) packageManager.getApplicationLabel(applicationInfo); return applicationName; } private void buildBroadcast(){ if (!isSendBroadcast){ return; } localBroadcastManager = LocalBroadcastManager.getInstance(this); localIntent = new Intent(ACTION); } private void sendLocalBroadcast(int status, int progress){ if (!isSendBroadcast || localIntent == null){ return; } localIntent.putExtra(STATUS, status); localIntent.putExtra(PROGRESS, progress); localBroadcastManager.sendBroadcast(localIntent); } private void buildNotification(){ manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); builder = new NotificationCompat.Builder(this); builder.setContentTitle(getString(R.string.update_app_model_prepare, appName)) .setWhen(System.currentTimeMillis()) .setProgress(100, 1, false) .setSmallIcon(icoSmallResId) .setLargeIcon(BitmapFactory.decodeResource( getResources(), icoResId)) .setDefaults(downloadNotificationFlag); manager.notify(notifyId, builder.build()); } private void start(){ builder.setContentTitle(appName); builder.setContentText(getString(R.string.update_app_model_prepare, 1)); manager.notify(notifyId, builder.build()); sendLocalBroadcast(UPDATE_PROGRESS_STATUS, 1); if (updateProgressListener != null){ updateProgressListener.start(); } } /** * * @param progress download percent , max 100 */ private void update(int progress){ if (progress - lastProgressNumber > updateProgress){ lastProgressNumber = progress; builder.setProgress(100, progress, false); builder.setContentText(getString(R.string.update_app_model_progress, progress, "%")); manager.notify(notifyId, builder.build()); sendLocalBroadcast(UPDATE_PROGRESS_STATUS, progress); if (updateProgressListener != null){ updateProgressListener.update(progress); } } } private void success(String path) { builder.setProgress(0, 0, false); builder.setContentText(getString(R.string.update_app_model_success)); Intent i = installIntent(path); PendingIntent intent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(intent); builder.setDefaults(downloadSuccessNotificationFlag); Notification n = builder.build(); n.contentIntent = intent; manager.notify(notifyId, n); sendLocalBroadcast(UPDATE_SUCCESS_STATUS, 100); if (updateProgressListener != null){ updateProgressListener.success(); } startActivity(i); stopSelf(); } private void error(){ Intent i = webLauncher(downloadUrl); PendingIntent intent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentText(getString(R.string.update_app_model_error)); builder.setContentIntent(intent); builder.setProgress(0, 0, false); builder.setDefaults(downloadErrorNotificationFlag); Notification n = builder.build(); n.contentIntent = intent; manager.notify(notifyId, n); sendLocalBroadcast(UPDATE_ERROR_STATUS, -1); if (updateProgressListener != null){ updateProgressListener.error(); } stopSelf(); } private static class DownloadApk extends AsyncTask<String, Integer, String> { private WeakReference<UpdateService> updateServiceWeakReference; public DownloadApk(UpdateService service){ updateServiceWeakReference = new WeakReference<>(service); } @Override protected void onPreExecute() { super.onPreExecute(); UpdateService service = updateServiceWeakReference.get(); if (service != null){ service.start(); } } @Override protected String doInBackground(String... params) { final String downloadUrl = params[0]; final File file = new File(UpdateService.getDownloadDir(updateServiceWeakReference.get()), UpdateService.getSaveFileName(downloadUrl)); if (DEBUG){ Log.d(TAG, "download url is " + downloadUrl); Log.d(TAG, "download apk cache at " + file.getAbsolutePath()); } File dir = file.getParentFile(); if (!dir.exists()){ dir.mkdirs(); } HttpURLConnection httpConnection = null; InputStream is = null; FileOutputStream fos = null; int updateTotalSize = 0; java.net.URL url; try { url = new URL(downloadUrl); httpConnection = (HttpURLConnection) url.openConnection(); httpConnection.setConnectTimeout(20000); httpConnection.setReadTimeout(20000); if (DEBUG){ Log.d(TAG, "download status code: " + httpConnection.getResponseCode()); } if (httpConnection.getResponseCode() != 200) { return null; } updateTotalSize = httpConnection.getContentLength(); if (file.exists()) { if (updateTotalSize == file.length()) { // 下載完成 return file.getAbsolutePath(); } else { file.delete(); } } file.createNewFile(); is = httpConnection.getInputStream(); fos = new FileOutputStream(file, false); byte buffer[] = new byte[4096]; int readSize = 0; int currentSize = 0; while ((readSize = is.read(buffer)) > 0) { fos.write(buffer, 0, readSize); currentSize += readSize; publishProgress((currentSize * 100 / updateTotalSize)); } // download success } catch (Exception e) { e.printStackTrace(); return null; } finally { if (httpConnection != null) { httpConnection.disconnect(); } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } return file.getAbsolutePath(); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); if (DEBUG){ Log.d(TAG, "current progress is " + values[0]); } UpdateService service = updateServiceWeakReference.get(); if (service != null){ service.update(values[0]); } } @Override protected void onPostExecute(String s) { super.onPostExecute(s); UpdateService service = updateServiceWeakReference.get(); if (service != null){ if (s != null){ service.success(s); }else { service.error(); } } } } /** * a builder class helper use UpdateService */ public static class Builder{ private String downloadUrl; private int icoResId = DEFAULT_RES_ID; //default app ico private int icoSmallResId = DEFAULT_RES_ID; private int updateProgress = UPDATE_NUMBER_SIZE; //update notification progress when it add number private String storeDir; //default sdcard/Android/package/update private int downloadNotificationFlag; private int downloadSuccessNotificationFlag; private int downloadErrorNotificationFlag; private boolean isSendBroadcast; protected Builder(String downloadUrl){ this.downloadUrl = downloadUrl; } public static Builder create(String downloadUrl){ if (downloadUrl == null) { throw new NullPointerException("downloadUrl == null"); } return new Builder(downloadUrl); } public String getDownloadUrl() { return downloadUrl; } public int getIcoResId() { return icoResId; } public Builder setIcoResId(int icoResId) { this.icoResId = icoResId; return this; } public int getIcoSmallResId() { return icoSmallResId; } public Builder setIcoSmallResId(int icoSmallResId) { this.icoSmallResId = icoSmallResId; return this; } public int getUpdateProgress() { return updateProgress; } public Builder setUpdateProgress(int updateProgress) { if (updateProgress < 1){ throw new IllegalArgumentException("updateProgress < 1"); } this.updateProgress = updateProgress; return this; } public String getStoreDir() { return storeDir; } public Builder setStoreDir(String storeDir) {//資料夾名 GangGang this.storeDir = storeDir; return this; } public int getDownloadNotificationFlag() { return downloadNotificationFlag; } public Builder setDownloadNotificationFlag(int downloadNotificationFlag) { this.downloadNotificationFlag = downloadNotificationFlag; return this; } public int getDownloadSuccessNotificationFlag() { return downloadSuccessNotificationFlag; } public Builder setDownloadSuccessNotificationFlag(int downloadSuccessNotificationFlag) {//標記等於-1 this.downloadSuccessNotificationFlag = downloadSuccessNotificationFlag; return this; } public int getDownloadErrorNotificationFlag() { return downloadErrorNotificationFlag; } public Builder setDownloadErrorNotificationFlag(int downloadErrorNotificationFlag) {//標記-1 this.downloadErrorNotificationFlag = downloadErrorNotificationFlag; return this; } public boolean isSendBroadcast() { return isSendBroadcast; } public Builder setIsSendBroadcast(boolean isSendBroadcast) { this.isSendBroadcast = isSendBroadcast; return this; } public Builder build(Context context){ if (context == null){ throw new NullPointerException("context == null"); } Intent intent = new Intent();//Intent { cmp=com.wcyq.gangrong/.utils.UpdateService } intent.setClass(context, UpdateService.class); intent.putExtra(URL, downloadUrl); if (icoResId == DEFAULT_RES_ID){//進來了 icoResId = getIcon(context); } if (icoSmallResId == DEFAULT_RES_ID){//進來了 icoSmallResId = icoResId; } intent.putExtra(ICO_RES_ID, icoResId); intent.putExtra(STORE_DIR, storeDir); intent.putExtra(ICO_SMALL_RES_ID, icoSmallResId); intent.putExtra(UPDATE_PROGRESS, updateProgress); intent.putExtra(DOWNLOAD_NOTIFICATION_FLAG, downloadNotificationFlag); intent.putExtra(DOWNLOAD_SUCCESS_NOTIFICATION_FLAG, downloadSuccessNotificationFlag); intent.putExtra(DOWNLOAD_ERROR_NOTIFICATION_FLAG, downloadErrorNotificationFlag); intent.putExtra(IS_SEND_BROADCAST, isSendBroadcast); context.startService(intent); return this; } private int getIcon(Context context){ final PackageManager packageManager = context.getPackageManager(); ApplicationInfo appInfo = null; try { appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } if (appInfo != null){ return appInfo.icon; } return 0; } }

  使用方法:

 private void requestAppVersion() {
        RequestParams params = new RequestParams();
        addRequestHeader(params);
        BaseApplication.getInstance().httpRequest.xPostjson(mContext, params, Constant.BASE_HTTP + ContantUrl.getVersionUpDate, new RequestResultJsonCallBack() {
            @Override
            public void onSucess(String result) {
                Logger.e(TAG, "requestAppVersion-------" + result);
                NewBaseBean info = Constant.getPerson(result, NewBaseBean.class);
                if (info.getCode() == Constant.RETURN_SUCCESS__STATE_CODE) {
                    UpdataAppBean bean = Constant.getPerson(result, UpdataAppBean.class);
                    List<UpdataAppBean.DataBean.ListBean> list = bean.getData().getList();
                    if (list != null && list.size() > 0) {
                        listBean = list.get(0);
                        String name = listBean.getName();
                        if (!name.equals(ContantUrl.SERVER_VERSION_NAME)) {
                            final String url = listBean.getUrl();
                            String text = mContext.getResources().getString(R.string.check_upgrade);
                            UpgradeDialog upgrade = new UpgradeDialog(MainActivity.this, text, new UpgradeDialog.OnClickconfirmListener() {
                                @Override
                                public void confirm() {
                                    if (!checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) && !checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) {
                                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION_MAIN);
                                        return;
                                    }

                                    UpdateService.Builder.create(url).setStoreDir(ContantUrl.AppFile).setDownloadSuccessNotificationFlag(Notification.DEFAULT_ALL).setDownloadErrorNotificationFlag(Notification.DEFAULT_ALL).build(mContext);
                                                                    Toast.makeText(mContext, "正在後臺下載", Toast.LENGTH_LONG).show();
                                    //通過瀏覽器去下載APK
                                    //                                    InstallUtils.installAPKWithBrower(mContext, url);

                                }
                            });
                            upgrade.show();
                            upgrade.setDetail(listBean.getDescription());

                        }
                    }
                } else {
                    Logger.e(TAG, info.getMessage());
                }

            }

            @Override
            public void onFailure(int errorCode, String errorMsg) {
                showErrorLogger(TAG, errorCode, errorMsg);
            }
        });
    }

    public boolean checkPermission(@NonNull String permission) {
        return ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSION_MAIN) {//許可權走的是這裡
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                UpdateService.Builder.create(listBean.getUrl()).setStoreDir(ContantUrl.AppFile).setDownloadSuccessNotificationFlag(Notification.DEFAULT_ALL).setDownloadErrorNotificationFlag(Notification.DEFAULT_ALL).build(mContext);
                Toast.makeText(mContext, "正在後臺下載", Toast.LENGTH_LONG).show();
                //刪除apk檔案(獲取許可權之後)
                FileUtils.deleteFile(new File(ContantUrl.absolutePath + File.separator + ContantUrl.AppFile + "/GangGang_release-1.0.apk"));
            } else {
                ToastUtil.show(mContext, "許可權被禁止,無法下載檔案");
            }
        }

    }

為了大家檢視方便,沒有使用分層處理. UpgradeDialog升級app內容的彈框,很好寫,可以自己寫一個,工具類可以照抄,但是邏輯程式碼僅給各位參考.

特別注意:

    1. 這裡適配了Android6.0以上的許可權,  在清單檔案中要註冊一個內容提供者,否者你的許可權申請不下來.

 程式碼如下:rc_file_path.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths> 

  清單檔案中要註冊

          <provider
              android:name="android.support.v4.content.FileProvider"
              android:authorities="${applicationId}.FileProvider"
              android:exported="false"
              android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/rc_file_path"/>
        </provider>

2. 可能你很久沒有用服務了,服務也是需要註冊的,否者你發現許可權成功了,提示正在下載,就是通知欄裡面就是沒有下載的進度條,你可能懷疑是不是樓上這個教你的傢伙,傻逼了? 其實是你自己沒有註冊服務,自己犯二了.

3. App下載進度條出來了,但是安裝到快100%的時候,媽的,程式閃退了 就是安裝不了

   請你在複製的時候,檢視一下,上面的工具類, 有一行我標了紅色了 ,你程式的包名一定要填寫上去!!!.因為你複製的是我Demo的包名.FileProvider.所以下載的時候發現不一樣,能給你安裝成功就他媽見鬼了.

4. 你下載App成功了,也進入下載頁面了,麻痺的就是安裝apk安裝不上去,這是什麼鬼? 檢查了程式碼也沒有錯啊,自己發現自己找了半天原因還是沒有找出來問題來? 怎麼辦?算了,我還是告訴你吧.你檢查下,你的app和簽名和你手機你的app的簽名是否一致,兩者的包名是否一致,如果不一致,是不會覆蓋安裝的,這個與你的版本沒毛關係.所以你安裝不上去

5.如果上面的4點你都做到了,程式碼也檢查了,還是出現問題,那隻能說明你人品太差了,自己好好檢查和分析原因debug吧! 以上遇到的坑希望能夠幫到你,如果能夠幫到你,並節約你開發的時間,請給我點個贊,follow我一下,謝謝!

相關推薦

Android App升級非常工具(應用升級),相關可能到的

App升級一般有兩種方式: 第一種,是在App內部升級,自己寫下載程式碼,一種是調到第三方瀏覽器中,讓瀏覽器下載本應用升級(之前文章有講過,連結地址:https://blog.csdn.net/wolfking0608/article/details/79619472)下面重

Android 獲取建立各種儲存路徑工具建許可權獲取)

前言 最近工作比較輕,從專案中總結抽象出一些工具類,利人利己,歡迎交流完善~ 概念介紹 1、內部儲存 Internal Storage: 注意內部儲存不是記憶體。內部儲存位於系統中很特殊的一個位置,如果你想將檔案儲存於內部儲存中,那麼檔案預設

Android動態許可權申請工具非常包含9組危險許可權

先看下動態許可權的工具類:package com.xiayiye.yhsh.permissionsdemo; import android.Manifest; import android.app.Activity; import android.app.AlertDia

更改沉浸式狀態列非常的一個工具 StatusBarUtil

1.設定狀態列顏色 StatusBarUtil.setColor(Activity activity, int color) 2.設定狀態列半透明 StatusBarUtil.setTranslucent(Activity activity, int statusBarAlpha)

Android探索之旅(第十篇) 推薦幾款非常的Bug除錯工具

首推 騰訊Bugly - 一種愉悅的開發方式是一款非常方便幫組開發者實時的檢測App的異常及應用統計,還有更加強大的應用更新及熱修復,讓你的App 6飛起 官網地址:https://bugly.qq

PHP非常的分頁

onf pan url pre ++ reg fig cti wal 分頁類: <?php /* * ********************************************* * @類名: page * @參數: $myde_tota

非常的CSS clip-path polygon工具

win .com blog cli polygon .cn image too mage http://betravis.github.io/shape-tools/polygon-drawing/ 非常好用的CSS clip-path polygon工具

pytorch:一個非常工具檔案

在pytorch中去寫訓練函式和測試函式是一件重複的事,因此可以寫成一個總的訓練檔案。 from datetime import datetime import torch import torch.nn.functional as F from torch import nn from

非常android特效

各種幫助類彙總:https://github.com/Blankj/AndroidUtilCode 常用的 iOS 風格 dialog 和 meterial design 風格的 dialog:https://github.com/glassLake/Dialo

dimens-Android非常簡單非常的螢幕適配

為什麼要進行Android螢幕適配? 關於為什麼要進行Android螢幕適配,什麼是dp、dpi這些概念我就不去一一講解了,網上很多文章。這裡我推薦幾篇講的比較好的: Android螢幕適配全攻略(最權威的官方適配指導) Android 螢幕適配:最全面的解決方案 Andr

python中非常的資料庫管理工具dataset

dataset對於操作JSON檔案、NoSQL非常好用。 官方文件:http://dataset.readthedocs.io/en/latest/ 補充: 連線mysql資料庫: db = dataset.connect('mysql://username:[ema

讓我們開發一個非常的捲簾工具

版權宣告:未經作者允許不得轉載,此外掛不得用於商業用途。 目錄 開發環境 外掛開發 __init__.py map_swipe_plugin.py map_swipe_tool.py active deactivate canvasPressEvent can

非常 世界上最快最好的視訊壓縮轉換工具(精品)

                測試了20多款 各種視訊壓縮 擷取軟體 真的是這款最快 快好用 效果也好!!!最快的視訊轉換壓縮工具。WisMencoder 能夠把您的電腦上的所有視訊格式,包括avi,mpg,rmvb,wmv,mp4,mov,dat等格式以最快的速度和最高的質量轉換為AVI格式。速度和質量都

幾款 Windows 系統 非常的常用工具軟體

1、驅動更新 :Driver Boost Free Driver Boost是國外老牌優化軟體廠商iobit推出的驅動更新/備份工具,相比於國內的主流驅動更新軟體,Driver Boost不捆綁推廣軟體、無廣告,一鍵輕鬆更新所有驅動和常用的執行庫(DirectX、Visua

yum升級CURL到最新版本的方法,非常

首先,先為你的伺服器獲取最新匹配的源:http://mirror.city-fan.org/ftp/contrib/yum-repo/ # 安裝新版libcurl的yum源rpm -ivh http://mirror.city-fan.org/ftp/contrib/y

非常的雙視窗管理工具】Fenetre Mac破解版

fenetre mac破解版是mac平臺上一款非常好用的雙視窗管理工具,可以幫助您在Mac桌面上的選單欄位置保留一個包含視訊,文件,照片或網站的視窗,讓您可以在工作時觀看Netflix,編碼時聽會議,邊看教程實時操作等等,非常強大!   fenetre mac破解版軟體介紹

一個SQL SERVER查詢分析器非常工具

使用效果: 1. 1.非常智慧的提示,使用起來其智慧、方便性,不比PL/SQL差; 2.功能非常強大,可見其“選項”面板; 3.適合SQL2000、SQL2005等。 如有需要的朋友請到:下載 安裝步驟: 1.解壓 2.雙擊 3.安

外掛,非常的請求REST的工具restclient,和非常的請求http的工具HttpRequester,

firefox外掛,非常棒,有圖有真相, restclient chrome://restclient/content/restclient.html HttpRequester 在firef

發現一個非常的扒站工具IDM

作為一個前端,看見一個好的網站會忍不住有想趴下全站來研究的衝動,今天就分享一個非常非常好用的扒站工具,免費使用的,無需費用:Internet Download Manager (IDM)  安裝好之後可以選擇中文版本,然後其他的都是傻瓜式操作。很簡單的。這個軟體有30天

非常的記憶體檢測工具

推薦理由:準確、快速大家比較熟悉的記憶體檢測工具有MemTest、微軟記憶體檢測(Microsoft Memory Diagnostic)等。但就本人的使用經歷來說,微軟的記憶體檢測工具更實用一些。下載地址:http://www.box.net/shared/03b7xbvk