##Day12##
#12.1获取签名信息#
前边将扫描程序实现后,接下来就要实现杀毒的效果,去匹配杀毒,对比金山卫士的病毒查杀,它每次也是扫描,扫描到病毒之后,会以不同颜色作为区分标明出来
怎么实现呢,前面说了android目前用的是黑名单查杀病毒,将E:\itcast\20150721-JAVAME\二期\王松\手机卫士day01\Day01资料\安全卫士\APK资料\com.ijinshan.mguard.1324644228599\assets中的antivirus.db(反病毒程序,抗病毒素)拖到sqlite expert中打开,点击查看datable,首先可以看出它是一个MD5,后边的desc是病毒的描述信息,重点是看前边的MD5,它是病毒的一个特征码,
所以其实它在扫描的时候,是拿到一个程序的特征码,然后MD5一下,转化成相应的特征码,去和数据库antivirus.db里面比较,如果数据库中有的话,它就认为你是一个病毒,没有的话,说明不是病毒,这个就是黑名单查杀
那么怎么去获取程序特征码呢?
程序的特征码其实就是一个程序的唯一标识,这个唯一标识不是我们的包名,我们都知道,上线发布到应用市场上的程序都是打过签名的,这个签名就是应用程序在市场上的唯一标识,它就是拿到签名MD5一下,转化为了特征码,然后进行操作的,所以我们需要获取一下签名信息
获取签名信息步骤:
1.添加标签
//2.获取安装的程序 参数必须设置标签,才可以获取到(在log中)签名信息(特别长) 否则会报空指针异常
List<PackageInfo> installedPackages = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);
2.获取签名信息
在AntivirusActivity.java中的//7.设置扫描的文字之后添加如下代码:
//获取应用的签名数组
Signature[] singnatures = packageInfo.signatures;
//将获取签名信息转化成字符串 我们的应用程序就一个签名,所以0代表的就是我们的签名
String singnature = singnatures[0].toCharsString();
//转化成特征码
singnature = MD5Utils.digestPassword(singnature);
注意:
为什么参数必须设置标签?
因为获取的时候,只要是flags里面标明的,都是我们在0的时候获取不到的,因为我们所有的里边是不包含这些的,设置这个PackageManager.GET_SIGNATURES标签就表明是要额外的再拿一下它的
为什么是签名数组呢?
在工作的时候,可能会遇到这种情况,但是机率很小,一般在给银行工作的时候会出现这种问题,即一个app是由两家公司来开发的,两家公司每家开发一半,这样做的原因是为了每家公司都不能拿到完整的信息,保证安全性,但两家公司开发就会出现一个问题,两家公司开发完后,一般打包签名的时候,是每家公司都会打包一个签名,注意,它那个签名是按照一定规则去做的,是后期可以整合成一个的,但是在获取签名的时候,有一定机率是获取到两个签名,即它还没有打包成一个签名,两个签名分别代表的是一半应用程序。所以是签名数组
#12.2杀毒操作#
上边我们拿到了MD5特征码,接下来就可以和antivirus.db(病毒数据库)进行匹配了,即我们又要进行一些拷贝数据库,查询数据的操作了,首先找到我们数据库,这里已经准备了一个数据库,找到E:\itcast\20150721-JAVAME\二期\王松\手机卫士day01\Day01资料\安全卫士\上课资料\binddu下的antivirus.db数据库,注意,这个数据库经过特殊处理,加上getkey.apk,t2t.apk这两个所谓的病毒,在这里边加上特征码,以便我们测试
将antivirus.db数据库拷贝到我们项目的assets目录下,紧接着要去拷贝数据库,如下:
1.拷贝数据库
找到splashactivity.java中的onCreate方法,在这里边有个copyDb();注意,以前在这里拷贝address.db数据库的时候,是把address.db数据库名字写死了,即:
private void copyDb(){
File file = new File(getFilesDir(), "address.db");
以及
// 2.通过assets管理者打开数据库
in = assets.open("address.db");
此时我们又要拷贝antivirus.db数据库,写死了是肯定不行的,
所以可以这样干,在private void copyDb()方法中,改为
private void copyDb(String name) {
File file = new File(getFilesDir(), name);
以及
// 2.通过assets管理者打开数据库
in = assets.open(name);
然后把onCreate中调用的方法改为:
copyDb("address.db");
copyDb("antivirus.db");
就可以实现拷贝多个数据库了
接下来运行一下应用中的手机杀毒功能,看数据库有没有拷贝成功,找到ddms,打开data/data 找到我们的应用cn.itcast.mobilesafexian02下的files,里面有antivirus.db数据库,这就说明我们拷贝成功了
2.查询数据,参考addressDao.java,创建AntivirusActivity.java如下:
package cn.itcast.mobilesafexian02.db.dao;
import java.io.File;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class AntiVirusDao {
/**
* 查询是否是病毒
* @param context
* @param md5
* @return
*/
//判断应用否是病毒,返回的是boolean值,参数需要上下文,我们比较的时候是比较MD5值,所以还要传入MD5过来
public static boolean isAntiViruse(Context context,String md5){
File file = new File(context.getFilesDir(), "antivirus.db");
//打开数据库
SQLiteDatabase database = SQLiteDatabase.openDatabase(file.getAbsolutePath(), null, SQLiteDatabase.OPEN_READONLY);
//查询数据库 参数:1:表名 antivirus.db数据库中的datable表 3:查询条件 4.查询参数
Cursor cursor = database.query("datable", null, "md5=?", new String[]{md5}, null, null, null, null);
if (cursor.moveToNext()) {
//数据库中有查询的数据的话返回true 表明是病毒
return true;
}
return false;
}
}
注意:我们通过MD5查询条件查询每个MD5,对应的是一条数据,所以这里我们用的是if,如果是查询的是多条记录的话就要用while
查询数据完成后,就要调用这个方法了
3.调用查询数据库的操作,判断应用是否是病毒 (在AntiVirusActivity.java中//转化成特征码 后边添加如下代码:)
boolean b = AntiVirusDao.isAntiViruse(getApplicationContext(), singnature);
4.根据返回boolean值设置显示应用的名称文本颜色 对比金山卫士病毒查杀的下拉列表,当是病毒的时候,它的颜色就会不一样
if (b) {
//如果是true,表明是病毒 那就将它的值改成红色
textView.setTextColor(Color.RED);
list.add(packageInfo.packageName);
}else{
textView.setTextColor(Color.BLACK);
}
此时在运行应用测试一下,发现病毒查杀的下拉列表中没有病毒,为了测试用,我们可以安装两个上边准备好的病毒getkey.apk,t2t.apk,cmd打开dos窗口,输入命令adb -s emulator-5554 install 然后将getkey.apk拖进来,回车,即可将病毒getkey.apk安装到相应的模拟器中,重复上边的步骤,将病毒t2t.apk也安装进去。
安装成功后,再次运行应用的手机杀毒功能,就会在下来列表出现两个红色名称的应用,但问题又来了,发现两个病毒了,还显示扫描完成,很安全,显然是不行的,所以还要改改这个显示
5.创建一个list集合,用来存储病毒应用的包名,并在发现有病毒的时候,添加到集合中
6.在扫描完成后根据list集合的长度,判断是否有病毒,有 提醒并卸载
if (list.size() > 0) {
tv_antivirus_name.setText("扫描完成,发现病毒");
//发现病毒,提醒用户是否卸载
AlertDialog.Builder builder = new Builder(AntivirusActivity.this);
builder.setTitle("警告!");
builder.setIcon(R.drawable.ic_launcher);
builder.setMessage("发现"+list.size()+"个病毒");
builder.setPositiveButton("卸载", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//卸载的操作
for (int i = 0; i < list.size(); i++) {
Intent intent = new Intent();
intent.setAction("android.intent.action.DELETE");
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse("package:"+list.get(i)));//content://
startActivityForResult(intent, 0);
}
}
});
builder.setNegativeButton("暂不处理", null);
builder.show();
}else{
//8.设置扫描完成的文字和停止动画操作
tv_antivirus_name.setText("扫描完成,很安全");
}
//停止动画
iv_antivirus_scanner.clearAnimation();
#12.3缓存清理框架#
1.创建两个fragment,继承v4包下的fragment
public class CacheFragment extends Fragment {
}
2.设置布局,并加载
public class CacheFragment extends Fragment {
//类似activity中oncreate方法,加载布局
//参数1:布局文件加载
//参数2:容器
//参数3:是否保存状态
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//参数1:布局文件
//参数2:容器
//参数3:是否挂载,一般false
return inflater.inflate(R.layout.fragment_cache, container, false);
}
}
3.将clearcacheactivity,改为继承fragmentActivity
public class ClearCacheActivity extends FragmentActivity {
4.修改将clearcacheactivity布局
<!-- 是fragment用来替换的布局控件 -->
<RelativeLayout
android:id="@+id/rl_clearcache_relative"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="200"
></RelativeLayout>
<LinearLayout
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="缓存清理"
android:background="@drawable/selector_contact_button"
android:onClick="cache"
/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="SD卡清理"
android:background="@drawable/selector_contact_button"
android:onClick="sd"
/>
</LinearLayout>
5.添加fragment,在oncreate方法中执行
cacheFragment = new CacheFragment();
sdFragment = new SDFragment();
//1.获取fragment的管理者
fragmentManager = getSupportFragmentManager();
//2.获取事务,保证fragment切换的一致性
FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
//3.添加fragment
//参数1:被替换的控件的id
//参数2:替换的fragment
beginTransaction.add(R.id.rl_clearcache_relative, cacheFragment);
//跟add效果相似
// beginTransaction.replace(arg0, arg1)
beginTransaction.add(R.id.rl_clearcache_relative, sdFragment);
//3.1隐藏fragment
beginTransaction.hide(sdFragment);
beginTransaction.commit();
6.按钮操作
public void cache(View v){
FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
beginTransaction.hide(sdFragment);
beginTransaction.show(cacheFragment);//显示fragment
beginTransaction.commit();
}
public void sd(View v){
FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
beginTransaction.hide(cacheFragment);
beginTransaction.show(sdFragment);//显示fragment
beginTransaction.commit();
}
#12.4进度条的操作#
参考杀毒进度条操作
/**
* 扫描
*/
private void scanner() {
//1.获取包的管理者
//getActivity() : 获取fragment挂载的activity
final PackageManager pm = getActivity().getPackageManager();
tv_antivirus_name.setText("正在初始化64核缓存清理引擎...");
new Thread(){
public void run() {
//5.睡1秒,让程序延时1秒钟扫描
SystemClock.sleep(1000);
//获取安装的程序
List<PackageInfo> installedPackages = pm.getInstalledPackages(0);
//设置总进度
pb_antivirus_progressbar.setMax(installedPackages.size());
//设置当前进度
int progress = 0;
for (PackageInfo packageInfo : installedPackages) {
//5.睡100毫秒,增加进度显示的真实性
SystemClock.sleep(100);
//设置进度
progress++;
pb_antivirus_progressbar.setProgress(progress);
//获取应用程序名称
final String name = packageInfo.applicationInfo.loadLabel(pm).toString();
//在退出的时候,子线程还在运行,还没被及时处理,所以getActivity()会出现为null的现象
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
tv_antivirus_name.setText("正在扫描:"+name);
}
});
}
}
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
//隐藏进度条和textivew文本
tv_antivirus_name.setVisibility(View.GONE);
pb_antivirus_progressbar.setVisibility(View.GONE);
}
});
}
};
}.start();
}
#12.5获取缓存的操作#
1.反射获取相应的方法
PackageManager pm = getPackageManager();
// pm.getPackageSizeInfo("com.itcast.cache", mStatsObserver);
// 反射操作
// 获取类加载器,获取相应类的class
Class<?> loadClass;
try {
loadClass = MainActivity.class.getClassLoader().loadClass(
"android.content.pm.PackageManager");
// 获取相应的方法
Method method = loadClass.getDeclaredMethod("getPackageSizeInfo",
String.class, IPackageStatsObserver.class);
// 执行相应的方法
method.invoke(pm, "com.itcast.cache", mStatsObserver);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
2.获取缓存大小
IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
throws RemoteException {
long cacheSize = pStats.cacheSize;
long codeSize = pStats.codeSize;
long dataSize = pStats.dataSize;
String cache = Formatter.formatFileSize(getApplicationContext(),
cacheSize);
String code = Formatter.formatFileSize(getApplicationContext(),
codeSize);
String data = Formatter.formatFileSize(getApplicationContext(),
dataSize);
System.out.println("cachesize:" + cache + " datasize:" + data
+ " codesize:" + code);
}
};
#12.6显示缓存#
1.将获取缓存的操作移植到手机卫士中,移植到for循环中,表示去获取每个应用的缓存信息
try {
Class<?> loadClass = getActivity().getClassLoader().loadClass(
"android.content.pm.PackageManager");
// 获取相应的方法
Method method = loadClass.getDeclaredMethod("getPackageSizeInfo",
String.class, IPackageStatsObserver.class);
// 执行相应的方法
method.invoke(pm, packageInfo.packageName, mStatsObserver);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
2.在获取应用缓存的方法中,判断是否有缓存,有就添加到list集合
if (cacheSize > 0) {
//将应用保存到集合中
list.add(new CacheInfo(pStats.packageName,cacheSize));
}
3.通过listview展示出来
private class Myadapter extends BaseAdapter{
@Override
public int getCount() {
return list.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = View.inflate(getActivity(), R.layout.item_cache, null);
viewHolder = new ViewHolder();
viewHolder.iv_itemcache_icon = (ImageView) view.findViewById(R.id.iv_itemcache_icon);
viewHolder.tv_itemcache_name = (TextView) view.findViewById(R.id.tv_itemcache_name);
viewHolder.tv_itemcache_cachesize = (TextView) view.findViewById(R.id.tv_itemcache_cachesize);
view.setTag(viewHolder);
}else{
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
CacheInfo cacheInfo = list.get(position);
String size = Formatter.formatFileSize(getActivity(), cacheInfo.getCachesize());
//显示缓存大小
viewHolder.tv_itemcache_cachesize.setText(size);
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(cacheInfo.getPackagename(), 0);
Drawable icon = applicationInfo.loadIcon(pm);
String name = applicationInfo.loadLabel(pm).toString();
//设置显示的应用的图标和名称
viewHolder.tv_itemcache_name.setText(name);
viewHolder.iv_itemcache_icon.setImageDrawable(icon);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return view;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
}
#12.7清理缓存#
1.进入详情界面
lv_cache_caches.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// 跳转到详情页面
Intent intent = new Intent();
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.parse("package:"
+ list.get(position).getPackagename()));
startActivity(intent);
}
});
2.全部清理系统中应用程序缓存
if (list.size() > 0) {
btn_cache_clear.setVisibility(View.VISIBLE);
btn_cache_clear
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 清理全部缓存
// freeStorageAndNotify
try {
Class<?> loadClass = getActivity()
.getClassLoader()
.loadClass(
"android.content.pm.PackageManager");
Method method = loadClass
.getDeclaredMethod(
"freeStorageAndNotify",
Long.TYPE,
IPackageDataObserver.class);
method.invoke(
pm,
Long.MAX_VALUE,
new MyIPackageDataObserver());
} catch (Exception e) {
// TODO Auto-generated catch
// block
e.printStackTrace();
}
//更新界面
list.clear();
myadapter.notifyDataSetChanged();
}
});
}
private class MyIPackageDataObserver extends IPackageDataObserver.Stub {
@Override
public void onRemoveCompleted(String packageName, boolean succeeded)
throws RemoteException {
// 缓存清理完之后会调用的方法
}
}
权限: <uses-permission android:name="android.permission.CLEAR_APP_CACHE"/>
#12.8全局bug收集#
1.创建一个application
//应用启动的时候,先调用就是application,才去调用的主界面
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
System.out.println("application启动了...");
Thread.currentThread().setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
}
private class MyUncaughtExceptionHandler implements UncaughtExceptionHandler{
//有未捕获的异常时会调用的方法,临终遗言
//Throwable : Exception和Error的父类
@Override
public void uncaughtException(Thread thread, Throwable ex) {
System.out.println("哥捕获了异常!!!!");
try {
//将出现的异常保存到本地文件中
ex.printStackTrace(new PrintStream(new File("/mnt/sdcard/log.txt")));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//杀死程序,自杀,android.os.Process.myPid():就会得到自己的pid
android.os.Process.killProcess(android.os.Process.myPid());
}
}
}
2.将清单文件中的application的name设置为我们的application
<application
android:name=".MyApplication"
#12.9代码混淆#
sdk\tools\proguard\proguard-android.txt
拷贝到 工程的根目录下,是个清单文件一个目录
修改project.properties路径
proguard.config=proguard-android.txt:proguard-project.txt
四大组件和自定义控件,以及已经混淆过的第三方sdk不能混淆了
#12.10广告#
1.厂商直接找你,第三方广告平台 1000次 1毛钱,app都是跟用户基数
2.第三方的广告平台
id : b55327eed7258e53
秘钥: b486156834be6e52