Android入门系列(九):Thread、Handler类和Message类、MediaPlayer和URLFile的基本使用

Android入门系列(九):Thread、Handler类和Message类、MediaPlayer和URLFile的基本使用,第1张

Android入门系列(九):Thread、Handler类和Message类、MediaPlayer和URL/File的基本使用 一、多线程

在Android中,默认情况所有的 *** 作都在主线程中进行,它负责管理 *** 作UI相关事务,用户自己创建的子线程不能 *** 作这些UI组件,但是提供了消息处理机制来解决该问题

1.线程创建

创建进程有两种方法:一通过Thread类的构造方法创建线程对象,重写run方法

二通过实现Runnable接口实现

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {

    }
});
public class MainActivity extends AppCompatActivity implements Runnable{

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

//        Thread thread = new Thread(new Runnable() {
//            @Override
//            public void run() {
//
//            }
//        });
    }

    @Override
    public void run() {
        
    }
}
2.线程启动

thread.run启动

run()和start()的区别

每个线程通过Thread对象的run方法来 *** 作,成为线程体,通过调用start来启动
run方法用于执行线程时候的运行代码,可以重复调用,start只能调用一次
start用来启动线程,实现多线程,先处于就绪状态,再通过run来完成其运行状态,而run的执行路径只有一条,如果先run的话,不会run方法达成main线程下的普通方法去执行,不会在线程中执行,不是多线程的工作

3.线程休眠

线程的暂停

Thread.sleep(long time)

time的单位为毫秒

4.线程中断

interrupt,使用这个方法可以给线程发送中断请求,标记它为中断状态

也可以通过Thread.currentThread().isInterrrupted()来获取所有线程是否中断,返回布尔值

如果在线程已经别标记为终端时进行wait,join或sleep,将会抛出InterruptedException异常,所以就需要isInterrupt来标记线程是否中断,通过对它的判断来完成 *** 作

写了以下代码

package com.thundersoft.session7;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements Runnable{

    private int i;
    private Thread thread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button start = findViewById(R.id.start);
        Button stop = findViewById(R.id.stop);
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                i = 0;
                thread = new Thread(MainActivity.this);
                thread.start();
                Log.i("thread", "thread =====>" + thread);
            }
        });
        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (thread != null){
                    thread.interrupt();
                }
                Log.i("thread","stop");
            }
        });
    }

    @Override
    public void run() {
        Thread currentThread = Thread.currentThread();
        while (!currentThread.isInterrupted()){
            i++;
            try {
                Thread.sleep(1000);
                Log.i("thread","" + i);
            }catch (InterruptedException e){
                e.printStackTrace();
                thread.interrupt();
            }

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
}

Activity实现了Runnable接口,重写run方法,在run方法中,判断当前线程是否为中断状态,如果不是,休眠一秒,输出日志

在sleep的源码中,有以下内容

if (millis == 0 && nanos == 0) {
    // ...but we still have to handle being interrupted.
    if (Thread.interrupted()) {
      throw new InterruptedException();
    }
    return;
}

判断sleep的线程对象是否为中断状态,如果是,则清楚状态(interrupted的含义),并且抛出异常,回到自己的代码中,抛出异常,则中断线程

start按钮的点击方法,创建了线程并且开启

stop按钮的点击方法,如果该线程对象为不为空就标记为中断状态,会被sleep检测到

5.播放音乐

通过线程播放音乐的实例

package com.thundersoft.session7;

import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import androidx.annotation.Nullable;

import java.io.IOException;

public class MusicActivity extends Activity implements Runnable {
    private Thread thread;
    private MediaPlayer mediaplayer;
    private String path;
    boolean isAlive = false;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout linearLayout = new LinearLayout(this);
        setContentView(linearLayout);
        Button button1 = new Button(this);
        Button button2 = new Button(this);
        button1.setText("点我开启线程播放音乐");
        button2.setText("点我关闭线程停止播放音乐");
        linearLayout.addView(button1);
        linearLayout.addView(button2);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                thread = new Thread(MusicActivity.this);
                if (isAlive){
                    Toast.makeText(MusicActivity.this, "线程已经执行", Toast.LENGTH_SHORT).show();
                }else {
                    thread.start();
                    Toast.makeText(MusicActivity.this, "开始播放", Toast.LENGTH_SHORT).show();
                    isAlive = true;
                }
            }
        });
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (thread != null){
                    thread.interrupt();
                }
            }
        });
    }

    @Override
    public void run() {
        Thread c = Thread.currentThread();
        try {
            display();
        } catch (IOException e) {
            e.printStackTrace();
        }
        while (!c.isInterrupted()){
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
                thread.interrupt();
                mediaplayer.stop();
            }
        }
    }

    private void display() throws IOException {
        path = "http://m10.music.126.net/20211228095512/4d1bde16a0860270925710fef8812e12/ymusic/2d31/cbe1/b405/9694012993255d512f94cb24e8d760bd.mp3";
        if (path == "") {
            Toast.makeText(MusicActivity.this, "未找到您要播放的音乐",
                    Toast.LENGTH_LONG).show();
        }
        mediaplayer = new MediaPlayer();
        mediaplayer.setDataSource(path);
        mediaplayer.prepare();
        mediaplayer.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mediaplayer != null){
            mediaplayer.release();
            mediaplayer = null;
        }
    }
}
二、消息处理类Handler

在一个线程之中,只能有一个Looper和MessageQueue,但是可以有很多个Handler,这些个Handler之间可以共享Looper和MessageQueue

Handler有两个主要作用,第一个是Message或者是Runnable应用post或者是通过sendMessage方法发送到消息队列中,发送时可以指定时间,延迟,还有Bundle数据

消息队列循环到当前队列时调用相应的Handle对象的HandleMessage方法来处理

第二个作用是在子线程和主线程之间进行通信,在工作线程和UI线程之间进行通信

1.Handler类常用方法

handleMessage(Message msg)处理消息的方法,通常被重写,在发送消息时,该方法会被回调

post(Runnable r)立即发送Runnable对象,最终将改对象封装撑Message对象

postAtTime(Runnable r,long uptimeMillis)定时发送runnable

postDelayed(Runnable r,long delayMillis)延迟发送runnable

sendEmptyMessage(int what)发送空消息

sendMessage(Message msg)立即发送Message对象

sendMessageAtTime/sendMessageDelayed定时和延时发送

2.Message

一个消息队列中会有多个消息对象,每个消息对象都可以通过

**Message.obtain()或Handler.obtainMessage()**方法获得消息

每个Message对象都包含以下五个属性

arg1/arg2都为int型,用来存放整形数据

obj为Object,用来存放发送给接收器的Object类对象

replyTo为Messenger,用来指定Message发送到何处的可选的Messager对象

what为int型,指定自定义的消息代码,用于给接收者了解

如果要携带其他类型的数据,通过将数据存放在Bundle中,使用Message的setDate方法将其添加到Message中

优先使用Message.arg1/2来存储信息,使用what来标识信息,比使用Bundle更节省内存

3.加载图片

实现通过线程加载图片,以播放音乐那个例子写的这次代码,但是出现only the original thread that created a view hierarchy can touch its views报错

意为加载view需要通过view线程来 *** 作,利用handle来解决问题,代码如下

package com.thundersoft.session7;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class PicActivity extends Activity implements Runnable{
    boolean state = true;
    private Thread thread;
    private ImageView imageView;
    private Handler handler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout linearLayout = new LinearLayout(this);
        linearLayout.setOrientation(LinearLayout.VERTICAL);
        setContentView(linearLayout);
        imageView = new ImageView(this);
        Button button1 = new Button(this);
        Button button2 = new Button(this);
        button1.setText("点我获取图片");
        button2.setText("点我关闭图片");
        linearLayout.addView(imageView);
        linearLayout.addView(button1);
        linearLayout.addView(button2);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (state){
                    thread = new Thread(PicActivity.this);
                    thread.start();
                    state = false;
                }else {
                    Toast.makeText(PicActivity.this, "图片已经被加载", Toast.LENGTH_SHORT).show();
                }
            }
        });
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (thread != null){
                    thread.interrupt();
                }
            }
        });

        if (android.os.Build.VERSION.SDK_INT > 9) {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }
        handler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                if (msg.what==0){
                    try {
                        loading();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (msg.what==1){
                    imageView.setVisibility(View.GONE);
                }
            }
        };

    }
    @Override
    public void run() {
        Thread c = Thread.currentThread();
        //            处理消息
        handler.sendEmptyMessage(0);
        while (!c.isInterrupted()){
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
                //                同样也要处理消息
                handler.sendEmptyMessage(1);
                thread.interrupt();
            }
        }
    }

    private void loading() throws IOException {
        String path = "https://pic2.zhimg.com/80/v2-fda7c13057f7182bd1c603b2d6b1fa00_720w.jpg";
        Bitmap bitmap = null;
        URL url;
        url = new URL(path);
        URLConnection conn = url.openConnection();
        conn.connect();
        InputStream is = conn.getInputStream();
        bitmap=BitmapFactory.decodeStream(is);
        imageView.setImageBitmap(bitmap);
    }
}

