安卓开发整理

原创
2022/08/24 16:37
阅读数 2.7K

先来一个HelloWorld.

XML布局文件

<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World" />
    
</LinearLayout>

事件响应代码为将英文修改成中文。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        设置屏幕组件需要用的布局
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.tv);
//        修改文本控件的文字
        tv.setText("你好,世界");
    }

}

最后在手机上显示的画面如下

创建第二个页面

在res/layout文件夹下面新建一个xml文件

在res/values的strings.xml文件中添加内容

<resources>
    <string name="app_name">OCR</string>
    <string name="text2">Activity Main2</string>
</resources>

activity_main2.xml的布局内容如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/text2" />

</LinearLayout>

在清单文件AndroidManifest.xml文件中添加activity_main2的配置。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.guanjian.ocr">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.OCR">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity2" />
    </application>

</manifest>

在Activity1中添加一个按钮来跳转到Activity2。

<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="跳转" />

</LinearLayout>

修改MainActivity的Java代码

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        设置屏幕组件需要用的布局
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.tv);
//        修改文本控件的文字
        tv.setText("你好,世界");

        Button button = findViewById(R.id.button);
//        给button设定点击事件的侦听
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                创建一个意图对象
                Intent intent = new Intent();
                intent.setClass(MainActivity.this,MainActivity2.class);
//                实现跳转
                startActivity(intent);
            }
        });
    }

}

在Java主目录中创建MainActivity2的响应类。

public class MainActivity2 extends AppCompatActivity {

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

在手机上的运行效果如下

Activity生命周期

上图的说明可以见以下代码

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ning";

    /**
     * 在页面载入的时候最先触发
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"Activity onCreate");
//        设置屏幕组件需要用的布局
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.tv);
//        修改文本控件的文字
        tv.setText("你好,世界");

        Button button = findViewById(R.id.button);
//        给button设定点击事件的侦听
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                创建一个意图对象
                Intent intent = new Intent();
                intent.setClass(MainActivity.this,MainActivity2.class);
//                实现跳转
                startActivity(intent);
            }
        });
    }

    /**
     * 在页面载入的时候第二个触发
     */
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG,"Activity onStart");
    }

    /**
     * 在页面载入的时候第三个触发,结束时页面可见
     */
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"Activity onResume");
    }

    /**
     * 在页面跳转离开的时候触发
     */
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG,"Activity onPause");
    }

    /**
     * 在页面完全消失的时候触发
     */
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG,"Activity onStop");
    }

    /**
     * 从其他页面返回该页面时首次执行
     * 然后执行onStart和onResume
     */
    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG,"Activity onRestart");
    }

    /**
     * 从主界面返回安卓桌面的时候触发
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"Activity onDestroy");
    }
}

当我们打开安卓app的时候会显示日志

D/ning: Activity onCreate
D/ning: Activity onStart
D/ning: Activity onResume

当我们点击跳转按钮会显示日志

D/ning: Activity onPause
D/ning: Activity onStop

当我们从跳转页面返回主界面时会显示日志

D/ning: Activity onRestart
D/ning: Activity onStart
D/ning: Activity onResume

当我们从主界面返回安卓桌面时会显示日志

D/ning: Activity onPause
D/ning: Activity onStop
D/ning: Activity onDestroy

当我们点击了跳转按钮立刻返回主界面时会显示日志

D/ning: Activity onPause
D/ning: Activity onResume

这里有一个值得说明的地方,当App隐藏于后台的时候,我们启动了非常占用内存的App,比如游戏,此时安卓系统会将该进程杀死释放内存给游戏使用。当我们再次进入该App的时候会显示D/ning: Activity onCreate而不是D/ning: Activity onRestart。这几个触发动作的具体应用如下

  1. onCreate:创建活动,把页面布局加载进内存,进入初始状态。
  2. onStart:开始活动,把活动页面显示在屏幕上,进入了就绪状态。
  3. onResume:恢复活动,活动页面进入活跃状态,能够与用户正常交互,例如允许响应用户的点击动作;允许用户输入文字等等。
  4. onPause:暂停活动,页面进入暂停状态,无法与用户进行正常交互。
  5. onStop:停止活动,页面将不在屏幕上显示。
  6. onDestroy:销毁活动,回收活动占用的系统资源,把页面从内存中清除。
  7. onRestart:重启活动,重新加载内存中的页面数据。
  8. onNewIntent:重用已有的活动实例。

Intent

Intent是各个组件之间信息沟通的桥梁,它用于Android各组件之间的通信,主要完成下列工作:

  1. 标明本次通信请求从哪里来,到哪里去,要怎么走。
  2. 发起方携带本次通信需要的数据内容,接收方从收到的意图中解析数据。
  3. 发起方若想判断接收方的处理结果,意图就要负责接收方传回应答的数据内容。
  • 显式Intent:直接指定来源活动与目标活动,属于精确匹配。它有3种构建方式
  1. 在Intent的构造函数中指定。
  2. 调用意图对象的setClass方法指定。
  3. 调用意图对象的setComponent方法指定。
            @Override
            public void onClick(View view) {
//                创建一个意图对象,第一种方式
//                Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//                第二种方式
                Intent intent = new Intent();
                intent.setClass(MainActivity.this,MainActivity2.class);
//                第三种方式
//                Intent intent = new Intent();
//                它可以调用第三方的控件,使用包名(字符串)和类名(字符串)来调用
//                ComponentName component = new ComponentName(MainActivity.this,MainActivity2.class);
//                intent.setComponent(component);
//                实现跳转
                startActivity(intent);
            }
  • 隐式Intent:没有明确指定要跳转的目标活动,只给出一个动作字符串让系统自动匹配,属于模糊匹配。

现在我们在Activity2的页面中添加如下的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/text2" />

    <TextView
        android:id="@+id/tv3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:text="点击以下按钮将向号码12345发起请求" />

    <Button
        android:id="@+id/btn_dial"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="跳到拨号页面" />

</LinearLayout>

在Activity2的Java代码中添加

public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        findViewById(R.id.btn_dial).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_dial:
                Intent intent = new Intent();
//                隐式跳转到拨号界面
                intent.setAction(Intent.ACTION_DIAL);
                Uri uri = Uri.parse("tel:12345");
                intent.setData(uri);
                startActivity(intent);
                break;
            default:
                break;
        }
    }
}

运行结果

  • 向下一个Activity传递数据

我们在Activity的主界面的布局文件中添加如下代码

<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="跳转" />

    <TextView
        android:id="@+id/tv_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="今天天气很晴朗" />

    <Button
        android:id="@+id/button_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送信息" />

</LinearLayout>

在Activity的Java代码中添加

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ning";
    private TextView tvSend;

    /**
     * 在页面载入的时候最先触发
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"Activity onCreate");
//        设置屏幕组件需要用的布局
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.tv);
//        修改文本控件的文字
        tv.setText("你好,世界");

        Button button = findViewById(R.id.button);
//        给button设定点击事件的侦听
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                创建一个意图对象,第一种方式
//                Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//                第二种方式
                Intent intent = new Intent();
                intent.setClass(MainActivity.this,MainActivity2.class);
//                第三种方式
//                Intent intent = new Intent();
//                它可以调用第三方的控件,使用包名(字符串)和类名(字符串)来调用
//                ComponentName component = new ComponentName(MainActivity.this,MainActivity2.class);
//                intent.setComponent(component);
//                实现跳转
                startActivity(intent);
            }
        });
        tvSend = findViewById(R.id.tv_send);
        findViewById(R.id.button_send).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//                创建一个包裹对象
                Bundle bundle = new Bundle();
                bundle.putString("request_time", new Date().toString());
                bundle.putString("request_context",tvSend.getText().toString());
                intent.putExtras(bundle);
                startActivity(intent);
            }
        });
    }

    /**
     * 在页面载入的时候第二个触发
     */
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG,"Activity onStart");
    }

    /**
     * 在页面载入的时候第三个触发,结束时页面可见
     */
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"Activity onResume");
    }

    /**
     * 在页面跳转离开的时候触发
     */
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG,"Activity onPause");
    }

    /**
     * 在页面完全消失的时候触发
     */
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG,"Activity onStop");
    }

    /**
     * 从其他页面返回该页面时首次执行
     * 然后执行onStart和onResume
     */
    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG,"Activity onRestart");
    }

    /**
     * 从主界面返回安卓桌面的时候触发
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"Activity onDestroy");
    }
}

在Activity2中获取意图中的数据放入tv2中。

public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {
    private TextView tv2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        findViewById(R.id.btn_dial).setOnClickListener(this);
        tv2 = findViewById(R.id.tv2);
//        从上一个页面的意图中获取包裹
        Bundle bundle = getIntent().getExtras();
        String requestTime = bundle.getString("request_time");
        String requestContext = bundle.getString("request_context");
        String desc = String.format("收到请求消息:\n请求时间:%s\n请求内容:%s",
                requestTime,requestContext);
        tv2.setText(desc);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_dial:
                Intent intent = new Intent();
//                隐式跳转到拨号界面
                intent.setAction(Intent.ACTION_DIAL);
                Uri uri = Uri.parse("tel:12345");
                intent.setData(uri);
                startActivity(intent);
                break;
            default:
                break;
        }
    }
}

运行结果

  • 向上一个Activity返回数据

现在我们要回一个信息给到主界面,说今天天气很热。

在Activity2中增加一个按钮,布局文件如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/text2" />

    <TextView
        android:id="@+id/tv_response"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="待返回的消息为:今天天气很热" />

    <Button
        android:id="@+id/btn_response"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送返回信息" />

    <TextView
        android:id="@+id/tv3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:text="点击以下按钮将向号码12345发起请求" />

    <Button
        android:id="@+id/btn_dial"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="跳到拨号页面" />

</LinearLayout>

现在我们在Activity的主界面的Java代码中就不仅仅是普通的跳转到Activity2了,而是需要注册一个可以等待返回的ActivityResult。跳转的方式也不再是startActivity(intent);注意,以下代码都改成了Lambda表达式的形式,关于lambda表达式的内容可以参考Java函数式编程整理

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ning";
    private TextView tvSend;
    private ActivityResultLauncher<Intent> register;

    /**
     * 在页面载入的时候最先触发
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"Activity onCreate");
//        设置屏幕组件需要用的布局
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.tv);
//        修改文本控件的文字
        tv.setText("你好,世界");

        Button button = findViewById(R.id.button);
//        给button设定点击事件的侦听
        button.setOnClickListener(view ->  {
//            创建一个意图对象,第一种方式
//            Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//            第二种方式
            Intent intent = new Intent();
            intent.setClass(MainActivity.this,MainActivity2.class);
//            第三种方式
//            Intent intent = new Intent();
//            它可以调用第三方的控件,使用包名(字符串)和类名(字符串)来调用
//            ComponentName component = new ComponentName(MainActivity.this,MainActivity2.class);
//            intent.setComponent(component);
//            实现跳转
            startActivity(intent);
        });
        tvSend = findViewById(R.id.tv_send);
//        注册ActivityResult,并通过回调函数获取返回的信息
        register = registerForActivityResult(new StartActivityForResult(), result ->  {
            if (result != null) {
                Intent intent = result.getData();
                if (intent != null && result.getResultCode() == Activity.RESULT_OK) {
                    Bundle bundle = intent.getExtras();
                    String responseTime = bundle.getString("response_time");
                    String responseContext = bundle.getString("response_context");
                    String desc = String.format("收到返回消息:\n返回时间:%s\n返回内容:%s",
                            responseTime,responseContext);
                    tv.setText(desc);
                }
            }
        });
        findViewById(R.id.button_send).setOnClickListener(view ->  {
            Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//                创建一个包裹对象
            Bundle bundle = new Bundle();
            bundle.putString("request_time", new Date().toString());
            bundle.putString("request_context",tvSend.getText().toString());
            intent.putExtras(bundle);
            register.launch(intent);
        });
    }

    /**
     * 在页面载入的时候第二个触发
     */
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG,"Activity onStart");
    }

    /**
     * 在页面载入的时候第三个触发,结束时页面可见
     */
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"Activity onResume");
    }

    /**
     * 在页面跳转离开的时候触发
     */
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG,"Activity onPause");
    }

    /**
     * 在页面完全消失的时候触发
     */
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG,"Activity onStop");
    }

    /**
     * 从其他页面返回该页面时首次执行
     * 然后执行onStart和onResume
     */
    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG,"Activity onRestart");
    }

    /**
     * 从主界面返回安卓桌面的时候触发
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"Activity onDestroy");
    }
}

Activity2中的返回信息的代码如下

public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {
    private TextView tv2;
    private final String msg = "今天天气很热";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        findViewById(R.id.btn_dial).setOnClickListener(this);
        tv2 = findViewById(R.id.tv2);
//        从上一个页面的意图中获取包裹
        Bundle bundle = getIntent().getExtras();
        String requestTime = bundle.getString("request_time");
        String requestContext = bundle.getString("request_context");
        String desc = String.format("收到请求消息:\n请求时间:%s\n请求内容:%s",
                requestTime,requestContext);
        tv2.setText(desc);
        findViewById(R.id.btn_response).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        Intent intent = new Intent();
        switch (view.getId()) {
            case R.id.btn_dial:
//                隐式跳转到拨号界面
                intent.setAction(Intent.ACTION_DIAL);
                Uri uri = Uri.parse("tel:12345");
                intent.setData(uri);
                startActivity(intent);
                break;
            case R.id.btn_response:
                Bundle bundle = new Bundle();
                bundle.putString("response_time",new Date().toString());
                bundle.putString("response_context",msg);
                intent.putExtras(bundle);
                setResult(Activity.RESULT_OK,intent);
//                页面返回跳转
                finish();
                break;
            default:
                break;
        }
    }
}

运行结果

运行时动态申请权限

安卓系统在6.0之前,只需要在清单文件中去配置权限就可以使用例如手机联系人、短信、相册等需要申请权限的应用。用户在安装的时候会进行提示。在安卓6.0之后不仅仅需要在清单文件中配置这些权限,而且会进行系统弹窗的询问,要使用这些权限需要在用户允许的情况下才可以使用。

  • Lazy模式

Lazy模式即懒汉式模式,当我们需要用到某个权限功能时才去请求权限。除此之外还有一个Hungry模式,即饿汉式模式,当我们打开App的时候,不管你有没有使用到某个权限功能,它都会对用户进行请求,让用户去一次性通过。

我们在Activity的布局文件中添加两个按钮去读取通讯录和发送短信。

<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="跳转" />

    <TextView
        android:id="@+id/tv_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="今天天气很晴朗" />

    <Button
        android:id="@+id/button_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送信息" />

    <Button
        android:id="@+id/btn_contact"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="读写通讯录" />

    <Button
        android:id="@+id/btn_sms"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送短信" />

</LinearLayout>

新增一个查看是否已经授权的工具类

public class PermissionUtil {

    /**
     * 检查多个权限,返回true表示已经完全启用权限,返回false表示未完全启用权限
     * @param act Activity
     * @param permissions 权限
     * @param requestCode 权限编码
     * @return
     */
    public static boolean checkPermission(Activity act,String[] permissions,int requestCode) {
//        安卓6.0之后才开始使用动态权限管理,M就是6.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//            默认授权
            int check = PackageManager.PERMISSION_GRANTED;
            for (String permission : permissions) {
                check = ContextCompat.checkSelfPermission(act,permission);
                if (check != PackageManager.PERMISSION_GRANTED) {
                    break;
                }
            }
//            未开启该授权,则请求系统弹窗,好让用户选择是否立即开启授权
            if (check != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(act,permissions,requestCode);
                return false;
            }
        }
        return true;
    }

    /**
     * 判断用户是否授权
     * @param grantResults 授权结果,0已授权,-1未授权
     * @return 全部授权返回true,有一个未授权返回false
     */
    public static boolean checkGrant(int[] grantResults) {
        if (grantResults != null) {
            for (int grant : grantResults) {
                if (grant != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
}

一个消息弹窗工具类

public class ToastUtil {
    /**
     * 弹窗消息
     * @param act
     * @param msg
     */
    public static void show(Activity act,String msg) {
        Toast toast = Toast.makeText(act,msg,Toast.LENGTH_LONG);
        toast.setGravity(Gravity.CENTER,0,0);
        toast.show();
    }
}

在清单文件中配置我们需要申请的权限

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.guanjian.ocr">

<!--    通讯录权限-->
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
<!--    短信权限-->
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.OCR">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity2" />
    </application>

</manifest>

Activity的整体Java代码如下,这里我们还没有真正去使用通讯录和短信功能,只是申请权限而已。

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ning";
    private TextView tvSend;
    private ActivityResultLauncher<Intent> register;
//    通讯录权限
    private static final String[] PERMISSIONS_CONTACTS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS
    };
//    短信权限
    private static final String[] PERMISSIONS_SMS = new String[]{
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };
//    通讯录权限编码
    private static final int REQUEST_CODE_CONTACTS = 1;
//    短信权限编码
    private static final int REQUEST_CODE_SMS = 2;

    /**
     * 在页面载入的时候最先触发
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"Activity onCreate");
//        设置屏幕组件需要用的布局
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.tv);
//        修改文本控件的文字
        tv.setText("你好,世界");

        Button button = findViewById(R.id.button);
//        给button设定点击事件的侦听
        button.setOnClickListener(view ->  {
//            创建一个意图对象,第一种方式
//            Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//            第二种方式
            Intent intent = new Intent();
            intent.setClass(MainActivity.this,MainActivity2.class);
//            第三种方式
//            Intent intent = new Intent();
//            它可以调用第三方的控件,使用包名(字符串)和类名(字符串)来调用
//            ComponentName component = new ComponentName(MainActivity.this,MainActivity2.class);
//            intent.setComponent(component);
//            实现跳转
            startActivity(intent);
        });
        tvSend = findViewById(R.id.tv_send);
//        注册ActivityResult,并通过回调函数获取返回的信息
        register = registerForActivityResult(new StartActivityForResult(), result ->  {
            if (result != null) {
                Intent intent = result.getData();
                if (intent != null && result.getResultCode() == Activity.RESULT_OK) {
                    Bundle bundle = intent.getExtras();
                    String responseTime = bundle.getString("response_time");
                    String responseContext = bundle.getString("response_context");
                    String desc = String.format("收到返回消息:\n返回时间:%s\n返回内容:%s",
                            responseTime,responseContext);
                    tv.setText(desc);
                }
            }
        });
//        发送一个消息给下一个Activity
        findViewById(R.id.button_send).setOnClickListener(view ->  {
            Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//                创建一个包裹对象
            Bundle bundle = new Bundle();
            bundle.putString("request_time", new Date().toString());
            bundle.putString("request_context",tvSend.getText().toString());
            intent.putExtras(bundle);
            register.launch(intent);
        });
//        使用通讯录
        findViewById(R.id.btn_contact).setOnClickListener(view -> {
            PermissionUtil.checkPermission(MainActivity.this,PERMISSIONS_CONTACTS,REQUEST_CODE_CONTACTS);
        });
//        发送短信
        findViewById(R.id.btn_sms).setOnClickListener(view -> {
            PermissionUtil.checkPermission(MainActivity.this,PERMISSIONS_SMS,REQUEST_CODE_SMS);
        });
    }

    /**
     * 在页面载入的时候第二个触发
     */
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG,"Activity onStart");
    }

    /**
     * 在页面载入的时候第三个触发,结束时页面可见
     */
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"Activity onResume");
    }

    /**
     * 在页面跳转离开的时候触发
     */
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG,"Activity onPause");
    }

    /**
     * 在页面完全消失的时候触发
     */
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG,"Activity onStop");
    }

    /**
     * 从其他页面返回该页面时首次执行
     * 然后执行onStart和onResume
     */
    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG,"Activity onRestart");
    }

    /**
     * 从主界面返回安卓桌面的时候触发
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"Activity onDestroy");
    }

    /**
     * 用户进行权限确认时触发
     * @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 REQUEST_CODE_CONTACTS:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"通讯录授权获取成功");
                }else {
                    ToastUtil.show(this,"获取通讯录读写权限失败");
                }
                break;
            case REQUEST_CODE_SMS:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"收发短信权限获取成功");
                }else {
                    ToastUtil.show(this,"获取收发短信权限失败");
                }
                break;
            default:
                break;
        }
    }
}

运行结果

当我们点击允许,日志打印

D/ning: 通讯录授权获取成功

当我们点击禁止

如果我们拒绝过一次,再点发送短信按钮,此时就不会再询问你是否授权了,而是直接弹出获取收发短信权限失败的消息。当然这样是不友好的,可以使用隐式意图跳转到安卓手动授权界面,修改Activity的Java代码如下

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ning";
    private TextView tvSend;
    private ActivityResultLauncher<Intent> register;
//    通讯录权限
    private static final String[] PERMISSIONS_CONTACTS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS
    };
//    短信权限
    private static final String[] PERMISSIONS_SMS = new String[]{
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };
//    通讯录权限编码
    private static final int REQUEST_CODE_CONTACTS = 1;
//    短信权限编码
    private static final int REQUEST_CODE_SMS = 2;

    /**
     * 在页面载入的时候最先触发
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"Activity onCreate");
//        设置屏幕组件需要用的布局
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.tv);
//        修改文本控件的文字
        tv.setText("你好,世界");

        Button button = findViewById(R.id.button);
//        给button设定点击事件的侦听
        button.setOnClickListener(view ->  {
//            创建一个意图对象,第一种方式
//            Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//            第二种方式
            Intent intent = new Intent();
            intent.setClass(MainActivity.this,MainActivity2.class);
//            第三种方式
//            Intent intent = new Intent();
//            它可以调用第三方的控件,使用包名(字符串)和类名(字符串)来调用
//            ComponentName component = new ComponentName(MainActivity.this,MainActivity2.class);
//            intent.setComponent(component);
//            实现跳转
            startActivity(intent);
        });
        tvSend = findViewById(R.id.tv_send);
//        注册ActivityResult,并通过回调函数获取返回的信息
        register = registerForActivityResult(new StartActivityForResult(), result ->  {
            if (result != null) {
                Intent intent = result.getData();
                if (intent != null && result.getResultCode() == Activity.RESULT_OK) {
                    Bundle bundle = intent.getExtras();
                    String responseTime = bundle.getString("response_time");
                    String responseContext = bundle.getString("response_context");
                    String desc = String.format("收到返回消息:\n返回时间:%s\n返回内容:%s",
                            responseTime,responseContext);
                    tv.setText(desc);
                }
            }
        });
//        发送一个消息给下一个Activity
        findViewById(R.id.button_send).setOnClickListener(view ->  {
            Intent intent = new Intent(MainActivity.this,MainActivity2.class);
//                创建一个包裹对象
            Bundle bundle = new Bundle();
            bundle.putString("request_time", new Date().toString());
            bundle.putString("request_context",tvSend.getText().toString());
            intent.putExtras(bundle);
            register.launch(intent);
        });
//        使用通讯录
        findViewById(R.id.btn_contact).setOnClickListener(view -> {
            PermissionUtil.checkPermission(MainActivity.this,PERMISSIONS_CONTACTS,REQUEST_CODE_CONTACTS);
        });
//        发送短信
        findViewById(R.id.btn_sms).setOnClickListener(view -> {
            PermissionUtil.checkPermission(MainActivity.this,PERMISSIONS_SMS,REQUEST_CODE_SMS);
        });
    }

    /**
     * 在页面载入的时候第二个触发
     */
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG,"Activity onStart");
    }

    /**
     * 在页面载入的时候第三个触发,结束时页面可见
     */
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"Activity onResume");
    }

    /**
     * 在页面跳转离开的时候触发
     */
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG,"Activity onPause");
    }

    /**
     * 在页面完全消失的时候触发
     */
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG,"Activity onStop");
    }

    /**
     * 从其他页面返回该页面时首次执行
     * 然后执行onStart和onResume
     */
    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG,"Activity onRestart");
    }

    /**
     * 从主界面返回安卓桌面的时候触发
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG,"Activity onDestroy");
    }

    /**
     * 用户进行权限确认时触发
     * @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 REQUEST_CODE_CONTACTS:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"通讯录授权获取成功");
                }else {
                    ToastUtil.show(this,"获取通讯录读写权限失败");
                    jumpToSettings();
                }
                break;
            case REQUEST_CODE_SMS:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"收发短信权限获取成功");
                }else {
                    ToastUtil.show(this,"获取收发短信权限失败");
                    jumpToSettings();
                }
                break;
            default:
                break;
        }
    }

    /**
     * 跳转到安卓权限设置界面
     */
    private void jumpToSettings() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
//        通过包名还跳转到我们自己的应用设置界面
        intent.setData(Uri.fromParts("package",getPackageName(),null));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

运行结果

在这里点击权限,进入权限设置界面

在这里将信息权限设为允许就可以了。

  • Hungry模式

新建一个布局页面activity_main3.xml

<?xml version="1.0" encoding="utf-8"?>
<!--线性布局-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">

    <Button
        android:id="@+id/btn_contact1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="读写通讯录" />

    <Button
        android:id="@+id/btn_sms1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="发送短信" />

</LinearLayout>

在清单中将主界面设置为Activity3

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.guanjian.ocr">
    <!-- 通讯录权限 -->
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <!-- 短信权限 -->
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.OCR">
        <activity
            android:name=".MainActivity" />
        <activity
            android:name=".MainActivity3"
            android:exported="true"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity2" />
    </application>

</manifest>

Activity3的Java代码如下

public class MainActivity3 extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "ning";
//    所有权限
    private static final String[] PERMISSIONS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS,
            Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS
    };
    //    所有权限编码
    private static final int REQUEST_CODE_All = 1;
    //    通讯录权限编码
    private static final int REQUEST_CODE_CONTACTS = 2;
    //    短信权限编码
    private static final int REQUEST_CODE_SMS = 3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        findViewById(R.id.btn_contact1).setOnClickListener(this);
        findViewById(R.id.btn_sms1).setOnClickListener(this);
//        在Activity创建的时候就进行权限判断
        PermissionUtil.checkPermission(this,PERMISSIONS,REQUEST_CODE_All);
    }

    @Override
    public void onClick(View view) {
//        不仅仅在启动的时候启动授权,在点击按钮的时候同样查看是否授权
        switch (view.getId()) {
            case R.id.btn_contact1:
                PermissionUtil.checkPermission(this,new String[]{PERMISSIONS[0],PERMISSIONS[1]},REQUEST_CODE_CONTACTS);
                break;
            case R.id.btn_sms1:
                PermissionUtil.checkPermission(this,new String[]{PERMISSIONS[2],PERMISSIONS[3]},REQUEST_CODE_SMS);
                break;
            default:
                break;
        }
    }

    /**
     * 用户进行权限确认时触发
     * @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 REQUEST_CODE_All:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"所有权限获取成功");
                }else {
                    for (int i = 0; i < grantResults.length; i++) {
//                        对于用户的每一个授权结果进行判断
                        if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                            switch (permissions[i]) {
                                case Manifest.permission.READ_CONTACTS:
                                case Manifest.permission.WRITE_CONTACTS:
                                    ToastUtil.show(this,"获取通讯录读写权限失败");
                                    jumpToSettings();
                                    return;
                                case Manifest.permission.SEND_SMS:
                                case Manifest.permission.READ_SMS:
                                    ToastUtil.show(this,"获取收发短信权限失败");
                                    jumpToSettings();
                                    return;
                                default:
                                    break;
                            }
                        }
                    }
                }
                break;
            case REQUEST_CODE_CONTACTS:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"通讯录授权获取成功");
                }else {
                    ToastUtil.show(this,"获取通讯录读写权限失败");
                    jumpToSettings();
                }
                break;
            case REQUEST_CODE_SMS:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"收发短信权限获取成功");
                }else {
                    ToastUtil.show(this,"获取收发短信权限失败");
                    jumpToSettings();
                }
                break;
            default:
                break;
        }
    }

    /**
     * 跳转到安卓权限设置界面
     */
    private void jumpToSettings() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
