android检查更新app是一个比较常见的功能。但是功能也比较单一。也是一块可以复用的代码。所以我这里想把自己检查更新的代码写下来。如果想使用直接移植过去就好。也不用重复开发了。
整个检查更新的大致过程
1.需要用户触发检查更新,所以界面里做个btn
2.用户点击btn后触发事件,弹出一个dialog或者toast提示用户是否是最新版本,需不需要更新
3.btn点击触发后,我们不能直接在UI层里做检查更新的操作。所以检查更新的逻辑就要开线程。这里的代码一想就是重点了吧。所以封装出来让btn onClick事件调用比较好。
4.检查更新的逻辑一般都是这样的:
第一,检查。
客户端内持有一个检查更新的url.这个url存在的目的就是为了检查是不是最新版本。一般这个url从服务端传来的内容包括:apk下载url、最新的apk版本、apk包名、apk更新信息。哈哈。是不是特别熟悉?传来的东西我们需要解析啦!那么,就要和后端的小伙伴商量定接口了吧?这里直接意淫一下,我们的接口定好了。服务器就用json提供上面说的四块儿内容。我们从url取出来就是json格式的数据了。拿回来以后直接json解析,解析完了就封装到本地的封装对象供我们使用。
既然是检查,那你拿回来最新版本的信息是不是跟本地有对比才有新旧之分?顺理成章。我们需要拿到本地的版本信息。上网查查有没有这么一个类啊?哎呦喂,果然有!就是PackageManager。好,通过它。我们有了本地的版本信息啦!下面就是一个核心而又简单的逻辑了。对比版本,if 后台版本>本地版本,那么下载后台版本(这里还不是更新呢)。else 告诉用户已经是最新版本。
第二,下载。
这一块想一想跟检查貌似以两块代码。嗯,我分开写,方便理思路。利用上面检查给的apk下载url下载到本地文件夹。下载的过程有时候需要给用户展示。所以这里用handler在ui展现一下下载过程。
第三,更新。
下载好了,剩下更新了。
MyApplication.java
这里写一个MyApplication类继承自Application,在里面获取一个全局的context,从此不再为拿不到context心烦了。
public class MyApplication extends Application {
public static Context mContext;
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
}
manifest.xml
versionCode versionName要写出来。是用来比较版本的。这里选用versionCode比较。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zharma.updatetest"
android:versionCode="1"
android:versionName="1.0.0">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
布局
首先说一下最直观的,apk更新最多出现在的就是在设置之类的界面中。这些界面的布局细节就不说了。这里为了以后复用简便也不做冗余的嵌套。直接就是一个btn。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.zharma.updatetest.MainActivity">
<Button
android:id="@+id/btnDownload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="检查更新"/>
</RelativeLayout>
btn点击事件
public class MainActivity extends Activity {
private Button btnDownload;
private View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnDownload:
//在这里做检查业务逻辑
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnDownload = (Button) findViewById(R.id.btnDownload);
btnDownload.setOnClickListener(listener);
}
}
好了。界面搭的简陋一点能跑就行。
检查、下载、更新
检查是一个url请求,然后返回后台提供的版本json数据,用gson解析、封装到封装对象中。获取本地版本号(我们用versionCode比较版本)对比处理。
这个过程我就一起写下来。
封装类:
UpdateInfo
public class UpdateInfo {
public int versionCode;
public String versionName;
public String url;
public String description;
}
/**
* Created by dahe on 16/8/21.
*/
public class CheckVersion implements Runnable{
//VERSIONINFO_URL是访问服务器拿到查询更新json数据的url,一般这个是在单独的一个数据类中写的。那样的话让CheckVersion继承自那个类。拿到url来用。现在这里设为空是为了方便看,还有以后更改url。
private static final String VERSIONINFO_URL = "我现在是空的,这个要自己填自己的查询url";
private static final int HAVE_NEW_VERSION = 0;
private static final int ALREADY_NEW_VERSION = 1;
private Context context = MyApplication.mContext;
private UpdateInfo updateInfo;
//获取到主线程的looper,对UI操作
private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case HAVE_NEW_VERSION:
openUpdateDialog();
break;
case ALREADY_NEW_VERSION:
Toast.makeText(context, "已经是最新版本", Toast.LENGTH_LONG).show();
break;
}
}
};
private void openUpdateDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("版本有更新");
builder.setMessage(updateInfo.description);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
downloadNewVersion();
}
});
builder.setNegativeButton("取消", null);
}
private void downloadNewVersion() {
final ProgressDialog pd = new ProgressDialog(context);
pd.setTitle("更新进度");
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.show();
new Thread(new Runnable() {
InputStream is;
BufferedInputStream bis;
FileOutputStream fos;
@Override
public void run() {
//这里完成下载
try {
URL downloadUrl = new URL(updateInfo.url);
HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
int maxlength = connection.getContentLength();
pd.setMax(maxlength);
is = connection.getInputStream();
bis = new BufferedInputStream(is);
File file = new File(Environment.getExternalStorageDirectory(), "newversion.apk");
fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len;
int loaded = 0;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
loaded += len;
pd.setProgress(loaded);
}
installApk(file);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
bis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
private void installApk(File file) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
context.startActivity(intent);
}
@Override
public void run() {
try {
String s = VERSIONINFO_URL;
URL url = new URL(s);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
InputStream is = connection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
Gson gson = new Gson();
updateInfo = gson.fromJson(br, UpdateInfo.class);
//后台版本
int serverVersionCode = updateInfo.versionCode;
//本地版本获取
int localVersionCode = getLocalVersionCode();
if (serverVersionCode > localVersionCode) {
//后台版本新!所以弹框提醒用户有新版本,让用户操作dialog更新
Message msg = new Message();
msg.what = HAVE_NEW_VERSION;
mHandler.sendMessage(msg);
} else {
//后台没有新版本,所以在界面反馈用户不用更新
Message msg = new Message();
msg.what = ALREADY_NEW_VERSION;
mHandler.sendMessage(msg);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private int getLocalVersionCode() throws PackageManager.NameNotFoundException {
PackageManager pm = context.getPackageManager();
PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);
int versionCode = info.versionCode;
return versionCode;
}
}
好了,主要逻辑写完了那么就可以在btn中开线程做查询、下载、更新的事情了。
public class MainActivity extends Activity {
private Button btnDownload;
private View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnDownload:
//在这里做检查业务逻辑
CheckVersion checkVersion = new CheckVersion();
new Thread(checkVersion).start();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnDownload = (Button) findViewById(R.id.btnDownload);
btnDownload.setOnClickListener(listener);
}
}
到这里,代码片全部写完。
最后用一张图回顾一下我都做了什么: