最新实战教程,让你了解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的类
下面是主界面的布局文件
下面是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卡,所以要添加相应的权限
现在就可以测试啦,记得要把服务器打开喔,还要昨天说的那个服务器的目录下面放一个apk喔,并把那个update.xml修改成与现在这个版本不一致的喔,不然不会更新的,不明白的可以看看下面的图
注意一下名称的对应
好啦,今天就说到这里啦
今天源码下载