GVKun编程网logo

android播放视频!Android多线程实现方式及并发与同步,深度好文(android 视频播放流程)

7

想了解android播放视频!Android多线程实现方式及并发与同步,深度好文的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于android视频播放流程的相关问题,此外,我们还将为您介绍关

想了解android播放视频!Android多线程实现方式及并发与同步,深度好文的新动态吗?本文将为您提供详细的信息,我们还将为您解答关于android 视频播放流程的相关问题,此外,我们还将为您介绍关于android – 播放视频文件的最佳方式?、Android 学习记录(6)— 将 java 中的多线程下载移植到 Android 中(即多线程下载在 Android 中的使用)③、Android——android 学习(android 目录与 AndroidManifest 解析)、Android入门(十七)Android多线程的新知识。

本文目录一览:

android播放视频!Android多线程实现方式及并发与同步,深度好文(android 视频播放流程)

android播放视频!Android多线程实现方式及并发与同步,深度好文(android 视频播放流程)

前言

本人毕业于非211、985一本,学的是计算机网络,大一在一位师哥的引导下接触了Android开发,随着近一步的接触,慢慢的喜欢上了Android开发。于是大二开始,我开始自学Android开发。

大四的时候开始找实习工作, 因为喜欢Android开发,找的都是Android开发岗位,最后经过一段时间的面试,拿到了阿里的实习offer。

今天就分享一下我的阿里面经和面试前的准备和学习方法,希望的想进大厂实习的同学提供一些帮助。

笔记部分展示

全部资料展示


PS:由于文章篇幅问题,小编挑了一个大家应该都感兴趣的知识,给大家展示资料的质量!
《设计思想解读开源框架》(一共1042页PDF)
第一章、 热修复设计

  • 第一节、 AOT/JIT & dexopt 与 dex2oat

  • 第二节、 热修复设计之 CLASS_ISPREVERIFIED 问题

  • 第三节、热修复设计之热修复原理

  • 第四节、Tinker 的集成与使用(自动补丁包生成)

    设计思想解读开源框架,第一章

    第二章、 插件化框架设计

  • 第一节、 Class 文件与 Dex 文件的结构解读

  • 第二节、 Android 资源加载机制详解

  • 第三节、 四大组件调用原理

  • 第四节、 so 文件加载机制

  • 第五节、 Android 系统服务实现原理

    设计思想解读开源框架,第二章

    第三章、 组件化框架设计

  • 第一节、阿里巴巴开源路由框——ARouter 原理分析

  • 第二节、APT 编译时期自动生成代码&动态类加载

  • 第三节、 Java SPI 机制

  • 第四节、 AOP&IOC

  • 第五节、 手写组件化架构

    设计思想解读开源框架,第三章

    第四章、图片加载框架

  • 第一节、图片加载框架选型

  • 第二节、Glide 原理分析

  • 第三节、手写图片加载框架实战

    设计思想解读开源框架,第四章


    第五章、网络访问框架设计

  • 第一节、网络通信必备基础

  • 第二节、OkHttp 源码解读

  • 第三节、Retrofit 源码解析

    设计思想解读开源框架,第五章


    第六章、 RXJava 响应式编程框架设计

  • 第一节、链式调用

  • 第二节、 扩展的观察者模式

  • 第三节、事件变换设计

  • 第四节、Scheduler 线程控制

    设计思想解读开源框架,第六章


    第七章、 IOC 架构设计

  • 第一节、 依赖注入与控制反转

  • 第二节、ButterKnife 原理上篇、中篇、下篇

  • 第三节、Dagger 架构设计核心解密

    设计思想解读开源框架,第七章


    第八章、 Android 架构组件 Jetpack

  • 第一节、 LiveData 原理

  • 第二节、 Navigation 如何解决 tabLayout 问题

  • 第三节、 viewmodel 如何感知 View 生命周期及内核原理

  • 第四节、 Room 架构方式方法

  • 第五节、 dataBinding 为什么能够支持 MVVM

  • 第六节、 WorkManager 内核揭秘

  • 第七节、 Lifecycles 生命周期

    设计思想解读开源框架,第八章

