版本不同,开启服务,根据判断条件,如果未下载 或未下载完,开启AsyncTast进行断点下载。
将基本数据保存在了SharedPreferences中
其中字段:
dlversion:版本号存储
dlstate:状态,1,下载中,还没下载完,2,已经下载完
Finished:保存下载的进度
isfirst:是否第一次安装,模拟的版本控制,实际应用中并不需要这个字段
经过测试,可以实现,文件断网或清理后台后,再次 下载能够断点下载
还有服务开启一个全局dialog,可是有的手机会禁止悬浮窗,所以下载完后不会弹出dialog,注意手动开启 下,还有实际开发中,不建议用此dialog,可以尝试用下窗口activity.
基本的逻辑思路和需要的资源文件都在下面
部分代码:
MainActivity
package com.example.tianf.myapplication;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button no_file, file_no_version, file_version;
private String version;
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mEditor;
private boolean isFirst=
true
;
// private String url = "http://123.129.203.149/imtt.dd.qq.com/16891/237CB1256C5982BA4697817845F992A4.apk?mkey=58f4936ac26ff6c9&f=a601&c=0&fsname=com.tianci.xueshengzhuan_9.03_903.apk&csr=1bbd&p=.apk";
private String url =
"http://27.221.28.172/imtt.dd.qq.com/16891/F1B90DFC2A91F2E7F54D3F2919C7383B.apk?mkey=58f5f2bbc26ff6c9&f=b208&c=0&fsname=com.mfxsjd_1.3.01.10493_1010301.apk&csr=1bbd&p=.apk"
;
@Override
protected void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListen();
mSharedPreferences = getSharedPreferences(
"DownloadInfo"
,
MODE_PRIVATE);
mEditor = mSharedPreferences.edit();
boolean isfirst = mSharedPreferences.getBoolean(
"isfirst"
,
true
);
if
(isfirst){
mEditor.putString(
"dlversion"
,
"1.0"
);
mEditor.putBoolean(
"isfirst"
,
false
);
}
mEditor.commit();
}
private void initView() {
no_file = (Button) findViewById(R.id.no_file);
file_no_version = (Button) findViewById(R.id.file_no_version);
file_version = (Button) findViewById(R.id.file_version);
}
private void initListen() {
no_file.setOnClickListener(
this
);
file_no_version.setOnClickListener(
this
);
file_version.setOnClickListener(
this
);
}
@Override
public void onClick(View v) {
//从服务器获取的版本号
switch
(v.getId()){
case
R.id.no_file:
break
;
case
R.id.file_no_version:
version=
"1.0"
;
Intent intent=
new
Intent(
this
,MyFirstService.class);
intent.putExtra(
"version"
,version);
intent.putExtra(
"url"
,url);
startService(intent);
break
;
case
R.id.file_version:
version=
"2.0"
;
Intent intent2=
new
Intent(
this
,MyFirstService.class);
intent2.putExtra(
"version"
,version);
intent2.putExtra(
"url"
,url);
startService(intent2);
break
;
}
}
}
|
package com.example.tianf.myapplication;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import static android.R.attr.version;
import static com.example.tianf.myapplication.Constants.AppName;
/**
* <pre>
* Author : 天府萧炎恋熏儿
* Time :
* Usage :
* desc :
* other :
* </pre>
*/
public class MyFirstService extends Service implements MyTast.DoBackListener {
MyTast myTast;
File apkFile;
private String url =
""
;
private String myVersion=
""
;
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mEditor;
Handler mHandler=
new
Handler();
String filePath;
private final static String MYURL =
"http://123.129.203.149/imtt.dd.qq.com/16891/237CB1256C5982BA4697817845F992A4.apk?mkey=58f4936ac26ff6c9&f=a601&c=0&fsname=com.tianci.xueshengzhuan_9.03_903.apk&csr=1bbd&p=.apk"
;
// 通过startService方法启动服务时,该方法不会运行
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return
null
;
}
//当服务对象不存在时,调用此方法
@Override
public void onCreate() {
// TODO Auto-generated method stub
super
.onCreate();
Log.i(
"===="
,
"========= onCreate"
);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
// getDialog();
// showClearDialog();
myVersion=intent.getStringExtra(
"version"
);
url=intent.getStringExtra(
"url"
);
mSharedPreferences = getSharedPreferences(
"DownloadInfo"
,
MODE_PRIVATE);
mEditor = mSharedPreferences.edit();
int dlstate = mSharedPreferences.getInt(
"dlstate"
, 3);
String dlversion=mSharedPreferences.getString(
"dlversion"
,
""
);
//首先判断正在下载的版本和服务器版本是否一致
if
(!TextUtils.isEmpty(dlversion)&&dlversion.equals(myVersion)){
switch
(dlstate){
//正在下载,但是还没下载完全
case
1:
//继续下载
getTast();
Log.i(
"state"
,
"正在下载,但是还没下载完全"
);
Toast.makeText(
this
,
"正在下载,但是还没下载完全"
, Toast.LENGTH_SHORT).show();
break
;
//下载完成,拥有完整app包
case
2:
showClearDialog();
Log.i(
"state"
,
"下载完成,拥有完整app包"
);
Toast.makeText(
this
,
"安装版本已是"
+myVersion+
",不需要下载"
, Toast.LENGTH_SHORT).show();
break
;
default
:
getTast();
Log.i(
"state"
,
"其它情况"
);
Toast.makeText(
this
,
"其它情况"
, Toast.LENGTH_SHORT).show();
break
;
}
}
else
{
//如果服务器版本和本地版本不一致,删除本地重新下载
//不同则删除之前的版本,重新下载
mEditor.remove(
"Finished"
);
mEditor.remove(
"dlstate"
);
mEditor.remove(
"dlversion"
);
mEditor.commit();
getTast();
Log.i(
"state"
,
"版本不同,重新下载"
);
Toast.makeText(
this
,
"版本不同,重新下载"
, Toast.LENGTH_SHORT).show();
}
return
START_NOT_STICKY;
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.i(
"===="
,
"========= onDestroy"
);
super
.onDestroy();
stopSelf();
}
/**
* @Title: installApk @Description: 安装APP @param @param
* context @param @param file @return void 返回类型 @throws
*/
public void installApk(Context context, File file) {
if
(!file.exists()) {
return
;
}
Intent intent =
new
Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive"
);
context.startActivity(intent);
stopSelf();
}
public String getApkDownloadPath() {
File sdDir =
null
;
boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
// 判断sd卡是否存在
if
(sdCardExist) {
sdDir = Environment.getExternalStorageDirectory();
// 获取跟目录
}
else
{
sdDir = Environment.getDownloadCacheDirectory();
//放到系统下载目录
}
return
sdDir.toString() + Constants.fileName;
}
Dialog mclearDialog;
public void showClearDialog() {
if
(mclearDialog ==
null
) {
mclearDialog =
new
Dialog(getApplicationContext(), R.style.dialog);
mclearDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
View dialogLayout = LayoutInflater.from(getApplicationContext()).inflate(R.layout.dialog_new_version,
null
);
Button next_updata = (Button) dialogLayout.findViewById(R.id.next_updata);
Button now_updata = (Button) dialogLayout.findViewById(R.id.now_updata);
next_updata.setOnClickListener(
new
View.OnClickListener() {
@Override
public void onClick(View v) {
if
(mclearDialog !=
null
) {
mclearDialog.dismiss();
mclearDialog =
null
;
}
stopSelf();
}
});
now_updata.setOnClickListener(
new
View.OnClickListener() {
@Override
public void onClick(View v) {
if
(mclearDialog !=
null
) {
mclearDialog.dismiss();
mclearDialog =
null
;
}
installApk(getApplicationContext(),apkFile);
}
});
mclearDialog.setContentView(dialogLayout);
}
mclearDialog.setCanceledOnTouchOutside(
true
);
mclearDialog.setCancelable(
true
);
mclearDialog.show();
}
public void getTast(){
myTast =
new
MyTast(
this
);
myTast.setContext(getApplicationContext());
myTast.execute(url,getApkDownloadPath(),Constants.AppName,myVersion);
}
@Override
public void onBack() {
String filePath = getApkDownloadPath() + Constants.AppName;
apkFile =
new
File(filePath);
showClearDialog();
}
}
|
package com.example.tianf.myapplication;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import static android.content.Context.MODE_PRIVATE;
/**
* <pre>
* Author : 天府萧炎恋熏儿
* Time :
* Usage :
* desc :
* other :
* </pre>
*/
/*
* 异步处理
*/
class MyTast extends AsyncTask<String, Integer, String> {
private Context context;
private int length;
private int start;
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mEditor;
public int mFinished;
// 下载地址
private String DIRSTR=
""
;
private String FILENAME =
""
;
private String myVersion=
""
;
public interface DoBackListener {
void onBack();
}
DoBackListener listener;
public MyTast(DoBackListener listener) {
this
.listener = listener;
}
@Override
protected String doInBackground(String... params) {
// 获取文件长度
getFileLength(params[0]);
DIRSTR=params[1];
FILENAME=params[2];
myVersion=params[3];
// 执行下载任务
return
download(params[0]);
}
@Override
protected void onPostExecute(String result) {
super
.onPostExecute(result);
Log.i(
"mtag"
, result);
if
(
"下载完成!"
.equals(result)){
listener.onBack();
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super
.onProgressUpdate(values);
// 更新进度条
Log.i(
"mtag"
,
"更新进度===="
+values[0]);
}
/*
* 获取文件长度
*/
private void getFileLength(String string) {
HttpURLConnection connection =
null
;
try
{
connection = (HttpURLConnection)
new
URL(string)
.openConnection();
connection.setRequestMethod(
"GET"
);
connection.setConnectTimeout(3000);
if
(connection.getResponseCode() != 200) {
return
;
}
length = connection.getContentLength();
}
catch
(Exception e) {
e.printStackTrace();
} finally {
if
(connection !=
null
) {
connection.disconnect();
}
}
}
/*
* 下载任务
*/
private String download(String string) {
HttpURLConnection connection =
null
;
InputStream
in
=
null
;
RandomAccessFile raf =
null
;
try
{
// 从SharedPreferences取出DownloadInfo里的下载进度值
mSharedPreferences = context.getSharedPreferences(
"DownloadInfo"
,
MODE_PRIVATE);
mEditor = mSharedPreferences.edit();
start = mSharedPreferences.getInt(
"Finished"
, 0);
mEditor.putString(
"dlversion"
, myVersion);
mEditor.putInt(
"dlstate"
, 1);
mEditor.commit();
mFinished = start;
connection = (HttpURLConnection)
new
URL(string)
.openConnection();
connection.setRequestMethod(
"GET"
);
connection.setConnectTimeout(3000);
Log.d(
"length: "
, length +
""
);
connection.setRequestProperty(
"Range"
,
"bytes="
+ start +
"-"
+ length);
if
(connection.getResponseCode() != 206) {
return
"206"
;
}
in
= connection.getInputStream();
File file =
new
File(DIRSTR, FILENAME);
raf =
new
RandomAccessFile(file,
"rwd"
);
Log.d(
"start:"
, start +
""
);
raf.seek(start);
byte[] buffer =
new
byte[1024 * 4];
int len;
while
((len =
in
.read(buffer)) != -1) {
raf.write(buffer, 0, len);
mFinished += len;
mEditor.putInt(
"Finished"
, mFinished);
mEditor.commit();
// 设置进度条的值
publishProgress(mFinished * 100 / length);
}
// 下载完成,从SharedPreferences移除下载进度
mEditor.remove(
"Finished"
);
mEditor.putInt(
"dlstate"
, 2);
mEditor.commit();
context.stopService(
new
Intent(context,MyFirstService.class));
return
"下载完成!"
;
}
catch
(Exception e) {
e.printStackTrace();
mEditor.putInt(
"Finished"
, mFinished);
mEditor.commit();
context.stopService(
new
Intent(context,MyFirstService.class));
return
e.getMessage();
} finally {
try
{
if
(raf !=
null
) {
raf.close();
}
if
(
in
!=
null
) {
in
.close();
}
if
(connection !=
null
) {
connection.disconnect();
}
}
catch
(IOException e) {
e.printStackTrace();
return
e.getMessage();
}
}
}
public void setContext(Context context){
this
.context=context;
}
}
|
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@drawable/white_radius_bg_shape" android:orientation="vertical"> <TextView android:id="@+id/exit" android:layout_width="250dp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_margin="25dp" android:gravity="center" android:text="APP有更新了!" android:textColor="@color/black" android:textSize="15sp" android:drawableTop="@mipmap/ic_launcher" /> <View android:layout_width="match_parent" android:layout_height="2px" android:background="@color/gray_line"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/next_updata" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:text="下次再说" android:padding="15dp" android:textColor="@color/black" android:textSize="15sp"/> <View android:layout_width="2px" android:layout_height="match_parent" android:background="@color/gray_line"/> <Button android:id="@+id/now_updata" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@null" android:text="立即更新" android:padding="15dp" android:textColor="@color/black" android:textSize="15sp"/> </LinearLayout> </LinearLayout> |
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#5daee7</color> <color name="colorAccent">#5daee7</color> <!-- 米色 --> <color name="wheat">#F5DEB3</color> <color name="white">#FFFFFF</color> <color name="red">#f10303</color> <color name="black">#1e1d1d</color> <color name="round_outside_color">#FFFEFF</color> <color name="round_inside_color">#FFFEFF</color> <color name="theme_blue">#5daee7</color> <color name="line_color">#ebebeb</color> <color name="hui_1">#666</color> <color name="hui_1ban">#3f666666</color> <color name="hui2">#888</color> <color name="hui2_ban">#ee888888</color> <color name="hui3">#ebebeb</color> <color name="hui4">#d6d7dc</color> <color name="theme_background">@color/white</color> <color name="theme_background2">#F3F4F6</color> <color name="light_green">#82DA43</color> <color name="theme_title">#5daee7</color> <color name="side_bar_text">#4A4A4A</color> <color name="shadow_bg">#66000000</color> <color name="edit_bg">#EFEFF4</color> <color name="darker_gray">#9b9b9b</color> <color name="navpage">#FFE1E8EB</color> <!--灰色分割线--> <color name="gray_line">#cccccc</color> <!--bigpictrue--> <color name="bigpicture_background">#55000000</color> <color name="bigpicture_backgroundselcect">#2bc912</color> <!--动态圈名字颜色--> <color name="dy_namered">#f04f43</color> <!--我的页面名字颜色--> <color name="mine_name">#2a2a2a</color> <!--所有button里面的文字的字体颜色--> <color name="all_bt_textcolor">#ffffff</color> </resources> |
<style name="dialog" parent="@android:style/Theme.Dialog"> <item name="android:windowFrame">@null</item> <item name="android:windowIsFloating">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> <item name="android:background">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:backgroundDimEnabled">true</item> <item name="android:backgroundDimAmount">0.6</item> </style>