1. 程式人生 > >簡單實現安卓app自動更新功能

簡單實現安卓app自動更新功能

一般的安卓app都有自動更新功能,實現app的更新,以讓使用者體驗新版本的功能,這裡也是專案中用到的,今天就來總結一下,程式碼應該有點多,還請耐心點哈。
安卓應用實現自動更新比較簡單,這裡跟大家介紹下:

第一步 伺服器端:

  • 服務端提供一個藉口,或者網址,我這裡就用的伺服器是tomcat,這裡提供一個網址如下:
//也就是一個json資料介面
  public static final String UPDATE_URL = "http://192.168.1.103:8080/update.json";

我們來看下json資料引數:

{
//app名字
appname: "愛新聞1.1"
, //伺服器版本號 serverVersion: "2", //伺服器標誌 serverFlag: "1", //是否強制更新 lastForce: "1", //apk下載地址,這裡我已經下載了官方的apk,放到了伺服器裡面 updateurl: "http://192.168.1.103:8080/36Kr.apk", //版本的更新的描述 upgradeinfo: "V1.1版本更新,你想不想要試一下哈!!!" }

好了以上的是伺服器端的資訊,在這裡不需要多說了,我們來看下客戶端的吧。

第二步 客戶端需要實現:

首先我們要去解析服務端給的json,那麼我們就要來建立一個model類了(程式碼過多,這裡只有欄位,getter和setter方法自己建立):

//app名字
    private String appname;
    //伺服器版本
    private String serverVersion;
    //伺服器標誌
    private String serverFlag;
    //強制升級
    private String lastForce;
    //app最新版本地址
    private String updateurl;
    //升級資訊
    private String upgradeinfo;

在這裡使用了一個輔助類,基本和model欄位差不多:

public class UpdateInformation {
    public
static String appname = MyApplication.getInstance() .getResources().getString(R.string.app_name); public static int localVersion = 1;// 本地版本 public static String versionName = ""; // 本地版本名 public static int serverVersion = 1;// 伺服器版本 public static int serverFlag = 0;// 伺服器標誌 public static int lastForce = 0;// 之前強制升級版本 public static String updateurl = "";// 升級包獲取地址 public static String upgradeinfo = "";// 升級資訊 public static String downloadDir = "wuyinlei";// 下載目錄 }

我們知道,我們在進入app的時候,這個時候如果檢測到伺服器端有了新的版本,就回彈出提示框,提示我們更新。這個我們在MainActivity裡面處理邏輯(onCreate()方法裡面):

 OkhttpManager.getAsync(Config.UPDATE_URL, new OkhttpManager.DataCallBack() {
            @Override
            public void requestFailure(Request request, Exception e) {

            }
            @Override
            public void requestSuccess(String result) {
                try {
                    Log.d("wuyiunlei",result);
                    JSONObject object = new JSONObject(result);
                    UpdateInfoModel model = new UpdateInfoModel();
                    model.setAppname(object.getString("appname"));
                    model.setLastForce(object.getString("lastForce"));
                    model.setServerFlag(object.getString("serverFlag"));
                    model.setServerVersion(object.getString("serverVersion"));
                    model.setUpdateurl(object.getString("updateurl"));
                    model.setUpgradeinfo(object.getString("upgradeinfo"));
                    tmpMap.put(DeliverConsts.KEY_APP_UPDATE, model);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                //傳送廣播
                sendBroadcast(new Intent(UpdateReceiver.UPDATE_ACTION));
            }
        });

當然了,我們也要註冊和結束廣播:

 /**
     * 廣播註冊
     */
    private void registerBroadcast() {
        mUpdateReceiver = new UpdateReceiver(false);
        mIntentFilter = new IntentFilter(UpdateReceiver.UPDATE_ACTION);
        this.registerReceiver(mUpdateReceiver, mIntentFilter);
    }
 /**
     * 廣播解除安裝
     */
    private void unRegisterBroadcast() {
        try {
            this.unregisterReceiver(mUpdateReceiver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

好了,接下來我們看下我們自定義的廣播接收者UpdateReceiver .java:


/**
 * 版本更新升級 廣播接受者
 *
 */
public class UpdateReceiver extends BroadcastReceiver {
    private AlertDialog.Builder mDialog;
    public static final String UPDATE_ACTION = "wuyinlei_aixinwen";
    private SharedPreferencesHelper mSharedPreferencesHelper;
    private boolean isShowDialog;

    public UpdateReceiver() {
    }

    public UpdateReceiver(boolean isShowDialog) {
        super();
        this.isShowDialog = isShowDialog;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        mSharedPreferencesHelper = mSharedPreferencesHelper
                .getInstance(MyApplication.getInstance());
        //當然了,這裡也可以直接new處hashmap
        HashMap<String, Object> tempMap = MyApplication.getInstance()
                .getTempMap();
        UpdateInfoModel model = (UpdateInfoModel) tempMap
                //就是一個標誌
                .get(DeliverConsts.KEY_APP_UPDATE);
        try {

            /**
             * 獲取到當前的本地版本
             */
            UpdateInformation.localVersion = MyApplication
                    .getInstance()
                    //包管理獨享
                    .getPackageManager()
                    //包資訊
                    .getPackageInfo(
                            MyApplication.getInstance()
                                    .getPackageName(), 0).versionCode;
            /**
             * 獲取到當前的版本名字
             */
            UpdateInformation.versionName = MyApplication
                    .getInstance()
                    .getPackageManager()
                    .getPackageInfo(
                            MyApplication.getInstance()
                                    .getPackageName(), 0).versionName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        //app名字
        UpdateInformation.appname = MyApplication.getInstance()
                .getResources().getString(R.string.app_name);
        //伺服器版本
        UpdateInformation.serverVersion = Integer.parseInt(model
                .getServerVersion());
        //伺服器標誌
        UpdateInformation.serverFlag = Integer.parseInt(model.getServerFlag());
        //強制升級
        UpdateInformation.lastForce = Integer.parseInt(model.getLastForce());
        //升級地址
        UpdateInformation.updateurl = model.getUpdateurl();
        //升級資訊
        UpdateInformation.upgradeinfo = model.getUpgradeinfo();

        //檢查版本
        checkVersion(context);

    }

    /**
     * 檢查版本更新
     * 
     * @param context
     */
    public void checkVersion(Context context) {
        if (UpdateInformation.localVersion < UpdateInformation.serverVersion) {
            // 需要進行更新
            mSharedPreferencesHelper.putIntValue(
                    //有新版本
                    SharedPreferencesTag.IS_HAVE_NEW_VERSION, 1);
            //更新
            update(context);
        } else {
            mSharedPreferencesHelper.putIntValue(
                    SharedPreferencesTag.IS_HAVE_NEW_VERSION, 0);
            if (isShowDialog) {
                //沒有最新版本,不用升級
                noNewVersion(context);
            }
            clearUpateFile(context);
        }
    }

    /**
     * 進行升級
     * 
     * @param context
     */
    private void update(Context context) {
        if (UpdateInformation.serverFlag == 1) {
            // 官方推薦升級
            if (UpdateInformation.localVersion < UpdateInformation.lastForce) {
                //強制升級
                forceUpdate(context);
            } else {
                //正常升級
                normalUpdate(context);
            }

        } else if (UpdateInformation.serverFlag == 2) {
            // 官方強制升級
            forceUpdate(context);
        }
    }

    /**
     * 沒有新版本
     * @param context
     */
    private void noNewVersion(final Context context) {
        mDialog = new AlertDialog.Builder(context);
        mDialog.setTitle("版本更新");
        mDialog.setMessage("當前為最新版本");
        mDialog.setNegativeButton("確定", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        }).create().show();
    }

    /**
     * 強制升級 ,如果不點選確定升級,直接退出應用
     * 
     * @param context
     */
    private void forceUpdate(final Context context) {
        mDialog = new AlertDialog.Builder(context);
        mDialog.setTitle("版本更新");
        mDialog.setMessage(UpdateInformation.upgradeinfo);

        mDialog.setPositiveButton("確定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent mIntent = new Intent(context, UpdateService.class);
                mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                mIntent.putExtra("appname", UpdateInformation.appname);
                mIntent.putExtra("appurl", UpdateInformation.updateurl);
                //啟動服務
                context.startService(mIntent);
            }
        }).setNegativeButton("退出", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                // 直接退出應用
                //ManagerActivity.getInstance().finishActivity();
                System.exit(0);
            }
        }).setCancelable(false).create().show();
    }

    /**
     * 正常升級,使用者可以選擇是否取消升級
     * 
     * @param context
     */
    private void normalUpdate(final Context context) {
        mDialog = new AlertDialog.Builder(context);
        mDialog.setTitle("版本更新");
        mDialog.setMessage(UpdateInformation.upgradeinfo);
        mDialog.setPositiveButton("確定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent mIntent = new Intent(context, UpdateService.class);
                mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                //傳遞資料
                mIntent.putExtra("appname", UpdateInformation.appname);
                mIntent.putExtra("appurl", UpdateInformation.updateurl);
                context.startService(mIntent);
            }
        }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        }).create().show();
    }

    /**
     * 清理升級檔案
     * 
     * @param context
     */
    private void clearUpateFile(final Context context) {
        File updateDir;
        File updateFile;
        if (Environment.MEDIA_MOUNTED.equals(Environment
                .getExternalStorageState())) {
            updateDir = new File(Environment.getExternalStorageDirectory(),
                    UpdateInformation.downloadDir);
        } else {
            updateDir = context.getFilesDir();
        }
        updateFile = new File(updateDir.getPath(), context.getResources()
                .getString(R.string.app_name) + ".apk");
        if (updateFile.exists()) {
            Log.d("update", "升級包存在,刪除升級包");
            updateFile.delete();
        } else {
            Log.d("update", "升級包不存在,不用刪除升級包");
        }
    }
}

