まず、実行時の権限について理解する
Android では、すべてのアクセス許可が 2 つのカテゴリに分類されるようになりました。1 つは通常のアクセス許可、もう 1 つは危険なアクセス許可です。通常の権限とは、ユーザーのセキュリティやプライバシーを直接脅かすものではない権限を指し、この部分の権限申請については、ユーザーが手動で操作することなく、システムが自動的に承認します。
1. 通常の権限
システムにより自動的に認証されるため、申請は不要です。
このようにして、プログラムをインストールするときに、プログラムがデバイス上でどのような権限を取得するかをユーザーに通知します。
2. 危険な権限
たとえば、カメラやマイクなどの許可を有効にするには、アプリのインストール後に、ユーザーが使用するときにユーザーを許可するウィンドウをポップアップ表示する必要があります。
危険なアクセス許可の場合、AndroidManifest.xml ファイルに次の 2 つのアクセス許可ステートメントを追加するだけで済みます。
3. プログラム実行時に許可を申請する
例として、電話をかける申し込みをします。
ステップ 1: 新しいプロジェクトを作成する runtimepermissiontest
ステップ 2: レイアウトを変更する
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/make_call"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="拨打电话"/>
</LinearLayout>
ステップ 3: Menifest ファイルのアプリケーション許可
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button makeCall=(Button) findViewById(R.id.make_call);
makeCall.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
//判断是否授权
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)!=PackageManager.PERMISSION_GRANTED){
//没授权就请求获取权限
ActivityCompat.requestPermissions(MainActivity.this,new String[]{
Manifest.permission.CALL_PHONE},1);
}else{
//授权了就直接拨打电话
call();
}
}
});
}
private void call(){
try {
Intent intent =new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}catch(SecurityException e){
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch(requestCode){
case 1:
if(grantResults.length>0 && grantResults[0]== PackageManager.PERMISSION_GRANTED){
call();
}else{
Toast.makeText(this, "你拒绝了授权", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}
次に、コンテンツプロバイダー
コンテンツ プロバイダー (コンテンツ プロバイダー) は、主に異なるアプリケーション間でのデータ共有機能を実現するために使用され、アクセスされたデータのセキュリティを確保しながら、あるプログラムが別のプログラムのデータにアクセスできるようにする一連のメカニズムを提供します。現在、コンテンツ プロバイダーの使用は、プログラム間でデータを共有する Android の標準的な方法です。
ファイル ストレージと SharedPreferences ストレージの 2 つのグローバルな読み取りおよび書き込み可能な操作モードとは異なり、コンテンツ プロバイダーは、プログラム内の個人データが漏洩する危険がないように、データのどの部分を共有するかを選択できます。
コンテンツ プロバイダーの一般的な使用法は 2 つあります。
一种是使用现有的内容提供器来读取和操作相应程序中的数据
另一种是创建自己的内容提供器给我们程序的数据提供外部访问接口。
1、ContentResolver の基本的な使用法、既存のコンテンツ プロバイダーを使用する
アプリケーションごとに、コンテンツ プロバイダーの共有データにアクセスする場合は、Content.Resolver クラスを使用する必要があります。このクラスのインスタンスは、Context の getContentResolver() メソッドを通じて取得できます。Content-Resolver は、データに対する CRUD 操作のための一連のメソッドを提供します。
insert()方法用于添加数据
update()方法用于更新数据,
delete()方法用于删除数据,
query()方法用于查询数据。
ContentResolver の追加、削除、変更、およびクエリのメソッドはテーブル名パラメーターを受け入れませんが、代わりにコンテンツ URI と呼ばれる Uri パラメーターを使用します。コンテンツ URI は、コンテンツ プロバイダー内のデータの一意の識別子を確立し、主に権限とパスの 2 つの部分で構成されます。権限はアプリケーションを区別するために使用され、一般的には競合を避けるためにパッケージ名が命名に使用されます。
ただし、これら 2 つの文字列を 2 つのコンテンツ URI として識別することは依然として困難であり、文字列の先頭にプロトコル ステートメントを追加する必要もあります。したがって、コンテンツ URI の最も標準的な形式は次のとおりです。
content://com.example.app.provider/tablel
content://com.example.app.provider/table2
コンテンツ URI 文字列を取得した後、パラメーターとして渡す前に、それを Uri オブジェクトに解析する必要があります。解析方法も非常に簡単で、コードは次のとおりです。
Uri uri = Uri.parse("content://com.example.app.provider/tablel")
Uriparse() メソッドを呼び出して、コンテンツ URI 文字列を Uri オブジェクトに解析します。これで、この Uri オブジェクトを使用して、table1 内のデータをクエリできるようになります。コードは次のとおりです。
Cursor sursor=getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
クエリが完了した後も Cursor オブジェクトが返されるので、Cursor オブジェクトからデータを 1 つずつ読み取ることができます。読み取りの考え方は、やはりカーソルの位置を移動してカーソルのすべての行を走査し、各行の対応する列のデータをフェッチすることです。コードは次のとおりです。
if (cursor != null) {
while (cursor.moveToNext()) {
String column]= cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
まず、table1 にデータを追加する方法を見てみましょう。コードは次のとおりです。
ContentValues values = new ContentValues();
values.put("column]"“text");
values.put("column2",1);
getContentResolver().insert(uri, values);
この新しく追加されたデータを更新して、column1 の値をクリアしたい場合は、ContentResolver の update() メソッドを使用できます。コードは次のとおりです。
ContentValues values = new ContentValues();
values.put("column]","");
getContentResolver().update(uri, values,"column1 = ? and column2 = ?",newString[] {
"text","1"});
上記のコードでは、selection パラメーターとselectionArgs パラメーターを使用して更新されるデータを制約し、すべての行が影響を受けないようにすることに注意してください。
最後に、ContentResolver の delete() メソッドを呼び出して、このデータ部分を削除できます。コードは次のとおりです。
getContentResolver(),delete(uri,"column2 = ?",new Stringl]["");
2. 独自のコンテンツプロバイダーを作成する
上記では、独自のプログラムで他のアプリケーションのデータにアクセスする方法について説明しました。他のプログラムが使用するアクセス インターフェイスに提供されるデータを実装するにはどうすればよいですか?
独自のコンテンツプロバイダーを作成する必要があります。
プログラム間でデータを共有する機能を実装したい場合は、コンテンツプロバイダーを使用することが公式推奨されており、ContentProviderを継承するクラスを新規作成することで独自のコンテンツプロバイダーを作成できます。ContentProviderクラスには6つの抽象メソッドが存在しますが、サブクラスで継承する場合には、この6つのメソッドをすべて書き換える必要があります。新しい MyProvider は ContentProvider から継承します。コードは次のとおりです。
public class MyProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}
1.onCreate()
初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作,返回 true表示内容提供器初始化成功,返回 false 则表示失败。注意,只有当存在 ContentResolver 尝试访问我们程序中的数据时,内容提供器才会被初始化。
2.クエリ()
从内容提供器中查询数据。使用uri参数来确定查询哪张表,proiection 参数用于确定查询哪些列,selection 和selectionArgs 参数用于约束查询哪些行,sortOrder 参数用于对结果进行排序,查询的结果存放在 Cursor 对象中返回。
3.挿入()
向内容提供器中添加一条数据。使用 uri 参数来确定要添加到的表,待添加的数据保存在values 参数中。添加完成后,返回一个用于表示这条新记录的URI。
4.update()
更新内容提供器中已有的数据。使用 uri 参数来确定更新哪一张表中的数据新数据保存在values 参数中,selection 和 selectionArgs 参数用于约束更新哪些行,受影响的行数将作为返回值返回。
5.削除()
从内容提供器中删除数据。使用 uri 参数来确定删除哪一张表中的数据,selection 和selectionArgs 参数用于约束删除哪些行,被删除的行数将作为返回值返回
6.getType()
根据传人的内容URI来返回相应的MIME类型
ほぼすべてのメソッドが Uri パラメーターを取得することがわかりますが、このパラメーターは、コンテンツリゾルバーの追加、削除、変更、およびクエリ メソッドを呼び出すときにも渡されます。ここで、受信した URI を分析し、そこから呼び出し元の期待を分析する必要があります。そしてデータ。
標準 URI には次の 2 つの状況があります。
以路径结尾就表示期访问该表中所有的数据:content://com.example.app.provider/tablel
id 结尾就表示期望访问该表中拥有相应 id 的数据。content://com.example.app.provider/tablel/1
ワイルドカードを使用して、これら 2 つの形式のコンテンツ URI をそれぞれ照合できます。ルールは次のとおりです。
*:表示匹配任意长度的任意字符
#:表示匹配任意长度的数字
したがって、任意のテーブルに一致するコンテンツ URI 形式は次のように記述できます。
content://com.example.app.provider/*
また、table1 のデータの任意の行と一致するコンテンツ URI 形式は次のように記述できます。
content://com.example.app.provider/tablel/#
次に、UriMatcher クラスを使用して、コンテンツ URI を照合する機能を簡単に実装できます。UriMatcher は adduRI() メソッドを提供します。このメソッドは、権限、パス、カスタム コードを渡す 3 つのパラメーターを受け取ります。
このようにして、UriMatcher の match() メソッドが呼び出されると、Uri オブジェクトを渡すことができ、戻り値は、対応する Uri オブジェクトと一致するカスタム コードになります。このコードを使用すると、呼び出し元が期待しているものであるかどうかを判断できます。テーブルがアクセスされているデータ。MyProvider のコードを次のように変更します。
public class MyProvider extends ContentProvider {
public static final int TABLE1_RIR=0;
public static final int TABLE1_TIME=1;
public static final int TABLE2_DIR=2;
public static final int TABLE2_TIME=3;
private static UriMatcher uriMatcher;
static {
//静态代码块,
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.example.app.provider","table1",TABLE1_RIR);
uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_TIME);
uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR);
uriMatcher.addURI("com.example.app.provider","table1",TABLE2_TIME);
}
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
switch(uriMatcher.match(uri)){
case TABLE1_RIR:
//查询table1表中的所有数据
break;
case TABLE1_TIME:
//查询table1表中的单条数据
break;
case TABLE2_DIR:
//查询table2表中的所有数据
break;
case TABLE2_TIME:
//查询table2表中的单条数据
break;
default:
break;
}
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}
さらに、聞きなれないメソッドがもう 1 つあり、それが getType() メソッドです。これは、Uri オブジェクトに対応する MIME タイプを取得するために、すべてのコンテンツ プロバイダーが提供する必要があるメソッドです。コンテンツURIに対応するMIME文字列は主に3つの部分から構成されており、Androidではこの3つの部分に対して以下の形式規定を設けています。
必须以 vnd 开头。
如果内容 URI以路径结尾,则后接 android.cursor.dir/,
如果内容 URI以id结尾,则后接android.cursor.item/。
最后接上 vnd,<authority>,<path>。
したがって、コンテンツ URI content://com.exampleapp.provider/tablel の場合、対応する MIME タイプは次のように記述できます。
vnd.android.cursor.dir/vnd.com.example.app.provider.tablel
コンテンツ URI content://comexampleappprovider/tablel/1 の場合、対応する MIME タイプは次のように記述できます。
vnd.android.cursor.item/vnd.com.example.app.provider.tablel
これで、MyProvider のコンテンツの改善を続けることができます。今回は getType() メソッドにロジックを実装するため、コードは次のようになります。
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch(uriMatcher.match(uri)){
case TABLE1_RIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
case TABLE1_TIME:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
case TABLE2_TIME:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
default:
break;
}
return null;
}
この時点で、完全なコンテンツ プロバイダーが作成され、任意のアプリケーションが ContentResolver を使用してプログラム内のデータにアクセスできるようになります。