我坚信,坚持学习,每天进步一点,滴水穿石,我们离成功都很近!

学习宝典

对我们开发者来说,一定要打好基础,随时准备战斗。不论寒冬是否到来,都要把自己的技术做精做深。虽然目前移动端的招聘量确实变少了,但中高端的职位还是很多的,这说明行业只是变得成熟规范起来了。竞争越激烈,产品质量与留存就变得更加重要,我们进入了技术赋能业务的时代。

@H_238_301@

不论遇到什么困难,都不应该成为我们放弃的理由!

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我针对Android程序员,我这边给大家整理了一套学习宝典!包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

Android学习PDF+架构视频+面试文档+源码笔记

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

…(img-sk8JQh7V-1615458244851)]

【延伸Android必备知识点】

[外链图片转存中…(img-TnYaaPdK-1615458244852)]

【Android部分高级架构视频学习资源】

android – 播放视频文件的最佳方式?

android – 播放视频文件的最佳方式?

我有一个视频文件的URL,我需要使用手机的原生视频播放器播放.
我想使用Intent.ACTION_VIEW并将URI设置为Intent数据,我能够在HTC英雄之外实现上述G1.在Hero的情况下,我必须在Intent中显式设置类Name以启动默认的Video Player:intent.setClassName(“com.htc.album”,“com.htc.album.ViewVideo”);

但是,我对这种方法并不满意,因为它可能在其他Android设备上破坏.请咨询:
1.无论手机如何,通过Intent播放此视频网址的最佳方式是什么?
2.如何在Intent中有条件地设置ClassClass,特定于手机?

注意:为我们的应用程序编写独立的视频播放器不是我们目前可以承受的选项.

谢谢!

解决方法:

Note: Writing a standalone Video
Player for our app is NOT an option we
can afford at the moment.

首先,视频播放器是not part of the SDK,依赖它是一个很大的错误,正如你已经发现的那样.

其次,创建一个简单的视频播放器需要大约40 lines of code,并且你已经花了更多的时间来避免编写那些仅用于编写第一行的40行代码.

Android 学习记录(6)— 将 java 中的多线程下载移植到 Android 中(即多线程下载在 Android 中的使用)③

Android 学习记录(6)— 将 java 中的多线程下载移植到 Android 中(即多线程下载在 Android 中的使用)③

在这一节中,我们就来讲多线程下载以及断点续传在 android 中怎么使用,前两节是为本节做准备的,没有看前两节的同学,最好看完前面的两篇文章再来看这篇。其实在 android 端的应用和 java 基本上是差不多的,只不过在 android 端我建议对于断点续传的记录的保存放在 android 的 sqlite3 的数据库中,这样是最好的,当然保存在 sd 卡中也行,我为了方便起见,我没有建立数据库,而是直接保存到了 sd 卡中。先看一下我在 android 中运行的效果图,如下:


我在这里的代码加上了进度条的显示和下载进度百分比的显示,为了让进度条和百分比不混乱,我为下载线程中计算下载进度的代码加上了锁。如下:

还有就是下边的一段代码我把记录下载进度的文件保存到了 SD 卡中,我建议最好用数据库。这段代码如下:

其它的我倒是没有什么好解释的了,跟上一篇文章的内容差不多,而且我在代码中已经写得很详细了,那么就请大家看代码吧!

MainActivity 中的代码如下:

package net.loonggg.android.downloader;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

