APK强制更新Demo,下载利用系统自带下载器实现

原理:

  1. 是否强制更新可以通过Dialog.setCancelable(boolean flag)来控制

  2. 下载这部分系统下载器功能已经足够强大,这里使用了trinea大神封装过后的DownloadManagerPro,具体介绍可以点击这里

MainActivity.java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

package info.enjoycode.appupdatedemo;

import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;

/**
* 应用强制更新和非强制更新demo
* http://www.enjoycode.info
*
* @author ChenYuanming
*/
public class MainActivity extends Activity implements OnClickListener {

String downurl = "http://gdown.baidu.com/data/wisegame/2c6a60c5cb96c593/QQ_182.apk";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
$(R.id.btnNormalUpdate).setOnClickListener(this);
$(R.id.btnForceUpdate).setOnClickListener(this);
}


public <t extends View> T $(int viewId) {
return (T) findViewById(viewId);
}

public </t><t extends View> T $(View view, int viewId) {
return (T) view.findViewById(viewId);
}

/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnNormalUpdate:
showUpdateDialog(false, getString(R.string.common_dialog_cancel), new AppVersionInfo(downurl, getString(R.string.normal_update)));
break;
case R.id.btnForceUpdate:
showUpdateDialog(true, getString(R.string.exit), new AppVersionInfo(downurl, getString(R.string.force_update)));

break;
}
}

private void showUpdateDialog(final boolean enforce, String strNegativeButton, final AppVersionInfo ver) {
Builder alert = new Builder(MainActivity.this);
alert.setCancelable(!enforce);
alert.setTitle(getString(R.string.update_software)).setMessage(ver.getUpdateinfo().replace(";", "\n"))
.setPositiveButton(getString(R.string.update), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Intent updateIntent = new Intent(MainActivity.this, UpdateService.class);
updateIntent.putExtra("appurl", ver.getDownurl());
updateIntent.putExtra("enforce", enforce);
startService(updateIntent);
}
}).setNegativeButton(strNegativeButton, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if (enforce) {
finish();
}
}
});
if (!isFinishing())
alert.create().show();
}

class AppVersionInfo {
String downurl, updateinfo;

AppVersionInfo(String downurl, String updateinfo) {
this.downurl = downurl;
this.updateinfo = updateinfo;
}

public String getDownurl() {
return downurl;
}

public String getUpdateinfo() {
return updateinfo;
}
}
}

UpdateService.java 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package info.enjoycode.appupdatedemo;

import java.io.File;

import android.annotation.TargetApi;
import android.app.DownloadManager;
import android.app.ProgressDialog;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.view.WindowManager;
import android.widget.Toast;

/**
* 更新
* http://www.enjoycode.info
* @author ChenYuanming
*/

public class UpdateService extends Service {
/**
* 下载地址
*/

private String down_url;
/**
* 是否强制更新,对应进度框能否取消
*/

private boolean enforce;
/**
* 系统DownloadManager下载过程中分配的id
*/

long downloadId;
// 声明进度条对话框
ProgressDialog progressDialog;
/**
* 下载用工具类:http://www.trinea.cn/android/android-downloadmanager/
*/

DownloadManagerPro downloadManagerPro;
/**
* SD卡存储目录
*/

public static final String DOWNLOAD_FOLDER_NAME = "Download";
/**
* SD卡存储文件名
*/

public static final String DOWNLOAD_FILE_NAME = "QQ.apk";

/**
* 应用保存完整路径
*/

String apkFilePath;
/**
* handler用更新状态
*/

public static final int UPDATE_STATUS = 0;

Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case UPDATE_STATUS:
int downloadInfos[] = downloadManagerPro.getDownloadBytes(downloadId);
if (downloadInfos[0] < downloadInfos[1]) {
if (enforce) {
progressDialog.setMessage(downloadInfos[0] + "/" + downloadInfos[1]);
progressDialog.setProgress((int) (downloadInfos[0] * 100 / downloadInfos[1]));
}
} else if (downloadInfos[0] > downloadInfos[1]) {
if (enforce) {
progressDialog.setMessage("初始化下载中");
}
} else {
if (enforce) {
progressDialog.setMessage(downloadInfos[1] + "/" + downloadInfos[1]);
progressDialog.setProgress(100);
progressDialog.cancel();
}
Toast.makeText(UpdateService.this, "下载完成", Toast.LENGTH_LONG).show();
install(getBaseContext(), apkFilePath);
}

if (downloadInfos[0] != downloadInfos[1]) {
//只要没下载完,每秒更新进度
sendEmptyMessageDelayed(UPDATE_STATUS, 1000);
}
break;
}
}
};


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


