在《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() {@Overridepublic 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() {@Overridepublic 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>();}@Overrideprotected void finalize() throws Throwable {// TODO Auto-generated method stubsuper.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 blocke.printStackTrace();return false;}} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.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() {@Overridepublic 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 blocke.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

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
  3. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  4. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  5. Android中对象的序列化
  6. Android(安卓)热修复一(热修复流程原理)
  7. android内置数据库的一个错误解决方案:Can't upgrade read-only d
  8. Android的Handler机制
  9. Android下的横向ListView源代码下载

随机推荐

  1. html表单、元素的来源及csss外部样式和上
  2. 单页面是什么呢?单页面怎么优化
  3. 怎么发帖可以让搜索引擎(百度)尽快收录?
  4. URL如何优化?网站url链接形式这样去做,利于
  5. 上下文选择器
  6. CSS 盒子模型理解
  7. 自定义一个kaniko镜像
  8. CSS 常用伪类简介
  9. SEO内容策略有哪几种?
  10. 交换友情链接注意事项