@SuppressLint("HandlerLeak")
public class MainActivity extends Activity {
	public int currentProcess = 0;// 下载文件的当前进度
	// 开启的线程的个数
	public static final int THREAD_COUNT = 3;
	public static int runningThread = 3;// 记录正在运行的下载文件的线程数
	private EditText et;
	private Button btn;
	private TextView tv;
	private ProgressBar pb;// 下载进度条

	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.arg1) {
			case 0:
				Toast.makeText(getApplicationContext(), "下载失败!",
						Toast.LENGTH_SHORT).show();
				break;
			case 1:
				Toast.makeText(getApplicationContext(), "下载完成!",
						Toast.LENGTH_SHORT).show();
				break;
			case 2:
				tv.setText("下载进度:" + pb.getProgress() * 100 / pb.getMax() + "%");
				break;
			default:
				break;
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		et = (EditText) findViewById(R.id.et);
		pb = (ProgressBar) findViewById(R.id.pb);
		btn = (Button) findViewById(R.id.btn);
		tv = (TextView) findViewById(R.id.tv_process);
		btn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				downLoad();
			}
		});
	}

	/**
	 * 下载文件的方法
	 */
	private void downLoad() {
		final String path = et.getText().toString();
		new Thread() {
			public void run() {
				try {
					// 1、连接服务器,获取一个文件,获取文件的长度,在本地创建一个大小跟服务器文件大小一样的临时文件
					URL url = new URL(path);
					HttpURLConnection conn = (HttpURLConnection) url
							.openConnection();
					conn.setConnectTimeout(5000);
					conn.setRequestMethod("GET");
					int code = conn.getResponseCode();
					if (code == 200) {
						// 服务器返回的数据的长度,实际就是文件的长度
						int length = conn.getContentLength();
						pb.setMax(length);// 为进度条设置最大值
						System.out.println("----文件总长度----" + length);
						// 在客户端本地创建出来一个大小跟服务器端文件一样大小的临时文件
						RandomAccessFile raf = new RandomAccessFile(
								"/sdcard/temp.apk", "rwd");
						// 指定创建的这个文件的长度
						raf.setLength(length);
						// 关闭raf
						raf.close();
						// 假设是3个线程去下载资源
						// 平均每一个线程下载的文件的大小
						int blockSize = length / THREAD_COUNT;
						for (int threadId = 1; threadId <= THREAD_COUNT; threadId++) {
							// 第一个线程开始下载的位置
							int startIndex = (threadId - 1) * blockSize;
							int endIndex = threadId * blockSize - 1;
							if (threadId == THREAD_COUNT) {
								endIndex = length;
							}
							System.out.println("----threadId---"
									+ "--startIndex--" + startIndex
									+ "--endIndex--" + endIndex);
							new DownloadThread(path, threadId, startIndex,
									endIndex).start();
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
					Message msg = new Message();
					msg.arg1 = 0;
					handler.sendMessage(msg);
				}
			};
		}.start();
	}

	/**
	 * 下载文件的子线程,每一个线程下载对应位置的文件
	 * 
	 * @author loonggg
	 * 
	 */
	public class DownloadThread extends Thread {
		private int threadId;
		private int startIndex;
		private int endIndex;
		private String path;

		/**
		 * @param path
		 *            下载文件在服务器上的路径
		 * @param threadId
		 *            线程id
		 * @param startIndex
		 *            线程下载的开始位置
		 * @param endIndex
		 *            线程下载的结束位置
		 */
		public DownloadThread(String path, int threadId, int startIndex,
				int endIndex) {
			this.path = path;
			this.threadId = threadId;
			this.startIndex = startIndex;
			this.endIndex = endIndex;
		}

		@Override
		public void run() {
			try {
				// 检查是否存在记录下载长度的文件,如果存在读取这个文件的数据
				// -------------------------替换成数据库----------------------------
				File tempFile = new File("/sdcard/" + threadId + ".txt");
				if (tempFile.exists() && tempFile.length() > 0) {
					FileInputStream fis = new FileInputStream(tempFile);
					byte[] temp = new byte[1024 * 10];
					int leng = fis.read(temp);
					// 已经下载的长度
					String downloadLen = new String(temp, 0, leng);
					int downloadInt = Integer.parseInt(downloadLen);
					// ------------------这两行代码是关于断点续传时,设置进度条的起点时的关键代码-------------------
					int alreadyDownloadInt = downloadInt - startIndex;
					currentProcess += alreadyDownloadInt;// 计算每个线程上次断点已经下载的文件的长度
					// ---------------------------------------------------------------------------------
					startIndex = downloadInt;
					fis.close();
				}
				// ---------------------------------------------------------------

				URL url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setRequestMethod("GET");
				// 重要:请求服务器下载部分的文件 指定文件的位置
				conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
						+ endIndex);
				conn.setConnectTimeout(5000);
				// 从服务器请求全部资源的状态码200 ok 如果从服务器请求部分资源的状态码206 ok
				int code = conn.getResponseCode();
				System.out.println("---code---" + code);
				InputStream is = conn.getInputStream();// 已经设置了请求的位置,返回的是当前位置对应的文件的输入流
				RandomAccessFile raf = new RandomAccessFile("/sdcard/temp.apk",
						"rwd");
				// 随机写文件的时候从哪个位置开始写
				raf.seek(startIndex);// 定位文件
				int len = 0;
				byte[] buffer = new byte[1024];
				int total = 0;// 记录已经下载的数据的长度
				while ((len = is.read(buffer)) != -1) {
					RandomAccessFile recordFile = new RandomAccessFile(
							"/sdcard/" + threadId + ".txt", "rwd");// 记录每个线程的下载进度,为断点续传做标记
					raf.write(buffer, 0, len);
					total += len;
					recordFile.write(String.valueOf(startIndex + total)
							.getBytes());
					recordFile.close();
					// 同步加锁,防止混乱
					synchronized (MainActivity.this) {
						currentProcess += len;// 获取当前的总进度
						// 特殊情况,ProgressBarH和ProgressDialog进度条和对话框可以在子线程里面更新UI,系统内部代码特殊处理
						pb.setProgress(currentProcess);// 更改界面上进度条的进度
						Message msg = Message.obtain();
						msg.arg1 = 2;
						// 发送更新消息,更新进度的百分比
						handler.sendMessage(msg);
					}
				}
				is.close();
				raf.close();
				System.out.println("线程:" + threadId + "下载完毕了!");
			} catch (Exception e) {
				e.printStackTrace();
				Message msg = handler.obtainMessage();
				msg.arg1 = 0;
				handler.sendMessage(msg);
			} finally {
				threadFinish();
			}
		}

		/**
		 * 我个人认为不锁定也可以,但是锁定可能更安全,如果谁有好的建议,到底用不用锁定,请给我留言
		 */
		private synchronized void threadFinish() {
			runningThread--;
			if (runningThread == 0) {// 所有的线程已经执行完毕
				for (int i = 1; i <= THREAD_COUNT; i++) {// 删除记录下载进度的文件
					File file = new File("/sdcard/" + i + ".txt");
					file.delete();
					Message msg = handler.obtainMessage();
					msg.arg1 = 1;
					handler.sendMessage(msg);
				}
			}
		}
	}

}
还有就是对应的 Activity_main.xml 的代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/et"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="http://dl1.byme001.com/phone_android.apk" />

    <ProgressBar
        android:id="@+id/pb"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_process"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下载进度"
        android:textSize="30sp" />

    <Button
        android:id="@+id/btn"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="下载" />

