安卓应用检查更新并下载安装

在这篇文章中(参见 android中如何下载文件并显示下载进度  )我们讲到了如何下载文件的问题,今天我介绍如何实现应用的自动更新,其中下载apk模块用到了前一篇文章中的知识。当然这只是一个实现的框架,你需要根据自己的需求是改变一些细节。

自动更新的原理

其实就是客户端将自己的版本号与服务端的版本号进行比对,版本号小于服务端则意味着有新版本,当然服务端的版本号是需要人工放上去的。

先看看效果:

为了代码的简洁,我这里用原生的alert对话框。

点击下载之后:

下载完成安装的界面我就不张贴了。


使用方法:

很简单,在需要检查更新的地方加入如下代码:

1
2
updateChecker.setCheckUrl( "http://jcodecraeer.com/update.php" );
updateChecker.checkForUpdates();

其中http://jcodecraeer.com/update.php返回的是服务器段存放的版本信息。服务端的版本信息分为三部分:

1.版本号;

2.版本描述;

3.存放apk的url(告诉客户端,在哪里下载新版本的apk);

以http://jcodecraeer.com/update.php返回的结果为例,返回的字符串具体是这样的:

1
{ "url" : "http://www.jcodecraeer.com/***.apk" , "versionCode" : "2" , "updateMessage" : "1.修改了app图标 2.设备详情的显示方式" }

这是一个json格式的字符串。


实现

有三个类:

其中

AppVersion是版本信息的模型类,基本上和服务端返回的东西是相对应的。

DownloadService是下载模块。

UpdateChecker是检查更新,调用下载模块,下载完安装的工具类。

AppVersion

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
package com.jcodecraeer.jcode.update;
public class AppVersion {
     private String updateMessage;
     private String apkUrl;
     private int apkCode;
     public static final String APK_DOWNLOAD_URL = "url" ;
     public static final String APK_UPDATE_CONTENT = "updateMessage" ;
     public static final String APK_VERSION_CODE = "versionCode" ;
                                                                                                                                           
     public void setUpdateMessage(String updateMessage) {
         this .updateMessage = updateMessage;
     }
                                                                                                                                           
     public String getUpdateMessage() {
         return updateMessage;
     }
                                                                                                                                           
     public void setApkUrl(String apkUrl) {
         this .apkUrl = apkUrl;
     }
                                                                                                                                           
     public String getApkUrl() {
         return apkUrl;
     }
                                                                                                                                           
     public void setApkCode(int apkCode) {
         this .apkCode = apkCode;
     }
                                                                                                                                           
     public int getApkCode() {
         return apkCode;
     }
}

DownloadService

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
package com.jcodecraeer.jcode.update;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import com.jcodecraeer.PullToRefreshListView;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.IntentService;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
import android.provider.ContactsContract;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.ImageColumns;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.widget.ShareActionProvider;
import android.widget.TextView;
import android.widget.Toast;
import android.view.ActionMode;
public class DownloadService extends IntentService {
     public static final int UPDATE_PROGRESS = 8344;
     public DownloadService() {
         super ( "DownloadService" );
     }
     @Override
     protected void onHandleIntent(Intent intent) {
         String urlToDownload = intent.getStringExtra( "url" );
         String fileDestination = intent.getStringExtra( "dest" );
         ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra( "receiver" );
         try {
             URL url = new URL(urlToDownload);
             URLConnection connection = url.openConnection();
             connection.connect();
             // this will be useful so that you can show a typical 0-100% progress bar
             int fileLength = connection.getContentLength();
             // download the file
             InputStream input = new BufferedInputStream(connection.getInputStream());
             OutputStream output = new FileOutputStream(fileDestination);
             byte data[] = new byte[100];
             long total = 0;
             int count;
             while ((count = input.read(data)) != -1) {
                 total += count;
                 // publishing the progress....
                 Bundle resultData = new Bundle();
                 resultData.putInt( "progress" ,(int) (total * 100 / fileLength));
                 receiver.send(UPDATE_PROGRESS, resultData);
                 output.write(data, 0, count);
             }
             output.flush();
             output.close();
             input.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         Bundle resultData = new Bundle();
         resultData.putInt( "progress" ,100);
         receiver.send(UPDATE_PROGRESS, resultData);
     }
}


UpdateChecker


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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
package com.jcodecraeer.jcode.update;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.zip.GZIPInputStream;
import org.json.JSONException;
import org.json.JSONObject;
import com.jcodecraeer.jcode.Code;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
import android.util.Log;
import android.widget.Toast;
public class UpdateChecker{
     private static final String TAG = "UpdateChecker" ;
     private Context mContext;
     //检查版本信息的线程
     private Thread mThread;
     //版本对比地址
     private String mCheckUrl;
     private AppVersion mAppVersion;
     //下载apk的对话框
     private ProgressDialog mProgressDialog;
                                                                                                                  
