手機衛士知識點一獲取更新以及下載安裝

一.如何獲取應用程序自動更新
1.服務器中,存儲一份JSON文件,里面記錄著應用的版本號,和應用的下載地址,當我們啟動程序的時候,會訪問這個JSON文件,如果本應用程序的版本號小于服務器的版本號,則會提示用戶是否更新.會有以下幾種情況:
1.用戶點擊取消,界面則跳轉到主界面
2.用戶點擊更新
2.1跳轉到系統的安裝頁面
2.1.1用戶點擊安裝按鈕,安裝完成后回到桌面
2.1.2用戶點擊取消按鈕,這時候我們開啟跳轉安裝頁面的方法要用startActivityForResult(intent,請求碼(用于分辨是誰開啟了安裝 頁面,一旦用戶點擊了取消,則回到前一個activity頁面,觸發onActivityResult方法,方法內判斷請求碼是否一樣,在去跳轉主界面))
示例代碼:
public class SplashActivity extends Activity {
private static final String TAG = "SplashActivity";
private static final int REQUEST_CODE = 100;
@BindView(R.id.tv_version_name) //注解,就等同于findViewById(R.id.xxx)找的過程
TextView tvVersionName; //將findViewById(R.id.xxx)找到的控件賦值給tvVersionName
private int localVersionCode;
private String desc;
private String downloadUrl;
private ProgressDialog progressDialog;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash);
    ButterKnife.bind(this);//這是第三方框架
    //獲取版本名稱方法
    String packageVersionName = getPackageVersionName();
    tvVersionName.setText(packageVersionName);
    //在應用程序上,有一個versioncode,將本地的versioncode和服務器上的versioncode做一個比對
    //本地versionCode(localVersionCode)<服務器versionCode(remoteVersionCode),則需要下載更新.

    //從sp中將存儲的是否需要更新的狀態,獲取出來 true 要檢測更新 false 不要檢測更新
    boolean updateAuto = SharePreUtil.getBooleanValue(this, Constant.UPDATE_ATUO, true);
    if(updateAuto){
        checkVersion();
    }else{
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    enterHome();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

private void checkVersion() {
    //1.獲取本地VersionCode
    localVersionCode = getPackageVersionCode();
    //2.發送網絡請求,獲取服務器的versionCode,okhttp加jar包 okhttp和okio這2個jar包
    //通過分析,服務器必須提供,versionCode,downloadUrl,新版本的描述,新版本的版本名稱,json
    //2.1 創建一個客戶端,此客戶端可以給服務器發請求
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .readTimeout(20, TimeUnit.SECONDS)
            .connectTimeout(20,TimeUnit.SECONDS).
            build();
    //2.2 請求對象創建
    String url = "http://10.0.2.2:8080/update.json";
    Request request = new Request.Builder()
            .get()
            .url(url)
            .build();
    //2.3 通過客戶端把請求發出去
    Call call = okHttpClient.newCall(request);
    //2.4 處理請求的結果
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            //請求失敗

            enterHome();
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            //請求有結果,成功
            //子線程中觸發onResponse方法,不能夠操作UI
            //獲取服務器中的json里面的數據
            ResponseBody body = response.body();
            String json = body.string();
            //手動解析,gson效率高
            try {
                JSONObject jsonObject = new JSONObject(json);

                desc = jsonObject.getString("desc");
                downloadUrl = jsonObject.getString("downloadUrl");
                int remoteVersionCode = jsonObject.getInt("versionCode");

                //服務器和本地的versionCode進行比對
                if (remoteVersionCode>localVersionCode){
                    //服務器有新版本,可供下載
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            showDialog();
                        }
                    });
                }else{
                    //服務器沒有更新的版本,可以直接進入應用程序的后一個界面
                    try {
                        Thread.sleep(3000);
                        enterHome();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } catch (JSONException e) {
                e.printStackTrace();
                enterHome();
            }
        }
    });
}

private void showDialog() {

    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("是否下載新版本?");
    builder.setMessage(desc);//設置對話框除標題外的描述內容
    builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
            //下載apk,下載路徑downloadUrl,考慮在何處存儲 files cache() sd卡
            downloadApk();
        }
    });
    builder.setNegativeButton("稍后再說", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
            //跳轉到應用程序的后一個界面
            enterHome();
        }
    });

    //如果用戶點擊回退按鈕,隱藏了對話框,則可以被如下方法監聽
    builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
        @Override
        public void onCancel(DialogInterface dialog) {
            enterHome();
        }
    });
    builder.show();
}