</LinearLayout>
当然大家别忘了在清单文件中设置网络权限和读写 SD 卡的权限:

<uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
到这里就完了!大家如果有什么不明白的,可以在下面留言!

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android——android 学习(android 目录与 AndroidManifest 解析)

Android——android 学习(android 目录与 AndroidManifest 解析)

  res 目录:存放 android 项目的各种资源文件

    layout:存放界面布局文件

    values:存放各种 xml 格式的资源文件

      strings.xml:字符串资源文件;

      colors.xml:颜色资源文件;

      dimens.xml:尺寸资源文件)

    drawable-ldpi:低分辨率图片文件

    drawable-mdpi:中分辨率图片文件

    drawable-hdpi:高分辨率图片文件

    drawable-xhdpi:超高分辨率图片文件

  src:一个普通的、保存 Java 源文件的目录

  AndroidManifest.xml:Android 项目的系统清单文件,用于控制 Android 应用的名称、图标、访问权限等整体属性。

            除此之外,Android 应用的 Activity、Service、ContentProvider、BroadcastRecevier 这 4 大组件都需要在该文件中配置。

<?xml version="1.0" encoding="utf-8"?>
<!--指定该android应用的包名,该包名可用于唯一地表示该应用 --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.crazyit.helloworld" android:versionCode="1" android:versionName="1.0">   <!--指定android应用的标签、图标-->
<application android:icon="@drawable/icon" android:label="@string/app_name">
  <!--定义一个android应用的一个activity组件,该Activity的类为HelloWorld,并指定该Activity的标签-->
