1. 程式人生 > >android6.0許可權封裝及全域性捕獲異常

android6.0許可權封裝及全域性捕獲異常

(一定要看完,後面有驚喜哦)

Android6.0以前,Android的許可權機制比較簡單,開發者在AndroidManifest檔案中宣告需要的許可權,APP安裝時,系統提示使用者APP將獲取的許可權,需要使用者同意授權才能繼續安裝,從此APP便永久的獲得了授權。然而,同期的iOS對於許可權的處理會更加靈活,許可權的授予並不是在安裝時,而是在APP執行時,使用者可以根據自身的需要,決定是否授予APP某一許可權,同時,使用者也可以很方便回收授予的許可權。顯然,動態許可權管理的機制,對於使用者的隱私保護是更加適用的,Android過於簡單的許可權機制也受到了不少人的吐槽。終於,Android6.0也釋出了動態許可權的機制。


危險許可權與普通許可權

一開始,聽到要加入許可權判斷和申請程式碼邏輯的程式設計師內心可能是崩潰的:正常的一個有一定規模的APP,很容易就七七八八的聲明瞭很多許可權,如果每個許可權都申請豈不是非常麻煩?

好歹,Google還算比較明智,並不是所有的許可權都需要執行時申請才能使用。Google對每個許可權的隱私危害性進行了評估。將許可權分為了兩大類:普通許可權和危險許可權。舉個例子,控制手機震動的許可權對於使用者並沒有什麼危害,只要開發者聲明瞭這個許可權,安裝後就可以一直被授權,也不能被回收,但是,像讀取sd卡資料這類許可權,很顯然就是危險許可權了,APP必須向用戶申請這個許可權。

Google還是很體貼我們開發者的,為了進一步減少開發的工作量和申請許可權對使用者的騷擾,對危險許可權根據各自的屬性進行了分組。舉個例子,讀sd卡和寫sd卡,這兩個許可權通常都是成對宣告和使用的,因此,它們被分為一組,而且,只要我們獲取了這個許可權組裡面的任意一個許可權,就可以獲取整個許可權組的許可權

所以說呢我們這些6.0許可權呢就是為了這些危險許可權設定的; android6.0封裝的原因:減少冗餘程式碼的編寫,提高程式碼的閱讀性和開發效率 步驟: 在積累BaseActivity中為子類提高許可權的檢查和許可權申請的方法,再在請求許可權的回撥吃力介面中, 暴露出子類要覆寫並實現其業務邏輯的方法,如果子類繼承基類就可以大大簡化6.0許可權申請的 步驟,根據基類暴露出需要覆寫的方法,實現其對應的業務邏輯; 0.常量類 1.在清單檔案中配置許可權 2.建立一個基類,並在基類中為子類提高一個許可權檢查的方法; 3.在基類中為子類提供一個許可權申請方法 4.在基類中集中處理請求許可權回撥的業務邏輯; 5.暴露給子類實現具體業務邏輯的方法,子類如果有此功能,覆寫方法即可,沒有的話就不用管此方法; 好了!概念基本就這些,開始上程式碼:

收先說下實現的效果吧:進行封裝6.0許可權,經過繼承封裝類,實現並呼叫裡面的方法,實現打電話與寫入SD卡,需要寫一個異常,使用全域性捕獲異常獲取到此異常,並寫入SD卡;

首先:

<!-- 打電話許可權是危險許可權,需要適配 -->
<uses-permission android:name="android.permission.CALL_PHONE"/>
<!--讀寫許可權是危險許可權,需要適配-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

BaseActivity:(我們封裝的Activity)

public class BaseActivity extends AppCompatActivity {

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

    /**
     * 為子類提供一個許可權檢查方法
     * @param permissions:表示不定引數,也就是呼叫這個方法的時候,可以傳入多個String物件(JDK5新特性)
     * @return
     */
    public boolean hasPermission(String ... permissions){
        for (String permission: permissions) {
            //ContextCompat 環境相容
            //checkSelfPermission:自我檢查許可權
            // PackageManager:包管理器
            // PERMISSION_GRANTED:許可授權
             if (ContextCompat.checkSelfPermission(this,permission)!= PackageManager.PERMISSION_GRANTED){
                 return true;
             }

        }
        return false;
    }
    /**
     * 在基類中為子類提供一個許可權申請的方法
     * 引數1:int ,區分碼   2:許可權
     */
    public void requewtPermission(int code,String...permissions ){
        //requestPermissions 請求的許可權  1.上下文  2.許可權  3.區分碼
        ActivityCompat.requestPermissions(this,permissions,code);
    }