private void downloadApk() {
    //下載apk,并且指定放置下載文件的路徑,sd卡
    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
        //創建一個進度條對話框,用于顯示下載進度
        progressDialog = new ProgressDialog(this);
        //默認情況下對話框進度條圓形轉圈的
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.show();

        //手機的sd卡可用
        //sd卡存儲文件的路徑
        final String path = Environment.getExternalStorageDirectory().getAbsolutePath()
                +File.separator+"mobilesafe.apk";
        //如何根據downloadUrl進行下載,okhttp下載
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .build();
        Request request = new Request.Builder()
                .get()
                .url(downloadUrl)
                .build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //請求失敗
                enterHome();
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //請求成功,從服務器的響應對象中獲取apk,流(服務器 輸入流(提供數據)  本地 輸出流(寫入文件))
                ResponseBody body = response.body();
                //告知progressDialog總進度,不變
                progressDialog.setMax((int) body.contentLength());

                //inputStream就是服務器把需要下載的apk以流的形式提供給客戶端
                InputStream inputStream = body.byteStream();
                File file = new File(path);
                FileOutputStream fos = new FileOutputStream(file);

                byte[] bytes = new byte[1024];
                int len = 0;
                int temp = 0;//用于記錄目前下載的到的位置
                while((len = inputStream.read(bytes))!=-1){
                    //將讀過的數據寫入文件中
                    fos.write(bytes,0,len);
                    //告知progressDialog在下載過程中的當前進度
                    temp += len;
                    //將當前的下載位置,設置給progressDialog
                    progressDialog.setProgress(temp);
                    //沒下載一段數據,睡眠一段時間
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //一旦循環結束,則sd卡中就有一個從服務器下載下來的apk
                //下載完成后,則隱藏對話框
                progressDialog.dismiss();
                //安裝apk,這個要裝的apk在哪里
                installApk(file);
            }
        });
    }
}

/**
 * 安裝指定路徑下的apk
 * @param file 需要安裝文件的路徑
 */
private void installApk(File file) {
    //找到系統的安裝界面,把安裝過程中要用到的東西傳遞進去,讓系統幫助我們安裝.
    /*<intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="content" />
        <data android:scheme="file" />
        <data android:mimeType="application/vnd.android.package-archive" />
    </intent-filter>*/
    Intent intent = new Intent("android.intent.action.VIEW");
    intent.addCategory("android.intent.category.DEFAULT");
    intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
    //通過隱式意圖開啟系統的安裝apk界面
    startActivityForResult(intent,REQUEST_CODE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    //在系統的安裝界面結束后,則會調用此方法,在此方法中需要開啟后續的界面,確保用戶能夠使用低版本的apk
    if (requestCode == REQUEST_CODE){
        enterHome();
    }
    super.onActivityResult(requestCode, resultCode, data);
}

private void enterHome() {
    Intent intent = new Intent(this, HomeActivity.class);
    startActivity(intent);
    finish();
}

private String getPackageVersionName() {
    //1.PackageManager 包的管理者對象
    PackageManager pm = getPackageManager();
    //2.獲取應用的配置信息,在此處傳遞0獲取的是基本信息(包名,版本名稱,版本號)
    try {
        PackageInfo packageInfo = pm.getPackageInfo(getPackageName(),0);
        String versionName = packageInfo.versionName;
        return versionName;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    return "";
}

private int getPackageVersionCode() {
    //1.PackageManager 包的管理者對象
    PackageManager pm = getPackageManager();
    //2.獲取應用的配置信息,在此處傳遞0獲取的是基本信息(包名,版本名稱,版本號)
    try {
        PackageInfo packageInfo = pm.getPackageInfo(getPackageName(),0);
        int versionCode = packageInfo.versionCode;
        return versionCode;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    return 0;
}
//給tvVersionName注冊上了點擊事件
@OnClick(R.id.tv_version_name)
public void onViewClicked() {
    //此方法中的代碼,就等同于onClick中的代碼
}

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,709評論 25 708
  • ¥開啟¥ 【iAPP實現進入界面執行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,489評論 0 17
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,818評論 18 139
  • (一) 看了一篇蔡瀾的《湖南湖北之旅》,主要寫他去長沙、武漢簽售的經歷。長沙沒有去過,只是一個要好的朋友在湖南大學...
    小月半腳閱讀 761評論 8 9
  • 小城不大,四面環山,一條街道,貫通東西,一汪湖水,連接南北,一個小時的腳程,足足可以逛遍全城。 小城清凈。沒有車水...
    9b4e79d93ff4閱讀 373評論 1 5