Android程序鎖的實現(xiàn)以及邏輯
本項目是一個比較有趣的項目源碼,可以給其他項目加鎖,程序鎖的原理是一個“看門狗”的服務(wù)定時監(jiān)視頂層activity,如果activity對應(yīng)的包名是之前上鎖的應(yīng)用程序的,則彈出一個頁面要求輸入解鎖密碼。
效果如下:

1.基本思路
①.創(chuàng)建已加鎖應(yīng)用的數(shù)據(jù)庫(字段:_id,packagename),如果應(yīng)用已加鎖,將加鎖應(yīng)用的包名維護(hù)到數(shù)據(jù)庫中
②.已加鎖+未加鎖 == 手機(jī)中所有應(yīng)用(AppInfoProvider)
2.已加鎖和未加鎖的數(shù)據(jù)適配器
class MyAdapter extends BaseAdapter{
private boolean isLock;
/**
* @param isLock 用于區(qū)分已加鎖和未加鎖應(yīng)用的標(biāo)示 true已加鎖數(shù)據(jù)適配器 false未加鎖數(shù)據(jù)適配器
*/
public MyAdapter(boolean isLock) {
this.isLock = isLock;
}
@Override
public int getCount() {
if(isLock){
tv_lock.setText("已加鎖應(yīng)用:"+mLockList.size());
return mLockList.size();
}else{
tv_unlock.setText("未加鎖應(yīng)用:"+mUnLockList.size());
return mUnLockList.size();
}
}
@Override
public AppInfo getItem(int position) {
if(isLock){
return mLockList.get(position);
}else{
return mUnLockList.get(position);
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
convertView = View.inflate(getApplicationContext(), R.layout.listview_islock_item, null);
holder = new ViewHolder();
holder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
holder.iv_lock = (ImageView) convertView.findViewById(R.id.iv_lock);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
final AppInfo appInfo = getItem(position);
final View animationView = convertView;
holder.iv_icon.setBackgroundDrawable(appInfo.icon);
holder.tv_name.setText(appInfo.name);
if(isLock){
holder.iv_lock.setBackgroundResource(R.drawable.lock);
}else{
holder.iv_lock.setBackgroundResource(R.drawable.unlock);
}
holder.iv_lock.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//添加動畫效果,動畫默認(rèn)是非阻塞的,所以執(zhí)行動畫的同時,動畫以下的代碼也會執(zhí)行
animationView.startAnimation(mTranslateAnimation);//500毫秒
//對動畫執(zhí)行過程做事件監(jiān)聽,監(jiān)聽到動畫執(zhí)行完成后,再去移除集合中的數(shù)據(jù),操作數(shù)據(jù)庫,刷新界面
mTranslateAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//動畫開始的是調(diào)用方法
}
@Override
public void onAnimationRepeat(Animation animation) {
//動畫重復(fù)時候調(diào)用方法
}
//動畫執(zhí)行結(jié)束后調(diào)用方法
@Override
public void onAnimationEnd(Animation animation) {
if(isLock){
//已加鎖------>未加鎖過程
//1.已加鎖集合刪除一個,未加鎖集合添加一個,對象就是getItem方法獲取的對象
mLockList.remove(appInfo);
mUnLockList.add(appInfo);
//2.從已加鎖的數(shù)據(jù)庫中刪除一條數(shù)據(jù)
mDao.delete(appInfo.packageName);
//3.刷新數(shù)據(jù)適配器
mLockAdapter.notifyDataSetChanged();
}else{
//未加鎖------>已加鎖過程
//1.已加鎖集合添加一個,未加鎖集合移除一個,對象就是getItem方法獲取的對象
mLockList.add(appInfo);
mUnLockList.remove(appInfo);
//2.從已加鎖的數(shù)據(jù)庫中插入一條數(shù)據(jù)
mDao.insert(appInfo.packageName);
//3.刷新數(shù)據(jù)適配器
mUnLockAdapter.notifyDataSetChanged();
}
}
});
}
});
return convertView;
}
}
mLockAdapter = new MyAdapter(true); lv_lock.setAdapter(mLockAdapter); mUnLockAdapter = new MyAdapter(false); lv_unlock.setAdapter(mUnLockAdapter);
3.已加鎖和未加鎖條目點擊事件處理
holder.iv_lock.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//添加動畫效果,動畫默認(rèn)是非阻塞的,所以執(zhí)行動畫的同時,動畫以下的代碼也會執(zhí)行
animationView.startAnimation(mTranslateAnimation);//500毫秒
//對動畫執(zhí)行過程做事件監(jiān)聽,監(jiān)聽到動畫執(zhí)行完成后,再去移除集合中的數(shù)據(jù),操作數(shù)據(jù)庫,刷新界面
mTranslateAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//動畫開始的是調(diào)用方法
}
@Override
public void onAnimationRepeat(Animation animation) {
//動畫重復(fù)時候調(diào)用方法
}
//動畫執(zhí)行結(jié)束后調(diào)用方法
@Override
public void onAnimationEnd(Animation animation) {
if(isLock){
//已加鎖------>未加鎖過程
//1.已加鎖集合刪除一個,未加鎖集合添加一個,對象就是getItem方法獲取的對象
mLockList.remove(appInfo);
mUnLockList.add(appInfo);
//2.從已加鎖的數(shù)據(jù)庫中刪除一條數(shù)據(jù)
mDao.delete(appInfo.packageName);
//3.刷新數(shù)據(jù)適配器
mLockAdapter.notifyDataSetChanged();
}else{
//未加鎖------>已加鎖過程
//1.已加鎖集合添加一個,未加鎖集合移除一個,對象就是getItem方法獲取的對象
mLockList.add(appInfo);
mUnLockList.remove(appInfo);
//2.從已加鎖的數(shù)據(jù)庫中插入一條數(shù)據(jù)
mDao.insert(appInfo.packageName);
//3.刷新數(shù)據(jù)適配器
mUnLockAdapter.notifyDataSetChanged();
}
}
});
}
});
4.程序鎖必須在服務(wù)中去維護(hù)

