Skip to content

Latest commit

 

History

History
528 lines (480 loc) · 20.9 KB

项目优化建议.md

File metadata and controls

528 lines (480 loc) · 20.9 KB

项目优化建议

一共有 6 点小建议:

第一点:

LockService 应用锁服务优化建议,服务不再继承 IntentService 了,继承 Service,在里面自己开线程。

循环的判断条件不用简单的布尔型,而用具有原子操作性的 AtomicBoolean 代替:

public class AppLockService extends Service {
    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

    @Override
    public void onCreate() {
        super.onCreate();
        //...
        AsyncTask.SERIAL_EXECUTOR.execute(new ServiceWorker());
    }

    private class ServiceWorker implements Runnable {

      @Override
      public void run() {
          while (!mIsServiceDestoryed.get()) {
              //...
          }
      }
    }

    @Override
   public void onDestroy() {
       mIsServiceDestoryed.set(true);
       super.onDestroy();
   }
}

第二点:

目前循环里面的获取 Sp 文件的操作太多太频繁了,因为读写 Sp 文件有缓冲时间,而且又是属于读写文件,在循环这么频繁的情况下性能感觉不好而已有点慢。 而加锁时间这些变量又需要实时改变的,所以不能写成全局变量,所以优化建议是可以用全局的静态变量来存储代替 sp 文件。而 sp 文件只保存最后一次 改变的时间,如果app被杀掉,下次打开的时候,sp 文件的值付给静态变量就可以。

第三点:

解锁界面,现在项目里面的解锁界面是一个 Activity,建议换成 Window 悬浮窗来实现,下面给个代码示例参考,可以直接使用或者自己实现:

public class UnlockView extends FrameLayout {

    private int mFailedPatternAttemptsSinceLastTimeout = 0;

    private WindowManager.LayoutParams mLayoutParams;
    private WindowManager mWindowManager;
    private Context mContext;
    private View mUnLockView;
    private Drawable iconDrawable;
    private String appLabel;

    private ImageView mBgView, mUnLockIcon, mBtnMore;
    private TextView mUnLockAppName, mUnlockFailTip;
    private LockPatternView mPatternView;

    private LockPatternUtils mPatternUtils;
    private LockPatternViewPattern mPatternViewPattern;
    private LockAppInfo mLockAppInfo;
    private ApplicationInfo mApplicationInfo;
    private PackageManager mPackageManager;

    public UnlockView(@NonNull Context context) {
        super(context, null, 0);
        init();
    }

    private void init() {
        mContext = getContext();

        mPackageManager = mContext.getPackageManager();

        mUnLockView = LayoutInflater.from(mContext).inflate(R.layout.layout_unlock_view, this);
        mBgView = mUnLockView.findViewById(R.id.bg_view);
        mUnLockIcon = mUnLockView.findViewById(R.id.unlock_icon);
        mBtnMore = mUnLockView.findViewById(R.id.btn_more);
        mUnLockAppName = mUnLockView.findViewById(R.id.unlock_app_name);
        mPatternView = mUnLockView.findViewById(R.id.unlock_lock_view);
        mUnlockFailTip = mUnLockView.findViewById(R.id.unlock_fail_tip);

        //创建悬浮窗
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        mLayoutParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                PixelFormat.TRANSPARENT
        );
        mLayoutParams.gravity = Gravity.CENTER;

