APK自動下載安裝
阿新 • • 發佈:2018-12-21
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();
}