MobileSafeNotes Day11

##Day11##

#11.1解锁操作# (发送广播的操作、服务中注册广播接受广播事件)

问题:打开API Demos时,弹出解锁密码,点击返回键退出API Demos,进入手机卫士时,本来应该显示手机卫士界面,界面却变成API Demos的解锁密码界面

解决:在退出API Demos时,API Demos的界面不可见了onStop(),让API Demos的解锁密码也不可见finish掉即可
在Watch Dog Activity.java中添加方法如下:
protected void onStop(){
	super.onStop();
//让API Demos的解锁密码不可见
	finish();
}

1.给button增加点击事件,在点击事件中判断密码输入是否正确,正确,finish输入密码界面,不正确提醒用户

首先,在Watch Dog Activity.java中点击布局文件activity_watchdog打开activity_watchdog.xml,给EditText控件添加id为ed_watchdog_password,然后给Button按钮设置点击事件unlock
然后在Watch Dog Activity.java中实现点击事件unlock方法,并将id ed_watchdog_password 初始化出来,即:
	ed_watchdog_password = (EditText) findViewById(R.id.ed_watchdog_password)

	public void unlock(View v){
		//获取输入的密码
		String password = ed_watchdog_password.getText().toString().trim();
		//判断密码是否正确
		if ("123".equals(password)) {
			//关闭输入密码界面
			finish();
		}else{
			Toast.makeText(getApplicationContext(), "密码错误", 0).show();
		}
	}

2.发现解锁之后,会重复显示输入密码界面()

原因:在服务中不断在监听当前打开的应用程序,判断包名在数据库中有没有,如果有 就重新走跳转到密码输入界面,所以就会不断弹出输入密码界面,解决办法:广播 (解锁后通知当前应用,不用再给加锁了,让activity给服务传递数据,有些同学会想到用startActivityforResult,但它是activity给activity传递数据用的,现在是activity给service传递数据,这个时候一般会用broadcast)
	a.在点击事件中 增加发送广播操作
		public void unlock(View v){
			//获取输入的密码
			String password = ed_watchdog_password.getText().toString().trim();
			//判断密码是否正确
			if ("123".equals(password)) {
				//发送广播
				Intent intent = new Intent();
				//发送的广播事件 ""表示我这个广播是要进行解锁操作的
				intent.setAction("content://cn.itcast.mobliesafexian02.unlock");
				//上边接收到包名后,再将包名给服务传递回去,表示我这个打开的应用已经解锁了
				intent.putExtra("packagename", packagename);
				sendBroadcast(intent);
				//关闭输入密码界面
				finish();
			}else{
				Toast.makeText(getApplicationContext(), "密码错误", 0).show();
			}
		}
	b.在服务中 注册广播进行接收
		//注册广播接受者
		unlockReceiver = new UnlockReceiver();
		//设置过滤条件
		IntentFilter intentFilter = new IntentFilter();
		//设置接受的广播事件
		intentFilter.addAction("content://cn.itcast.mobliesafexian02.unlock");
		//注册广播接受者 有注册就有注销
		registerReceiver(unlockReceiver, intentFilter);

		private class UnlockReceiver extends BroadcastReceiver{

			@Override
			public void onReceive(Context context, Intent intent) {
				//获取传递过来的包名
				unlockPackageName = intent.getStringExtra("packagename");
			}
		}

		然后在while循环进行判断 (如果包名不等于传递过来的包名)
		//6.判断包名是否在数据库中
		if (watchDogDao.queryLockApp(packageName)) {
			//如果包名不等于传递过来的包名
			if (!packageName.equals(unlockPackageName)) {
				//跳转到密码输入界面
				Intent intent = new Intent(WatchDogService.this,WatchDogActivity.class);
				//给跳转到的activity指定一个任务栈
				intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				//将包名传递给密码输入界面,用于显示图标和名称
				intent.putExtra("packagename", packageName);
				startActivity(intent);
			}
		}
	最后在onDestroy方法中注销广播接收者
	
	//如果广播接收者不为空(如果解锁接受者不为空,说明解锁了,那就达到目的了)
	if(unlockReceiver !=null){
		//注销广播接收者
		unregisterReceiver(unlockReceiver);
		//将广播接收者置为空
		unlockReceiver = null;
		
	}
	

3.增加锁屏之后,解锁,再次显示输入密码操作
	//注册锁屏的广播接受者
	screenOffReceiver = new ScreenOffReceiver();
	//设置过滤条件
	IntentFilter screenIntentFilter = new IntentFilter();
	screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
	registerReceiver(screenOffReceiver, screenIntentFilter);

	
	public class ScreenOffReceiver extends BroadcastReceiver{

		@Override
		public void onReceive(Context context, Intent intent) {
			unlockPackageName = null;
		}
	}
有注册就有注销,在onDestroy方法中注销:
	if(screenOffReceiver != null){
			unregisterReceiver(screenOffReceiver);
			screenOffReceiver =null;
		}

#11.2bug处理# (重点)

问题:打开应用程序,进入设置中心界面,以前都是点击退出键退出,现在不退出了,而是点击home键,然后打开API Demos 输入密码解锁后,弹出的是居然是设置中心界面(settingactivity界面),在点击退出,弹出的是功能列表界面(homeactivity界面),再返回,才是API Demos的界面

原因:我们打开的每一个应用程序都有一个任务栈,

① 比如我们首先是在桌面上,那么首先是“桌面的任务栈(launcher)”,
② 然后打开手机卫士,当前我们的任务栈就变成了“手机卫士的任务栈”,并且在launcher前面,
③ 然后打开“homeactivity界面”,那么在“手机卫士任务栈”中就有了“homeactivity”,然后打开“手机卫士界面”,就有了“手机卫士中设置中心(settingActivity)”在“手机卫士任务栈”中。
④ 这个做完之后,我们在点击home键,那么launcher任务栈又跑到了“手机卫士的任务栈”前边,
⑤ 这个时候,我们打开API Demos,就变成了API Demos任务栈,但是弹出输入密码键面,这个输入密码键面是属于手机卫士的,
⑥ 所以在“手机卫士的任务栈”中又有了一个watchdogactivity,这个时候我们看到的是watchdogactivity的界面,这就表明“手机卫士的任务栈”又跑到了前边,
⑦ 但是输入密码点击解锁之后,把watchdogactivity给finish掉了,所以弹出的是“手机卫士的任务栈”中的settingActivity界面,在把settingactivity退出,就是homeactivity界面了,再把homeactivity界面退出,就相当于把整个手机卫士应用退出了,就显示出API Demos界面了


解决:	
	当点击API Demos时,弹出的是watchdogactivity,这个watchdogactivity就直接放到了手机卫士的任务栈当中了,现在我们可以这样干:
让watchdogactivity单独占一个任务栈,即基础部分学的四种启动模式之一:singleInstance

此时 ⑤ 变成了 这个时候,我们打开API Demos,就变成了API Demos任务栈,但是弹出输入密码键面,这个输入密码键面是属于手机卫士的, 但是我们给watchdogactivity单独设置了一个任务栈,就变成了再apidemos任务栈前边有多了一个任务栈,这个任务栈是单独给watchdogactivity的
⑥ 点击解锁后,退出了watchdogactivity,这个任务也就没有了,所以弹出的是API Demos的界面

1. 4种启动模式 (重点)
	 android:launchMode="singleInstance"

	<!--  面试: 说一下android的四种启动模式以及它们的区别? 
		standard : 默认的启动
		singleTop : 如果activity在任务栈顶部的,就不去创建新的
		singleTask :如果任务栈中有activity,调用的时候,就会把,activity上部的其他activity给移除
		singleInstance : 就是单独给一个activity设置一个任务栈
	-->
但是还会有一个问题:
	点击API Demos,输入密码解锁后进入了API Demos界面,然后点击home键,会弹出最近列表,其中有手机卫士应用,点击手机卫士应用,又变成了输入密码键面,
原因:
解决:如下2

2.从最近列表中移除应用显示
	即在清单文件中对应的watchDogActivity配置中添加如下:
	android:excludeFromRecents="true"

	excludeFromRecents : 是否在最近列表中显示   true:不显示  false:显示   ,但是在清单文件中给哪个activity设置,activity调用打开的时候才会生效

#11.3解锁数据库优化操作# (重点)

问题:解决完上面的问题后,重新审视watchdogservice.java代码,发现还有一些问题,即开启线程时时刻刻监听用户打开的应用程序,并且我们在下边做了判断包名是否在数据库中的操作,即每隔500毫秒查询一次数据库,看下查询数据库中(queryLockApp)我们做了哪些操作,即-->获取数据库(getReadableDatabase),然后一直查询(query),再关闭数据库(close),每五百毫秒打开关闭一次数据库,是很耗费系统资源(cpu资源)的,所以一般我们不会这么干,前边我们在写数据的时候,还写过查询全部数据库,我们接下来就要用到这个查询全部数据库的操作了:

解决:
到watchdogservice.java中的run方法这里,调用一下查询全部的操作
               //调用查询全部的操作,返回一个List集合(将数据库中的所有数据存放到集合中,也就是存放到内存中)
				lockApps = watchDogDao.getAllLockApp();
		并且在while循环中加入如下代码:		
		//查看包名是否包含在返回的集合中 得到一个boolean值
						boolean islock = lockApps.contains(packageName);
						//如果有的话,说明已经加锁了
						//6.判断包名是否在数据库中
						if (islock) {

	好处:这样写,我只是始终打开子线程服务之后查询了一次数据库,之后一直将集合中的数据和打开应用程序的包名进行判断,看包名有没有,这样的操作可以解小打开关闭数据库的次数了,这个也是开发当中经常做的操作,即当遇到频繁的打开关闭数据库的操作的时候,将数据库中的所有数据存到集合里面,也就是说将原先一致查询数据库的操作改成将数据库的数据全部放到内存当中,然后在内存当中去进行查询操作,在内存当中查询比到数据库中查询要少消耗资源

问题:给api demos加锁是我们以前做的操作,但是如果打开软件管理,给三击加锁,这个时候点击三击应用,按理来说也应该弹出输入密码界面,但是却没有弹出
刚才我们将数据库中的所有数据存到了内存当中的集合,我们查询数据库的时候,三击的时候没有加锁,现在我们加锁了,但是数据库中的数据没有更新,所以接下来,我们还需要去更新集合了,即在加锁或者解锁的时候要更新集合,这里就要用到内容观察者了

判断一下,当加锁或者解锁的时候,去通知内容观察者内容发生变化了,注意,加锁解锁在哪里做比较合适,加锁的时候会调用添加包名的操作,所以可以在添加包名成功之后,去告诉内容观察者,数据已经变化,可以去更新数据了

1.在加锁或者解锁的时候通知内容观察者去更新数据 (写在watchdogdao.java中的添加包名中)
		//告诉内容观察者,数据已经变化,可以去更新数据 
		ContentResolver resolver = context.getContentResolver();
		Uri uri = Uri.parse("content://cn.itcast.mobliesafexian02.unlock.change");
		//通知内容观察者,数据已经发生变化了
		resolver.notifyChange(uri, null);
2.内容观察者就可以去执行更新操作了(写在watchdogservice.java中的oncreate方法中的子线程中)

		//在加锁或者解锁的时候要更新集合,用内容观察者,查看到数据变化的时候会调用onChange方法
		Uri uri = Uri.parse("content://cn.itcast.mobliesafexian02.unlock.change");
		
		//更新操作  在内容解析者中注册内容观察者,参数:1:需要一个uri 2.notifyForDescendents 需要一个匹配 true或false都可以 3.observer :需要一个内容观察者 new一个内容观察者出来,在它的参数位置new一个Handler出来
		getContentResolver().registerContentObserver(
					uri, true, new ContentObserver(null) {
						//实现onchange方法
						public void onChange(boolean selfChange) {
							//更新数据 将数据库中的所有数据存放到集合中,也就是存放到内存中
							lockApps = watchDogDao.getAllLockApp();
						};
				});

#11.4获取短信的操作#

/**
 * 获取短信
 */
public static void getAllSMS(Context context){
	
	//1.获取内容解析者
	ContentResolver resolver = context.getContentResolver();
	//2.获取内容提供者地址sms    sms表的地址
	//3.生成查询的地址
	Uri uri = Uri.parse("content://sms");
	//4.查询数据
	Cursor cursor = resolver.query(uri, new String[]{"address","date","type","body"}, null, null, null);
	while(cursor.moveToNext()){
		String address = cursor.getString(0);
		String date = cursor.getString(1);
		String type = cursor.getString(2);
		String body = cursor.getString(3);
		System.out.println("address:"+address+"   date:"+date+"   type:"+type+"   body:"+body);
	}
	
}

#11.5保存短信# (重点)

/**
 * 获取短信
 */
public static void getAllSMS(Context context){
	
	//1.获取内容解析者
	ContentResolver resolver = context.getContentResolver();
	//2.获取内容提供者地址sms    sms表的地址
	//3.生成查询的地址
	Uri uri = Uri.parse("content://sms");
	//4.查询数据
	Cursor cursor = resolver.query(uri, new String[]{"address","date","type","body"}, null, null, null);
	//1.xml序列器
	XmlSerializer serializer = Xml.newSerializer();
	try {
		//2.文件保存位置及保存的编码格式
		//os : 流信息
		//encoding : 编码格式
		serializer.setOutput(new FileOutputStream(new File("/mnt/sdcard/backup.xml")), "utf-8");
		//3.设置文件头信息
		//standalone : 是否已单独文件保存
		serializer.startDocument("utf-8", true);
		//4.根标签
		serializer.startTag(null, "smss");
		while(cursor.moveToNext()){
			//5.设置每个短信的标签
			serializer.startTag(null, "sms");
			//6.设置每个数据的标签,成对出现
			serializer.startTag(null, "address");
			String address = cursor.getString(0);
			//7.设置标签的文本内容
			serializer.text(address);
			serializer.endTag(null, "address");
			
			serializer.startTag(null, "date");
			String date = cursor.getString(1);
			serializer.text(date);
			serializer.endTag(null, "date");
			
			serializer.startTag(null, "type");
			String type = cursor.getString(2);
			serializer.text(type);
			serializer.endTag(null, "type");
			
			serializer.startTag(null, "body");
			String body = cursor.getString(3);
			serializer.text(body);
			serializer.endTag(null, "body");
			
			System.out.println("address:"+address+"   date:"+date+"   type:"+type+"   body:"+body);
			serializer.endTag(null, "sms");
		}
		serializer.endTag(null, "smss");
		serializer.endDocument();
		//8.将数据刷新到的文件中
		serializer.flush();
	} catch (IllegalArgumentException e) {
		e.printStackTrace();
	} catch (IllegalStateException e) {
		e.printStackTrace();
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	
}

#11.6进度条两种方式# (重点)

1.progressdialog
	progressDialog = new ProgressDialog(this);
	//设置不能取消
	progressDialog.setCancelable(false);
	progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

// progressDialog.setMax(max);//设置总进度,max:总进度
// progressDialog.setProgress(value);//设置当前进度,value :当前进度
progressDialog.show();
2.progerssbar,操作跟progressDialog相似

#11.7回调方法#(重点)

1.在处理的业务类中,编写一个接口,同时声明出要使用的方法
		public interface BackUpSMSListener{
			/**
			 * 设置总进度
			 * @param max
			 */
			void max(int max);
			/**
			 * 设置当前进度
			 * @param progress
			 */
			void progress(int progress);
		}
2.将接口以参数的形式,传递给要调用的业务方法,并在其中执行相关的接口的方法
	public static void getAllSMS(Context context,BackUpSMSListener listener){
		listener.max(count);
	}
3.在调用业务方法的时候,在方法中实现接口操作,在实现方法中去实现相应的操作
	SmsEngine.getAllSMS(getApplicationContext(),new BackUpSMSListener() {
				
				@Override
				public void progress(int progress) {
					//progressDialog.setProgress(progress);
					pb_atools_sms.setProgress(progress);
				}
				
				@Override
				public void max(int max) {
					//progressDialog.setMax(max);
					pb_atools_sms.setMax(max);
				}
	});

#11.8还原短信#

/**
 * 还原短信
 * @param v
 */
public void restoresms(View v){
	//内容解析者
	ContentResolver resolver = getContentResolver();
	Uri uri = Uri.parse("content://sms");
	ContentValues values = new ContentValues();
	values.put("address", 95588);
	values.put("date", System.currentTimeMillis()+"");
	values.put("type", 1);
	values.put("body", "zhuan zhang le $10000000000000000");
	resolver.insert(uri, values);
	
}

#11.9可扩展的listview#

1.在布局文件中布局
	<ExpandableListView 
    android:id="@+id/expandablelistview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    ></ExpandableListView>
2.在代码中调用
	expandablelistview = (ExpandableListView) findViewById(R.id.expandablelistview);
	expandablelistview.setAdapter(new Myadapter());

	adapter

	private class Myadapter extends BaseExpandableListAdapter {
			// 获取组的个数
			@Override
			public int getGroupCount() {
				// TODO Auto-generated method stub
				return 8;
			}
	
			// 获取每组有多少个孩子 groupPosition : 代表的组的位置,从0开始的
			@Override
			public int getChildrenCount(int groupPosition) {
				// TODO Auto-generated method stub
				return groupPosition+1;
			}
	
			// 获取组的样式
			@Override
			public View getGroupView(int groupPosition, boolean isExpanded,
					View convertView, ViewGroup parent) {
				TextView textView = new TextView(getApplicationContext());
				textView.setText("             我是第"+groupPosition+"组");
				return textView;
			}
	
			// 获取组的孩子的样式
			//childPosition : 还在的位置
			@Override
			public View getChildView(int groupPosition, int childPosition,
					boolean isLastChild, View convertView, ViewGroup parent) {
				TextView textView = new TextView(getApplicationContext());
				textView.setText("我是第"+groupPosition+"组的第"+childPosition+"个孩子");
				textView.setTextColor(Color.RED);
				return textView;
			}
	
			// 获取组的对象对应的数据
			@Override
			public Object getGroup(int groupPosition) {
				// TODO Auto-generated method stub
				return null;
			}
	
			// 获取组的孩子的对象的数据
			@Override
			public Object getChild(int groupPosition, int childPosition) {
				// TODO Auto-generated method stub
				return null;
			}
	
			// 获取组的id
			@Override
			public long getGroupId(int groupPosition) {
				// TODO Auto-generated method stub
				return 0;
			}
	
			// 获取组的孩子的id
			@Override
			public long getChildId(int groupPosition, int childPosition) {
				// TODO Auto-generated method stub
				return 0;
			}
	
			// 判断id是否稳定,判断有没有id ,有的true,没有的false
			@Override
			public boolean hasStableIds() {
				// TODO Auto-generated method stub
				return false;
			}
	
			// 设置孩子是否可以点击,false:可以
			@Override
			public boolean isChildSelectable(int groupPosition, int childPosition) {
				// TODO Auto-generated method stub
				return false;
			}

	}

#11.10抽屉的效果# (重点)

1.第一种方式
	 	<!-- handle : 抽屉的把手
		content : 抽屉的内容
	     -->
	   <SlidingDrawer 
	       android:layout_width="match_parent"
	       android:layout_height="match_parent"
	       android:handle="@+id/handle"
	       android:content="@+id/content"
	       android:orientation="horizontal"
	       >
	       <LinearLayout 
	           android:id="@+id/handle"
	           android:layout_width="wrap_content"
	           android:layout_height="match_parent"
	           android:orientation="vertical"
	           >
	           <ImageView 
	           android:layout_width="wrap_content"
	           android:layout_height="wrap_content"
	           android:src="@drawable/shenmabg"
	           
	           />
	       </LinearLayout>
	       <FrameLayout 
	           android:id="@+id/content"
	           android:layout_width="match_parent"
	           android:layout_height="match_parent"
	           android:background="#ff0000"
	           ></FrameLayout>
	   </SlidingDrawer>
2.第二种方式
	<android.support.v4.widget.DrawerLayout 
	    xmlns:android="http://schemas.android.com/apk/res/android"
	    android:id="@+id/dl"
	    android:layout_width="match_parent"
	    android:layout_height="match_parent"
	    android:orientation="vertical" >
		<RelativeLayout 
		    android:layout_width="match_parent"
	    	android:layout_height="match_parent"
		    >
		    <TextView
		        android:id="@+id/textView1"
		        android:layout_width="match_parent"
		        android:layout_height="wrap_content"
		        android:text="流量统计" 
		        android:textSize="25sp"
		        android:gravity="center_horizontal"
		        android:paddingTop="10dp"
		        android:paddingBottom="10dp"
		        android:background="#8866ff00"
				/>
	    </RelativeLayout>
	    <LinearLayout 
	        android:layout_gravity="right"
	        android:layout_width="match_parent"
	    	android:layout_height="match_parent"
	    	android:background="#ff0000"
	        ></LinearLayout>
	    <LinearLayout 
	        android:layout_gravity="left"
	        android:layout_width="match_parent"
	    	android:layout_height="match_parent"
	    	android:background="#0000ff"
	        ></LinearLayout>
	</android.support.v4.widget.DrawerLayout>

	代码中调用
		dl = (DrawerLayout) findViewById(R.id.dl);
		//打开左边的控件
		dl.openDrawer(Gravity.LEFT);
		//打开右边的控件
		dl.openDrawer(Gravity.RIGHT);

#11.11杀毒发展史#

1.病毒:计算机程序,恶意行为,三类
	1.恶搞,炫耀自己的技术,熊猫烧香,损人不利己
	2.灰鸽子 : 盗号的,QQ号,游戏的账号
	3.肉机:把你的电脑做成傀儡机,他通过程序控制你的电脑,
2.杀毒方式
	1.黑名单方式,把一些已有病毒特征码保存到数据库,通过匹配数据库中特征进行操作,滞后性,裸机(没有任何防护的电脑,吸引病毒),蜜罐(网络的主干道上),云查杀:就是把你的电脑当成蜜罐,发现有病毒就上传
	2.白名单,360,把已经检测过没问题的程序放到数据库中,当程序运行的时候,如果发现是在数据库中就不在去检测杀毒,可以及时的发现解决病毒

#11.12杀毒界面# (重点layer-list)

layer-list : framlayout,一个控件叠着另一个控件,在下面的显示的时候是在最上边

1.res-> drawable -> xxx.xml   注意:id不可以变

	<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
	    <item
	        android:id="@android:id/background"
	        >
	      <bitmap android:src="@drawable/security_progress_bg"
	        android:gravity="center" />
	    </item>
	    <item 
	        android:id="@android:id/secondaryProgress"
	        >
	      <bitmap android:src="@drawable/security_progress"
	        android:gravity="center" />
	    </item>
	    <item
	        android:id="@android:id/progress"
	        >
	      <bitmap android:src="@drawable/security_progress"
	        android:gravity="center" />
	    </item>
	</layer-list>
2.使用
	<!-- progress : 设置progerssbar的进度 -->
  	<ProgressBar
          android:id="@+id/progressBar1"
          style="?android:attr/progressBarStyleHorizontal"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_below="@+id/tv_antivirus_name"
          android:layout_toRightOf="@+id/iv_antivirus_antivimageview"
          android:layout_marginLeft="10dp"
          android:layout_marginRight="10dp"
          android:progressDrawable="@drawable/anti_progressbar_drawable"
          android:progress="30"
          android:layout_marginTop="5dp"
          />

#11.13扫描程序# (重点)

旋转动画  面试:动画有几类,有什么区别,每类有哪些操作,
	//旋转动画
	//参数1:开始的角度
	//参数2:结束的角度
	//剩下的参数:控制动画的位置
	RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,  Animation.RELATIVE_TO_SELF, 0.5f);
	
	rotateAnimation.setDuration(2000);
	//旋转的次数
	rotateAnimation.setRepeatCount(Animation.INFINITE);//INFINITE : 一直旋转
	//解决旋转停顿的问题
	LinearInterpolator interpolator = new LinearInterpolator();
	rotateAnimation.setInterpolator(interpolator);
	iv_antivirus_scanner.startAnimation(rotateAnimation);

进度条及扫描效果的实现
/**
 * 扫描程序
 */
private void scanner() {
	//1.获取包的管理者
	final PackageManager pm = getPackageManager();
	tv_antivirus_name.setText("正在初始化64核扫描引擎...");
	new Thread(){
		public void run() {
			//5.睡1秒,让程序延时1秒钟扫描
			SystemClock.sleep(1000);
			//2.获取安装的程序
			List<PackageInfo> installedPackages = pm.getInstalledPackages(0);
			//3.设置progerssbar的总进度
			pb_antivirus_progressbar.setMax(installedPackages.size());
			//4.设置当前进度
			int progress = 0;
			for (PackageInfo packageInfo : installedPackages) {
				//5.睡100毫秒,增加进度显示的真实性
				SystemClock.sleep(100);
				//4.设置当前进度
				progress++;
				pb_antivirus_progressbar.setProgress(progress);
				//6.获取应用的名称
				final String name = packageInfo.applicationInfo.loadLabel(pm).toString();
				runOnUiThread(new Runnable() {
					
					@Override
					public void run() {
						//7.设置扫描的文字
						tv_antivirus_name.setText("正在扫描:"+name);
						//9.设置在下方空白处显示扫描显示的应用成名称
						TextView textView = new TextView(getApplicationContext());
						textView.setText(name);
						textView.setTextColor(Color.BLACK);
						ll_antivirus_safeapk.addView(textView, 0);//index:将view添加到哪个位置
					}
				});
			}
			runOnUiThread(new Runnable() {
				
				@Override
				public void run() {
					//8.设置扫描完成的文字和停止动画操作
					tv_antivirus_name.setText("扫描完成,很安全");
					//停止动画
					iv_antivirus_scanner.clearAnimation();
				}
			});
		};
	}.start();
}
基于SSM框架的智能家政保洁预约系统,是一个旨在提高家政保洁服务预约效率和管理水平的平台。该系统通过集成现代信息技术,为家政公司、家政服务人员和消费者提供了一个便捷的在线预约和管理系统。 系统的主要功能包括: 1. **用户管理**:允许消费者注册、登录,并管理他们的个人资料和预约历史。 2. **家政人员管理**:家政服务人员可以注册并更新自己的个人信息、服务类别和服务时间。 3. **服务预约**:消费者可以浏览不同的家政服务选项,选择合适的服务人员,并在线预约服务。 4. **订单管理**:系统支持订单的创建、跟踪和管理,包括订单的确认、完成和评价。 5. **评价系统**:消费者可以在家政服务完成后对服务进行评价,帮助提高服务质量和透明度。 6. **后台管理**:管理员可以管理用户、家政人员信息、服务类别、预约订单以及处理用户反馈。 系统采用Java语言开发,使用MySQL数据库进行数据存储,通过B/S架构实现用户与服务的在线交互。系统设计考虑了不同用户角色的需求,包括管理员、家政服务人员和普通用户,每个角色都有相应的权限和功能。此外,系统还采用了软件组件化、精化体系结构、分离逻辑和数据等方法,以便于未来的系统升级和维护。 智能家政保洁预约系统通过提供一个集中的平台,不仅方便了消费者的预约和管理,也为家政服务人员提供了一个展示和推广自己服务的机会。同时,系统的后台管理功能为家政公司提供了强大的数据支持和决策辅助,有助于提高服务质量和管理效率。该系统的设计与实现,标志着家政保洁服务向现代化和网络化的转型,为管理决策和控制提供保障,是行业发展中的重要里程碑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值