    /**
     * 基類中集中處理請求許可權回撥的業務邏輯
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
       switch (requestCode){
           //打電話許可權的回撥處理
           case Constants.CALL_PHONE:
               //判斷打電話許可權申請是否成功,成功就執行打電話的邏輯
               //注意:因為集合裡只有一個許可權申請,引數為0代表打電話許可權
               if (grantResults[0]==PackageManager.PERMISSION_GRANTED){
                   //成功
                   doChallPhone();
               }else {
                   //失敗
                   Toast.makeText(BaseActivity.this,"沒有許可權",Toast.LENGTH_LONG).show();
               }
               break;
           case Constants.WRITE_EXTERNAL_STORAGE:
               break;
       }
    }

    /**
     * 打電話的業務邏輯
     * 寫一個空實現,而不是抽象方法,因為有些頁面沒有打電話功能,如果是抽象方法,還必須覆寫此方法
     * 不是太好,子類如果有此功能,覆寫此方法即可,不用再管許可權配置
     */
    public void doChallPhone(){

    }
    //寫入SD卡
    public void doWrite() {
    }
}

常量類:

 * 常量類
 */

public class Constants {
    /**
     * 許可權相關的常量類
     */
    public static final int CALL_PHONE=660;
    public static final int WRITE_EXTERNAL_STORAGE=5;
      


下面在MainActivity中實現功能:
public class MainActivity extends BaseActivity {
    private Button but;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        but.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //是否有許可權  false就是沒有許可權,然後進行申請
                if (hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    //呼叫許可權申請的方法,傳入引數  引數1:產量類中的常量值   引數2:設定許可權
                    requewtPermission(Constants.WRITE_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE);
                } else {
                    //否則  有許可權  直接操作打電話
                    doWrite();
                }
            }
        });

    }

    //點選打電話
    public void phone(View view) {
        //是否有許可權  false就是沒有許可權,然後進行申請
        if (hasPermission(Manifest.permission.CALL_PHONE)) {
            //呼叫許可權申請的方法,傳入引數  引數1:產量類中的常量值   引數2:設定許可權
            requewtPermission(Constants.CALL_PHONE, Manifest.permission.CALL_PHONE);
        } else {
            //否則  有許可權  直接操作打電話
            doChallPhone();
        }
    }

    //實現打電話的方法
    @Override
    public void doChallPhone() {
        //執行打電話到10086操作
        Intent intent = new Intent(Intent.ACTION_CALL);
        Uri uri = Uri.parse("tel:" + "10086");
        intent.setData(uri);
        try {
            startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doWrite() {
        super.doWrite();
        try {
            File file = new File(Environment.getExternalStorageDirectory(), "fuck.txt");
            FileOutputStream fos = new FileOutputStream(file);
            String info = "I am a chinanese!";
            fos.write(info.getBytes());
            fos.close();
            System.out.println("寫入成功:");
           //寫一個空指標
            Object o = null;
            o.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void initView() {
        but = (Button) findViewById(R.id.but);

    }


}

為了捕獲這個異常,還需要全域性捕獲異常類;

/**
 * UncaughtException處理類,當程式發生Uncaught異常的時候,有該類來接管程式,並記錄傳送錯誤報告.
 *
 * @author user
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {

    public static final String TAG = "CrashHandler";

    //系統預設的UncaughtException處理類
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler例項
    private static CrashHandler INSTANCE = new CrashHandler();
    //程式的Context物件
    private Context mContext;
    //用來儲存裝置資訊和異常資訊
    private Map<String, String> infos = new HashMap<String, String>();

    //用於格式化日期,作為日誌檔名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

    /**
     * 保證只有一個CrashHandler例項
     */
    private CrashHandler() {
    }

    /**
     * 獲取CrashHandler例項 ,單例模式
     */
    public static CrashHandler getInstance() {
        return INSTANCE;
    }

    /**
     * 初始化
     *
     * @param context
     */
    public void init(Context context) {
        mContext = context;
        //獲取系統預設的UncaughtException處理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        //設定該CrashHandler為程式的預設處理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 當UncaughtException發生時會轉入該函式來處理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            //如果使用者沒有處理則讓系統預設的異常處理器來處理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }
            //退出程式
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }

    /**
     * 自定義錯誤處理,收集錯誤資訊 傳送錯誤報告等操作均在此完成.
     *
     * @param ex
     * @return true:如果處理了該異常資訊;否則返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        //使用Toast來顯示異常資訊
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "很抱歉,程式出現異常,即將退出.", Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();
        //收集裝置引數資訊
        collectDeviceInfo(mContext);
        //儲存日誌檔案
        saveCrashInfo2File(ex);
        return true;
    }

    /**
     * 收集裝置引數資訊
     *
     * @param ctx
     */
    public void collectDeviceInfo(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "an error occured when collect package info", e);
        }
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
                Log.d(TAG, field.getName() + " : " + field.get(null));
            } catch (Exception e) {
                Log.e(TAG, "an error occured when collect crash info", e);
            }
        }
    }

    /**
     * 儲存錯誤資訊到檔案中
     *
     * @param ex
     * @return 返回檔名稱, 便於將檔案傳送到伺服器
     */
    private String saveCrashInfo2File(Throwable ex) {

        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }

        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);
        try {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + "-" + timestamp + ".log";
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = "/sdcard/crash/";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(sb.toString().getBytes());
                fos.close();
            }
            return fileName;
        } catch (Exception e) {
            Log.e(TAG, "an error occured while writing file...", e);
        }
        return null;
    }
}
異常捕獲類進行初始化:
public class CrashApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        CrashHandler mCustomCrashHandler = CrashHandler.getInstance();
        mCustomCrashHandler.init(getApplicationContext());
    }
}
別忘了在清單檔案註冊;

