zoukankan      html  css  js  c++  java
  • Android基于IIS的APK下载(四)数据下载

    在《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

  • 相关阅读:
    Jenkins安装(一)
    Ansible(一) 安装与简单测试
    zabbix中文乱码
    mysql5.7免密登录
    Zabbix 监控 Nginx 模板
    zabbix通过snmp监控主机
    zabbix5.0+grafana 使用脚本安装
    Eth-trunk配置-LACP模式
    Eth-Trunk配置-手动模式
    文件系统简单理解与实操(ext4)
  • 原文地址:https://www.cnblogs.com/sparkleDai/p/7605037.html
Copyright © 2011-2022 走看看