原文再续书接上一回,昨天我们把那个与服务器端的交互以及解析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修改成与现在这个版本不一致的喔,不然不会更新的,不明白的可以看看下面的图
注意一下名称的对应
好啦,今天就说到这里啦
今天源码下载