佈局就不寫了,就兩個按鈕;

完事!!!!!!!!!!!!!!!!

相關推薦

android6.0許可權封裝全域性捕獲異常

(一定要看完,後面有驚喜哦) Android6.0以前,Android的許可權機制比較簡單,開發者在AndroidManifest檔案中宣告需要的許可權,APP安裝時,系統提示使用者APP將獲取的許可權,需要使用者同意授權才能繼續安裝,從此APP便永久的獲得了授權。然而,

Android6.0 許可權申請封裝

之前一篇部落格初試了Android6.0系統的動態許可權申請,成功之後開始思考將許可權申請功能封裝以供更加方便的呼叫。 查閱6.0系統許可權相關的API,整個許可權申請需要呼叫三個方法: 1. ContextCompat.checkSelfPermi

SSM框架全域性捕獲異常

我們都知道,專案有兩種異常,一種是ERROR,一種是Exception,ERROR導致專案直接崩盤,無法執行,且不能捕獲,Exception可以捕獲且不影響專案執行,今天要做的就是捕獲Exception,當前專案開發使用SSM框架,我原本使用的方法是Controller控制層每一個類每一個方

@ControllerAdvice和@ExceptionHandler實現全域性捕獲異常

##全域性捕獲異常:相當於整個web請求專案全域性捕獲異常,一般對整個controller層丟擲的異常做統一處理。 ##異常處理有兩種方式:1、捕獲返回json格式;2、捕獲返回頁面的 @ControllerAdvice(basePackages= {"com.demo"}) public cl

使用者註冊登陸&引數校驗器&全域性捕獲異常&分散式session

工具類準備 分散式ID工具類 package com.example.miaosha_xdp.util; import java.util.UUID; public class UUIDUtil { public static String uuid(){ /**

Android6.0許可權申請

最近由於專案功能迭代,本身圖省事不想要在程式碼中動態的申請許可權,所以在build.gradle中的minsdk寫成了19 ,而targetSdkVersion 也是19,這樣就避免了在6.0系統上動態申請許可權,本來一切都是ok的,但是在小米8上的8.1系統版本中出現了問題,程式碼中獲

Android如何全域性捕獲異常呢???

工具類 package me.maxd.demo.utils; import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.PackageInfo; im

Android6.0 CarPlay 路由問題導致連線異常

[email protected]_6dq:/ # route -A inet6                                             Kernel IPv6 routing table Destination                           

Android6.0許可權申請程式碼

1.申請一個許可權WRITE_EXTERNAL_STORAGE private void requestPermision() { int hasWriteContactsPermission = ActivityCompat.checkSelfPerm

Android6.0許可權組 動態申請所有許可權詳細介紹

同一組的任何一個許可權被授權了,其他許可權也自動被授權。例如,一旦WRITE_CONTACTS被授權了,app也有READ_CONTACTS和GET_ACCOUNTS了。1、需要手動申請的許可權:Permission GroupPermissionsandroid.permi

Android6.0+許可權申請

在啟動頁onCreate()使用方法applyForPermissions(); private String[] permissions; //許可權申請 private void applyForPermissions(){ // a

從根本解決Android6.0許可權檢測問題 附demo

耐心讀完,理解許可權處理的關鍵點。最後附有原始碼~~ 一、背景: android6.0系統開啟了敏感許可權使用者授權功能。如果開發的APP中不新增許可權獲取申請,預設是不會彈框提示使用者授權的,如此以來系統預設禁止使用拍照、儲存等敏感許可權,如果開發者設

Android 6.0許可權機制開發流程詳解

許可權機制變更的背景 在Android6.0之前,app在安裝時會提示使用者此app需要使用哪些許可權,但使用者只能選擇同意或拒絕安裝,而不能單獨對某項許可權進行授予或拒絕。只要使用者選擇了安裝,即表示使用者接受了app對這些許可權的使用,如果使用者不希望app獲取某些涉及隱

Android6.0許可權快速申請(基於RxJava2更優雅)

一、前言 前言?哪來那麼多廢話,直接進入正題! 二、需要申請的許可權 咳咳咳,進入正題前,我們還是先了解一下哪些許可權是Android6.0後需要手動申請的吧,對症下藥,才是王道嘛! group:android.permission-group

跳過Android6.0+許可權的方法

發現了一個小竅門 在清單檔案中設定 <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="22" /> targetSdkVersion < 23 (6.0)  執行在6.0+手機

android6.0許可權通用管理(不在頭大)

一個類解決Android6.0許可權的繁雜,完美應付日常開發,歡迎指教~  在activity中涉及到需要申請許可權的功能時,比如點選按鈕照相,這是主要一段程式碼,沒什麼難度就是做了一個統一回調: /*** * request permission

Android自定義全域性捕獲異常並上傳,實現實時收集APP崩潰crash資訊

一、異常收集 目的:在APP上線後,可能會出現一些BUG導致了APP的閃退,使用者體驗就非常致命,我們一定要第一時間找到問題的所在,去處理掉問題,處理有方法有兩種,一是發一個修改後的新版本,另一個是用熱修復釋出一個更新補丁,具體選擇哪一種符合自己需求就行。 我們主要說的異常

android6.0許可權問題(小米手機已解決)

關於android6.0版本問題,最直接的解決方案就是把targetSdkVersion最大隻能設定為22。如果你不想這麼做就需要按照goole官方給出的方法一步步的來。 首先你需要在AndroidManifest.xml檔案中註冊一下你要申請的許可權; 接

Android6.0許可權總結

/** * Created by Leonidas on 2018/5/14. * Version: V1.0 * Description:繼承了Activity,實現Android6.0的執行時許可權檢測, * 需要進行執行時許可權檢測的Activity可以繼承這個類。 *

一行程式碼搞定漂亮的Android6.0許可權申請介面

概述 隨著Android6.0的普及,許可權申請也變成了我們開發中必寫的一段程式碼。比如sd卡許可權、定位許可權、拍照許可權,這些幾乎都是每個app必備的。而一般情況下我們都會在需要許可權之前申請,然後各種判斷。那既然是一些必備的是許可權,我們為何不在我們一次