【安卓开发】探究服务

news2024/9/22 11:41:29

10.2 Android多线程编程

定义一个线程只需要新建一个类继承自Thread,然后重写父类的run方法,在里面编写耗时逻辑即可

class MyThread extends Thread{
    @Override
    public void run(){
        // 处理具体的逻辑
    }
}

那么如何启动呢
new Mythread().start()
这样继承的方式耦合性高,更多都会使用Runnable接口的方式定义一个线程,如下:

class MyThread implements Runnable{
    @Override
    public void run(){
        // 具体逻辑
    }
}

调用方式也要改变

MyThread myThread = new MyThread();
new Thread(myThread).start();

如果不想专门定义一个类去实现runnable,也可以用匿名类的方式:

    new Thread(new Runnable(){
        @Override
        public void run(){
            // 相应逻辑
        }
    }).start();

下面来看下Android多线程和java多线程不同的地方:

10.2.2 在子线程中更新UI

Android的UI也是线程不安全的,要想更新程序中的UI元素,必须在主线程中进行
下面测试下,首先加点组件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/change_text"
        android:text="Change text"/>
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/text"
        android:layout_centerInParent="true"
        android:text="hello world"
        android:textSize="20sp"></TextView>

</RelativeLayout>

下面代码直接运行的话会出错

public class MainActivity extends AppCompatActivity {

    private TextView text;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.text);
        Button changeText = (Button) findViewById(R.id.change_text);
        changeText.setOnClickListener(this);
    }

    @Override
    public void onClick(View v){
        switch (v.getId()){
            case R.id.change_text:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        text.setText("Nice to meet you");
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}

因为是在子线程中更新的UI导致的,由此可见确实不能在子线程更新UI,但是有时候必须要在子线程中执行耗时任务,然后根据任务结果更新相应UI
对于这种情况,Android 提供了一套异步处理机制,先学习如何使用

public class MainActivity extends AppCompatActivity {
    
    public static final int UPDATE_TEXT = 1;
    private TextView text;
    
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case UPDATE_TEXT:
                    // 在这里进行UI操作
                    text.setText("nice to meet you");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.text);
        Button changeText = (Button) findViewById(R.id.change_text);
        changeText.setOnClickListener(this);
    }

    @Override
    public void onClick(View v){
        switch (v.getId()){
            case R.id.change_text:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message); // 将message对象发送出去
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}

首先定义了常量 UPDATE_TEXT 用来表示更新 textview 这个动作,然后新增了个 Handler 对象,并重写父类的 handleMessage() 方法。

10.2.3 解析异步消息处理机制

Android 异步消息处理主要由 4个部分组成:Message、Handler、MessageQueue 和 Looper

  1. Message:在线程之间传递的消息,前面我们用了what字段,还可以使用 arg1 和 arg2 带一些整型数据,obj 带对象
  2. Handler:主要用于发和处理消息,发消息一般用Handler的sendMessage() 方法,发出的消息最终会传递到 Handler 的 handleMessage() 方法中。
  3. MessageQueue:消息队列的意思,存放所有通过 Handler 发送的消息,这部分消息会一直在消息队列中等待被处理,每个线程中只会有一个 MessageQueue 对象。
  4. Looper:调用 Looper 的 loop() 方法后,会进入无限循环中,每当发现 MessageQueue 中存在一个消息就取出,并传递到 HandlerMessage() 方法中,每个线程中也只会有一个 Looper 对象。
    异步消息处理机制
    在9.2.1中 用到的 runOnUiThread()方法其实就是对上述异步消息处理机制的接口封装。

10.2.4 使用 AsyncTask

AsyncTask是一个抽象类,我们必须要建一个子类继承它,继承时我们可以为 AsyncTask 类指定3个泛型参数,这3个参数用途如下:

  • Params:执行 AsyncTask 时需要传入的参数,可用于在后台任务使用。
  • Progress:后台任务执行时,如果需要在界面显示进度,则使用这里指定的泛型作为进度单位
  • Result:任务完毕后,返回结果