@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
enforce = intent.getBooleanExtra("enforce", false);
down_url = intent.getStringExtra("appurl");
if (isSdCardExist()) {
if (enforce) {
initProgressDialog();
} else {
Toast.makeText(UpdateService.this, "应用后台更新中", Toast.LENGTH_LONG).show();
}
File folder = Environment.getExternalStoragePublicDirectory(DOWNLOAD_FOLDER_NAME);
if (!folder.exists()) {
folder.mkdirs();

}
apkFilePath = folder.getAbsolutePath() + File.separator + DOWNLOAD_FILE_NAME;
/**
* 如果对下载参数不了解,或者是理解上有问题 详情请见原文链接:http://www.trinea.cn/android/android-downloadmanager/
*/

DownloadManager.Request request = new DownloadManager.Request(Uri.parse(down_url));
request.setDestinationInExternalPublicDir(DOWNLOAD_FOLDER_NAME, DOWNLOAD_FILE_NAME);//存储位置、目录、文件名
request.setTitle(getString(R.string.download_notification_title));//通知栏标题
request.setDescription(getString(R.string.download_notification_description));//通知栏描述
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);//notify的显示形式,可以全部隐藏,可以下载时显示,可以下载完显示,可以下载时和下载完都显示
request.setVisibleInDownloadsUi(true);//是否显示当前下载 在系统的下载界面上
// request.allowScanningByMediaScanner();//允许媒体抓取
// request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);//下载网络形式限定
//request.setShowRunningNotification(false);//废弃的方法
request.setAllowedOverRoaming(true);//移动网络情况下是否允许漫游
downloadManagerPro = new DownloadManagerPro((DownloadManager) getSystemService(DOWNLOAD_SERVICE));
downloadId = downloadManagerPro.getDownloadId(request);
handler.sendEmptyMessage(UPDATE_STATUS);
} else {
Toast.makeText(getApplicationContext(), getApplication().getString(R.string.no_sd), Toast.LENGTH_LONG).show();
}
}
return super.onStartCommand(intent, flags, startId);
}


private void initProgressDialog() {
// 创建ProgressDialog对象
progressDialog = new ProgressDialog(UpdateService.this);
// 设置进度条风格,风格为圆形,旋转的
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
// 设置ProgressDialog 标题
progressDialog.setTitle(getApplicationContext().getString(R.string.common_dialog_title));
// 设置ProgressDialog提示信息
progressDialog.setMessage(getApplicationContext().getString(R.string.software_updating) + "···");
// 设置ProgressDialog标题图标
// progressDialog.setIcon(R.drawable.ic_launcher);
// 设置ProgressDialog 的进度条是否不明确 false 就是不设置为不明确
progressDialog.setIndeterminate(false);
// 设置ProgressDialog 进度条进度
progressDialog.setProgress(100);
// 设置ProgressDialog 是否可以按退回键取消
progressDialog.setCancelable(!enforce);
progressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
// 让ProgressDialog显示
progressDialog.show();
}

/**
* install app
*
* @param context 上下文环境
* @param filePath 文件路径
* @return whether apk exist
*/

public static boolean install(Context context, String filePath) {
Intent i = new Intent(Intent.ACTION_VIEW);
File file = new File(filePath);
if (file.length() > 0 && file.exists() && file.isFile()) {
i.setDataAndType(Uri.parse("file://" + filePath), "application/vnd.android.package-archive");
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
return true;
}
return false;
}

/**
* 判断sd卡是否存在
*
* @return boolean
*/

private boolean isSdCardExist() {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return true;
} else
return false;
}


@Override
public void onDestroy() {
super.onDestroy();
downloadManagerPro.remove(downloadId);
}
}

DownloadManagerPro.java 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package info.enjoycode.appupdatedemo;

import android.annotation.TargetApi;
import android.app.DownloadManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;

/**
* 下载进度查询
* create by trinea
*/