①基本思路
- 判斷當(dāng)前開啟的應(yīng)用(現(xiàn)在手機(jī)可見任務(wù)棧)
- 如果開啟的應(yīng)用在已加鎖的列表中,彈出攔截界面
- 看門狗服務(wù),一直(死循環(huán)(子線程,可控))對開啟的應(yīng)用做監(jiān)聽
public class WatchDogService extends Service {
private boolean isWatch;
private AppLockDao mDao;
private List<String> mPacknameList;
private InnerReceiver mInnerReceiver;
private String mSkipPackagename;
private MyContentObserver mContentObserver;
@Override
public void onCreate() {
//維護(hù)一個看門狗的死循環(huán),讓其時刻監(jiān)測現(xiàn)在開啟的應(yīng)用,是否為程序鎖中要去攔截的應(yīng)用
mDao = AppLockDao.getInstance(this);
isWatch = true;
watch();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.SKIP");
mInnerReceiver = new InnerReceiver();
registerReceiver(mInnerReceiver, intentFilter);
//注冊一個內(nèi)容觀察者,觀察數(shù)據(jù)庫的變化,一旦數(shù)據(jù)有刪除或者添加,則需要讓mPacknameList重新獲取一次數(shù)據(jù)
mContentObserver = new MyContentObserver(new Handler());
getContentResolver().registerContentObserver(
Uri.parse("content://applock/change"), true, mContentObserver);
super.onCreate();
}
class MyContentObserver extends ContentObserver{
public MyContentObserver(Handler handler) {
super(handler);
}
//一旦數(shù)據(jù)庫發(fā)生改變時候調(diào)用方法,重新獲取包名所在集合的數(shù)據(jù)
@Override
public void onChange(boolean selfChange) {
new Thread(){
public void run() {
mPacknameList = mDao.findAll();
};
}.start();
super.onChange(selfChange);
}
}
class InnerReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//獲取發(fā)送廣播過程中傳遞過來的包名,跳過次包名檢測過程
mSkipPackagename = intent.getStringExtra("packagename");
}
}
private void watch() {
//1,子線程中,開啟一個可控死循環(huán)
new Thread(){
public void run() {
mPacknameList = mDao.findAll();
while(isWatch){
//2.監(jiān)測現(xiàn)在正在開啟的應(yīng)用,任務(wù)棧
//3.獲取activity管理者對象
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
//4.獲取正在開啟應(yīng)用的任務(wù)棧
List<RunningTaskInfo> runningTasks = am.getRunningTasks(1);
RunningTaskInfo runningTaskInfo = runningTasks.get(0);
//5.獲取棧頂?shù)腶ctivity,然后在獲取此activity所在應(yīng)用的包名
String packagename = runningTaskInfo.topActivity.getPackageName();
//如果任務(wù)棧指向應(yīng)用有切換,將mSkipPackagename空字符串
//6.拿此包名在已加鎖的包名集合中去做比對,如果包含次包名,則需要彈出攔截界面
if(mPacknameList.contains(packagename)){
//如果現(xiàn)在檢測的程序,以及解鎖了,則不需要去彈出攔截界面
if(!packagename.equals(mSkipPackagename)){
//7,彈出攔截界面
Intent intent = new Intent(getApplicationContext(),EnterPsdActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("packagename", packagename);
startActivity(intent);
}
}
//睡眠一下,時間片輪轉(zhuǎn)
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onDestroy() {
//停止看門狗循環(huán)
isWatch = false;
//注銷廣播接受者
if(mInnerReceiver!=null){
unregisterReceiver(mInnerReceiver);
}
//注銷內(nèi)容觀察者
if(mContentObserver!=null){
getContentResolver().unregisterContentObserver(mContentObserver);
}
super.onDestroy();
}
}
public class EnterPsdActivity extends Activity {
private String packagename;
private TextView tv_app_name;
private ImageView iv_app_icon;
private EditText et_psd;
private Button bt_submit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//獲取包名
packagename = getIntent().getStringExtra("packagename");
setContentView(R.layout.activity_enter_psd);
initUI();
initData();
}
private void initData() {
//通過傳遞過來的包名獲取攔截應(yīng)用的圖標(biāo)以及名稱
PackageManager pm = getPackageManager();
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(packagename,0);
Drawable icon = applicationInfo.loadIcon(pm);
iv_app_icon.setBackgroundDrawable(icon);
tv_app_name.setText(applicationInfo.loadLabel(pm).toString());
} catch (NameNotFoundException e) {
e.printStackTrace();
}
bt_submit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String psd = et_psd.getText().toString();
if(!TextUtils.isEmpty(psd)){
if(psd.equals("123")){
//解鎖,進(jìn)入應(yīng)用,告知看門口不要再去監(jiān)聽以及解鎖的應(yīng)用,發(fā)送廣播
Intent intent = new Intent("android.intent.action.SKIP");
intent.putExtra("packagename",packagename);
sendBroadcast(intent);
finish();
}else{
ToastUtil.show(getApplicationContext(), "密碼錯誤");
}
}else{
ToastUtil.show(getApplicationContext(), "請輸入密碼");
}
}
});
}
private void initUI() {
tv_app_name = (TextView) findViewById(R.id.tv_app_name);
iv_app_icon = (ImageView) findViewById(R.id.iv_app_icon);
et_psd = (EditText) findViewById(R.id.et_psd);
bt_submit = (Button) findViewById(R.id.bt_submit);
}
@Override
public void onBackPressed() {
//通過隱式意圖,跳轉(zhuǎn)到桌面
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
super.onBackPressed();
}
}