则一个最简单的自定义 AsyncTask 就可以写成如下方式:
class DownloadTask extends AsyncTask<void, Integer, Boolean>
目前我们自定义的 DownloadTask 还是一个空任务,我们还需要去重写 AsyncTask 中的几个方法才能完成对任务的定制,主要有 4 个:

  1. onPreExecute() :这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框。
  2. doInBackground(Params…):这个方法中代码都会在子线程中允许,应该在这里处理耗时任务,任务完成的话就可以通过return语句来执行结果返回,如果 AsyncTask 的第三个泛型参数是 void 就可以不返回任务执行结果。这个方法中不可以进行 UI 操作,要更新UI的话 调用 publishProgress(Progress…)
  3. onProgressUpdate(Progress…):当在后台任务中调用 publishProgress (Progress… )方法后,onProgressUpdate 很快就会被调用,在这个方法中就可以对 UI 操作。
  4. onPostExecute(Result):后台任务执行完毕并通过return返回时,这个方法很快就会被调用

简单来说 doInBackground 中放具体任务, onProgressUpdate 中UI操作,onPostExecute 执行收尾工作。

想要启动任务 只需要 加上 execute() 下就行

10.3 服务的基本用法

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public void onCreate(){
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId){
        return super.onStartCommand(intent, flags, startId);
    }
    
    @Override
    public void onDestroy(){
        super.onDestroy();
    }
}

通常我们希望服务一启动就立刻执行某个动作,就可以把逻辑写在 onStartCommand() 方法中,销毁时,在 onDestroy()方法中回收不再使用的资源。
新的服务也需要在 AndroidManifest。xml中声明

10.3.2 启动和停止服务

通过 Intent 来实现的

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_height="match_parent"
    android:layout_width="match_parent">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/start_service"
        android:text="Start Service"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/stop_service"
        android:text="Stop Service"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService = (Button) findViewById(R.id.start_service);
        Button stopService = (Button) findViewById(R.id.stop_service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.start_service:
                Intent startIntent = new Intent(this, MyService.class);
                startService(startIntent); // 启动服务
                break;
            case R.id.stop_service:
                Intent stopIntent = new Intent(this, MyService.class);
                stopService(stopIntent); // 停止服务
                break;
            default:
                break;
        }
    }
}

注意,这里是完全由活动来决定服务什么时候停止的,不点stop的话服务会一直处于允许状态,那么就会一直在运行,在 MyService 的任何一个位置调用 stopSelf() 方法就能让服务停止。

onCreate 和 onStartCommand 区别:create 只在服务第一次创建的时候调用的,而 onStartCommand() 方法则在每次启动服务时都会调用。

10.3.3 活动和服务进行通信

通过 Binder 对象实现管理

对 MyService 类做出如下修改:

    private DownloadBinder mBinder = new DownloadBinder();
    
    class DownloadBinder extends Binder{
        public void startDownload(){
            Log.d("MyService", "start xiazai");
        }
        
        public int getProgress(){
            Log.d("Myservice", "jindu");
            return 0;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
    
    public MyService() {
    }

下面添加两个按钮用来绑定和取消绑定活动与服务,然后修改主活动代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    private MyService.DownloadBinder downloadBinder;
    
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService = (Button) findViewById(R.id.start_service);
        Button stopService = (Button) findViewById(R.id.stop_service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
        
        Button bindService = (Button) findViewById(R.id.bind_service);
        Button unbindService = (Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.start_service:
                Intent startIntent = new Intent(this, MyService.class);
                startService(startIntent); // 启动服务
                break;
            case R.id.stop_service:
                Intent stopIntent = new Intent(this, MyService.class);
                stopService(stopIntent); // 停止服务
                break;
            case R.id.bind_service:
                Intent bindIntent = new Intent(this, MyService.class);
                bindService(bindIntent, connection, BIND_AUTO_CREATE); //绑定服务
                break;
            case R.id.unbind_service:
                unbindService(connection); // 取消绑定
                break;
            default:
                break;
        }
    }
}

bindService 三个参数 第一个是新创建的 Intent 对象,第二个是前面的 ServiceConnection 的实例,第三个参数是个标志位,传入 BIND_AUTO_CREATE 表示活动和服务进行绑定后自动创建服务,会导致 服务的 onCreate 函数执行。
任何一个服务在整个应用程序都是通用的,一个服务可以绑定多个活动。

10.5 服务的更多技巧

10.5.1 使用前台服务

后台的服务可能会在内存不足时被回收,但是前台服务不会

    public void onCreate(){
        super.onCreate();
        Log.d("Myservice", "execute onCreate");
        
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
        Notification notification = new NotificationCompat.Builder(this).setContentText("this is a content text")
                .setContentTitle("dadasda").setWhen(System.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                .setContentIntent(pi).build();
        startForeground(1, notification);
    }

只修改了 onCreate 中的代码就可以了

10.5.2 使用 IntentService

服务中的代码都是在主线程中的,如果在其中运行一些耗时的逻辑,很容易出现 ANR (application not responding)

这个时候就需要多线程技术了,在服务类的里面开启一个子线程。

    @Override
    public int onStartCommand(Intent intent, int flags, int startId){
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 处理具体的逻辑
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

但这种服务一旦启动,但会一直运行,必须调用 stopService()或者 stopSelf()才能停止

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/392937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hive拉链表

概述 拉链表&#xff1a;维护历史状态以及最新状态数据的表 作用场景 1. 数据量比较大。 2. 表中的部分字段会被更新&#xff0c;比如用户的地址&#xff0c;银行利率&#xff0c;订单的状态等。 3. 需要查看某一个时间点或者时间段的历史快照信息&#xff0c;比如&#xff0c;…

Linux GCC 编译详解

文章目录一、GCC 编译器简介二、GCC 工作流编程语言的发展GCC 工作流程gcc 和 g 的区别三、使用 GCC 编译GCC 编译格式GCC 编译流程多个源文件编译一、GCC 编译器简介 首先&#xff0c;什么是编译器呢&#xff1f; 我们可以使用编辑器&#xff08;如 linux 下的 vi、windows 下…

Tomcat安装步骤及详细配置教程

Tomcat安装及配置教程主要分为四步&#xff1a; 步骤一&#xff1a;首先确认自己是否已经安装JDK 步骤二&#xff1a;下载安装Tomcat 步骤三&#xff1a;Tomcat配置环境变量 步骤四&#xff1a;验证Tomcat配置是否成功 一、首先确认自己是否已经安装JDK WinR打开运行&…

sklearn手册(持续更新ing...)

诸神缄默不语-个人CSDN博文目录 本文是一个随时使用sklearn时可供参考的手册。我有使用sklearn的基础&#xff0c;且准备后期直接用sklearn官方的教程文档参考撰写系统性学习sklearn包使用方法的sklearn用户教程一文&#xff0c;因此本文就不介绍基础了。 最近更新时间&#…

ubuntu 20.04 Linux下查看当前文件夹的大小

问题描述 由于使用远程的 ssh 连接 ubuntu 20.04&#xff0c;所以不清楚如何查看 当前文件夹的大小 直接使用 df -h&#xff0c;只能查看 当前系统 磁盘的使用情况 需求 通过Linux shell 命令行&#xff0c;查看 当前文件夹占用的大小 其实很简单&#xff0c;使用 Linux sh…

如何使用码匠连接 Oracle

目录 在码匠中集成 Oracle 在码匠中使用 Oracle 关于码匠 Oracle 是一种关系型数据库&#xff0c;可用于存储和管理大量结构化数据。Oracle 数据源支持多种操作系统&#xff0c;包括 Windows、Linux 和 Unix 等&#xff0c;同时也提供了各种工具和服务&#xff0c;例如 Orac…

从设计转到了产品,实习生工作复盘

一、写在前面从设计转到了产品&#xff0c;产品的门槛好像相对于设计确实要低一些。通过产品新人必读书《人人都是产品经理》、知乎产品大神的观点和我个人的理解&#xff0c;可以得出一个结论——并不是每个人都是合格的产品经理&#xff0c;包括我在内的很多人都是因为公司有…

​2023年湖北武汉自己怎么报考二建?报考二建学历不符怎么办?启程别

2023年湖北武汉自己怎么报考二建&#xff1f;报考二建学历不符怎么办&#xff1f;启程别 2023年武汉自己怎么报考二建&#xff0c;首先个人是没有办法报名的&#xff0c;报考二建必须以企业的名义报名。报考二建的基本条件是什么&#xff1f;启程别告诉你&#xff0c;“启程别”…

Spark AQE

Spark AQEAQE/RBO/CBOAQEAQE特点Join 策略调整自动分区合并自动倾斜处理Spark 3.0 添加了自适应查询执行&#xff08;AQE&#xff09;、动态分区剪裁&#xff08;DPP&#xff09;、扩展的 Join Hints AQE/RBO/CBO Spark SQL 2.0前&#xff0c;仅支持启发式、静态的优化过程。…

mybatis面试题 一

一、MyBatis工作原理&#xff1f; 1、 创建SqlSessionFactory 2、 通过SqlSessionFactory创建SqlSession 3、 通过sqlsession执行数据库操作 4、 调用session.commit()提交事务 5、 调用session.close()关闭会话 1&#xff09;读取 MyBatis 配置文件&#xff1a;mybatis-c…

zynq7000系列芯片介绍

ZYNQ从架构上可以划分为两大模块&#xff0c;一个是PS&#xff08;处理器系统&#xff09;&#xff0c;另一个是PL&#xff08;可编程逻辑&#xff09; PS由APU、内存接口、IO外设、互连线4大模块组成。 1、APU&#xff08;Application Processor Unit)应用处理单元 即PS【可编…

面向对象设计模式:行为型模式之状态模式

文章目录一、引入二、状态模式2.1 Intent 意图2.2 Applicability 适用性2.3 类图2.4 Collaborations 合作2.5 Implementation 实现2.5 状态模式与策略模式的对比2.5 状态模式实例&#xff1a;糖果机2.6 状态模式实例&#xff1a;自行车升降档一、引入 State Diagram 状态图&am…

docker数据卷及软件部署方式

目录 一、docker数据卷管理 1.docker数据卷概念 2.数据卷命令 3.创建容器并挂载数据卷 二、docker软件部署 1.docker部署mysql方式 下载MySQL5.7镜像文件 创建所需的数据卷目录 创建mysql容器并挂载数据卷 进入数据库授权远程连接用户访问 2. docker部署nginx方式 下…

C语言-基础了解-16-C字符串

C字符串 一、C字符串 在 C 语言中&#xff0c;字符串实际上是使用空字符 \0 结尾的一维字符数组。因此&#xff0c;\0 是用于标记字符串的结束。 空字符&#xff08;Null character&#xff09;又称结束符&#xff0c;缩写 NUL&#xff0c;是一个数值为 0 的控制字符&#x…

剑指 Offer 31. 栈的压入、弹出序列

一、题目描述 输入两个整数序列&#xff0c;第一个序列表示栈的压入顺序&#xff0c;请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。 例如&#xff0c;序列 {1,2,3,4,5} 是某栈的压栈序列&#xff0c;序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列…

centos7 安装 hyperf

​​​​​​PHP > 7.4 Swoole PHP 扩展 > 4.5&#xff0c;并关闭了 Short Name OpenSSL PHP 扩展 JSON PHP 扩展 PDO PHP 扩展 Redis PHP 扩展 Protobuf PHP 扩展 composer create-project hyperf/hyperf-skeleton 推荐安装项 全部选n php.ini [swoole] extens…

LQB小板焊接V3版本的小板原理图,PCB图,注意事项和步骤

第一部分&#xff0c;这个部分&#xff0c;可以不焊接&#xff0c;直接用买的下载器进行下载代码&#xff0c;外接一个下载器&#xff0c;网上大概是10元左右&#xff0c;以后学习stm32的芯片的时候&#xff0c;这个下载器就是一个串口转换器&#xff0c;也可以使用。。 当然也…

realloc也可以缩容了??

作者&#xff1a;小树苗渴望变成参天大树 作者宣言&#xff1a;认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; realloc的细节前言一、realloc的原地扩容和异地扩容二、关于realloc是否可以缩容问题前…

快速搭建本地服务器

一、anywhere 1、npm install anywhere -g 2、打开位于文件夹下的终端页&#xff0c;输入anywhere 9999 9999这里是设置端口号&#xff0c;端口号自行设置&#xff0c;也可以不输入xxx会默认8080端口号 二、http-server 1、npm install http-server -g 2、打开位于文件夹下…

[golang]Go语言从入门到实践-反射

反射三定律&#xff1a; 1.变量---->反射变量 2.变量---->反射变量---->接口 3.变量----->(通过取地址&)反射变量---->修改变量的值 反射的类型和种类&#xff1a; 切片、集合、结构体、指针、函数与反射...... 总结&#xff1a; 内置包函数 reflect 的…