1. 程式人生 > >APK自動下載安裝

APK自動下載安裝

1.建立ProcessInterface介面用於時時更新progressbar

public interface ProcessInterface {
     /**
     * 更新UI ,用於顯示更新的進度
     *
     * @param progress 進度
     */
    void updateProgressBar(int progress);

    /**
     * 設定所下載檔案的最大值
     *
     * @param max 下載檔案的最大值
     */
    void setMax(int max);

    void hideProgress();
}

2.MainActivity實現這個介面

    @Override
    public void updateProgressBar(final int progress) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                int max = mUpdateBar.getMax();
                String precentVal = (Util.div(progress, max, 2) * 100) + "";
                precentVal = precentVal.substring(0, precentVal.lastIndexOf("."));
                mProgressText.setText("程式已下載" + precentVal + "%...");
                mUpdateBar.setProgress(progress);
                if (max == progress) {
                    mUpdateLayout.setVisibility(View.GONE);
                    UpdateApkUtil.insertApk();
                }
            }
        });
    }

    @Override
    public void setMax(final int max) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mUpdateBar.setMax(max);
            }
        });
    }

    @Override
    public void hideProgress() {
        mUpdateLayout.setVisibility(View.GONE);
    }

3.ProgressBar的設計 (1)Utils類

    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

(2)ProgressBar佈局檔案

 <LinearLayout
        android:id="@+id/updateapkview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/black"
        android:clickable="true"
        android:visibility="gone">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/tv_update"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:text="程式準備下載..."
                    android:textColor="@color/white" />

                <ProgressBar
                    android:id="@+id/progressBar_update"
                    style="@style/progressBarCustomStyle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:layout_marginRight="10dp"
                    android:gravity="center"
                    android:padding="10dp"
                    android:progress="10" />
            </LinearLayout>
    </LinearLayout>

(3)Drawable檔案新增ProgressBar樣式 progress_horizontal.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip" />

            <gradient
                android:angle="270"
                android:centerColor="@color/color_f6"
                android:centerY="0.75"
                android:endColor="@color/color_f6"
                android:startColor="@color/color_f6" />
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <corners android:radius="5dip" />

                <gradient
                    android:angle="270"
                    android:centerColor="@color/projectblue"
                    android:centerY="0.75"
                    android:endColor="@color/projectblue"
                    android:startColor="@color/projectblue" />
            </shape>
        </clip>
    </item>

</layer-list>

4.這個是最重要邏輯類UpdateApkUtil ,用於更新下載

public class UpdateApkUtil {
    private static Context mContext;
    private static String savePath;
    private static String newApkUrl;

    /**
     * 下載apk執行緒
     */
    public static class DownloadApkThread extends Thread {
        public DownloadApkThread(Context ctx){
            mContext = ctx;
        }

        private boolean cancelUpdate = false;

        public void run() {
            try {
                newApkUrl= "https://***/download.apk";
                if (!Environment.getExternalStorageState().equals(
                        Environment.MEDIA_MOUNTED)) {
                    MyLogger.i("1111", "未檢測到SD卡");
                    return;
                }
                savePath = String.format("%s/Download/download.apk",
                        Environment.getExternalStorageDirectory().getAbsolutePath());
                MyLogger.i("1111", "savePath: "+savePath);
                URL url = new URL(newApkUrl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.connect();
                int length = conn.getContentLength();

                if (mContext != null) {
                        ((ProcessInterface)mContext).setMax(length);
                }
                InputStream is = conn.getInputStream();
                File file = new File(savePath);
                File dir = file.getParentFile();
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                if (file.exists()) {
                    file.delete();
                }

                FileOutputStream fos = new FileOutputStream(file);
                int count = 0;
                byte buf[] = new byte[1024];
                do {
                    int numread = is.read(buf);
                    count += numread;
                    if (numread <= 0) {
                        break;
                    }
                    fos.write(buf, 0, numread);
                    if (null != mContext) {
                            //此處登出progressBar的更新
                            ((ProcessInterface)mContext).updateProgressBar(count);
                    }
                } while (!cancelUpdate);
                fos.close();
                is.close();
            } catch (Exception e) {
                ((Activity) mContext).runOnUiThread(downRunnable);
                e.printStackTrace();
            }
        }

        public void clear(){
            mContext = null;
        }
    }


    /**
     * 下載失敗提示
     */
    public static Runnable downRunnable = new Runnable() {
        @Override
        public void run() {
            ((ProcessInterface)mContext).hideProgress();
            Toast.makeText(mContext,"網路連線失敗,暫無法更新!",Toast.LENGTH_SHORT).show();

        }
    };

    public static void insertApk() {
        try {
            File file = new File(savePath);
            if (file.exists()) {
                Intent intent = new Intent();
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.setAction(Intent.ACTION_VIEW);
                Uri data;
                // 判斷版本大於等於7.0
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    // 給目標應用一個臨時授權
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    data = FileProvider.getUriForFile(mContext, "com.tayh.transport.fileprovider", file);
                } else {
                    data = Uri.fromFile(file);
                }

                intent.setDataAndType(data, "application/vnd.android.package-archive");
                mContext.startActivity(intent);
                ((Activity) mContext).finish();
            } else {
                Toast.makeText(mContext,"apk未找到,請確認程式包是否已刪除!",Toast.LENGTH_SHORT).show();
                MyLogger.i("1111","apk未找到");
            }
        }catch (Exception E){
            E.printStackTrace();
            Toast.makeText(mContext, "自動更新失敗!",
                    Toast.LENGTH_LONG).show();
        }

    }

5.MainActivity新增開啟下載,建議在下載前增加SD卡許可權申請

 @Override
    public void requestPermissionsSuccess() {
        //許可權請求使用者已經全部允許      
        mUpdateLayout.setVisibility(View.VISIBLE);
        downloadApkThread = new UpdateApkUtil.DownloadApkThread(this);
        downloadApkThread.start();
    }

6.Mainfest檔案設定,注意:一定要增加REQUEST_INSTALL_PACKAGES這個許可權,否則有的手機可能不自動安裝

<uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

7.由於下載類是個執行緒,所以MainAcitvity結束需要關閉,否則會造成記憶體洩漏

    @Override
    protected void onDestroy() {
if(loadApkThread != null && !downloadApkThread.isInterrupted()){
            MyLogger.i(TAG,"downloadApkThread not interrupt");
            downloadApkThread.interrupt();
            downloadApkThread.clear();
            downloadApkThread = null;
        }
        super.onDestroy();
    }