     private File apkFile;
     public void setCheckUrl(String url) {
         mCheckUrl = url;
     }
                                                                                                                  
     public UpdateChecker(Context context) {
         mContext = context;
         // instantiate it within the onCreate method
         mProgressDialog = new ProgressDialog(context);
         mProgressDialog.setMessage( "正在下载" );
         mProgressDialog.setIndeterminate( false );
         mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
         mProgressDialog.setCancelable( true );
         mProgressDialog.setOnCancelListener( new DialogInterface.OnCancelListener() {
             @Override
             public void onCancel(DialogInterface dialog) {
                                                                                                                              
             }
         });
         mProgressDialog.setOnDismissListener( new DialogInterface.OnDismissListener() {
             @Override
             public void onDismiss(DialogInterface dialog) {
                 // TODO Auto-generated method stub
                                                                                                                              
             }
         });
     }
                                                                                                                  
     public void checkForUpdates() {
         if (mCheckUrl == null ) {
             //throw new Exception("checkUrl can not be null");
             return ;
         }
         final  Handler handler = new Handler(){
             public void handleMessage(Message msg) {    
                 if (msg.what == 1) {
                     mAppVersion = (AppVersion) msg.obj;
                     try {
                         int versionCode = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionCode;
                         if (mAppVersion.getApkCode() > versionCode) {
                                 showUpdateDialog();
                         } else {
                             //Toast.makeText(mContext, "已经是最新版本", Toast.LENGTH_SHORT).show();
                         }
                     } catch (PackageManager.NameNotFoundException ignored) {
                         //
                     }
                 }
             }
         };
                                                                                                                      
         mThread = new Thread() {
             @Override
             public void run() {
                 //if (isNetworkAvailable(mContext)) {
                 Message msg = new Message();
                 String json = sendPost();
                 Log.i( "jianghejie" , "json = " +json);
                 if (json!= null ){
                     AppVersion appVersion = parseJson(json);
                     msg.what = 1;
                     msg.obj = appVersion;
                     handler.sendMessage(msg);
                 } else {
                     Log.e(TAG, "can't get app update json" );
                 }
             }
         };
         mThread.start();
     }
     protected String sendPost() {
         HttpURLConnection uRLConnection = null ;
         InputStream is = null ;
         BufferedReader buffer = null ;
         String result = null ;
         try {
             URL url = new URL(mCheckUrl);
             uRLConnection = (HttpURLConnection) url.openConnection();
             uRLConnection.setDoInput( true );
             uRLConnection.setDoOutput( true );
             uRLConnection.setRequestMethod( "POST" );
             uRLConnection.setUseCaches( false );
             uRLConnection.setConnectTimeout(10 * 1000);
             uRLConnection.setReadTimeout(10 * 1000);
             uRLConnection.setInstanceFollowRedirects( false );
             uRLConnection.setRequestProperty( "Connection" , "Keep-Alive" );
             uRLConnection.setRequestProperty( "Charset" , "UTF-8" );
             uRLConnection.setRequestProperty( "Accept-Encoding" , "gzip, deflate" );
             uRLConnection.setRequestProperty( "Content-Type" , "application/json" );
             uRLConnection.connect();
             is = uRLConnection.getInputStream();
             String content_encode = uRLConnection.getContentEncoding();
             if ( null != content_encode && ! "" .equals(content_encode) && content_encode.equals( "gzip" )) {
                 is = new GZIPInputStream(is);
             }
             buffer = new BufferedReader( new InputStreamReader(is));
             StringBuilder strBuilder = new StringBuilder();
             String line;
             while ((line = buffer.readLine()) != null ) {
                 strBuilder.append(line);
             }
             result = strBuilder.toString();
         } catch (Exception e) {
             Log.e(TAG, "http post error" , e);
         } finally {
             if (buffer!= null ){
                 try {
                     buffer.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
             if (is!= null ){
                 try {
                     is.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
             if (uRLConnection!= null ){
                 uRLConnection.disconnect();
             }
         }
         return result;
     }
     private AppVersion parseJson(String json) {
         AppVersion appVersion = new AppVersion();
         try {
             JSONObject obj = new JSONObject(json);
             String updateMessage = obj.getString(AppVersion.APK_UPDATE_CONTENT);
             String apkUrl = obj.getString(AppVersion.APK_DOWNLOAD_URL);
             int apkCode = obj.getInt(AppVersion.APK_VERSION_CODE);
             appVersion.setApkCode(apkCode);
             appVersion.setApkUrl(apkUrl);
             appVersion.setUpdateMessage(updateMessage);
         } catch (JSONException e) {
             Log.e(TAG, "parse json error" , e);
         }
         return appVersion;
     }
                                                                                                                  
     public void showUpdateDialog() {
         AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
         //builder.setIcon(R.drawable.icon);
         builder.setTitle( "有新版本" );
         builder.setMessage(mAppVersion.getUpdateMessage());
         builder.setPositiveButton( "下载" ,
             new DialogInterface.OnClickListener() {
                 public void onClick(DialogInterface dialog, int whichButton) {
                     downLoadApk();
                 }
         });
         builder.setNegativeButton( "忽略" ,
             new DialogInterface.OnClickListener() {
                 public void onClick(DialogInterface dialog, int whichButton) {
                                                                                                                                 
                 }
         });
         builder.show();     
     }
                                                                                                                  
     public void downLoadApk() {
         String apkUrl = mAppVersion.getApkUrl();
         String dir = mContext.getExternalFilesDir( "apk" ).getAbsolutePath();
         File folder = Environment.getExternalStoragePublicDirectory(dir);
         if (folder.exists() && folder.isDirectory()) {
              //do nothing
         } else {
             folder.mkdirs();
         }
         String filename = apkUrl.substring(apkUrl.lastIndexOf( "/" ),apkUrl.length());
         String destinationFilePath =  dir + "/" + filename;
         apkFile = new File(destinationFilePath);
         mProgressDialog.show();
         Intent intent = new Intent(mContext, DownloadService.class);
         intent.putExtra( "url" , apkUrl);
         intent.putExtra( "dest" , destinationFilePath);
         intent.putExtra( "receiver" , new DownloadReceiver( new Handler()));
         mContext.startService(intent);
     }
                                                                                                                  
     private class DownloadReceiver extends ResultReceiver{
         public DownloadReceiver(Handler handler) {
             super (handler);
         }
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             super .onReceiveResult(resultCode, resultData);
             if (resultCode == DownloadService.UPDATE_PROGRESS) {
                 int progress = resultData.getInt( "progress" );
                 mProgressDialog.setProgress(progress);
                 if (progress == 100) {
                     mProgressDialog.dismiss();
                     //如果没有设置SDCard写权限,或者没有sdcard,apk文件保存在内存中,需要授予权限才能安装
                     String[] command = { "chmod" , "777" ,apkFile.toString()};
                     try {
                         ProcessBuilder builder = new ProcessBuilder(command);
                         builder.start();
                         Intent intent = new Intent(Intent.ACTION_VIEW);
                         intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive" );
                         mContext.startActivity(intent);
                     } catch (Exception e){
                                                                                                                                      
                     }
                 }
             }
         }
     }
}

所有代码都已经贴出来了。

这个代码是完全可以用的,但是有些细节问题需要改进。 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值