在run中请求图片sendEmptyMessage的what为0,取消图片为1,new一个Handle对象来对what的判断来进行相应 *** 作

4.循环播放图片

和上面的实例差不多

package com.thundersoft.session7;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class AdActivity extends Activity implements Runnable{
    boolean state = true;
    private Thread thread;
    private ImageView imageView;
    private Handler handler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout linearLayout = new LinearLayout(this);
        linearLayout.setOrientation(LinearLayout.VERTICAL);
        setContentView(linearLayout);
        imageView = new ImageView(this);
        imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
        imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        imageView.setImageResource(R.drawable.ad1);
        linearLayout.addView(imageView);

        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (state){
                    thread = new Thread(AdActivity.this);
                    thread.start();
                    state = false;
                }else {
                    if (thread != null){
                        thread.interrupt();
                    }
                }
            }
        });

//        if (android.os.Build.VERSION.SDK_INT > 9) {
//            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
//            StrictMode.setThreadPolicy(policy);
//        }
        handler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                if (msg.what==0){
                    imageView.setImageResource(R.drawable.ad1);
                }
                if (msg.what==1){
                    imageView.setImageResource(R.drawable.ad2);
                }
            }
        };

    }

    @Override
    public void run() {
        Thread c = Thread.currentThread();
        //            处理消息
        while (!c.isInterrupted()){
            try {
                handler.sendEmptyMessage(0);
                Thread.sleep(500);
                handler.sendEmptyMessage(1);
                Thread.sleep(500);
            }catch (InterruptedException e){
                e.printStackTrace();
                handler.sendEmptyMessage(0);
                thread.interrupt();
            }
        }
    }
}
三、实践 1.要求

实现一个登录页面,提供记住密码和用户名功能,登陆后显示列表视图必须包含登录用户名,且点击列表会显示点击那一项

登录后主界面列表选择第一个选项后,显示一组图片,图片加载完成前需要显示进度条,加载完成后,长按某一个图片d出提示框是否删除,如果确定就不再显示该图片

登录后主界面列表选择第二个选项后,跳转到另一个Activity,并且传过去一个字符串,在新的Activity中包含2个Fragment,左边的Fragment类似手机设置,含一个亮度设置选项,点击后右边的Fragment显示具体的亮度设置界面

登录后主界面列表选择第三个选项后,跳转到另一个Activity,并且传过去int/byte/Serializable等多种类型的数据,在新的Activity中显示传入的数据,检查是否所传所有类型的数据都正确接收,同时在Activity启动的时候开始监听android.net.conn.CONNECTIVITY_CHANGE,Activity退出后不再监听,当网络状态改变的时候提示用户网络状态改变情况。

登录后主界面列选择第四个选项后,跳转到另一个Activity,运用本章学习的资源、样式等知识,实现Activity中显示类似自己手机设置列表的效果(跟进到自己手机的设置界面一样)

登录后主界面列表选择第五个选项后,跳转到另一个Activity显示记账本,其含一个 记账项输入、金额输入、添加按钮和一个列表,添加记账功能往自定义Content Provider写入,数据可以存储在自定义数据库,或者xml文件里,或者最简单的存在列表变量里面;

(以上是Android 八 当中的实践要求)

登陆后主页面列表选择第六个选项后,通过输入网址下载MP3,显示下载进度,下载完成后立即播放,实现停止功能,并且显示播放的次数

2.代码实现

主程序代码改动:新加了字符串

if (index == 6){
    startActivity(new Intent().setClass(MainActivity.this,MusicActivity.class));
}

MusicActivity.java

package com.thundersoft.login;

import android.app.Activity;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Timer;
import java.util.TimerTask;

public class MusicActivity extends Activity{
    private EditText editText = null;
    private Button download,pause = null;
    private ProgressBar schedule = null;
    private ProgressBar schedule1 = null;
    private TextView times = null;
    private TextView result = null;

    boolean state = true;
    private Handler handler;
    private static MediaPlayer mediaPlayer = null;

    private int iTimes = 0;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.music);
        editText = findViewById(R.id.url);
        download = findViewById(R.id.download);
        pause = findViewById(R.id.pause);
        schedule = findViewById(R.id.schedule);
        schedule1 = findViewById(R.id.schedule1);
        times = findViewById(R.id.times);
        result = findViewById(R.id.resultm);

        String url = editText.getText().toString();
        editText.setText("http://m10.music.126.net/20220101190407/e141e7a49dafae12790f300bd052255b/ymusic/0f59/5252/0353/834dc09348e89fa0d480496a7475eff8.mp3");

        download.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (editText.length() != 0){
                    if (state){
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                download();
                            }
                        }).start();
                        state = false;
                    }else {
                        Toast.makeText(MusicActivity.this, "正在下载", Toast.LENGTH_SHORT).show();
                    }
                }else {
                    Toast.makeText(MusicActivity.this, "请输入下载地址", Toast.LENGTH_SHORT).show();
                }
            }
        });

        pause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mediaPlayer.pause();
            }
        });

        handler = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                if (msg.what == 0x101){
                    schedule.setProgress(msg.arg1);
                    result.setText("已下载" + String.valueOf(msg.arg1) + "%");
                    if (msg.arg1 == 100){
                        result.setText("下载完成!");
                    }
                }
                if (msg.what == 0x102){
                    schedule1.setProgress(msg.arg1);
                }
                super.handleMessage(msg);
            }
        };

    }


    private void download() {
        try {
//            获取url链接并打开
            URL url = new URL(editText.getText().toString());
            URLConnection urlConnection = url.openConnection();
//            获取连接长度
            int length = urlConnection.getContentLength();
//            通过流来建立通道
            ReadableByteChannel c = Channels.newChannel(url.openStream());
//            创建文件内存
            File file = new File("data/data/com.thundersoft.login/test.mp3");
            if (!file.exists()){
                file.createNewFile();
            }
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            FileChannel channel = fileOutputStream.getChannel();
//            创建字节缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int len;
            int sum = 0;
            while ((len = c.read(byteBuffer)) != -1){
                sum += len;
                int progress = (sum*100)/length;
                byteBuffer.flip();
                while (byteBuffer.hasRemaining()){
                    channel.write(byteBuffer);
                }
                byteBuffer.clear();
//                发送消息
                Message m = handler.obtainMessage();
                m.what = 0x101;
                m.arg1 = progress;
                handler.sendMessage(m);
            }
            display();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void display() {
        mediaPlayer = new MediaPlayer();
        mediaPlayer = MediaPlayer.create(this,Uri.parse("data/data/com.thundersoft.login/test.mp3"));
        mediaPlayer.start();

        schedule1.setMax(mediaPlayer.getDuration());
        Timer timer = new Timer();
        TimerTask timerTask = new TimerTask(){
            @Override
            public void run() {
                schedule1.setProgress(mediaPlayer.getCurrentPosition());
            }
        };
        timer.schedule(timerTask,0,10);

        iTimes++;
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                times.setText(""+iTimes);
//                因为没有开始按钮所以点击暂停完成播放自动开始下一次播放
                display();
            }
        });
    }

}

点击下载按钮,创建线程下载,下载方法中,将文件的下载进度传递给message通过handler发送出去观察进度

3.实现效果


其实是有声音的

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5692386.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存