Android清單文件詳解(二) ---- 應用程序權限聲明(安卓中的清單文件)

      網友投稿 1214 2022-05-30

      我們知道,Android系統的各個模塊提供了非常強大的功能(比如電話,電源和設置等),通過使用這些功能,應用程序可以表現的更強大,更靈活。不過,使用這些功能并不是無條件的,而是需要擁有一些權限。接下來,我們就開始講解另一個非常重要的知識點——應用程序權限聲明,其中主要包括應用程序的權限聲明,自定義應用程序的訪問權限和SDK版本限定。

      1.——應用程序的權限申請

      應用程序在不同的場景下可能需要上表所示的某些權限,比如當我們需要使用SD卡時,則需要申請SD卡相關權限。下面我們舉例來解釋這個問題。

      在這個實例中,我們將改造HelloWorld應用程序,并在sdcard的根目錄下添加一個名為“abc.txt”的文本文件。由于需要訪問外部存儲設器,因此需要申請android.permission.WRITE_EXTERNAL_STORAGE權限,否則代碼將會失敗。具體步驟如下所示。

      ①需要在HelloWorld應用程序的AndroidManifest.xml文件中添加相應的權限,如下列代碼所示:

      ②要在原來的代碼中添加一些創建文件的代碼,如下所示:

      public class MainActivity extends FragmentActivity { private static final String SDCARD= Environment.getExternalStorageDirectory()+File.separator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(savedInstanceState==null){ getSupportFragmentManager().beginTransaction().add(android.R.id.content,new FileFragment()).commit(); } } public static class FileFragment extends Fragment { public FileFragment(){} @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view=inflater.inflate(R.layout.file_fragment,container,false); Button mybut= (Button) view.findViewById(R.id.mybut); mybut.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { File sdcardFile=new File(SDCARD+"abc.txt"); try { sdcardFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } }); return view; } } }

      ③啟動程序,這時在sdcard所鏈接的目錄下,我們會發現已經建立了abc.txt文件,如下圖所示。

      最后我們來做一個實驗,將AndroidManifest.xml文件中的節點刪除,在相同的目錄下并沒有發現所創建的文件,這樣操作之后,我們就能在日志里面發現一些異常信息,如下圖所示。

      從日志中我們可以發現程序拋出了java.io.IOException異常,并且提示“Permission denied”,而它發生的地方,正好是創建文件的地方。因此,我們可以得出一個結論,在試圖讀寫外部存儲設備的時候,必須先要申請android.permission.WRITE_EXTERNAL_STORAGE這個權限,否則程序拋出警告性異常,相關操作也就無法進行。

      Android提供了豐富的軟硬件功能模塊,它能讓應用程序變得強大,開發過程也更便捷,但在使用前,必須要為應用程序申請必要的權限。這點非常重要,否則應用程序就會出現莫名其妙的錯誤。

      正如前面的實例中看到的結果,如果沒有相應的權限,就無法創建文件,而程序并沒有顯示一個異常的提示,這時我們就可能要花費大量的時間去找問題的根源。

      因此,本人建議大家開發之前仔細分析需求,分析應用為什么功能,而這些動能是否需要權限才可以訪問。

      2.節點——自定義應用程序的訪問權限

      前面我們學習了如何使用權限。其實,應用程序除了可以使用權限之外,還可以定義自己的權限,用來限制對本應用程序或其他應用程序的特殊組件或功能訪問。在這里,我們來學習節點的作用——如何聲明自己的權限。

      手動向AndroidManifest.xml文件中添加一個節點,它只能包含在節點下,其語法如下所示:

      android:icon="drawable resource"

      android:logo="drawable resource"

      android:label="string resource"

      android:name="string"

      android:permissionGroup="string"

      android:protectionLevel=["normal"|"dangerous"|"signature"|"signatureOrSystem"]/>

      以上代碼中,需要說明的有以下3個屬性。

      ①android:name:聲明權限的名稱。這個名稱必須是唯一的,因此,應該使用Java風格的命名,比如com.test.permission.TEST。

      ②android:permissionGroup:聲明權限從屬于哪一個權限組,這個權限組可以是Android預編譯的,也可以是自定義的。下表列除了Android系統預編譯的系統權限組。

      ③android:protectionLevel:描述了隱含在權限中的潛在風險,該屬性的值可以是下表中的一個字符串。

      節點中,除了上面介紹的3個屬性外,還有其他一些屬性只是為了便于閱讀而存在,這里我們不在詳細介紹。

      在Android系統提供的應用程序中,有一些定義了自己的權限,比如Launcher。下面的代碼片段用Launcher 的AndroidManifest.xml片段來說明如何手動聲明自己的權限:

      android:name="com.android.launcher.permission.INSTALL_SHORT-CUT"

      android:permissionGroup="android.permission-group.SYSTEM_TOOLS"

      Android清單文件詳解(二) ---- 應用程序權限聲明(安卓中的清單文件)

      android:protectionLevel="normal"

      android:label="@string/permlab_install_shortcut"

      android:description="@string/perdesc_install_shortcut"/>

      因為聲明權限這一功能使用頻率比較低,因此讀者在開發應用程序的時候需要思考是否有必要聲明自己的權限。

      3.節點——SDK版本限定

      大家都知道,軟件對于平臺版本是的一定要求的。如果平臺版本能夠達到軟件運行的要求,那就能保證軟件的穩定性。比如,大家都知道NFC功能是不能在Android 1.5中運行的,如果正在開發帶類似功能的應用程序,那就必須對所在平臺有所要求,而節點正是用來滿足這種需求的。

      節點使用一個整型值來表達應用程序與一個或多個Android平臺版本的兼容性。值得注意的是,這些整型值代表的是API級別。應用程序給定的API級別將和一個給定Android系統的API級別做比較。當然,對于不同的Android設備,這可能會有所不同。需要說明的是,這個節點用于指定API級別,而不是用于指定SDK的版本號或Android平臺的。

      使用此節點的代碼如下所示:

      android:targetSdkVersion="integer"

      android:maxSdkVersion="integer"/>

      ①android:minSdkVersion:用于指定要運行應用程序所需的最小API級別。如果系統的API級別比該屬性指定的值要小,則Android系統會阻止用戶安裝此應用。在大多數情況下,應指定這個屬性。如果沒有指定該屬性,那么系統會認為此屬性值為“1”。

      ②android:targetSdkVersion:用于指定應用程序的目標API級別。

      ③android:maxSdkVersion:指定最大的API級別。

      4.節點——應用的監控器

      節點用于監控應用程序與系統交互,它會在應用程序組件實例化之前被實例化。這個節點在多數情況下用于單元測試。其語法結構如下:

      android:handleProfiling=["true"|"false"]

      android:icon="drawable resource"

      android:label="string resource"

      android:name="string"

      android:targetPackage="string">

      下面詳細說明這些屬性:

      ①android:functionalTest:標識這個Instrumentation類是否作為一個功能測試運行,它的默認值是false。

      ②android:handleProfiling:標識這個Instrumentation對象是否打開和關閉性能分析,它的默認值是false。

      ③android:icon:這個屬性代表這個Instrumentation類的圖標

      ④android:label:該屬性是Instrumentation類的標題。

      ⑤android:name:該屬性是Instrumentation子類的名稱,是一個類的Java風格的名稱,例如com.example.liyuanjinglyj.myInstrumentation。

      ⑥android:targetPackage:該屬性是需要監控的目標應用程序名稱,這個名稱來自與目標應用程序AndroidManifest.xml中節點的package屬性值。

      當然在eclipse需要專門創建一個測試項目,而且還要配置這個節點,但是在android studio并不需要這么做,考慮到2015年底谷歌不再支持eclipse,所以只講解android studio單元測試,而講解這個節點是因為到現在eclipse依然有大量的開發者使用,所以,講解了節點的詳細說明。

      下面舉例說明如何進行單元測試:

      ①創建項目,依然拿我們一直使用的HelloWorld做實驗,你會發現項目目錄里面有這樣一個測試包:

      ②右鍵點擊這個包,選擇NEW-CLASS菜單項,新建MyFirstTest繼承自ActivityInstrumentationTestCase2,如下圖所示:

      package com.example.liyuanjing.helloworld; import android.test.ActivityInstrumentationTestCase2; /** * Created by liyuanjing on 2015/7/14. */ public class MyFirstTest extends ActivityInstrumentationTestCase2{ public MyFirstTest(){ super(MainActivity.class); } }

      在這段代碼中,ActivityInstrumentationTestCase2中的要換成我們測試的類,因為我們要測試MainActivity所以換成這個。

      ③我們測試內容為,在編輯框中輸入一些字符,當點擊按鈕后,文本框等于編輯框輸入的字符串,MainActivity代碼如下:

      public class MainActivity extends FragmentActivity { private static final String SDCARD= Environment.getExternalStorageDirectory()+File.separator; private FileFragment fileFragment=new FileFragment(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(savedInstanceState==null){ getSupportFragmentManager().beginTransaction().add(android.R.id.content,fileFragment).commit(); } } public static class FileFragment extends Fragment { private View mRootView; public FileFragment(){} @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view=inflater.inflate(R.layout.file_fragment,container,false); final EditText myEdit=(EditText)view.findViewById(R.id.myEdit); final TextView content=(TextView)view.findViewById(R.id.content); Button myBut=(Button)view.findViewById(R.id.myBut); myBut.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { content.setText(myEdit.getText().toString()); } }); this.mRootView=view; return view; } public View getRootView(){ return this.mRootView; } } public FileFragment getFragment(){ return fileFragment; } }

      測試類的代碼如下:

      public class MyFirstTest extends ActivityInstrumentationTestCase2{ private TextView content; private EditText myEdit; private Button myBut; private MainActivity mainActivity; public MyFirstTest(){ super(MainActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); this.mainActivity=(MainActivity)getActivity(); this.content=(TextView)this.mainActivity.getFragment().getRootView().findViewById(R.id.content); this.myEdit=(EditText)this.mainActivity.getFragment().getRootView().findViewById(R.id.myEdit); this.myBut=(Button)this.mainActivity.getFragment().getRootView().findViewById(R.id.mybut); } public void testButtonClick(){ getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_H); getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_E); getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_L); getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_L); getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_O); getInstrumentation().waitForIdleSync(); getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { myBut.performClick(); } }); getInstrumentation().waitForIdleSync(); SystemClock.sleep(1000); assertEquals(this.content.getText().toString(),this.myEdit.getText().toString()); } }

      注意:

      Ⅰ在MyFirstTest類的構造放中,由于指定了被測試項目的Activity的詳細信息,就使得MyFirstTest與Activity關聯上。

      ⅡsetUp()方法是一個重寫的方法,它的作用是在測試前做必要的設置,比如實例化一些對象,打開網絡等,它與tearDown()方法成對出現。這里我們沒有實現tearDown()方法,當測試完成以后,框架會自動回調基類,也就是tearDown()方法。

      最后,運行測試類,如下圖所示:

      當系統完成測試后,就可以在Android studio集成開發環境中得到下圖所示的結果:

      在圖中可以看到,編寫的Test測試類返回值為OK,這就證明測試達到了預期的目的。

      Android 網絡

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:《考取HCIA證書看我就夠了》第四篇:[HCIA-IoT]物聯網技術之平臺層技術概覽
      下一篇:241_Redis_集群_主從架構(redis主從與集群)
      相關文章
      亚洲欧洲日韩在线电影| 亚洲丁香色婷婷综合欲色啪| 亚洲网站在线免费观看| 亚洲av无码无在线观看红杏| 中文字幕精品亚洲无线码一区应用| 伊人久久亚洲综合影院| 337P日本欧洲亚洲大胆精品| 亚洲精品精华液一区二区| 亚洲欧美黑人猛交群| 亚洲欧美国产国产综合一区 | 亚洲精品国产精品乱码视色| 亚洲熟妇无码另类久久久| 国产亚洲AV手机在线观看| 亚洲香蕉网久久综合影视| 久久亚洲av无码精品浪潮| 老司机亚洲精品影视www| 国产亚洲精品成人AA片新蒲金| 国产亚洲精品精品国产亚洲综合| 久久亚洲高清综合| 亚洲欧洲日产国码无码网站| 亚洲精品一品区二品区三品区| 亚洲av综合色区| 久久亚洲AV成人无码电影| 亚洲高清中文字幕| 亚洲一级毛片免费在线观看| 亚洲欧洲日产国码www| 亚洲一级毛片视频| 亚洲国产欧洲综合997久久| 久久久久久亚洲av无码蜜芽| 亚洲成人影院在线观看| 国产亚洲精aa成人网站| 亚洲综合无码精品一区二区三区| 亚洲精品国产精品乱码在线观看| 亚洲av丰满熟妇在线播放| 亚洲视频一区在线| 亚洲色大成WWW亚洲女子| 豆国产96在线|亚洲| 亚洲精品国产日韩无码AV永久免费网| 国产成人精品曰本亚洲79ren| 亚洲成AV人片在线播放无码| 亚洲精品福利网站|