IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android学习笔记-服务 -> 正文阅读

[移动开发]Android学习笔记-服务

后台默默的劳动者

01 服务

??服务是android中实现程序后台运行的解决方案.适合执行那些不需要和用户交互要求长期运行的任务.

??服务的运行不依赖于其他任何用户界面,即使程序被切换到后台,或者用户打开另外一个应用,服务仍然可以正常运行.

02 子线程更新UI会出现错误

??Android的UI也是线程不安全的.想更新UI元素,必须在主线程中进行,否则会有异常.

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                textView.setText("hello");
            }
        }).start();
    }
});
//启动了线程修改主线程的UI,导致了错误

03 异步消息处理案例

private Handler handler = new Handler(Looper.myLooper()){
    @Override
    public void handleMessage(@NonNull Message msg) {
        switch (msg.what){
            case UPDATE_TEXT:
                //进行UI操作
                textView.setText("hello");
                break;
            default:
                break;
        }
    }
};
new Thread(new Runnable() {
    @Override
    public void run() {
        //子线程更新UI
        Message message= new Message();
        message.what = UPDATE_TEXT;
        handler.sendMessage(message);
    }
}).start();

04 异步消息处理机制

1. Message

??Message是线程之间传递的消息.可以携带少量信息. Message的what字段,arg1和arg2字段可以携带整型数据.obj字段携带一个obj对象.

2. Handler

??处理者,用于发送和处理消息.发送一般使用Handler的sendMessage()方法. 一系列处理后消息会到达Handler的handleMessage()方法中.

3. MessageQueue

??消息队列.用于存放所有通过Handler发送的消息. 消息会一直存在于消息队列中等待被处理.每个线程中只会有一个MessageQueue对象.

4. Looper

??每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入一个无限循环当中.每当发现MessageQueue中存在一条消息,就会把它取出,传递到Handler的hanleMessage()方法中.每个线程也只有一个Looper对象

流程:

  1. 在主线程中创建一个Handler对象,并重写handleMessage()方法.
  2. 子线程要进行UI操作,就创建一个Message对象,并通过主线程的Handler对象将消息发出去.
  3. 消息被添加到Handler中的MessageQueue队列中.
  4. Looper一直从MessageQueue中取出消息并发回给Handler的handleMessage()方法中.

05 使用AsyncTask

抽象类,使用时需继承.有三个泛型参数可以指定

class DownloadTask extends AsyncTask<Void, Integer, Boolean>{
    
}

1. Params

??执行AsyncTask时需要传入的参数,可用于在后台任务中使用

2. Progress

??后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位

3. Result

??当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型.

这里可以指定第一个泛型参数为void.表示执行时不需要传入参数给后台任务.第二个泛型参数指定为Integer,表示使用整型数据作为进度显示单位.第三个参数为布尔型,表示用布尔型数据来反馈执行结果.

经常需要重写的方法如下:

1. onPreExecute()
??这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示
一个进度条对话框等。

02. doInBackground(Params…)
??这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成,就可以通过return语句将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用onpublishProgress (Progress…)方法来完成。
03. onProgressUpdate(Progress…)
??当在后台任务中调用了publishProgress(Progress…)方法后,onProgressUpdate (Progress…)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
04. onPostExecute(Result)
??当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数
据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如说提醒任务执
行的结果,以及关闭进度条对话框等。

下列代码不完整

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {

    @Override
    protected void onPreExecute() {
        //1 显示进度对话框
        progressDialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... voids) {
       try{
           while (true){
               //2 进行耗时操作
               int downloadPercent = doDownload();//这是一个虚构的下载方法;
               publishProgress(downloadPercent); //3 因为不能进行UI操作,所以将下载进度传到onProgressUpdate()方法中.
               if (downloadPercent >=100){
                   break;
               }
           }
       }catch (Exception e){
           return false;
       }
       return true;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        //4 进行UI操作,显示下载进度
        progressDialog.setMessage("DownLoaded" + values[0] + "%");
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        //5 显示下载结果
        progressDialog.dismiss();// 关闭对话框
        if (aBoolean){
            Toast.makeText(context,"Download succeed",Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show();
        }
    }
}

想要执行该任务,只需

//代码都是在子线程中运行的.
new DownloadTask().execute();

??这里虚构了一个doDownload()方法,用于计算当前的下载进度并返回,我们假设这个方法已经存在了。在得到了当前的下载进度后,下面就该考虑如何把它显示到界面上了,由于doInBackground()方法是在子线程中运行的,在这里肯定不能进行UI操作,所以我们可以调用publishProgress()方法并传入当前的下载进度,这样onProgressUpdate()方法就会很快被调用,在这里就可以进行UI操作了。当下载完成后,doInBackground()方法会返回一个布尔型变量,这样onPostExecute()方法就会很快被调用,这个方法也是在主线程中运行的。然后,在这里我们会根据下载的结果弹出相应的Toast提示,从而完成整个DownloadTask任务。

??简单来说,使用AsyncTask的诀窍就是,在doInBackground()方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作

06 服务的用法

1 定义一个服务

??new->service->service .将Exported(是否允许除了当前程序之外其他程序访问这个服务)和Enabled(是否启用服务)属性勾中.

重写onCreate()、onStartCommand()和onDestroy()

??其中onCreate()方法会在Service创建的时候调用,onStartCommand()方法会在每次Service启动的时候调用,onDestroy()方法会在Service销毁的时候调用。

2 启动和重启服务

代码案例

主活动

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

@Override
public void onClick(View v) {
    switch (v.getId()){
        case R.id.startService:
            //01 开启服务
            Intent startIntent = new Intent(this,MyService.class);
            startService(startIntent);
            break;
        case R.id.stopService:
            //02 关闭服务
            Intent stopIntent = new Intent(this,MyService.class);
            stopService(stopIntent);
            break;
        default:
            break;
    }
}

自定义服务

public class MyService extends Service {
    private static final String TAG = "Service";
    public MyService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: executed");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: executed");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: executed");
    }

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

结果: oncreate方法和onstartcommand方法执行

3 活动和服务进行通信

原理:回调.

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private MyService.DownloadBinder downloadBinder;

    //01 用以绑定服务
    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 bindService = (Button) findViewById(R.id.bindService);
        bindService.setOnClickListener(this);
        Button unbindService = (Button) findViewById(R.id.unbindService);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.bindService:
                //02 生成服务并进行绑定
                Intent bindIntent = new Intent(this,MyService.class);
                bindService(bindIntent,connection,BIND_AUTO_CREATE);
                break;
            case R.id.unbindService:
                //03 解绑服务
                Intent unbindIntent = new Intent(this,MyService.class);
                unbindService(connection);
                break;
            default:
                break;
        }
    }
}

绑定服务时只会调用服务的oncreate方法,不会调用onstartcommand方法.

07 服务的生命周期

??前面我们使用到的onCreate()、onStartCommand()、onBind()和onDestroy()
方法都是在Service的生命周期内
可能回调
的方法。

??一旦在项目的任何位置调用了Context的startService()方法,相应的Service就会启动,并回调onStartCommand()方法。如果这个Service之前还没有创建过,onCreate()方法会先于onStartCommand()方法执行。Service启动了之后会一直保持运行状态,直到stopService()或stopSelf()方法被调用,或者被系统回收。注意,虽然每调用一次startService()法,onStartCommand()就会执行一次,但实际上每个Service只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopSelf()方法,Service就会停止。

前台服务

在service的onCreate中添加如下代码

//创建通知管理
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

//创建通道渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
    NotificationChannel channel = new NotificationChannel(
        "vashon","测试通知",NotificationManager.IMPORTANCE_HIGH);
    manager.createNotificationChannel(channel);
}
//我的消息具体内容
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
    this,0,intent,0);
//创建通知
Notification notification = new NotificationCompat.Builder(this,"vashon")
    .setContentTitle("我的通知")
    .setContentText("这是通知内容")
    .setSmallIcon(R.drawable.ic_launcher_background)//不能是RGB图
    .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background))//大图标
    .setColor(Color.parseColor("#ff0000")) //小图标颜色
    .setAutoCancel(true) //点击后取消
    .setContentIntent(pendingIntent)   //通知具体信息
    .build();
startForeground(1,notification);

在manifest.xml中添加权限

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

可以看到前台服务在通知栏中.移除不了

08 使用IntentService(在子线程处理任务)

??因为服务默认在主线程中运行,有时服务需要处理耗时的逻辑,容易出现ANR. IntentService解决了这个问题.

创建IntentService类

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";


    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        //打印当前进程id
        Log.d(TAG, "onHandleIntent: Thread id is" + Thread.currentThread().getId());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: executed");
    }
}

主活动中加入代码

case R.id.startIntentService:
//打印当前进程id
    Log.d(TAG, "Thread id is " + Thread.currentThread().getId());
    Intent intentService = new Intent(this,MyIntentService.class);
    startService(intentService);
    break;

结果如下:

D/MainActivity: Thread id is 2
D/MyIntentService: onHandleIntent: Thread id is2508
D/MyIntentService: onDestroy: executed
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-07 12:12:20  更:2021-08-07 12:14:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 12:55:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码