//        通过包名还跳转到我们自己的应用设置界面
        intent.setData(Uri.fromParts("package",getPackageName(),null));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

在我们启动运行之前需要先将手机上的App卸载,否则之前授权过了,再次启动运行授权这里是无效的。

运行结果

当我们打开App的时候

如果我们禁止了上面的授权,当点击按钮时会跳转到安卓授权界面进行手工授权。

添加联系人

首先新增一个页面布局文件activity_main4.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="top">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="联系人姓名" />

    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入姓名"
        android:inputType="text"
        android:text="Jack" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="联系人号码" />

    <EditText
        android:id="@+id/number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入号码"
        android:inputType="number"
        android:text="1331314134" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="联系人邮箱" />

    <EditText
        android:id="@+id/email"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入邮箱"
        android:inputType="textEmailAddress"
        android:text="133@123.com" />

    <Button
        android:id="@+id/btn_add_contact"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="添加联系人" />

    <Button
        android:id="@+id/btn_query_contact"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查询联系人" />

</LinearLayout>

在清单文件中将activity4设为主界面

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.guanjian.ocr">

    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <uses-feature android:name="android.hardware.type.watch" /> <!-- 通讯录权限 -->
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <!-- 短信权限 -->
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.OCR">

        <activity android:name=".MainActivity3" />
        <activity android:name=".MainActivity" />
        <activity
            android:name=".MainActivity4"
            android:exported="true"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity2" />
    </application>

</manifest>

创建一个联系人实体类

@Data
@ToString
public class Contact {
    private String name;
    private String number;
    private String email;
}

Activity4的Java代码如下

public class MainActivity4 extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "ning";
    private EditText name;
    private EditText number;
    private EditText email;
    //    通讯录权限
    private static final String[] PERMISSIONS_CONTACTS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS,
    };
    //    通讯录权限编码
    private static final int REQUEST_CODE_CONTACTS = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main4);
        name = findViewById(R.id.name);
        number = findViewById(R.id.number);
        email = findViewById(R.id.email);
        findViewById(R.id.btn_add_contact).setOnClickListener(this);
        findViewById(R.id.btn_query_contact).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_add_contact:
                if (PermissionUtil.checkPermission(this,PERMISSIONS_CONTACTS,REQUEST_CODE_CONTACTS)) {
                    Contact contact = new Contact();
                    contact.setName(name.getText().toString().trim());
                    contact.setNumber(number.getText().toString().trim());
                    contact.setEmail(email.getText().toString().trim());
                    addContacts(getContentResolver(), contact);
                }
                break;
            case R.id.btn_query_contact:
                break;
            default:
                break;
        }
    }

    /**
     * 用户进行权限确认时触发
     * @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 REQUEST_CODE_CONTACTS:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"通讯录授权获取成功");
                }else {
                    ToastUtil.show(this,"获取通讯录读写权限失败");
                    jumpToSettings();
                }
                break;
            default:
                break;
        }
    }

    /**
     * 往手机通讯录中添加一个联系人信息
     * @param resolver
     * @param contact
     */
    private void addContacts(ContentResolver resolver,Contact contact) {
        ContentValues values = new ContentValues();
//        往raw_contacts表中添加联系人记录,并获取添加后联系人编号
        Uri uri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
        long rawContactId = ContentUris.parseId(uri);

        ContentValues name = new ContentValues();
//        关联联系人编号
        name.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//        姓名的数据类型
        name.put(Contacts.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
//        联系人的姓名
        name.put(Contacts.Data.DATA2,contact.getName());
//        往提供器添加联系人的姓名记录
        resolver.insert(ContactsContract.Data.CONTENT_URI,name);

        ContentValues phone = new ContentValues();
//        关联联系人编号
        phone.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//        电话号码的数据类型
        phone.put(Contacts.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
//        联系人的家庭电话号码
        phone.put(Contacts.Data.DATA1,contact.getNumber());
//        联系人的工作电话号码
        phone.put(Contacts.Data.DATA2,CommonDataKinds.Phone.TYPE_MOBILE);
//        往提供器添加联系人的电话号码记录
        resolver.insert(ContactsContract.Data.CONTENT_URI,phone);

        ContentValues email = new ContentValues();
//        关联联系人编号
        email.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//        电话邮件的电子邮件
        email.put(Contacts.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE);
//        联系人的家庭电子邮件
        email.put(Contacts.Data.DATA1,contact.getEmail());
//        联系人的工作电子邮件
        email.put(Contacts.Data.DATA2,CommonDataKinds.Email.TYPE_WORK);
//        往提供器添加联系人的电话号码记录
        resolver.insert(ContactsContract.Data.CONTENT_URI,email);
    }

    /**
     * 跳转到安卓权限设置界面
     */
    private void jumpToSettings() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
//        通过包名还跳转到我们自己的应用设置界面
        intent.setData(Uri.fromParts("package",getPackageName(),null));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

运行结果

查询联系人

在Activity4的Java代码中添加

public class MainActivity4 extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "ning";
    private EditText name;
    private EditText number;
    private EditText email;
    //    通讯录权限
    private static final String[] PERMISSIONS_CONTACTS = new String[]{
            Manifest.permission.READ_CONTACTS,
            Manifest.permission.WRITE_CONTACTS,
    };
    //    通讯录权限编码
    private static final int REQUEST_CODE_CONTACTS = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main4);
        name = findViewById(R.id.name);
        number = findViewById(R.id.number);
        email = findViewById(R.id.email);
        findViewById(R.id.btn_add_contact).setOnClickListener(this);
        findViewById(R.id.btn_query_contact).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_add_contact:
                if (PermissionUtil.checkPermission(this,PERMISSIONS_CONTACTS,REQUEST_CODE_CONTACTS)) {
                    Contact contact = new Contact();
                    contact.setName(name.getText().toString().trim());
                    contact.setNumber(number.getText().toString().trim());
                    contact.setEmail(email.getText().toString().trim());
                    addContacts(getContentResolver(), contact);
                }
                break;
            case R.id.btn_query_contact:
                if (PermissionUtil.checkPermission(this,PERMISSIONS_CONTACTS,REQUEST_CODE_CONTACTS)) {
                    readPhoneContacts(getContentResolver());
                }
                break;
            default:
                break;
        }
    }

    /**
     * 用户进行权限确认时触发
     * @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 REQUEST_CODE_CONTACTS:
                if (PermissionUtil.checkGrant(grantResults)) {
                    Log.d(TAG,"通讯录授权获取成功");
                }else {
                    ToastUtil.show(this,"获取通讯录读写权限失败");
                    jumpToSettings();
                }
                break;
            default:
                break;
        }
    }

    /**
     * 往手机通讯录中添加一个联系人信息
     * @param resolver
     * @param contact
     */
    private void addContacts(ContentResolver resolver,Contact contact) {
        ContentValues values = new ContentValues();
//        往raw_contacts表中添加联系人记录,并获取添加后联系人编号
        Uri uri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
        long rawContactId = ContentUris.parseId(uri);

        ContentValues name = new ContentValues();
//        关联联系人编号
        name.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//        姓名的数据类型
        name.put(Contacts.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
//        联系人的姓名
        name.put(Contacts.Data.DATA2,contact.getName());
//        往提供器添加联系人的姓名记录
        resolver.insert(ContactsContract.Data.CONTENT_URI,name);

        ContentValues phone = new ContentValues();
//        关联联系人编号
        phone.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//        电话号码的数据类型
        phone.put(Contacts.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
//        联系人的家庭电话号码
        phone.put(Contacts.Data.DATA1,contact.getNumber());
//        联系人的工作电话号码
        phone.put(Contacts.Data.DATA2,CommonDataKinds.Phone.TYPE_MOBILE);
//        往提供器添加联系人的电话号码记录
        resolver.insert(ContactsContract.Data.CONTENT_URI,phone);

        ContentValues email = new ContentValues();
//        关联联系人编号
        email.put(Contacts.Data.RAW_CONTACT_ID,rawContactId);
//        电话邮件的电子邮件
        email.put(Contacts.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE);
//        联系人的家庭电子邮件
        email.put(Contacts.Data.DATA1,contact.getEmail());
//        联系人的工作电子邮件
        email.put(Contacts.Data.DATA2,CommonDataKinds.Email.TYPE_WORK);
//        往提供器添加联系人的电话号码记录
        resolver.insert(ContactsContract.Data.CONTENT_URI,email);
    }


    /**
     * 查询通讯录信息
     * @param resolver
     */
    private void readPhoneContacts(ContentResolver resolver) {
        Cursor cursor = resolver.query(ContactsContract.RawContacts.CONTENT_URI,
                new String[]{ContactsContract.RawContacts._ID},
                null, null, null);
        while (cursor.moveToNext()) {
            int rawContactId = cursor.getInt(0);
            Uri uri = Uri.parse("content://com.android.contacts/contacts/" + rawContactId + "/data");
            Cursor dataCursor = resolver.query(uri, new String[]{Contacts.Data.MIMETYPE, Contacts.Data.DATA1, Contacts.Data.DATA2},
                    null, null, null);
            Contact contact = new Contact();
            while (dataCursor.moveToNext()) {
                String data1 = dataCursor.getString(dataCursor.getColumnIndex(Contacts.Data.DATA1));
                String mimeType = dataCursor.getString(dataCursor.getColumnIndex(Contacts.Data.MIMETYPE));
                switch (mimeType) {
//                    姓名
                    case CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE:
                        contact.setName(data1);
                        break;
//                    电话
                    case CommonDataKinds.Phone.CONTENT_ITEM_TYPE:
                        contact.setNumber(data1);
                        break;
//                     邮件
                    case CommonDataKinds.Email.CONTENT_ITEM_TYPE:
                        contact.setEmail(data1);
                        break;
                    default:
                        break;
                }
            }
            dataCursor.close();
            if (contact.getName() != null) {
                Log.d(TAG,contact.toString());
            }
        }
        cursor.close();
    }

    /**
     * 跳转到安卓权限设置界面
     */
    private void jumpToSettings() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
//        通过包名还跳转到我们自己的应用设置界面
        intent.setData(Uri.fromParts("package",getPackageName(),null));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

运行结果(部分日志)

D/ning: Contact(name=华为消费者服务热线, number=950800, email=mobile@huawei.com)
D/ning: Contact(name=新惠百货, number=18588994445, email=null)
D/ning: Contact(name=阿里巴巴, number=057128223456, email=null)
D/ning: Contact(name=大都会人寿, number=400 818 8168, email=null)

NDK

NDK的全称是Native Development kit,是Andriod开发中Java调用C/C++库的一个开发工具包。

现在我们来手动创建一个JNI HelloWorld项目,创建后的目录如下

它比一般的安卓项目多了一个cpp的文件夹,里面存放的是C++的源文件。要编译这些源文件,我们需要配置NDK。在local.properties文件中添加

ndk.dir=/Users/admin/Documents/android-ndk-r17c

先建一个activity_main.xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">


    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World" />
    
</LinearLayout>

新建一个获取字符串的类

public class GetString {
    public static native String getStr();
}

这个native表示调用C或C++的代码来运行。在控制台进入src的java目录,运行

javah -jni com.guanjian.hellojni.c.GetString

完成后可以得到一个C/C++的头文件

将该文件剪切到cpp文件夹中,该文件的内容如下

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_guanjian_hellojni_c_GetString */

#ifndef _Included_com_guanjian_hellojni_c_GetString
#define _Included_com_guanjian_hellojni_c_GetString
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_guanjian_hellojni_c_GetString
 * Method:    getStr
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_guanjian_hellojni_c_GetString_getStr
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

在cpp文件夹中新建一个GetString.cpp

#include <jni.h>
#include "com_guanjian_hellojni_c_GetString.h"

JNIEXPORT jstring JNICALL Java_com_guanjian_hellojni_c_GetString_getStr
        (JNIEnv *env, jclass) {
    return env->NewStringUTF("HelloJNI");
}

修改CMakeLists.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.10.2)

# Declares and names the project.

project("hellojni")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             GetString.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

Activity的Java代码如下

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

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

        // Example of a call to a native method
        TextView tv = findViewById(R.id.tv);
        tv.setText(GetString.getStr());
    }
}

运行结果

安卓直接使用.so库文件

展开阅读全文
加载中

作者的其它热门文章

打赏
0
1 收藏
分享
打赏
1 评论
1 收藏
0
分享
返回顶部
顶部