<activity android:name=".HelloWorld" android:label="@string/app_name"> <intent-filter>           <!--指定程序入口为该Activity-->
<action android:name="android.intent.action.MAIN" />           <!--指定加载该应用时运行该Activity-->


<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="9" /> </manifest>

  应用程序权限说明:

  一个 android 应用可能调用 Android 系统功能,也可能被其他应用调用,所以它需要声明自身所需要的权限

  1. 声明运行该应用本身所需要的权限

  通过 <manifest .../> 元素添加 < user-permission .../> 子元素,为程序本身声明权限。

 

<!-- 声明应用本身需要打电话的权限 -->
<uses-permission android:name="android.permission.CALL_PHONE" />

  2. 声明调用该应用所需要的权限

  通过为应用的各组件元素,如 <activity .../> 元素添加 <uses-permission .../> 子元素即可声明调用该程序所需的权限。

<!-- 声明应用本身需要发短信的权限 -->
<uses-permission android:name="android.permission.SEND_SMS" />

Android入门(十七)Android多线程

Android入门(十七)Android多线程

原文链接:http://www.orlion.ga/670/

一、在子线程中更新UI    

    Android中不允许在子线程中更新UI,只能在主线程中更新,但是我们有时候必须在子线程中执行一些耗时的任务,然后根据运行结果来更新UI,对于这种情况Android提供了一套异步消息处理机制。

    创建项目AndroidThreadDemo,修改activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <Button 
        android:id="@+id/change_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Change Text" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/hello_world" 
        android:textSize="20sp"/>

</RelativeLayout>

    MainActivity.java:

package ga.orlion.androidthreaddemo;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener{
	
	public static final int UPDATE_TEXT = 1;
	
	private TextView text;
	
	private Button changeText;
	
	private Handler handler = new Handler() {
		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);
		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方法,在这里对具体的 Message进行处理。如果发现 Message的 what字段的值等于 UPDATE_TEXT,就将 TextView显示的内容改成 Nice to meet you。

    下面再来看一下 Change Text按钮的点击事件中的代码。可以看到,这次我们并没有在子线程里直接进行 UI操作,而是创建了一个 Message(android.os.Message)对象,并将它的 what 字段的值指定为 UPDATE_TEXT,然后调用 Handler 的 sendMessage()方法将这条Message发送出去。很快,Handler就会收到这条 Message,并在 handleMessage()方法中对它进行处理。注意此时 handleMessage()方法中的代码就是在主线程当中运行的了,所以我们可以放心地在这里进行 UI操作。接下来对 Message携带的 what字段的值进行判断,如果等于UPDATE_TEXT,就将 TextView显示的内容改成 Nice to meet you。

 

二、解析异步处理机制

    

    Android中的异步处理主要由四个部分组成,Message、Handler、MessageQueue和Looper

    1.Message

    Message是在线程间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据,上面的代码中我们用了Message的what字段,除此之外还可以使用arg1和arg2字段来携带整型数据,使用obj字段携带一个Object对象

    2.Handler

    Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。

    3.MessageQueue

    MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象

    4.Looper

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

    异步消息处理的整个流程:首先需要在主线程中创建一个Handler对象,并重写handleMessage()方法,然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发会Handler的handleMessage()方法中。由于Handler是在主线程中被创建的,所以此时handleMessage()方法中的代码也会在主线程中执行。

        

一条Message经过这样一个流程的辗转调用后,也就从子线程进入到了主线程,从不能

更新 UI变成了可以更新 UI,整个异步消息处理的核心思想也就是如此。

 

三、使用AsyncTask

    

    AsyncTask是一个抽象类,在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:

    1.Params

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

    2.Progress

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

    3.Result

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

    

    因此,一个最简单的自定义AsyncTask可以写成如下:

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

    这里我们把AsyncTask的第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个放行参数指定为Integer,表示使用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。

    目前我们自定义DownloadTask还是一个空任务,并不能进行任何实际的操作,我们还需要取重写AsyncTask中的几个方法才能完成对任务的定制。经常需要去重写的方法有一下四个:

    1.onPreExecute()

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

    2.doInBackground(Params…)

    这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成

    3.onProgressUpdate(Progress…)

    当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就会很快被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

    4.onPostExecute(Result)

    当后台任务执行完毕并通过return语句进行返回时,这个方法就会很快被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

    因此,一个完整的自定义AsyncTask就可以写成如下方式:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
    @Override
    protected void onPreExecute() {
        progressDialog.show(); // 显示进度对话框
    }
    @Override
    protected Boolean doInBackground(Void... params) {
        try {
            while (true) {
                int downloadPercent = dodownload(); // 这是一个虚构的方法
                publishProgress(downloadPercent);
                if (downloadPercent >= 100) {
                    break;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }
    @Override
    protected void onProgressUpdate(Integer... values) {
        // 在这里更新下载进度
        progressDialog.setMessage("Downloaded " + values[0] + "%");
    }
    @Override
    protected void onPostExecute(Boolean result) {
        progressDialog.dismiss(); // 关闭进度对话框
        // 在这里提示下载结果
        if (result) {
            Toast.makeText(context, "Download succeeded",            Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(context, " Download Failed",            Toast.LENGTH_SHORT).show();
        }
    }
}

    在这个 DownloadTask中,我们在 doInBackground()方法里去执行具体的下载任务。这个方法里的代码都是在子线程中运行的,因而不会影响到主线程的运行。注意这里虚构了一个dodownload()方法,这个方法用于计算当前的下载进度并返回,我们假设这个方法已经存在了。在得到了当前的下载进度后,下面就该考虑如何把它显示到界面上了,由于doInBackground()方法是在子线程中运行的,在这里肯定不能进行 UI操作,所以我们可以调用 publishProgress()方法并将当前的下载进度传进来,这样 onProgressUpdate()方法就会很快被调用,在这里就可以进行 UI操作了。

    当下载完成后,doInBackground()方法会返回一个布尔型变量,这样 onPostExecute()方法就会很快被调用,这个方法也是在主线程中运行的。然后在这里我们会根据下载的结果来弹出相应的 Toast提示,从而完成整个 DownloadTask任务。

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

    如果想要启动这个任务,只需编写以下代码即可:

new DownloadTask().execute();

总结

以上是小编为你收集整理的Android入门(十七)Android多线程全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

今天关于android播放视频!Android多线程实现方式及并发与同步,深度好文android 视频播放流程的讲解已经结束,谢谢您的阅读,如果想了解更多关于android – 播放视频文件的最佳方式?、Android 学习记录(6)— 将 java 中的多线程下载移植到 Android 中(即多线程下载在 Android 中的使用)③、Android——android 学习(android 目录与 AndroidManifest 解析)、Android入门(十七)Android多线程的相关知识,请在本站搜索。

本文标签: