您的位置:首页 > 移动开发 > Android开发

Android基于IIS的APK下载(四)数据下载

2014-02-25 11:41 405 查看
在《Android基于IIS的APK下载(三)用JSON传输更新数据》一文中已经从服务器中拿到了更新数据,并且呈现到了UI中,结合前面的文章及效果图(参见下图),可以看到UI中的更新列表一行一行的呈现,而每一行的末尾有一个行为按钮,对应着不同的行为,这个行为要如何实现呢?


我们再看一下UpdateItemsAdapter中getView的部分代码

updateItem.SetBehavior(isNewVersion ? UPDATE_BEHAVIORS.UPDATE
: UPDATE_BEHAVIORS.NO_UPDATE);

behavior_button.setEnabled(isNewVersion);
behavior_button.setText(updateItem.GetBehavior());
behavior_button.setTag(updateItem);

behavior_button.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
ExecuteBehavior(behavior_button);
}
});


代码中可以看到,updateItem有设置行为的动作,而这个行为是根据是否有新版本来设置的。之后该行为会呈现到behavior_button中,并且将updateItem设置到behavior_button的tag中,还设置了单击事件,事件里面调用ExecuteBehavior(behavior_button),下面是这个函数的实现代码。

private void ExecuteBehavior(final Button behavior_button) {
try {

UpdateItem updateItem = (UpdateItem) behavior_button.getTag();
if (updateItem == null) {
return;
}

if (updateItem.GetBehavior() == UPDATE_BEHAVIORS.INSTALL) {
if (updateItem.GetSavePath() == null
|| updateItem.GetSavePath().length() <= 0) {
return;
}
InstallApk(updateItem.GetSavePath());
return;
} else if (updateItem.GetBehavior() == UPDATE_BEHAVIORS.NO_UPDATE) {
return;
}

final String url = updateItem.GetUrl();
final String savePath = FetchSavePath(url);

final Handler downloadHandler =InitDownloadHandler(behavior_button);

String aysncDownloadThreadName = RequestSp.DownLoadFileAsync(url, savePath, downloadHandler);
if (aysncDownloadThreadName != null
&& aysncDownloadThreadName.length() > 0) {
_aysncDownloadThreadNames.add(aysncDownloadThreadName);
}

} catch (Exception e) {
behavior_button.setEnabled(true);
}
}

private Handler InitDownloadHandler(final Button behavior_button)
{
Handler _downloadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
UpdateItem updateItem = (UpdateItem) behavior_button
.getTag();
switch (msg.what) {
case REQUEST_MESSAGES.DOWNLOAD_START: {
behavior_button.setEnabled(false);
break;
}
case REQUEST_MESSAGES.DOWNLOAD_PERCENT: {
Bundle bundle = msg.getData();
float downloadPercent = bundle
.getFloat(REQUEST_KEYS.DOWNLOAD_PERCENT);
behavior_button.setText(String.format("%1$.2f",
downloadPercent) + "%");
break;
}
case REQUEST_MESSAGES.DOWNLOAD_COMPLETED: {
Bundle bundle = msg.getData();
String savePath = bundle
.getString(REQUEST_KEYS.DOWNLOAD_SAVE_PATH);
behavior_button.setEnabled(true);
behavior_button
.setText(UPDATE_BEHAVIORS.INSTALL);
if (updateItem != null) {
updateItem.SetBehavior(UPDATE_BEHAVIORS.INSTALL);
updateItem.SetSavePath(savePath);
}
break;
}
case REQUEST_MESSAGES.DOWNLOAD_EXCEPTION: {
behavior_button.setEnabled(true);
String info = "Download " + updateItem.GetUrl() + " Fail";
MessageBoxSp.Show(_context, info);
break;
}
default: {
behavior_button.setEnabled(true);
String info = "Download " + updateItem.GetUrl() + " Fail";
MessageBoxSp.Show(_context, info);
break;
}

}
behavior_button.setTag(updateItem);
}
};

return _downloadHandler;
}

private String FetchSavePath(String url) {

String saveDir = Environment.getExternalStorageDirectory()
+ "/download/";
File saveDirfile = new File(saveDir);

if (!saveDirfile.exists()) {
saveDirfile.mkdirs();
}

int fileNameStart = url.lastIndexOf("/");
String fileName = url.substring(fileNameStart + 1);

return saveDir + fileName;
}

private void InstallApk(String filePath) {

IntentSp.StartActivity(_context, Uri.fromFile(new File(filePath)),
"application/vnd.android.package-archive", false);
}


注:

1、从behavior_button的tag中获取updateItem,然后获取相应的行为进行操作。

2、如果是INSTALL行为,将会调用InstallApk。如果不是INSTALL行为,而是NO_UPDATE行为,则不执行任何动作。如果这两个动作都不是,则是UPDATE行为,即认为是要下载数据。所以会提取URL,并根据URL获取相应的savePath。

3、在数据下载时,每一个下载都会开启一个线程,并不断更新下载数据的百分比。由于要在线程中更新UI,所以要用到handler来处理。在InitDownloadHandler中实现了下载的handler.

4、由于每一个下载都会开启一个线程,所以在RequestSp.DownLoadFileAsync中返回了线程的名字(采用UUID来命名以保证唯一性),并将该名字记录起来,在UpdateItemsAdapter释放的时候(即在finalize函数中),关闭线程,以更好的控制下载线程。下面是finalize的代码。

private List<String> _aysncDownloadThreadNames=null;

public UpdateItemsAdapter(List<UpdateItem> updateItems, Context context) {
_updateItems = updateItems;
_context = context;
_aysncDownloadThreadNames=new ArrayList<String>();
}

@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
super.finalize();
if (_aysncDownloadThreadNames == null
|| _aysncDownloadThreadNames.size() <= 0) {
return;
}

while (_aysncDownloadThreadNames.size() > 0) {
String asyncDownloadThreadName = _aysncDownloadThreadNames.get(0);
RequestSp.AbortAsyncDownload(asyncDownloadThreadName);
_aysncDownloadThreadNames.remove(0);
}
}


RequestSp.java

package com.kitsp.httpsp;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.UUID;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

public class RequestSp {
private final static int HTTP_200 = 200;
private static HashMap<String, Boolean> _asyncDownloadFlags = new HashMap<String, Boolean>();

public static InputStream Get(String url) throws Exception {

HttpEntity httpEntity = GetHttpEntity(url);
if (httpEntity == null) {
return null;
}

return httpEntity.getContent();
}

public static HttpEntity GetHttpEntity(String url) throws Exception {

HttpGet httpGet = new HttpGet(url);

HttpClient httpClient = new DefaultHttpClient();

HttpResponse httpResp = httpClient.execute(httpGet);

if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {
//Get back data.
// String result = EntityUtils.toString(httpResp.getEntity(),
// "UTF-8");
// return result;
return httpResp.getEntity();
} else {
return null;
}

}

public static boolean DownLoadFile(String httpUrl, String savePath) {

final File file = new File(savePath);

try {
URL url = new URL(httpUrl);
try {
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();

if (conn.getResponseCode() >= 400) {
return false;
}

InputStream is = conn.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
long length = conn.getContentLength();
byte[] buf = new byte[1024];
conn.connect();
int readCount = 0;
while (true) {

if (is == null) {
break;
}

readCount = is.read(buf);

if (readCount <= 0) {
break;
}

fos.write(buf, 0, readCount);
}

conn.disconnect();
fos.close();
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
return true;
}

/**
*
* @param httpUrl
* @param savePath
* @param handler
*            :Async handler
* @return Handler:Control thread in outer.
*/
public static String DownLoadFileAsync(final String httpUrl,
final String savePath, final Handler handler) {

if (handler == null) {
return null;
}

final String threadName = UUID.randomUUID().toString();
Thread downloadThread = new Thread(new Runnable() {
@Override
public void run() {
DownloadDataAsync(httpUrl, savePath, handler, threadName);
}
});
downloadThread.setName(threadName);
_asyncDownloadFlags.put(threadName, true);
downloadThread.start();
return threadName;
}

public static void AbortAsyncDownload(String asyncDownloadThreadName) {
if (asyncDownloadThreadName == null
|| asyncDownloadThreadName.length() <= 0) {
return;
}

_asyncDownloadFlags.remove(asyncDownloadThreadName);
}

private static void DownloadDataAsync(String httpUrl,
final String savePath, final Handler handler,
final String threadName) {
File file = new File(savePath);

HttpURLConnection conn;
try {
final URL url = new URL(httpUrl);
conn = (HttpURLConnection) url.openConnection();

if (conn.getResponseCode() >= 400) {
handler.sendEmptyMessage(REQUEST_MESSAGES.DOWNLOAD_EXCEPTION);
return;
}
InputStream is = conn.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
long totalCount = conn.getContentLength();
byte[] buf = new byte[1024];
conn.connect();
int readCount = 0;
int downloadedCount = 0;
float percent = 0;
Message msg = null;
Bundle bundle = null;
handler.sendEmptyMessage(REQUEST_MESSAGES.DOWNLOAD_START);

while (true) {

if(_asyncDownloadFlags.isEmpty()){
break;
}

if(!_asyncDownloadFlags.get(threadName)){
break;
}

if (is == null) {
break;
}

readCount = is.read(buf);
downloadedCount += readCount;
percent = (float) (downloadedCount * 1.0 / totalCount * 100);
msg = new Message();
msg.what = REQUEST_MESSAGES.DOWNLOAD_PERCENT;
bundle = new Bundle();
bundle.putFloat(REQUEST_KEYS.DOWNLOAD_PERCENT, percent);
msg.setData(bundle);
handler.sendMessage(msg);

if (readCount <= 0) {
break;
}

fos.write(buf, 0, readCount);
}

conn.disconnect();
fos.close();
is.close();

msg = new Message();
msg.what = REQUEST_MESSAGES.DOWNLOAD_COMPLETED;
bundle = new Bundle();
bundle.putString(REQUEST_KEYS.DOWNLOAD_SAVE_PATH, savePath);
msg.setData(bundle);
handler.sendMessage(msg);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
handler.sendEmptyMessage(REQUEST_MESSAGES.DOWNLOAD_EXCEPTION);
return;
}
}
}


1、每调用一次DownLoadFileAsync,就会启支一个线程,并且生成一个UUID作为线程的名字,记录到_asyncDownloadFlags中,将对应的标志设轩为true。该标志控制着线程的运行。

2、在AbortAsyncDownload中会根据线程的名字移除相应的项。这样在该项移除后,线程就无法获取到该标志,从而结束。当然如果要确保线程安全,这里的_asyncDownloadFlags以及前文的_aysncDownloadThreadNames需要使用线程安全的对象来代替,不然有可能会引发竞态等不可预料的结果。

REQUEST_MESSAGES.java

package com.kitsp.httpsp;

public class REQUEST_MESSAGES {
public final static int DOWNLOAD_START=1001;
public final static int DOWNLOAD_PERCENT=1002;
public final static int DOWNLOAD_COMPLETED=1003;
public final static int DOWNLOAD_EXCEPTION=1004;
public final static int DOWNLOAD_ABORT=1005;
}


REQUEST_KEYS.java

package com.kitsp.httpsp;

public class REQUEST_KEYS {
public final static String DOWNLOAD_PERCENT="DOWNLOAD_PERCENT";
public final static String DOWNLOAD_SAVE_PATH="DOWNLOAD_SAVE_PATH";
public final static String DOWNLOAD_CONTROL="DOWNLOAD_CONTROL";
}


前面在InstallApk中还调用了IntentSp中的方法,这是封装到一个包里的,代码附上。

package com.kitsp.contentsp;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

public class IntentSp {

/**
*
* @param activity
* @param isSaveActivityToHistory
*            true:save activity to history.System may back to the activity
*            when other activity finish. false:no save.
*/
public static void RestartActivity(Activity activity,
boolean isSaveActivityToHistory) {
if (activity == null) {
return;
}
Intent intent = new Intent();
String packageName = activity.getPackageName();
String className = activity.getLocalClassName();
String componentClassName = packageName + "." + className;
if (className != null && className.split(".").length > 0) {
componentClassName = className;
}
ComponentName componentName = new ComponentName(packageName,
componentClassName);

intent.setComponent(componentName);
if (!isSaveActivityToHistory) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
}
activity.startActivity(intent);
activity.finish();
return;
}

/**
*
* @param context
* @param cls
* @param isSaveActivityToHistory
*            true:save activity to history.System may back to the activity
*            when other activity finish. false:no save.
*/
public static void StartActivity(Context context, Class<?> cls,
boolean isSaveActivityToHistory) {
if (context == null || cls == null) {
return;
}

Intent intent = new Intent();
if (!isSaveActivityToHistory) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
}
intent.setClass(context, cls);
context.startActivity(intent);
}

/**
*
* @param context
* @param action
* @param isSaveActivityToHistory
*            true:save activity to history.System may back to the activity
*            when other activity finish. false:no save.
*/
public static void StartActivity(Context context, String action,
boolean isSaveActivityToHistory) {
if (context == null || action == null) {
return;
}

Intent intent = new Intent(action);
if (!isSaveActivityToHistory) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
}
context.startActivity(intent);
}