5.隱藏最近打開的activity
<activity android:excludeFromRecents="true" android:name="com.itheima.mobilesafe.EnterPwdActivity" android:launchMode="singleInstance" />
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android仿知乎標(biāo)題欄隨ScrollView滾動變色
這篇文章主要為大家詳細(xì)介紹了android仿知乎標(biāo)題欄隨ScrollView滾動變色,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06
Android編程實現(xiàn)應(yīng)用強(qiáng)制安裝到手機(jī)內(nèi)存的方法
這篇文章主要介紹了Android編程實現(xiàn)應(yīng)用強(qiáng)制安裝到手機(jī)內(nèi)存的方法,涉及Android中屬性設(shè)置的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-11-11
Android手冊之Toolbar搜索聯(lián)動及監(jiān)聽小技巧
這篇文章主要為大家介紹了Android手冊之Toolbar搜索聯(lián)動及監(jiān)聽小技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Android RecyclerView 滾動到中間位置的方法示例
這篇文章主要介紹了Android RecyclerView 滾動到中間位置的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
Android安卓中循環(huán)錄像并檢測內(nèi)存卡容量
這篇文章主要介紹了Android安卓中循環(huán)錄像并檢測內(nèi)存卡容量,當(dāng)內(nèi)存卡空間已滿時,本文還實現(xiàn)自動刪除視頻列表里面的第一個文件,需要的朋友可以參考下2015-06-06
Android基礎(chǔ)之使用Fragment控制切換多個頁面
Android官方已經(jīng)提供了Fragment的各種使用的Demo例子,在我們SDK下面的API Demo里面就包含了Fragment的各種使用例子,需要看Demo的朋友,直接看API Demo那個程序就可以了,不用到處去找。里面分開不同功能,實現(xiàn)了不同的類2013-07-07

