在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启动
3.线程休眠run()和start()的区别
每个线程通过Thread对象的run方法来 *** 作,成为线程体,通过调用start来启动
run方法用于执行线程时候的运行代码,可以重复调用,start只能调用一次
start用来启动线程,实现多线程,先处于就绪状态,再通过run来完成其运行状态,而run的执行路径只有一条,如果先run的话,不会run方法达成main线程下的普通方法去执行,不会在线程中执行,不是多线程的工作
线程的暂停
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.实现效果
其实是有声音的
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)