Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 【android】仿360手機衛士的簡易設計思路及源碼

【android】仿360手機衛士的簡易設計思路及源碼

編輯:關於android開發

【android】仿360手機衛士的簡易設計思路及源碼


筆者最近一直忙於滿廣州的跑,實習好難找好難找,博客也是有點久沒去更新。仿360手機衛士的實現的目的更多的是出於對常用知識點的一個鞏固吧,比較適合像我這種接觸沒多久的學習者在學習之余拿來練手保持寫代碼的感覺的的一個不錯的小項目。

涉及的技術:

都是些非常常用的android常用開發,但涉及面比較廣吧。(小編這邊也談不出啥新意,就簡簡單單舉幾個小例子,見諒見諒~!~)也是出於練手的目的吧,在寫這個項目的時候也是用到了比較多的控件,有時一個問題也用到了不同的方法:比方說在解析服務器的xml文件即用到一般的json數據解析,也用到了gjson數據解析數據(據說這個gjson目前被大部分公司所使用,小編是否有點out了,哈哈);listview花樣就更多了,除了常規的listview +viewholder小優化思路,有些地方也是用到單個類別頭部固定的listview;有些地方也用到自定義listview實現側拉刪除,同時還可以給listview條目增加動畫增加視覺效果。另外,在listview數據加載時,可以是傳統的加載,還有用到分批加載、分頁加載。(哈哈,還是蠻好玩的)其他細節如果大家有興趣直接進代碼小看一番也不錯,當然也用到許多動畫和自定義控件,在下面各個部分的功能介紹小編也會小提一下啦。

功能介紹如下:

文章有限,小編這邊就不羅列代碼。。。

1、手機防盜

功能描述:在主界面點擊手機防盜按鈕,彈出密碼輸入框(若為第一次進入則彈出設置密碼框界面),密碼的存儲方式經過MD5加密後存儲在SharePreference中,在驗證完密碼後跳裝到手機防盜頁面(若還未對設置防盜信息會跳轉到手機防盜設置頁面),那就先來看下手機防盜設置頁面:設置安全號碼、綁定sim卡、以及是否開啟防盜設置。手機防盜頁面對設置的這些信息進行展示以及提供用戶修改按鈕。防盜功能:(1)開機監聽手機sim卡變化,如果變化向安全號碼發送提醒短信;(2)在手機丟失的情況下通過遠程短信控制實現:被盜手機清除數據、被盜手機定位、被盜手機鎖屏設置屏保以及控制被盜手機播報報警音樂。

技術實現:(1)檢測sim卡變化:在手機防盜頁面監聽sim卡按鈕變化,如果按鈕開啟如果在手機防盜和sim卡按鈕開機情況下,開機廣播BootCompleteReceiver會獲取當前手機sim卡號,和原來保存的sim卡號進行比較(通過TelephonyManager),若不一樣,通過SmsManager對象以及安全號碼發送短信。(2)手機定位、手機數據清理、手機防盜以及手機報警音樂播放:通過短信監聽廣播,通過獲取短息內容來與原先設置好的口令驚醒比較,數據清理和一鍵鎖屏通過設備管理器DevicePolicyManager實現,手機定位通過一個service監聽位置變化。(3)從手機聯系人處選擇號碼:相當於去讀寫手機內置的數據庫吧.

部分功能展示

\

 

2、通訊衛士

功能描述:(1)、添加黑名單(兩種方式:通過輸入框輸入方面;以及通過調用手機聯系人方式);(2)、查詢黑名單;(3)、攔截在黑名單中的短息以及電話。

技術實現:(1)添加黑名單:建立一個數據庫表,對其進行封裝,將黑名單號碼保存在數據庫中。(2)查詢黑名單:由於沒有找到合適的數據庫,因此黑名單數據庫就用剛剛建立的一個黑名單數據庫來代替,查詢數據庫。(3)短信和電話攔截:(這個功能在設置中心進行設置,默認為開啟狀態--即直接在splash頁面直接開啟黑名單服務),通過活動管理器ActivityManager獲取後台正在運行的服務,判定黑名單服務是否已經開啟。1、電話攔截:通過電話管理器TeleponyManager監聽手機電話狀態,獲取來電號碼,查詢數據庫,如果黑名單數據庫中存在該號碼,通過反射方式獲取系統攔截電話方法攔截電話,並且通過contentprovider方式去刪除電話記錄中的來電記錄;2、短信攔截:在服務中動態的注冊廣播,通過SmsManager對象回去短信的號碼,查詢數據庫,調用abortBroadcast來攔截短信。

