最近友盟因为应用商店审核遭拒等原因,十月份停止版本更新服务,所以我们的项目集成的友盟版本更新就糟了秧了...求安慰.然后就只能自己动手丰衣足食喽.
版本更新说白无非就是在一个远程仓库中存放一个apk文件然后提供一个URL,而我们需要做的无非就是把这个文件下载下来,然后覆盖手机上原来的版本,从而实现了更新.
整理一下思路,用到的技术有文件下载,存放在本地,监测是否下载完成,提示安装.另外因为dialog会引起莫名的错误,所以所有的弹框全部采用的是dialogfragment.下面几种是我用过的方法.
方法一
public void downFile(final String url) {
pBar = new ProgressDialog(getContext()); //进度条,在下载的时候实时更新进度
pBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pBar.setCancelable(false);
pBar.setTitle("正在下载");
pBar.setMessage("请稍候...");
pBar.setProgress(0);
pBar.show();
//如果相等的话表示当前的sdcard挂载在手机上并且是可用的
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
new Thread() {
public void run() {
try {
URL path = new URL(url);
HttpURLConnection conn = (HttpURLConnection) path.openConnection();
conn.setConnectTimeout(5000);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(url);
HttpResponse response;
try {
response = client.execute(get);
HttpEntity entity = response.getEntity();
int length = (int) entity.getContentLength(); //获取文件大小
pBar.setMax(length); //设置进度条的总长度
InputStream is = entity.getContent();
FileOutputStream fileOutputStream = null;
if (is != null) {
File file = new File(
Environment.getExternalStorageDirectory(),
"huanshoulv.apk");
fileOutputStream = new FileOutputStream(file);
byte[] buf = new byte[1024]; //缓冲区,即一次读取1024个比特
int ch = -1;
int process = 0;
while ((ch = is.read(buf)) != -1) {
fileOutputStream.write(buf, 0, ch);
process += ch;
pBar.setProgress(process); //更新进度
}
}
fileOutputStream.flush();
if (fileOutputStream != null) {
fileOutputStream.close();
}
update();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} else {
return;
}
}
//安装文件
void update() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(new File(Environment
.getExternalStorageDirectory(), "huanshoulv.apk")),
"application/vnd.android.package-archive");
PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentTitle("下载完成").setContentText("点击安装").setContentIntent(pendingIntent);
nf = builder.build();
nm.notify(0, nf);
startActivity(intent);
android.os.Process.killProcess(android.os.Process.myPid());
}
方法二
public class MyDownloadAnsy extends AsyncTask<String, Integer, Integer> {
@Override
protected void onPreExecute() {
super.onPreExecute();
builder = new NotificationCompat.Builder(getContext()).setSmallIcon(R.mipmap.icon_wx).setContentInfo("下载中...").setContentTitle("正在下载");
nf = builder.build();
}
@Override
protected Integer doInBackground(String... params) {
HttpURLConnection con = null;
InputStream is = null;
OutputStream os = null;
try {
String path = params[0];
MyLog.e("path : " + path);
URL url = new URL(path);
con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5 * 1000); //设置超时时间
if (con.getResponseCode() == 200) { //判断是否连接成功
int fileLength = con.getContentLength();
is = con.getInputStream(); //获取输入
os = new FileOutputStream("/sdcard/huanshoulv.apk");
byte[] buffer = new byte[1024 * 1024 * 10];
long total = 0;
int count;
int pro1 = 0;
int pro2 = 0;
while ((count = is.read(buffer)) != -1) {
total += count;
if (fileLength > 0)
pro1 = (int) (total * 100 / fileLength); //传递进度(注意顺序)
if (pro1 != pro2)
publishProgress(pro2 = pro1);
os.write(buffer, 0, count);
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (con != null) {
con.disconnect();
}
}
return 1;
}
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
if (result == 1) {
MessagePop.ToastMessage(getContext(), "下载完成");
}
}
@Override
protected void onProgressUpdate(Integer... values) {
Log.d("===", "" + values[0]);
super.onProgressUpdate(values);
builder.setProgress(100, values[0], false);
nf = builder.build();
nm.notify(0, nf);
if (values[0] == 100) { //下载完成后点击安装
Intent it = new Intent(Intent.ACTION_VIEW);
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
it.setDataAndType(Uri.parse("file:///sdcard/huanshoulv.apk"), "application/vnd.android.package-archive");
PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, it, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentTitle("下载完成").setContentText("点击安装").setContentIntent(pendingIntent);
nf = builder.build();
nm.notify(0, nf);
}
}
}
“`
方法三
上面的两种方式就是最简单文件下载然后监听安装,不过不同手机或许会出现不同的问题,还好谷歌给我们支持了,就是Android系统的DownloadManager ,个人觉得比较方便实用,它可以检测下载速度,显示下载进度,文件大小,断点下载等强大功能,下载过程中他会自动检测网络状态,网络断开时停止下载,连接上时自动接着下载,还可以设置成只有在wifi下面才进行下载,代码里面关键代码都写了注释,比较简单。
private static DownloadManager manager;
private static long downloadId;
private static String DOWNLOAD_FILE_NAME = "update.apk";
public static void versionUpdate(Context context, String url) {
manager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_RUNNING);//正在下载
Cursor c = manager.query(query);
if (c.moveToNext()) {
//正在下载中,不得重新下载
MessagePop.ToastMessage(context, "正在下载中,不得重新下载!");
} else {
//创建下载请求
DownloadManager.Request down = new DownloadManager.Request(Uri.parse(url));
//设置允许使用的网络类型,这里是移动网络和wifi都可以
down.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
//显示在下载界面,即下载后的文件在系统下载管理里显示
down.setVisibleInDownloadsUi(true);
//设置下载标题
down.setTitle("版本更新");
down.setDescription("APP");
//显示Notification
down.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
down.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
//设置下载后文件存放的位置
down.setDestinationInExternalFilesDir(context, null, DOWNLOAD_FILE_NAME);
// down.setDestinationInExternalPublicDir("/" + getPackageName() + "/", "update.apk");
//将下载请求放入队列,返回值为downloadId
downloadId = manager.enqueue(down);
}
}
然后写一个dialogfragment来弹出对话框提示你是否升级,
public class DialogFragmentUpdate extends DialogFragment {
private TextView tv_title;
private TextView tv_content;
private TextView tv_cancel;
private TextView tv_confirm;
private View view_line;
public static DialogFragmentUpdate newInstance(AppInfo appInfo) {
DialogFragmentUpdate instance = new DialogFragmentUpdate();
Bundle args = new Bundle();
args.putSerializable("appInfo", appInfo);
instance.setArguments(args);
return instance;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
final Window dialogWindow = getDialog().getWindow();
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
dialogWindow.setGravity(Gravity.CENTER);
lp.width = DimensionUtil.dip2px(getContext(), 315);
lp.height = DimensionUtil.dip2px(getContext(), 350);
dialogWindow.setAttributes(lp);
View view = inflater.inflate(R.layout.dialogfragment_version_update, null);
view_line = view.findViewById(R.id.view_line);
view_line.setVisibility(View.VISIBLE);
tv_title = (TextView) view.findViewById(R.id.tv_title);
final AppInfo appInfo = (AppInfo) getArguments().getSerializable("appInfo");
tv_title.setText("最新版本:" + appInfo.getVersion());
tv_content = (TextView) view.findViewById(R.id.tv_content);
tv_content.setText(appInfo.getDescription());
tv_cancel = (TextView) view.findViewById(R.id.tv_cancel);
tv_cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismissAllowingStateLoss();
}
});
tv_confirm = (TextView) view.findViewById(R.id.tv_confirm);
tv_confirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ConnectivityManager connMgr = (ConnectivityManager) getContext()
.getSystemService(getContext().CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr
.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
boolean isWifiConn = networkInfo.isConnected();
networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isMobileConn = networkInfo.isConnected();
if (isWifiConn) {//Wifi
VersionUtil.versionUpdate(getContext(), appInfo.getUrl());
MessagePop.ToastMessage(getContext(), getString(R.string.loading_status));
dismissAllowingStateLoss();
} else if (isMobileConn) {//流量
//将当前的Fragment清除,并放入回退栈
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.remove(DialogFragmentUpdate.this);
ft.addToBackStack(null);
DialogFragmentPrompt prompt = DialogFragmentPrompt.newInstance(appInfo);
prompt.show(getFragmentManager(), "prompt");
} else {//无网络
MessagePop.ToastMessage(getContext(), getString(R.string.connect_error));
}
}
});
return view;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setCancelable(true);
}
}
用到的实体类AppInfo.java
public class AppInfo implements Serializable {
private int type; //类型1-iOS 2-Android
private String description; //更新描述
private String url; //下载链接
private String version; //版本号
private boolean needUpdate; //标志位 是否需要更新
private boolean force_update; //强制更新
public static AppInfo getAppInfo(JsonElement jsonElement) {
Gson gson = new Gson();
return gson.fromJson(jsonElement, AppInfo.class);
}
public int getType() {
return type;
}
public String getDescription() {
return description;
}
public String getUrl() {
return url;
}
public String getVersion() {
return version;
}
public boolean isNeedUpdate() {
return needUpdate;
}
public boolean isForce_update() {
return force_update;
}
}
接下来就只是引用这个dialogfragment就好了.
另外,我们需要写一个Receiver用来监听文件是否下载完成,完成的话提示安装.
public class APKDownloadCompleteReceiver extends BroadcastReceiver {
private DownloadManager manager;
@Override
public void onReceive(Context context, Intent intent) {
manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
//通过downloadId去查询下载的文件名
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
Query query = new Query();
query.setFilterById(downloadId);
Cursor myDownload = manager.query(query);
if (myDownload.moveToFirst()) {
int fileNameIdx = myDownload.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
String fileName = myDownload.getString(fileNameIdx);
installAPK(fileName, context);
}
}
}
//安装APK
private void installAPK(String filePath, Context context) {
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//广播里面操作需要加上这句,存在于一个独立的栈里
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
context.startActivity(intent);
}
}
在Mainfest.xml中配置文件Receiver
<receiver android:name=".widget.receiver.APKDownloadCompleteReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
最后这样就可以了,亲测有效,至于兼容性问题,这是谷歌大大提供的应该不会有问题.