接下最後我們來看下服務吧UpdateService .java:

/**
 * 不要忘記註冊,在mainfest檔案中
*/
public class UpdateService extends Service {
    // BT位元組參考量
    private static final float SIZE_BT = 1024L;
    // KB位元組參考量
    private static final float SIZE_KB = SIZE_BT * 1024.0f;
    // MB位元組參考量
    private static final float SIZE_MB = SIZE_KB * 1024.0f;

    private final static int DOWNLOAD_COMPLETE = 1;// 完成
    private final static int DOWNLOAD_NOMEMORY = -1;// 記憶體異常
    private final static int DOWNLOAD_FAIL = -2;// 失敗

    private String appName = null;// 應用名字
    private String appUrl = null;// 應用升級地址
    private File updateDir = null;// 檔案目錄
    private File updateFile = null;// 升級檔案

    // 通知欄
    private NotificationManager updateNotificationManager = null;
    private Notification updateNotification = null;

    private Intent updateIntent = null;// 下載完成
    private PendingIntent updatePendingIntent = null;// 在下載的時候

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        appName = intent.getStringExtra("appname");
        appUrl = intent.getStringExtra("appurl");
        updateNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        updateNotification = new Notification();
        //通知圖示
        updateNotification.icon = R.mipmap.head;
        //通知資訊描述
        updateNotification.tickerText = "正在下載 " + appName;
        updateNotification.when = System.currentTimeMillis();
        updateIntent = new Intent(this, MyApplication.class);
        updatePendingIntent = PendingIntent.getActivity(this, 0, updateIntent,
                0);
        updateNotification.contentIntent = updatePendingIntent;
        updateNotification.contentIntent.cancel();
        updateNotification.contentView = new RemoteViews(getPackageName(),
                //這個佈局很簡單,就是一個圖片和兩個textview,分別是正在下載和下載進度
                R.layout.download_notification);
        updateNotification.contentView.setTextViewText(
                R.id.download_notice_name_tv, appName + " 正在下載");
        updateNotification.contentView.setTextViewText(
                R.id.download_notice_speed_tv, "0MB (0%)");
        updateNotificationManager.notify(0, updateNotification);
        new UpdateThread().execute();
    }

    /**
     * 在這裡使用了asynctask非同步任務來下載
     */
    class UpdateThread extends AsyncTask<Void, Void, Integer> {
        @Override
        protected Integer doInBackground(Void... params) {
            return downloadUpdateFile(appUrl);
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);

            if (result == DOWNLOAD_COMPLETE) {
                Log.d("update", "下載成功");
                String cmd = "chmod 777 " + updateFile.getPath();
                try {
                    Runtime.getRuntime().exec(cmd);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Uri uri = Uri.fromFile(updateFile);
                //安裝程式
                Intent installIntent = new Intent(Intent.ACTION_VIEW);
                installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                installIntent.setDataAndType(uri,
                        "application/vnd.android.package-archive");
                updatePendingIntent = PendingIntent.getActivity(
                        UpdateService.this, 0, installIntent, 0);
                updateNotification.contentIntent = updatePendingIntent;
                updateNotification.contentView.setTextViewText(
                        R.id.download_notice_speed_tv,
                        getString(R.string.update_notice_finish));
                updateNotification.tickerText = appName + "下載完成";
                updateNotification.when = System.currentTimeMillis();
                updateNotification.defaults = Notification.DEFAULT_SOUND;
                updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;
                updateNotificationManager.notify(0, updateNotification);
                //啟動安裝程式
                UpdateService.this.startActivity(installIntent);
                stopSelf();
            } else if (result == DOWNLOAD_NOMEMORY) {
                //如果記憶體有問題
                updateNotification.tickerText = appName + "下載失敗";
                updateNotification.when = System.currentTimeMillis();
                updateNotification.contentView.setTextViewText(
                        R.id.download_notice_speed_tv,
                        getString(R.string.update_notice_nomemory));
                updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;
                updateNotification.defaults = Notification.DEFAULT_SOUND;
                updateNotificationManager.notify(0, updateNotification);
                stopSelf();
            } else if (result == DOWNLOAD_FAIL) {
                //下載失敗
                updateNotification.tickerText = appName + "下載失敗";
                updateNotification.when = System.currentTimeMillis();
                updateNotification.contentView.setTextViewText(
                        R.id.download_notice_speed_tv,
                        getString(R.string.update_notice_error));
                updateNotification.flags |= Notification.FLAG_AUTO_CANCEL;
                updateNotification.defaults = Notification.DEFAULT_SOUND;
                updateNotificationManager.notify(0, updateNotification);
                stopSelf();
            }
        }

    }

    /**
     * 下載更新程式檔案
     * @param downloadUrl   下載地址
     * @return
     */
    private int downloadUpdateFile(String downloadUrl) {
        int count = 0;
        long totalSize = 0;   //總大小
        long downloadSize = 0;   //下載的大小
        URI uri = null;

        //這個已經捨棄了,要用的話,就要加上org.apache.http.legacy.jar這個jar包
        HttpGet httpGet = null;
        try {
            uri = new URI(downloadUrl);
            httpGet = new HttpGet(uri);
        } catch (URISyntaxException e) {
            String encodedUrl = downloadUrl.replace(' ', '+');
            httpGet = new HttpGet(encodedUrl);
            e.printStackTrace();
        }
        HttpClient httpClient = new DefaultHttpClient();
        HttpResponse httpResponse = null;
        FileOutputStream fos = null;
        InputStream is = null;
        try {
            httpResponse = httpClient.execute(httpGet);
            if (httpResponse != null) {
                int stateCode = httpResponse.getStatusLine().getStatusCode();
                if (stateCode == HttpStatus.SC_OK) {
                    HttpEntity entity = httpResponse.getEntity();
                    if (entity != null) {
                        totalSize = entity.getContentLength();
                        //如果記憶體可用
                        if (MemoryAvailable(totalSize)) {
                            is = entity.getContent();
                            if (is != null) {
                                fos = new FileOutputStream(updateFile, false);
                                byte buffer[] = new byte[4096];
                                int readsize = 0;
                                while ((readsize = is.read(buffer)) > 0) {
                                    fos.write(buffer, 0, readsize);
                                    downloadSize += readsize;
                                    if ((count == 0)
                                            || (int) (downloadSize * 100 / totalSize) >= count) {
                                        count += 5;
                                        updateNotification.contentView
                                                .setTextViewText(
                                                        R.id.download_notice_speed_tv,
                                                        getMsgSpeed(downloadSize,totalSize));
                                        updateNotificationManager.notify(0,
                                                updateNotification);
                                    }
                                }
                                fos.flush();
                                if (totalSize >= downloadSize) {
                                    return DOWNLOAD_COMPLETE;
                                } else {
                                    return DOWNLOAD_FAIL;
                                }
                            }
                        } else {
                            if (httpGet != null) {
                                httpGet.abort();
                            }
                            return DOWNLOAD_NOMEMORY;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (httpClient != null) {
                httpClient.getConnectionManager().shutdown();
            }
        }
        return DOWNLOAD_FAIL;
    }

    /**
     * 可用記憶體大小
     * @param fileSize
     * @return
     */
    private boolean MemoryAvailable(long fileSize) {
        fileSize += (1024 << 10);
        if (MemoryStatus.externalMemoryAvailable()) {
            if ((MemoryStatus.getAvailableExternalMemorySize() <= fileSize)) {
                if ((MemoryStatus.getAvailableInternalMemorySize() > fileSize)) {
                    createFile(false);
                    return true;
                } else {
                    return false;
                }
            } else {
                createFile(true);
                return true;
            }
        } else {
            if (MemoryStatus.getAvailableInternalMemorySize() <= fileSize) {
                return false;
            } else {
                createFile(false);
                return true;
            }
        }
    }

    /**
     * 獲取下載進度
     * @param downSize
     * @param allSize
     * @return
     */
    public static String getMsgSpeed(long downSize, long allSize) {
        StringBuffer sBuf = new StringBuffer();
        sBuf.append(getSize(downSize));
        sBuf.append("/");
        sBuf.append(getSize(allSize));
        sBuf.append(" ");
        sBuf.append(getPercentSize(downSize, allSize));
        return sBuf.toString();
    }

    /**
     * 獲取大小
     * @param size
     * @return
     */
    public static String getSize(long size) {
        if (size >= 0 && size < SIZE_BT) {
            return (double) (Math.round(size * 10) / 10.0) + "B";
        } else if (size >= SIZE_BT && size < SIZE_KB) {
            return (double) (Math.round((size / SIZE_BT) * 10) / 10.0) + "KB";
        } else if (size >= SIZE_KB && size < SIZE_MB) {
            return (double) (Math.round((size / SIZE_KB) * 10) / 10.0) + "MB";
        }
        return "";
    }

    /**
     * 獲取到當前的下載百分比
     * @param downSize   下載大小
     * @param allSize    總共大小
     * @return
     */
    public static String getPercentSize(long downSize, long allSize) {
        String percent = (allSize == 0 ? "0.0" : new DecimalFormat("0.0")
                .format((double) downSize / (double) allSize * 100));
        return "(" + percent + "%)";
    }


    /**
     * 建立file檔案
     * @param sd_available    sdcard是否可用
     */
    private void createFile(boolean sd_available) {
        if (sd_available) {
            updateDir = new File(Environment.getExternalStorageDirectory(),
                    UpdateInformation.downloadDir);
        } else {
            updateDir = getFilesDir();
        }
        updateFile = new File(updateDir.getPath(), appName + ".apk");
        if (!updateDir.exists()) {
            updateDir.mkdirs();
        }
        if (!updateFile.exists()) {
            try {
                updateFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            updateFile.delete();
            try {
                updateFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

MemoryStatus工具類:

public class MemoryStatus {
    private static final int ERROR = -1;

    // 判斷SD卡是否存�?
    static public boolean externalMemoryAvailable() {
        return Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED);
    }

    // 獲取內部儲存器有用空間大�?
    static public long getAvailableInternalMemorySize() {
        File path = Environment.getDataDirectory();
        StatFs stat = new StatFs(path.getPath());
        long blockSize = stat.getBlockSize();
        long availableBlocks = stat.getAvailableBlocks();
        return availableBlocks * blockSize;
    }

    // 獲取內部儲存器空間�?大小
    static public long getTotalInternalMemorySize() {
        File path = Environment.getDataDirectory();
        StatFs stat = new StatFs(path.getPath());
        long blockSize = stat.getBlockSize();
        long totalBlocks = stat.getBlockCount();
        return totalBlocks * blockSize;
    }

    // 獲取SD卡有用空間大小,錯誤返回-1
    static public long getAvailableExternalMemorySize() {
        if (externalMemoryAvailable()) {
            File path = Environment.getExternalStorageDirectory();
            StatFs stat = new StatFs(path.getPath());
            long blockSize = stat.getBlockSize();
            long availableBlocks = stat.getAvailableBlocks();
            return availableBlocks * blockSize;
        } else {
            return ERROR;
        }
    }

    // 獲取SD卡�?空間大小,錯誤返�?1
    static public long getTotalExternalMemorySize() {
        if (externalMemoryAvailable()) {
            File path = Environment.getExternalStorageDirectory();
            StatFs stat = new StatFs(path.getPath());
            long blockSize = stat.getBlockSize();
            long totalBlocks = stat.getBlockCount();
            return totalBlocks * blockSize;
        } else {
            return ERROR;
        }
    }

    /**
     * 根據給定的檔案的路徑來計算資料夾的大�?
     * 
     * @param dir
     *            檔案的路�?
     * @return 資料夾的大小
     */
    static public long getFileSize(File dir) {
        if (dir == null) {
            return 0;
        }
        if (!dir.isDirectory()) {
            return 0;
        }
        long dirSize=0;
        File[] files=dir.listFiles();
        for (File file : files) {
            if(file.isFile())
            {
                dirSize+=file.length();
            }else if (file.isDirectory()) {
                dirSize+=getFileSize(file); //如果是目�?那就進行遞迴 來計算檔案的大小
            }
        }
        return dirSize;
    }

    // 把檔案大小轉化字串
    static public String formatSize(long size) {
        Log.d("zttjiangqq", "檔案的大小為:"+size);
        String suffix = null;

        if(size==0)
        {
            return "";
        }

        if (size >= 1024) {
            suffix = "KB";
            size /= 1024;
            if (size >= 1024) {
                suffix = "MB";
                size /= 1024;
                if (size >= 1024) {
                    suffix = "G";
                    size /= 1024;
                }
            }
        }
        StringBuilder resultBuffer = new StringBuilder(Long.toString(size));
        int commaOffset = resultBuffer.length() - 3;
        while (commaOffset > 0) {
            resultBuffer.insert(commaOffset, ',');
            commaOffset -= 3;
        }
        if (suffix != null)
            resultBuffer.append(suffix);
        
            
           

相關推薦

簡單實現app自動更新功能

一般的安卓app都有自動更新功能,實現app的更新,以讓使用者體驗新版本的功能,這裡也是專案中用到的,今天就來總結一下,程式碼應該有點多,還請耐心點哈。 安卓應用實現自動更新比較簡單,這裡跟大家介紹下: 第一步 伺服器端: 服務端提供一個藉口,或者網

APP版本更新自動開啟

安卓APP自動更新版本功能及安裝完成後自動開啟 專案中要求有更新檢查最新版本的功能,功能實現後,發現新版本APP下載安裝後就結束了,沒有重新啟動,客戶還以為是閃退了,後來才發現原來是安裝過程太快了。於是,就要加上一個安裝完成後自動開啟的功能。網上查了很多以後,

ionic app 自動更新

準備工作 ngcordova 外掛: 獲取伺服器上的版本號 得到伺服器上的apk的versionCode後,使用$cordovaAppVersion獲取當前執行的apk的versionCode後,將兩者進行比較。 $http.get(url)

【SikuliX】SikuliX+Vysor實現app自動化測試

簡介 Vysor用於顯示Android裝置介面和操作 SikuliX用於編寫自動化指令碼 安裝Vysor 開啟谷歌瀏覽器,點選擴充套件程式設定 搜尋Vysor,進行安裝 點選 View

在Unity3D項目中接入ShareSDK實現平臺微信分享功能(可使用ShareSDK默認UI或自定義UI)

顯示 選項 dev template 腳本 配置文件 all 自己 show   最近公司的大廳要重做,我協助主程一起制作新大廳和新框架,前面制作的編輯器也派上了用場。等全部功能做完後我會再寫一個復盤,這兩天主程在忙於寫熱更新的功能,所以把接入分享SDK功能的任務交給了我,

一個非常簡單的方法使用JavaScript打包一個網頁成為app(打包遠景論壇)

utf 網易 hub 文件 圖標 http taobao targe 應用 前言: 此方法非常簡單,可以挑一些移動端做的好的網站進行打包,比如 淘寶網, 京東網,網易新聞, 遠景論壇 大神看了輕噴,可以自己做了玩一下,本質上也是在瀏覽器訪問頁面,靈感來源於酷安

【轉載】Android基礎——實現藍牙2.0模塊的通信功能

效果 and str action dem 就是 fonts 根據 一個 實現安卓藍牙2.0模塊的通信功能 事先說明: 安卓藍牙2.0的開發和BLE4.0的開發完全是不一樣的,不過很多設備都采用雙模藍牙,所以掌握2.0也是很有必要的 安卓藍牙需要手動申請定位權限,可

[編譯] 6、開源兩個簡單且有用的APP命令行開發工具和nRF51822命令行開發工具

android 關註 eabi ref 文件 不存在 alt stdin vim 星期四, 27. 九月 2018 12:00上午 - BEAUTIFULZZZZ 一、前言 前幾天給大家介紹了如何手動搭建安卓APP命令行開發環境和nRF51822命令行開發環境,中秋這

Vux+Cordova打包的App實現微信分享朋友和朋友圈

知識儲備 Cordova Plugin ShareSDK 外掛 什麼是Cordova Plugin ShareSDK Cordova Plugin ShareSDK封裝了ShareSDK的android和ios平臺的分享功能。在hybird app開發中可以方便的完成分享功能。如:ion

thinkphp5實現根據渠道號不同實現和IOS的APP支付和H5支付

<?php namespace app\api\controller; use think\Controller; use app\common\model\ShopInfo as ShopInfoModel; use app\common\model\UserOrde

[編譯] 6、開源兩個簡單且有用的APP命令列開發工具和nRF51822命令列開發工具

星期四, 27. 九月 2018 12:00上午 - BEAUTIFULZZZZ 一、前言 前幾天給大家介紹瞭如何手動搭建安卓APP命令列開發環境和nRF51822命令列開發環境,中秋這幾天我把上面篇文章的操作流程全部做成了shell指令碼,使得可以讓其他人簡單執行下指令碼、就能夠直接建立綠色開發環境,豈

自帶分享功能出現的exposed beyond app through ClipData.Item.getUri

在Oncreat裡面新增: //-------------------------------------------------------------------------------------------------------------- // StrictMode.Vm

初次簡單逆向破解APP

最近下載了一個直播app,因為有會員時間限制,只能只用十分鐘,但最近接觸了一些逆向知識,抱著嘗試的態度嘗試首次成功,特發帖紀錄。 先準備好apk,放到桌面  看看app有沒有加固,如果加固了,逆向起來就有一定的難度,相反沒有加固的程式,如同裸體在大街之上。 所以首先用AndroidKi

Retrofit2實現App自動更新

原理 Retrofit2和okhttp實現了apk的下載 自定義類實現Retrofit2的Callback類在裡面通過IO流寫入檔案並且使用RxBus訂閱下載進度 自定義類實現okhttp3的ResponseBody類並且在裡面使用RxBus釋出下載進度資訊

App登出登陸實現

一、Intent intent10=new Intent(MainActivity.this,LoginActivity.class); intent10.setFlags(Intent.FLAG_A

APP測試之使用Burp Suite實現HTTPS抓包方法

APP的測試重點小部分在APP本身,大部分還是在網路通訊上(單機版除外)。所以在安卓APP測試過程中,網路抓包非常重要,一般來說,app開發會採用HTTP協議、Websocket、socket協議,一般來說,HTTP協議最多,Websocket是後起之秀,socket最少,

一個APP功能模組組成

在網上搜索了一下,想找到一篇關於安卓APP的功能模組的文章,發現沒有找到,就決定自己寫一篇,主要目的是讓自己學習新知識、複習舊知識,整合自己的知識庫,形成一套具有核心競爭力的屬於自己的一套東西。剛開始只是粗略的寫一下,後期繼續優化補充。(字型大小:宋體 14px) 按照自己

JavaFX本地應用自動更新功能實現——FXLauncher

                看了官方的demo,還是研究了好久才實現了此功能。描述實在是太簡單了。 參考地址:fxldem

android+app測試基礎4+簡單專案

測試一款安卓app,茄子醫生 主要測試了四個模組 第一個模組:四個引導頁,我們測試是否可以點選跳過,點選跳過後是否能夠找到體驗按鈕 第二個模組:就是登陸模組,簡單的測試一下 第三模組:就是日曆模組,日曆模組看數字是否和今天的日期一致 第四個模組:就是模擬新增預約模組的流程

APP底部導航欄(有訊息圓點指示器),實現fragment切換(eclipse)

本專案使用了相對佈局和單選按鈕實現了安卓app常見的底部導航欄(帶有訊息圓點指示器),效果如果所示 一、佈局程式碼如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/androi