        initLockPatternView();
    }

    public void setLockAppInfo(LockAppInfo lockAppInfo) {
        mLockAppInfo = lockAppInfo;
    }

    private final static int MSG_ADDVIEW = 100;
    private final static int MSG_GO_HOME = 200;

    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_ADDVIEW:
                    mWindowManager.addView(UnlockView.this, mLayoutParams);
                    break;
                case MSG_GO_HOME:
                    closeUnLockView();
                    break;
            }
        }
    };

    /**
     * 打开解锁界面
     */
    public void showUnLockView() {
        if (mLockAppInfo == null) {
            return;
        }
        initBgView();
        mHandler.obtainMessage(MSG_ADDVIEW).sendToTarget();
    }

    /**
     * 关闭解锁界面
     */
    private boolean closeUnLockView() {
        if (mWindowManager != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (isAttachedToWindow()) {
                    mWindowManager.removeViewImmediate(this);
                    return true;
                } else {
                    return false;
                }
            } else {
                try {
                    if (getParent() != null) {
                        mWindowManager.removeViewImmediate(this);
                    }
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
        } else {
            return false;
        }
    }

    /**
     * 按Home键
     */
    public void closeUnLockViewFormHomeAction() {
        if (getParent() != null && mHandler != null) {
            mHandler.sendEmptyMessageDelayed(MSG_GO_HOME, 500);
        }
    }

    /**
     * 背景图片
     */
    private void initBgView() {
        mApplicationInfo = mLockAppInfo.getAppInfo();
        if (mApplicationInfo != null) {
            try {
                iconDrawable = mPackageManager.getApplicationIcon(mApplicationInfo);
                appLabel = mLockAppInfo.getAppName();
                mUnLockIcon.setImageDrawable(iconDrawable);
                mUnLockAppName.setText(appLabel);
                mUnlockFailTip.setText(mContext.getString(R.string.password_gestrue_tips));
                mBgView.setBackground(iconDrawable);
                mBgView.getViewTreeObserver().addOnPreDrawListener(
                        new ViewTreeObserver.OnPreDrawListener() {
                            @Override
                            public boolean onPreDraw() {
                                mBgView.getViewTreeObserver().removeOnPreDrawListener(this);
                                mBgView.buildDrawingCache();
                                Bitmap bmp = BlurUtil.drawableToBitmap(iconDrawable, mBgView);
                                BlurUtil.blur(mContext, BlurUtil.big(bmp), mBgView);  //高斯模糊
                                return true;
                            }
                        });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 初始化解锁控件
     */
    private void initLockPatternView() {
        mPatternView.setLineColorRight(0x80ffffff);
        mPatternUtils = new LockPatternUtils(mContext);
        mPatternViewPattern = new LockPatternViewPattern(mPatternView);
        mPatternViewPattern.setPatternListener(new LockPatternViewPattern.onPatternListener() {
            @Override
            public void onPatternDetected(List<LockPatternView.Cell> pattern) {
                if (mPatternUtils.checkPattern(pattern)) { //解锁成功,更改数据库状态
                    mPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
                    //TODO
                    closeUnLockView();
                } else {
                    mPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
                    if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
                        mFailedPatternAttemptsSinceLastTimeout++;
                        int retry = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT - mFailedPatternAttemptsSinceLastTimeout;
                        if (retry >= 0) {
                            String format = mContext.getResources().getString(R.string.password_error_count);
                            mUnlockFailTip.setText(format);
                        }
                    } else {
                        mUnlockFailTip.setText(mContext.getResources().getString(R.string.password_short));
                    }
                    if (mFailedPatternAttemptsSinceLastTimeout >= 3) { //失败次数大于3次
                        mPatternView.postDelayed(mClearPatternRunnable, 500);
                    }
                    if (mFailedPatternAttemptsSinceLastTimeout >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { //失败次数大于阻止用户前的最大错误尝试次数
                        mPatternView.postDelayed(mClearPatternRunnable, 500);
                    } else {
                        mPatternView.postDelayed(mClearPatternRunnable, 500);
                    }
                }
            }
        });
        mPatternView.setOnPatternListener(mPatternViewPattern);
        mPatternView.setTactileFeedbackEnabled(true);
    }

    private Runnable mClearPatternRunnable = new Runnable() {
        public void run() {
            mPatternView.clearPattern();
        }
    };

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            if (getParent() != null) {
                LockUtil.launchHome(mContext);
                mHandler.sendEmptyMessageDelayed(MSG_GO_HOME, 500);
            }
            return true;
        }
        return false;
    }
}

第四点:

如果你有使用 RxJava,那就结合 RxJava 去修改吧,效果很棒。
比如,目前项目里面获取应用列表的操作是放到一个服务里面做的,当时这么做的原因是,把这样一个复杂的操作放到后台做,就不会因为 耗时而导致卡顿甚至ANR的情况。如果你用了 RxJava ,线程切换变得非常简单,可以用 RxJava 实现就行,下面给出代码示例,LoadAppHelper 是获取应用列表的帮助类, DbManager 是数据库操作管理类,非常简单,可以自己实现,就不贴代码了:

//初始化数据
LoadAppHelper.loadAllLockAppInfoAsync(this)
   .subscribeOn(Schedulers.newThread())
   .observeOn(AndroidSchedulers.mainThread())
   .filter(new Predicate<List<LockAppInfo>>() {
       @Override
       public boolean test(List<LockAppInfo> lockAppInfos) throws Exception {
           if (isFirstTime) {
               DbManager.get()
                       .saveLockAppInfoListAsync(lockAppInfos)
                       .subscribeOn(Schedulers.newThread())
                       .observeOn(AndroidSchedulers.mainThread())
                       .subscribe(new Consumer<Boolean>() {
                           @Override
                           public void accept(Boolean aBoolean) throws Exception {
                               animator.start();
                           }
                       });
               return false;
           } else {
               return true;
           }
       }
   })
   .observeOn(Schedulers.newThread())
   .map(new Function<List<LockAppInfo>, List<LockAppInfo>>() {
       @Override
       public List<LockAppInfo> apply(List<LockAppInfo> appList) throws Exception {
           //数据库数据与最新获取的app数据作对比
           List<LockAppInfo> dbList = DbManager.get().queryInfoList();
           if (appList.size() > dbList.size()) { //如果有安装新应用
               List<LockAppInfo> resultList = new ArrayList<>();
               HashMap<String, LockAppInfo> hashMap = new HashMap<>();
               for (LockAppInfo info : dbList) {
                   hashMap.put(info.getPackageName(), info);
               }
               for (LockAppInfo info : appList) {
                   if (!hashMap.containsKey(info.getPackageName())) {
                       resultList.add(info);
                   }
               }
               //将新应用数据插入数据库
               if (resultList.size() != 0) {
                   DbManager.get().saveInfoList(resultList);
               }
           } else if (appList.size() < dbList.size()) { //如果有卸载应用
               List<LockAppInfo> resultList = new ArrayList<>();
               HashMap<String, LockAppInfo> hashMap = new HashMap<>();
               for (LockAppInfo info : appList) {
                   hashMap.put(info.getPackageName(), info);
               }
               for (LockAppInfo info : dbList) {
                   if (!hashMap.containsKey(info.getPackageName())) {
                       resultList.add(info);
                   }
               }
               //将卸载的应用从数据库删除
               if (resultList.size() != 0) {
                   DbManager.get().deleteInfoByList(resultList);
               }
           }
           return DbManager.get().queryInfoList();
       }
   })
   .observeOn(AndroidSchedulers.mainThread())
   .subscribe(new Consumer<List<LockAppInfo>>() {
       @Override
       public void accept(List<LockAppInfo> lockAppInfos) throws Exception {
           if (lockAppInfos.size() != 0) {
               animator.start();
           } else {
               Toast.makeText(mContext, "数据处理错误", Toast.LENGTH_SHORT).show();
           }
       }
   }, new Consumer<Throwable>() {
       @Override
       public void accept(Throwable throwable) throws Exception {
           Toast.makeText(mContext, "数据处理错误", Toast.LENGTH_SHORT).show();
       }
   });

LoadAppHelper:

/**
 * 加载app列表帮助类
 */
public class LoadAppHelper {

    /**
     * 获取手机上的所有应用
     */
    private static List<ResolveInfo> loadPhoneAppList(PackageManager packageManager) {
        Intent intent = new Intent(Intent.ACTION_MAIN, null);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        return packageManager.queryIntentActivities(intent, 0);
    }

    /**
     * 初始化推荐加锁的应用
     */
    private static List<String> loadRecommendApps() {
        List<String> packages = new ArrayList<>();
        packages.add("com.android.gallery3d");       //相册
        packages.add("com.android.mms");             //短信
        packages.add("com.tencent.mm");              //微信
        packages.add("com.android.contacts");        //联系人和电话
        packages.add("com.facebook.katana");         //facebook
        packages.add("com.facebook.orca");           //facebook Messenger
        packages.add("com.mediatek.filemanager");    //文件管理器
        packages.add("com.sec.android.gallery3d");   //也是个相册
        packages.add("com.android.email");           //邮箱
        packages.add("com.sec.android.app.myfiles"); //三星的文件
        packages.add("com.android.vending");         //应用商店
        packages.add("com.google.android.youtube");  //youtube
        packages.add("com.tencent.mobileqq");        //qq
        packages.add("com.tencent.qq");              //qq
        packages.add("com.android.dialer");          //拨号
        packages.add("com.twitter.android");         //twitter
        return packages;
    }

    /**
     * 将app信息封装成需要的数据
     */
    private static List<LockAppInfo> loadLockAppInfo(Activity activity) {
        List<LockAppInfo> list = new ArrayList<>();
        try {
            PackageManager mPackageManager = activity.getPackageManager();
            List<ResolveInfo> resolveInfos = loadPhoneAppList(mPackageManager);
            for (ResolveInfo resolveInfo : resolveInfos) {
                String packageName = resolveInfo.activityInfo.packageName;
                boolean isRecommend = isRecommendApp(packageName);
                LockAppInfo info = new LockAppInfo(packageName, false, isRecommend);
                ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
                String appName = mPackageManager.getApplicationLabel(appInfo).toString();
                if (!isFilterOutApps(packageName)) {
                    boolean isSysApp = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
                    info.setLocked(isRecommend);
                    info.setAppName(appName);
                    info.setSysApp(isSysApp);
                    info.setAppType(isSysApp ? "SystemApp" : "OtherApp");
                    info.setSetUnLock(false);
                    info.setAppInfo(appInfo);
                    list.add(info);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 异步获取
     */
    public static Observable<List<LockAppInfo>> loadAllLockAppInfoAsync(final Activity activity) {
        return Observable.create(new ObservableOnSubscribe<List<LockAppInfo>>() {
            @Override
            public void subscribe(ObservableEmitter<List<LockAppInfo>> emitter) throws Exception {
                emitter.onNext(loadLockAppInfo(activity));
            }
        });
    }

    public static Observable<List<LockAppInfo>> loadLockedAppInfoAsync(final Activity activity) {
        return Observable.create(new ObservableOnSubscribe<List<LockAppInfo>>() {
            @Override
            public void subscribe(ObservableEmitter<List<LockAppInfo>> emitter) throws Exception {
                List<LockAppInfo> list = loadLockAppInfo(activity);
                List<LockAppInfo> lockAppInfos = new ArrayList<>();
                for (LockAppInfo info : list) {
                    if (info.isLocked()) {
                        lockAppInfos.add(info);
                    }
                }
                emitter.onNext(lockAppInfos);
            }
        });
    }

    public static Observable<List<LockAppInfo>> loadUnLockAppInfoAsync(final Activity activity) {
        return Observable.create(new ObservableOnSubscribe<List<LockAppInfo>>() {
            @Override
            public void subscribe(ObservableEmitter<List<LockAppInfo>> emitter) throws Exception {
                List<LockAppInfo> list = loadLockAppInfo(activity);
                List<LockAppInfo> lockAppInfos = new ArrayList<>();
                for (LockAppInfo info : list) {
                    if (!info.isLocked()) {
                        lockAppInfos.add(info);
                    }
                }
                emitter.onNext(lockAppInfos);
            }
        });
    }

    public static Observable<List<LockAppInfo>> loadSystemAppInfoAsync(final Activity activity) {
        return Observable.create(new ObservableOnSubscribe<List<LockAppInfo>>() {
            @Override
            public void subscribe(ObservableEmitter<List<LockAppInfo>> emitter) throws Exception {
                List<LockAppInfo> list = loadLockAppInfo(activity);
                List<LockAppInfo> lockAppInfos = new ArrayList<>();
                for (LockAppInfo info : list) {
                    if (info.isSysApp()) {
                        lockAppInfos.add(info);
                    }
                }
                emitter.onNext(lockAppInfos);
            }
        });
    }

    public static Observable<List<LockAppInfo>> loadUserAppInfoAsync(final Activity activity) {
        return Observable.create(new ObservableOnSubscribe<List<LockAppInfo>>() {
            @Override
            public void subscribe(ObservableEmitter<List<LockAppInfo>> emitter) throws Exception {
                List<LockAppInfo> list = loadLockAppInfo(activity);
                List<LockAppInfo> lockAppInfos = new ArrayList<>();
                for (LockAppInfo info : list) {
                    if (!info.isSysApp()) {
                        lockAppInfos.add(info);
                    }
                }
                emitter.onNext(lockAppInfos);
            }
        });
    }

    /**
     * 是否是推荐加锁app
     */
    private static boolean isRecommendApp(String packageName) {
        List<String> packages = loadRecommendApps();
        return !TextUtils.isEmpty(packageName) && packages.contains(packageName);
    }

    /**
     * 过滤的应用白名单
     */
    private static boolean isFilterOutApps(String packageName) {
        return packageName.equals(Constants.APP_PACKAGE_NAME) ||
                packageName.equals("com.android.settings") ||
                packageName.equals("com.google.android.googlequicksearchbox");
    }
}

第五点:

如果你愿意,可以把服务做成远程服务,因为基于IPC实现服务,可以减少应用内存峰值,避免OOM。

第六点

关于进程保活,其实不能绝对的保证一定能成功,下面给出一些我在某星球里面收集到相关的资料,可以参考一下: