1 通知

参考文章:《创建通知》

当某个应用不在前台运行当又需要向用户发送一些信息,就可以使用 通知 来实现。
通知可以在 活动、广播接收器和服务中创建,在活动中创建通知的场景比较少,因为一般的只有程序进入到后台的时候才需要使用通知。

1.1 NotificationManager

NotificationManager 类用于对通知 Notification 对象管理,NotificationManager 可以通过调用 ContextgetSystemService() 方法获取,getSystemService() 方法可以接收一个字符串参数用于确定系统的哪个服务,此时传入 Context.NOTIFICATION_SERVICE 即可。
而获取 Notification 对象的问题在于 Android 系统的每个版本都对 通知 这个部分功能进行修改,导致 API 不稳定,解决方法就是使用 support 库中的兼容 API,support-v4 库中提供了 NotificationCompat 类,使用它可以保证兼容性地获取到空的 Notification 对象。

// channlId 是识别符,确定唯一性饥渴
Notification notification = new NotificationCompat.Builder(Context context, String channelId).build();

以上是一个空的 Notification 对象,可以在调用 build 方法之前 连缀 任意多的设置方法创建一个丰富的 Notification 对象,最后再通过 NotificationManager 类的 notify() 方法将通知显示出来,notify() 方法接受两个参数,第一个参数是 id(需要保证每个通知的 id 都不同),第二个参数就是 Notification 实例,整体例子如下:

Button button = (Button) findViewById(R.id.notify);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(MainActivity.this, null)
                .setContentTitle("这是标题")
                .setContentText("这是内容")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .build();
        notificationManager.notify(1, notification);
    }
});

1.2 PendingIntent

在上面代码中弹出的通知是不可点击的,想要编写通知的点击逻辑,就需要介绍一下 PendingIntent 对象了,如同名字的意思,PendingIntentIntent 类似,都可以指明为“意图”,都可以启动活动、服务以及发送广播等。
二者不同在于,Intent 倾向立即执行某个动作,而 PendingIntent 更倾向于在某个合适的时间去执行某个动作。
PendingIntent 实例可以通过调用 PendingIntent 类的几个静态方法获取,分别是:getActivity()getBroadcast()getService()。这几个方法接收的参数都是相同,其中的第三个参数就是 Intent 对象,用于构建出 PendingIntent 对象的“意图”。接着在 NotificationCompat.Builder 中调用 setContentIntent 方法,将PendingIntent 作为参数传入即可,例子代码如下:

Button button = (Button) findViewById(R.id.notify);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this, NotificationActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivities(MainActivity.this, 0, new Intent[]{intent}, 0);
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = new NotificationCompat.Builder(MainActivity.this, null)
                .setContentTitle("这是标题")
                .setContentText("这是内容")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true)
                .build();
        notificationManager.notify(2, notification);
    }
});

其中点击了通知后,系统状态上的通知图标也不会消失,因为此时需要显式地调用消失的方法,一种是在 NotificationCompat.Builder 中调用 setAutoCancel() 方法,第二种是显式的调用 NotificationManager 中的 cancel() 方法将它取消,此时传入的就是此前调用 notify 方法时设置的唯一辨识值。

2 网络技术

2.1 HttpURLConnection

在过去,Android 上发送 HTTP 请求一般有两种方式:HttpURLConnectionHttpClient,不过由于 HttpClient 的 api 过多,且扩展困难,所以不建议使用它。
在使用 HttpURLConnection 之前需要声明权限,在 AndroidManifest.xml 文件中添加以下声明:

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

同时为了用户数据和设备的安全,新的 Android 系统要求默认使用加密连接,这意味着将禁止 App 使用所有未加密的连接,因此直接使用 HttpURLConnection 进行非加密的明文流量的 http 网络请求会出现异常,而 https 请求则不会收到影响,此时的解决方法有三种(OkHttp 同理):

  • APP改用 https 请求
  • targetSdkVersion 降到 27 以下
  • 更改网络安全配置,直接在 AndroidManifest.xml 文件中的 application 标签中添加 android:usesCleartextTraffic="true" 属性

使用 HttpURLConnection 需要新建一个子线程,否则会报错,具体逻辑如下:

private void sendRequest() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            HttpURLConnection connection = null;
            BufferedReader reader = null;
            try {
                // 创建 uri
                URL url = new URL("https://www.baidu.com");
                connection = (HttpURLConnection) url.openConnection();
                // 设置请求类型
                connection.setRequestMethod("GET");
                // 设置链接时长
                connection.setConnectTimeout(8000);
                // 设置读取时长
                connection.setReadTimeout(8000);
                // 获取返回数据
                InputStream in = connection.getInputStream();
                reader = new BufferedReader(new InputStreamReader(in));
                StringBuilder reponse = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    reponse.append(line);
                }
                // Only the original thread that created a view hierarchy can touch its views.
                // TextView textView = (TextView) findViewById(R.id.text);
                // textView.setText(reponse);
                Log.d("接受到数据", reponse.toString());
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭资源
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    connection.disconnect();
                }
            }
        }
    }).start();
}

其中由于请求需要在子线程进行,而 Android 并不允许在子线程中进行 UI 操作,所以如果想要在获取到数据后显示在界面上就需要使用到 runOnUiThread() 方法了,然后在里面编写更新 UI 元素的逻辑,最后再例子代码如下:

private void showResponse(final String response) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            TextView textView = (TextView) findViewById(R.id.text);
            textView.setText(response);
        }
    });
}

如果请求方法使用 Post,即只需在获取输入流之前将要提交的数据写出即可,注意每条数据都要以键值对的形式存在,数据与数据之间用 & 符号隔开,如:

// 设置请求类型
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&pw=123456");

2.2 OkHttp

OkHttp 是一个开源库,所以使用它之前需要先添加依赖,如下:

implementation 'com.squareup.okhttp3:okhttp:3.4.1'

OkHttp 的用法如同 HttpURLConnection,首先需要创建一个 OkHttpClient 实例,然后创建一个 Request 对象用来封装一些请求信息,最后调用 OkHttpClient 对象的 newCall 方法创建一个 Call 对象,并调用它的 execute 方法来发送请求,同样的,以上逻辑都需要在子线程中进行,例子代码如下:

private void sendReuqestOfOkHttp() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            // 创建 OkHttpClient
            OkHttpClient client = new OkHttpClient();
            // 想要发起一条 HTTP 请求,就需要创建一个 Request 对象
            // 与通知 Notification 的用法类似,在使用 build 方法之前,可以连缀多个方法来丰富 reqeust 对象
            Request request = new Request.Builder().url("https://www.baidu.com")
                    .build();
            // 调用 OkHttpClient 对象的 newCall 方法创建一个 Call 对象,并调用它的 execute 方法来执行请求
            // 从而获取 response 对象
            try {
                Response response = client.newCall(request).execute();
                // 调用 response 对象的 body().string() 方法可以获取返回数据的字符串格式
                showResponse(response.body().string());
                Log.d("OkHttp 接收的数据", response.body().toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

而如果想要使用 Post 请求,则还需要创建 RequestBody 对象来存放待提交的参数,最后在 Request.Builder 中使用 post 方法,将其传入,例子代码如下:

// 创建 RequestBody 对象
RequestBody requestBody = new FormBody.Builder()
			.add("userName", "admin")
			.add("pw", "123")
			.build();
// 创建 request
Request request = new Request.Builder().url("https://www.baidu.com")
		.post(requestBody)
		.build();
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