部分功能展示

\

 

3、軟件管理

功能描述:獲取系統安裝的所用應用,分類成手機應用和系統應用通過listview顯示。通過點擊每個條目的應用對該應用操作:運行、卸載、分享以及詳細信息。

技術實現:(1)、獲取手機安卓的應用:通過系統提供的包管理器獲取應用信息,(2)、運行、卸載、分享、詳細信息:通過StartAcitvity()跳轉到相對應的activity操作。

部分功能展示

\

4、進程管理

功能描述:獲取系統正在運行的應用的詳細信息,顯示在listview中。並供用戶選擇單個條目的清理進程或全部清理。

技術實現:通過包管理器PackageManager以及任務管理器獲取系統正在運行的所用進程的詳細信息,通過自定義listview展示(側滑清理單個進程,一鍵清理除自身外的所用進程),清理主要調用killbackgroupprocess方法。

部分功能展示

\

5、手機殺毒

功能描述:掃描手機中的所用應用,若發現病毒跳轉到病毒處理頁面供用戶選擇是否清楚。

技術實現:通過系統的包管理者PackageManager獲取應用包名,對包名進行md5處理,與數據庫中的md5病毒名md5對比(手機中的應用是以apk形式存在的)。若為病毒跳轉到應用卸載界面供用戶清理。

部分功能展示

\

6、電話歸屬地查詢

功能描述:(1)、電話歸屬地查詢,(2)、來去電歸屬地顯示

技術實現:(1)電話歸屬地查詢:和數據庫相關操作,小項目用的比較多封裝啥的都差不多;(2)來電歸屬地顯示:顯示:自定義WindowManager顯示自定義view,來電監聽:定義一個來電監聽service,監聽電話狀態獲取電話的號碼,調用數據庫查詢這個號碼的歸屬地,並將它顯示在view上;歸屬地風格:(很簡單,幾個背景);歸屬地位置設置(通過ontouchevent監聽觸點位置,獲取移動點坐標,並重繪(onmeasure onlayout ondraw(就這三個過程)))不過需要注意點是,在activity中重繪和windowmanager中還是有區別的,詳細的對比代碼這塊有比較詳細的注釋見(AddressService以及DragViewActivity)。

部分功能展示

\\

\

7、程序鎖

功能描述:對系統所有應用供用戶選擇加鎖並對被加鎖程序密碼保護

技術實現:(1)已加鎖與應用和胃加鎖應用展示:通過兩個fragmengt,用戶點擊加鎖或未加鎖fragment上的listviewde item對應用包名增加和刪除操作。(2)對加鎖程序密碼保護:在服務中實現一個看門狗程序,一直監視應用任務棧中的第一個應用,若第一個應用存在在被保護應用數據庫中跳轉到密碼輸入界面。

目前存在問題:目前這邊跳轉界面還未能實現,問題暫時可能的定位是權限問題需要進一步排查吧。

基本功能展示就到這,還有許多bug,這段時間也是比較忙,bug進展比較慢。而在BaseActivity中做的工作很簡單:監視手勢滑動,代碼也很簡單直接看代碼:

public abstract class BaseActivity extends Activity{
    private GestureDetector gestureDetector;
    public SharedPreferences mPrefs;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mPrefs = getSharedPreferences("config",MODE_PRIVATE);

        gestureDetector = new GestureDetector(this,new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if(e1.getRawX() - e2.getRawX() > 200){
                    showNextPage();
                    return true;
                }else if(e2.getRawX() - e1.getRawX() > 200)
                {
                    showPreviousPage();
                    return true;
                }
                return super.onFling(e1, e2, velocityX, velocityY);
            }
        });
    }

    protected abstract void showPreviousPage();

    protected abstract void showNextPage();

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gestureDetector.onTouchEvent(event);// 委托手勢識別器處理觸摸事件
        return super.onTouchEvent(event);
    }
}

splash界面做初始化操作:當用戶第一次下載時,將外部數據庫拷貝到用戶手機sd卡目錄下,判斷版本更新,判斷是否創建桌面快捷方式,以及啟動停止service等,還是直接看代碼吧:
public class SpashActivity extends AppCompatActivity {
    protected static final int CODE_UPDATE_DIALOG = 0;
    protected static final int CODE_URL_ERROR = 1;
    protected static final int CODE_NET_ERROR = 2;
    protected static final int CODE_JSON_ERROR = 3;
    protected static final int CODE_ENTER_HOME = 4;// 進入主頁面

    private TextView tvVersion;
    private RelativeLayout rlRoot;

    // 服務器返回的信息
    private String mVersionName;// 版本名
    private int mVersionCode;// 版本號
    private String mDesc;// 版本描述
    private String mDownloadUrl;// 下載地址

    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case CODE_UPDATE_DIALOG:
                    showUpdateDailog();
                    break;
                case CODE_URL_ERROR:
                    Toast.makeText(SpashActivity.this, "url錯誤", Toast.LENGTH_SHORT)
                            .show();
                    enterHome();
                    break;
                case CODE_NET_ERROR:
                    Toast.makeText(SpashActivity.this, "網絡錯誤", Toast.LENGTH_SHORT)
                            .show();
                    enterHome();
                    break;
                case CODE_JSON_ERROR:
                    Toast.makeText(SpashActivity.this, "數據解析錯誤",
                            Toast.LENGTH_SHORT).show();
                    enterHome();
                    break;
                case CODE_ENTER_HOME:
                    enterHome();
                    break;

                default:
                    break;
            }
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spash);

        copyDB("address.db");// 拷貝歸屬地查詢數據庫

        //拷貝資產目錄下的病毒數據庫文件
        copyDB("antivirus.db");

        startService(new Intent(this, CallSafeService.class));

        //Intent watchDogIntent = new Intent(this, WatchDogService.class);
        //startService(watchDogIntent);

        initView();
        opEvents();
        checkVersion();
    }

    private void opEvents() {
        tvVersion.setText("版本名:" + getVersionName());

        AlphaAnimation alphaAnimation = new AlphaAnimation(0.3f,1f);
        alphaAnimation.setDuration(2000);
        rlRoot.startAnimation(alphaAnimation);
    }

    private String getVersionName() {
        PackageManager packageManager = getPackageManager();
        try {
            PackageInfo packageInfo = packageManager.getPackageInfo(
                                            getPackageName(),0);
            String  versionName = packageInfo.versionName;
            return versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 獲取本地app的版本號
     *
     * @return
     */
    private int getVersionCode() {
        PackageManager packageManager = getPackageManager();
        try {
            PackageInfo packageInfo = packageManager.getPackageInfo(
                    getPackageName(), 0);// 獲取包的信息

            int versionCode = packageInfo.versionCode;
            return versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            // 沒有找到包名的時候會走此異常
            e.printStackTrace();
        }

        return -1;
    }

    private void initView() {
        tvVersion = (TextView) findViewById(R.id.tv_version);
        rlRoot = (RelativeLayout) findViewById(R.id.rl_root);
    }

    private void checkVersion() {
        final long startTime = System.currentTimeMillis();
        // 啟動子線程異步加載數據
        new Thread() {

            @Override
            public void run() {
                Message msg = Message.obtain();
                HttpURLConnection conn = null;
                try {
                    // 本機地址用localhost, 但是如果用模擬器加載本機的地址時,可以用ip(10.0.2.2)來替換
                    URL url = new URL("http://10.0.2.2:8080/update.json");
                    conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");// 設置請求方法
                    conn.setConnectTimeout(5000);// 設置連接超時
                    conn.setReadTimeout(5000);// 設置響應超時, 連接上了,但服務器遲遲不給響應
                    conn.connect();// 連接服務器

                    int responseCode = conn.getResponseCode();// 獲取響應碼
                    if (responseCode == 200) {
                        InputStream inputStream = conn.getInputStream();
                        String result = StreamUtils.readFromStream(inputStream);
                        // System.out.println("網絡返回:" + result);

                        // 解析json
                        JSONObject jo = new JSONObject(result);
                        mVersionName = jo.getString("versionName");
                        mVersionCode = jo.getInt("versionCode");
                        mDesc = jo.getString("description");
                        mDownloadUrl = jo.getString("downloadUrl");
                        // System.out.println("版本描述:" + mDesc);

                        if (mVersionCode > getVersionCode()) {// 判斷是否有更新
                            // 服務器的VersionCode大於本地的VersionCode
                            // 說明有更新, 彈出升級對話框
                            msg.what = CODE_UPDATE_DIALOG;
                        } else {
                            // 沒有版本更新
                            msg.what = CODE_ENTER_HOME;
                        }
                    }
                } catch (MalformedURLException e) {
                    // url錯誤的異常
                    msg.what = CODE_URL_ERROR;
                    e.printStackTrace();
                } catch (IOException e) {
                    // 網絡錯誤異常
                    msg.what = CODE_NET_ERROR;
                    e.printStackTrace();
                } catch (JSONException e) {
                    // json解析失敗
                    msg.what = CODE_JSON_ERROR;
                    e.printStackTrace();
                } finally {
                    long endTime = System.currentTimeMillis();
                    long timeUsed = endTime - startTime;// 訪問網絡花費的時間
                    if (timeUsed < 2000) {
                        // 強制休眠一段時間,保證閃屏頁展示2秒鐘
                        try {
                            Thread.sleep(2000 - timeUsed);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    mHandler.sendMessage(msg);
                    if (conn != null) {
                        conn.disconnect();// 關閉網絡連接
                    }
                }
            }
        }.start();
    }

    /**
     * 升級對話框
     */
    protected void showUpdateDailog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("最新版本:" + mVersionName);
        builder.setMessage(mDesc);
        // builder.setCancelable(false);//不讓用戶取消對話框, 用戶體驗太差,盡量不要用
        builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("立即更新");
                download();
            }
        });

        builder.setNegativeButton("以後再說", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                enterHome();
            }
        });

        // 設置取消的監聽, 用戶點擊返回鍵時會觸發
        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {

            @Override
            public void onCancel(DialogInterface dialog) {
                enterHome();
            }
        });

        builder.show();
    }

    /**
     * 下載apk文件
     */
    protected void download() {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {

            //tvProgress.setVisibility(View.VISIBLE);// 顯示進度

            String target = Environment.getExternalStorageDirectory()
                    + "/update.apk";
            // XUtils
            HttpUtils utils = new HttpUtils();
            utils.download(mDownloadUrl, target, new RequestCallBack() {

                // 下載文件的進度
                @Override
                public void onLoading(long total, long current,
                                      boolean isUploading) {
                    super.onLoading(total, current, isUploading);
                   // System.out.println("下載進度:" + current + "/" + total);
                   // tvProgress.setText("下載進度:" + current * 100 / total + "%");
                }

                // 下載成功
                @Override
                public void onSuccess(ResponseInfo arg0) {
                    System.out.println("下載成功");
                    // 跳轉到系統下載頁面
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.addCategory(Intent.CATEGORY_DEFAULT);
                    intent.setDataAndType(Uri.fromFile(arg0.result),
                            "application/vnd.android.package-archive");
                    // startActivity(intent);
                    startActivityForResult(intent, 0);// 如果用戶取消安裝的話,
                    // 會返回結果,回調方法onActivityResult
                }

                // 下載失敗
                @Override
                public void onFailure(HttpException arg0, String arg1) {
                    Toast.makeText(SpashActivity.this, "下載失敗!",
                            Toast.LENGTH_SHORT).show();
                }
            });
        } else {
            Toast.makeText(SpashActivity.this, "沒有找到sdcard!",
                    Toast.LENGTH_SHORT).show();
        }
    }

    // 如果用戶取消安裝的話,回調此方法
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        enterHome();
        super.onActivityResult(requestCode, resultCode, data);
    }

    /**
     * 進入主頁面
     */
    private void enterHome() {
        Intent intent = new Intent(this, HomeActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 拷貝數據庫
     *
     * @param dbName
     */
    private void copyDB(String dbName) {
        File filesDir = getFilesDir();
        System.out.println("路徑:" + filesDir.getAbsolutePath());
        File destFile = new File(getFilesDir(), dbName);// 要拷貝的目標地址

       if (destFile.exists()) {
            System.out.println("數據庫" + dbName + "已存在!");
            return;
        }

        FileOutputStream out = null;
        InputStream in = null;

        try {
            //in = getAssets().open(dbName);
            in = getClassLoader().getResourceAsStream("assets/" + dbName);
            int length = in.available();
            //System.out.println("databasesize" + length);
            out = new FileOutputStream(destFile);

            int len = 0;
            byte[] buffer = new byte[1024];

            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved