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

  • 相关阅读:
    Serialization and deserialization are bottlenecks in parallel and distributed computing, especially in machine learning applications with large objects and large quantities of data.
    Introduction to the Standard Directory Layout
    import 原理 及 导入 自定义、第三方 包
    403 'Forbidden'
    https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
    These interactions can be expressed as complicated, large scale graphs. Mining data requires a distributed data processing engine
    mysqldump --flush-logs
    mysql dump 参数
    mysql dump 参数
    如果是在有master上开启了该参数,记得在slave端也要开启这个参数(salve需要stop后再重新start),否则在master上创建函数会导致replaction中断。
  • 原文地址:https://www.cnblogs.com/sparkleDai/p/7605037.html
Copyright © 2011-2022 走看看