/**
*
* @param context
* @param packageName
* @param className
* @param isSaveActivityToHistory
*            true:save activity to history.System may back to the activity
*            when other activity finish. false:no save.
*/
public static void StartActivity(Context context, String packageName,
String className, boolean isSaveActivityToHistory) {
if (context == null) {
return;
}

if (packageName == null || packageName == "") {
return;
}

if (className == null || className == "") {
return;
}

Intent intent = new Intent();
if (!isSaveActivityToHistory) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
}
ComponentName cn = new ComponentName(packageName, className);
if (cn != null) {
intent.setComponent(cn);
context.startActivity(intent);
}
}

public static void StartActivity(Context context, Uri data, String type,
boolean isSaveActivityToHistory) {
if (context == null) {
return;
}

if(data==null)
{
return;
}

if(type==null||type.length()<=0)
{
return;
}

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(data, type);
if (!isSaveActivityToHistory) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
}
context.startActivity(intent);
}

}


附上JSON的数据格式

{

"Items":[
{
"Name":"TestApk",
"FeaturePackage":"com.example.apkupdate",
"Version":2.1.1.8,
"Url":"http://192.168.1.5:9000/TestApk.apk"
},
{
"Name":"TestApk2",
"FeaturePackage":"com.example.apkupdate",
"Version":1.1.1.9,
"Url":"http://192.168.1.5:9000/TestApk2.apk"
},
{
"Name":"TestApk3",
"FeaturePackage":"com.example.apkupdate3",
"Version":2.1.1.0,
"Url":"http://192.168.1.5:9000/TestApk3.apk"
},
{
"Name":"TestApk4",
"FeaturePackage":"com.example.apkupdate3",
"Version":2.1.1.3,
"Url":"http://192.168.1.5:9000/TestApk4.apk"
}
]

}


现在数据下载已经实现了,还剩最后一关,IIS的配置。请参看下文Android基于IIS的APK下载(五)IIS的配置

转载请注明出处 Android基于IIS的APK下载(四)数据下载

完整代码在此处下载https://github.com/sparkleDai/ApkUpdate
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: