Android项目实战--手机卫士03--完成app更新的逻辑和主界面



最新实战教程,让你了解Android自动化刷量、作弊与防作弊的那些事,案例:刷友盟统计、批量注册苹果帐号




 

 

原文再续书接上一回,昨天我们把那个与服务器端的交互以及解析xml的内容给搭建出来啦,那么今天我们就来完成一下下载新版的apk并安装的逻辑写一下

既然要下载apk,那么肯定是另开一个线程下载的啦,所以我们在这里就新建一个类啦

com.xiaobin.security.engine.DownloadTask

package com.xiaobin.security.engine;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.app.ProgressDialog;

public class DownloadTask
{
	
	public static File getFile(String path, String filePath, ProgressDialog progressDialog) throws Exception
	{
		URL url = new URL(path);
		HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
		httpURLConnection.setConnectTimeout(2000);
		httpURLConnection.setRequestMethod("GET");
		if(httpURLConnection.getResponseCode() == 200)
		{
			int total = httpURLConnection.getContentLength();
			progressDialog.setMax(total);
			
			InputStream is = httpURLConnection.getInputStream();
			File file = new File(filePath);
			FileOutputStream fos = new FileOutputStream(file);
			byte[] buffer = new byte[1024];
			int len;
			int process = 0;
			while((len = is.read(buffer)) != -1)
			{
				fos.write(buffer, 0, len);
				process += len;
				progressDialog.setProgress(process);
			}
			fos.flush();
			fos.close();
			is.close();
			return file;
		}
		return null;
	}

}


 

好啦,那个写好了,从服务器里面拿到一个最新版的apk之后,我们就要在SplashActivity里面写一些安装的逻辑啦,还有一个内部类,用来启动另一个线程下载

 

 

com.xiaobin.security.ui.SplashActivity

package com.xiaobin.security.ui;

import java.io.File;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.xiaobin.security.R;
import com.xiaobin.security.domain.UpdateInfo;
import com.xiaobin.security.engine.DownloadTask;
import com.xiaobin.security.engine.UpdateInfoService;

public class SplashActivity extends Activity
{
	private TextView tv_version;
	private LinearLayout ll;
	private ProgressDialog progressDialog;
	
	private UpdateInfo info;
	private String version;
	
	private static final String TAG = "Security";
	
	@SuppressLint("HandlerLeak")
	private Handler handler = new Handler()
	{
		public void handleMessage(Message msg) 
		{
			if(isNeedUpdate(version))
			{
				showUpdateDialog();
			}
		};
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.splash);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		
		tv_version = (TextView) findViewById(R.id.tv_splash_version);
		version = getVersion();
		tv_version.setText("版本号  " + version);
		
		ll = (LinearLayout) findViewById(R.id.ll_splash_main);
		AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
		alphaAnimation.setDuration(2000);
		ll.startAnimation(alphaAnimation);
		
		progressDialog = new ProgressDialog(this);
		progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		progressDialog.setMessage("正在下载...");
		
		new Thread()
		{
			public void run() 
			{
				try
				{
					sleep(3000);
					handler.sendEmptyMessage(0);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			};
		}.start();
		
	}
	
	private void showUpdateDialog()
	{
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setIcon(android.R.drawable.ic_dialog_info);
		builder.setTitle("升级提醒");
		builder.setMessage(info.getDescription());
		builder.setCancelable(false);
		
		builder.setPositiveButton("确定", new DialogInterface.OnClickListener()
		{
			
			@Override
			public void onClick(DialogInterface dialog, int which)
			{
				if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
				{
					File dir = new File(Environment.getExternalStorageDirectory(), "/security/update");
					if(!dir.exists())
					{
						dir.mkdirs();
					}
					String apkPath = Environment.getExternalStorageDirectory() + "/security/update/new.apk";
					UpdateTask task = new UpdateTask(info.getUrl(), apkPath);
					progressDialog.show();
					new Thread(task).start();
				}
				else
				{
					Toast.makeText(SplashActivity.this, "SD卡不可用,请插入SD卡", Toast.LENGTH_SHORT).show();
					loadMainUI();
				}
			}
		});
		builder.setNegativeButton("取消", new DialogInterface.OnClickListener()
		{

			@Override
			public void onClick(DialogInterface dialog, int which)
			{
				loadMainUI();
			}
			
		});
		builder.create().show();
	}

	private boolean isNeedUpdate(String version)
	{
		UpdateInfoService updateInfoService = new UpdateInfoService(this);
		try
		{
			info = updateInfoService.getUpdateInfo(R.string.serverUrl);
			String v = info.getVersion();
			if(v.equals(version))
			{
				Log.i(TAG, "当前版本:" + version);
				Log.i(TAG, "最新版本:" + v);
				loadMainUI();
				return false;
			}
			else
			{
				Log.i(TAG, "需要更新");
				return true;
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
			Toast.makeText(this, "获取更新信息异常,请稍后再试", Toast.LENGTH_SHORT).show();
			loadMainUI();
		}
		return false;
	}

	private String getVersion()
	{
		try
		{
			PackageManager packageManager = getPackageManager();
			PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
			
			return packageInfo.versionName;
		}
		catch (NameNotFoundException e)
		{
			e.printStackTrace();
			return "版本号未知";
		}
	}
	
	private void loadMainUI()
	{
		Intent intent = new Intent(this, MainActivity.class);
		startActivity(intent);
		finish();
	}
	
	/**
	 * 安装apk
	 * @param file 要安装的apk的目录
	 */
	private void install(File file)
	{
		Intent intent = new Intent();
		intent.setAction(Intent.ACTION_VIEW);
		intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
		finish();
		startActivity(intent);
	}
	
	//===========================================================================================
	
	/**
	 * 下载的线程
	 *
	 */
	class UpdateTask implements Runnable
	{
		private String path;
		private String filePath;
		
		public UpdateTask(String path, String filePath)
		{
			this.path = path;
			this.filePath = filePath;
		}

		@Override
		public void run()
		{
			try
			{
				File file = DownloadTask.getFile(path, filePath, progressDialog);
				progressDialog.dismiss();
				install(file);
			}
			catch (Exception e)
			{
				e.printStackTrace();
				progressDialog.dismiss();
				Toast.makeText(SplashActivity.this, "更新失败", Toast.LENGTH_SHORT).show();
				loadMainUI();
			}
		}
		
	}

}

 

ps:上面那个com.xiaobin.security.ui.SplashActivity类里面的那个handler是我为让用户清楚看到那个启动界面而设置的,不然,因为都是在同一个局域网里面,一下子就会完成那个更新的判断的啦,如果不用更新,那就会看不到那个启动界面的啦,所以为了我们能够看到那个界面,我让它休眠了2秒钟,才进行与服务器更新的,所以有什么不明白的也可以问一下

就这样子,我们就把启动界面时候,从服务器获取最新版的内容,然后提示用户是不是要更新的处理弄好啦!

 

既然启动界面弄好啦,那么,我们接下来,肯定是要做我们最重要的主界面啦

 

我们的主界面用到的是一个叫GridView的控件,它需要一个adapter,所以我们也要新建一个adapter的类

 

下面是主界面的布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="40dip"
        android:gravity="center_vertical|center_horizontal"
        android:background="@drawable/title_background"
        android:orientation="vertical">
        
        <TextView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:textSize="22sp"
            android:text="@string/main"/>
        
    </LinearLayout>
    
    <GridView
        android:id="@+id/gv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:verticalSpacing="8dip"
        android:numColumns="2" />

</LinearLayout>


 

下面是adapter类的代码

com.xiaobin.security.adapter.MainUIAdapter

package com.xiaobin.security.adapter;

import android.content.Context;
import android.content.SharedPreferences;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.xiaobin.security.R;

public class MainUIAdapter extends BaseAdapter
{
	private static final String[] NAMES = new String[] {"手机防盗", "通讯卫士", "软件管理", "流量管理", "任务管理", "手机杀毒", 
			"系统优化", "高级工具", "设置中心"};
	
	private static final int[] ICONS = new int[] {R.drawable.widget01, R.drawable.widget02, R.drawable.widget03, 
			R.drawable.widget04, R.drawable.widget05, R.drawable.widget06, R.drawable.widget07, 
			R.drawable.widget08, R.drawable.widget09};
	
	//声明成静态,起到一定的优化作用,关于adapter还有别的优化方法的,有机会我们再说
	private static ImageView imageView;
	private static TextView textView;
	
	private Context context;
	private LayoutInflater inflater;
	private SharedPreferences sp;
	
	public MainUIAdapter(Context context)
	{
		this.context = context;
		inflater = LayoutInflater.from(this.context);
		sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);
	}

	@Override
	public int getCount()
	{
		return NAMES.length;
	}

	@Override
	public Object getItem(int position)
	{
		return position;
	}

	@Override
	public long getItemId(int position)
	{
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent)
	{
		View view = inflater.inflate(R.layout.main_item, null);
		imageView = (ImageView) view.findViewById(R.id.iv_main_icon);
		textView = (TextView) view.findViewById(R.id.tv_main_name);
		imageView.setImageResource(ICONS[position]);
		textView.setText(NAMES[position]);
			
		if(position == 0)
		{
			String name = sp.getString("lostName", "");
			if(!name.equals(""))
			{
				textView.setText(name);
			}
		}
		
		return view;
	}
	
}


 

adapter也写好啦,那么接下来,肯定就是我们的主界面啦

com.xiaobin.security.ui.MainActivity

package com.xiaobin.security.ui;

import com.xiaobin.security.R;
import com.xiaobin.security.adapter.MainUIAdapter;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.GridView;
import android.widget.AdapterView.OnItemClickListener;

public class MainActivity extends Activity implements OnItemClickListener
{
	private GridView gridView;
	
	private MainUIAdapter adapter ;
	private SharedPreferences sp;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		sp = this.getSharedPreferences("config", Context.MODE_PRIVATE);
		gridView = (GridView) findViewById(R.id.gv_main);
		adapter = new MainUIAdapter(this);
		gridView.setAdapter(adapter);
		gridView.setOnItemClickListener(this);
		gridView.setOnItemLongClickListener(new OnItemLongClickListener()
		{
			@Override
			public boolean onItemLongClick(AdapterView<?> parent, final View view, int position, long id)
			{
				if(position == 0)	//这个是因为,如果我们的手机被盗了,用户一看到第一个手机防盗,那样肯定会先卸载我们的程序的,所以我们在手机防盗这个item里面,设置了一个重命名的功能
				{
					AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
					builder.setTitle("设置");
					builder.setMessage("请输入要理性的名称");
					final EditText et = new EditText(MainActivity.this);
					et.setHint("新名称");
					builder.setView(et);
					builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
					{
						@Override
						public void onClick(DialogInterface dialog, int which)
						{
							String name = et.getText().toString();
							if(name.equals(""))
							{
								Toast.makeText(MainActivity.this, "输入内容不能为空", Toast.LENGTH_SHORT).show();
							}
							else
							{
								Editor editor = sp.edit();
								editor.putString("lostName", name);
								editor.commit();
								
								TextView tv = (TextView) view.findViewById(R.id.tv_main_name);
								tv.setText(name);
								adapter.notifyDataSetChanged();
							}
						}
					});
					builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
					{
						@Override
						public void onClick(DialogInterface dialog, int which)
						{
							// TODO Auto-generated method stub
							
						}
					});
					builder.create().show();
				}
				return false;
			}
		});
	}

	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position, long id)
	{
		switch(position)
		{
			case 0 : //手机防盗
				break;
				
			case 1 : //通讯卫士
				break;
				
			case 2 : //软件管理
				break;
				
			case 3 : //流量管理
				break;
				
			case 4 : //任务管理
				break;
				
			case 5 : //手机杀毒
				break;
				
			case 6 : //系统优化
				break;
				
			case 7 : //高级工具
				break;
				
			case 8 : //设置中心
				break;
				
			default : 
				break;
		}
	}

}


上面的那个重命名的功能,是我们把重新命名的名字,存放到一个SharedPreferences里面的,然后下一次启动的时候,就先检查里面有没有对应的值,没有就用默认的,有就用用户自己命名的

 

好啦,今天的代码基本上写得差不多的啦,现在只剩下添加权限啦,因为要读取sd卡,所以要添加相应的权限

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


现在就可以测试啦,记得要把服务器打开喔,还要昨天说的那个服务器的目录下面放一个apk喔,并把那个update.xml修改成与现在这个版本不一致的喔,不然不会更新的,不明白的可以看看下面的图

 

注意一下名称的对应

 

好啦,今天就说到这里啦

 

今天源码下载

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值