@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public class DownloadManagerPro {

public static final Uri CONTENT_URI = Uri.parse("content://downloads/my_downloads");
/**
* represents downloaded file above api 11 *
*/

public static final String COLUMN_LOCAL_FILENAME = "local_filename";
/**
* represents downloaded file below api 11 *
*/

public static final String COLUMN_LOCAL_URI = "local_uri";

private DownloadManager downloadManager;

public DownloadManagerPro(DownloadManager downloadManager) {
this.downloadManager = downloadManager;
}
public long getDownloadId(DownloadManager.Request request){
return downloadManager.enqueue(request);
}
/**
* get download status
*
* @param downloadId long
* @return 返回下载状态
*/

public int getStatusById(long downloadId) {
return getInt(downloadId, DownloadManager.COLUMN_STATUS);
}

/**
* 删除某个下载任务
* @param ids long
* @return the number of downloads actually removed
*/

public int remove(long... ids){
return downloadManager.remove(ids);
}

/**
* get downloaded byte, total byte
*
* @param downloadId long
* @return a int array with two elements
* <ul>
* <li>result[0] represents downloaded bytes, This will initially be -1.</li>
* <li>result[1] represents total bytes, This will initially be -1.</li>
* </ul>
*/

public int[] getDownloadBytes(long downloadId) {
int[] bytesAndStatus = getBytesAndStatus(downloadId);
return new int[]{bytesAndStatus[0], bytesAndStatus[1]};
}

/**
* get download status {@link android.app.DownloadManager}
* @param downloadId ID(long)
* @return 返回下载状态
*/

public int getDownloadStatus(long downloadId) {
int[] bytesAndStatus = getBytesAndStatus(downloadId);
return bytesAndStatus[2];
}

/**
* get downloaded byte, total byte and download status
*
* @param downloadId long
* @return a int array with three elements
* <ul>
* <li>result[0] represents downloaded bytes, This will initially be -1.</li>
* <li>result[1] represents total bytes, This will initially be -1.</li>
* <li>result[2] represents download status, This will initially be 0.</li>
* </ul>
*/

public int[] getBytesAndStatus(long downloadId) {
int[] bytesAndStatus = new int[]{-1, -1, 0};
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor c = null;
try {
c = downloadManager.query(query);
if (c != null && c.moveToFirst()) {
bytesAndStatus[0] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
bytesAndStatus[1] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
bytesAndStatus[2] = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
}
} finally {
if (c != null) {
c.close();
}
}
return bytesAndStatus;
}



/**
* get download file name
*
* @param downloadId 文件id
* @return 文件名(应该是包含文件名的)
*/

public String getFileName(long downloadId) {
return getString(downloadId, (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ? COLUMN_LOCAL_URI
: COLUMN_LOCAL_FILENAME));
}

/**
* get download uri
*
* @param downloadId 文件id
* @return get uri maybe startwith("content:")
*/

public String getUri(long downloadId) {
return getString(downloadId, DownloadManager.COLUMN_URI);
}
/**
* get failed code or paused reason
*
* @param downloadId 文件id
* @return <ul>
* <li>if status of downloadId is {@link android.app.DownloadManager#STATUS_PAUSED}, return
* {@link #getPausedReason(long)}</li>
* <li>if status of downloadId is {@link android.app.DownloadManager#STATUS_FAILED}, return {@link #getErrorCode(long)}</li>
* <li>if status of downloadId is neither {@link android.app.DownloadManager#STATUS_PAUSED} nor
* {@link android.app.DownloadManager#STATUS_FAILED}, return 0</li>
*
*/

public int getReason(long downloadId) {
return getInt(downloadId, DownloadManager.COLUMN_REASON);
}

/**
* get paused reason
* @param downloadId 文件id
* @return <ul>
* <li>if status of downloadId is {@link android.app.DownloadManager#STATUS_PAUSED}, return one of
* {@link android.app.DownloadManager#PAUSED_WAITING_TO_RETRY}<br />
* {@link android.app.DownloadManager#PAUSED_WAITING_FOR_NETWORK}<br />
* {@link android.app.DownloadManager#PAUSED_QUEUED_FOR_WIFI}<br />
* {@link android.app.DownloadManager#PAUSED_UNKNOWN}</li>
* <li>else return {@link android.app.DownloadManager#PAUSED_UNKNOWN}</li>
* </ul>
*/

public int getPausedReason(long downloadId) {
return getInt(downloadId, DownloadManager.COLUMN_REASON);
}

/**
* get failed error code
*
* @param downloadId 下载文件的唯一标示
* @return one of {@link android.app.DownloadManager#*}
*/

public int getErrorCode(long downloadId) {
return getInt(downloadId, DownloadManager.COLUMN_REASON);
}



/**
* get string column
*
* @param downloadId 下载文件的唯一标示
* @param columnName 列名
* @return 查询结果
*/

private String getString(long downloadId, String columnName) {
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
String result = null;
Cursor c = null;
try {
c = downloadManager.query(query);
if (c != null && c.moveToFirst()) {
result = c.getString(c.getColumnIndex(columnName));
}
} finally {
if (c != null) {
c.close();
}
}
return result;
}

/**
* get int column
*
* @param downloadId 现在文件的唯一标示
* @param columnName 列名
* @return 查询结果
*/

private int getInt(long downloadId, String columnName) {
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
int result = -1;
Cursor c = null;
try {
c = downloadManager.query(query);
if (c != null && c.moveToFirst()) {
result = c.getInt(c.getColumnIndex(columnName));
}
} finally {
if (c != null) {
c.close();
}
}
return result;
}
}

Demo下载

完整demo下载

文章目录
  1. 1. 原理:
  2. 2. MainActivity.java代码
  3. 3. UpdateService.java 代码
  4. 4. DownloadManagerPro.java 代码
  5. 5. Demo下载