热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android按下多任务java_Android使用AsyncTask实现多任务多线程断点续传下载

这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以

这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下。

这里模拟应用市场app下载实现了一个Demo,因为只有一个界面,所以没有将下载放到Service中,而是直接在Activity中创建。在正式的项目中,下载都是放到Service中,然后通过BroadCast通知界面更新进度。

上代码之前,先看下demo的运行效果图吧。

31397a4c8a77499d8c9927ae6f2b6488.gif

下面我们看代码,这里每一个文件的下载定义一个Downloador来管理下载该文件的所有线程(暂停、下载等)。Downloador创建3个DownloadTask(这里定义每个文件分配3个线程下载)来下载该文件特定的起止位置。这里要通过文件的大小来计算每个线程所下载的起止位置,详细可以参考《AsyncTask实现多线程断点续传》。

1、Downloador类

package com.bbk.lling.multitaskdownload.downloador;

import android.content.Context;

import android.content.Intent;

import android.os.AsyncTask;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.text.TextUtils;

import android.util.Log;

import android.widget.Toast;

import com.bbk.lling.multitaskdownload.beans.AppContent;

import com.bbk.lling.multitaskdownload.beans.DownloadInfo;

import com.bbk.lling.multitaskdownload.db.DownloadFileDAO;

import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;

import com.bbk.lling.multitaskdownload.utils.DownloadUtils;

import org.apache.http.HttpResponse;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.Executor;

import java.util.concurrent.Executors;

/**

* @Class: Downloador

* @Description: 任务下载器

* @author: lling(www.cnblogs.com/liuling)

* @Date: 2015/10/13

*/

public class Downloador {

public static final String TAG = "Downloador";

private static final int THREAD_POOL_SIZE = 9; //线程池大小为9

private static final int THREAD_NUM = 3; //每个文件3个线程下载

private static final int GET_LENGTH_SUCCESS = 1;

public static final Executor THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

private List tasks;

private InnerHandler handler = new InnerHandler();

private AppContent appContent; //待下载的应用

private long downloadLength; //下载过程中记录已下载大小

private long fileLength;

private Context context;

private String downloadPath;

public Downloador(Context context, AppContent appContent) {

this.context = context;

this.appContent = appContent;

this.downloadPath = DownloadUtils.getDownloadPath();

}

/**

* 开始下载

*/

public void download() {

if(TextUtils.isEmpty(downloadPath)) {

Toast.makeText(context, "未找到SD卡", Toast.LENGTH_SHORT).show();

return;

}

if(appContent == null) {

throw new IllegalArgumentException("download content can not be null");

}

new Thread() {

@Override

public void run() {

//获取文件大小

HttpClient client = new DefaultHttpClient();

HttpGet request = new HttpGet(appContent.getUrl());

HttpResponse response = null;

try {

response = client.execute(request);

fileLength = response.getEntity().getContentLength();

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (request != null) {

request.abort();

}

}

//计算出该文件已经下载的总长度

List lists = DownloadInfoDAO.getInstance(context.getApplicationContext())

.getDownloadInfosByUrl(appContent.getUrl());

for (DownloadInfo info : lists) {

downloadLength += info.getDownloadLength();

}

//插入文件下载记录到数据库

DownloadFileDAO.getInstance(context.getApplicationContext()).insertDownloadFile(appContent);

Message.obtain(handler, GET_LENGTH_SUCCESS).sendToTarget();

}

}.start();

}

/**

* 开始创建AsyncTask下载

*/

private void beginDownload() {

Log.e(TAG, "beginDownload" + appContent.getUrl());

appContent.setStatus(AppContent.Status.WAITING);

long blockLength = fileLength / THREAD_NUM;

for (int i = 0; i

long beginPosition = i * blockLength;//每条线程下载的开始位置

long endPosition = (i + 1) * blockLength;//每条线程下载的结束位置

if (i == (THREAD_NUM - 1)) {

endPosition = fileLength;//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度

}

DownloadTask task = new DownloadTask(i, beginPosition, endPosition, this, context);

task.executeOnExecutor(THREAD_POOL_EXECUTOR, appContent.getUrl());

if(tasks == null) {

tasks = new ArrayList();

}

tasks.add(task);

}

}

/**

* 暂停下载

*/

public void pause() {

for (DownloadTask task : tasks) {

if (task != null && (task.getStatus() == AsyncTask.Status.RUNNING || !task.isCancelled())) {

task.cancel(true);

}

}

tasks.clear();

appContent.setStatus(AppContent.Status.PAUSED);

DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);

}

/**

* 将已下载大小归零

*/

protected synchronized void resetDownloadLength() {

this.downloadLength = 0;

}

/**

* 添加已下载大小

* 多线程访问需加锁

* @param size

*/

protected synchronized void updateDownloadLength(long size){

this.downloadLength += size;

//通知更新界面

int percent = (int)((float)downloadLength * 100 / (float)fileLength);

appContent.setDownloadPercent(percent);

if(percent == 100 || downloadLength == fileLength) {

appContent.setDownloadPercent(100); //上面计算有时候会有点误差,算到percent=99

appContent.setStatus(AppContent.Status.FINISHED);

DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);

}

Intent intent = new Intent(Constants.DOWNLOAD_MSG);

if(appContent.getStatus() == AppContent.Status.WAITING) {

appContent.setStatus(AppContent.Status.DOWNLOADING);

}

Bundle bundle = new Bundle();

bundle.putParcelable("appContent", appContent);

intent.putExtras(bundle);

context.sendBroadcast(intent);

}

protected String getDownloadPath() {

return downloadPath;

}

private class InnerHandler extends Handler {

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case GET_LENGTH_SUCCESS :

beginDownload();

break;

}

super.handleMessage(msg);

}

}

}

2、DownloadTask类

package com.bbk.lling.multitaskdownload.downloador;

import android.content.Context;

import android.os.AsyncTask;

import android.util.Log;

import com.bbk.lling.multitaskdownload.beans.DownloadInfo;

import com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;

import org.apache.http.Header;

import org.apache.http.HttpResponse;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import org.apache.http.message.BasicHeader;

import java.io.File;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.RandomAccessFile;

import java.net.MalformedURLException;

/**

* @Class: DownloadTask

* @Description: 文件下载AsyncTask

* @author: lling(www.cnblogs.com/liuling)

* @Date: 2015/10/13

*/

public class DownloadTask extends AsyncTask {

private static final String TAG = "DownloadTask";

private int taskId;

private long beginPosition;

private long endPosition;

private long downloadLength;

private String url;

private Downloador downloador;

private DownloadInfoDAO downloadInfoDAO;

public DownloadTask(int taskId, long beginPosition, long endPosition, Downloador downloador,

Context context) {

this.taskId = taskId;

this.beginPosition = beginPosition;

this.endPosition = endPosition;

this.downloador = downloador;

downloadInfoDAO = DownloadInfoDAO.getInstance(context.getApplicationContext());

}

@Override

protected void onPreExecute() {

Log.e(TAG, "onPreExecute");

}

@Override

protected void onPostExecute(Long aLong) {

Log.e(TAG, url + "taskId:" + taskId + "executed");

// downloador.updateDownloadInfo(null);

}

@Override

protected void onProgressUpdate(Integer... values) {

//通知downloador增加已下载大小

// downloador.updateDownloadLength(values[0]);

}

@Override

protected void onCancelled() {

Log.e(TAG, "onCancelled");

// downloador.updateDownloadInfo(null);

}

@Override

protected Long doInBackground(String... params) {

//这里加判断的作用是:如果还处于等待就暂停了,运行到这里已经cancel了,就直接退出

if(isCancelled()) {

return null;

}

url = params[0];

if(url == null) {

return null;

}

HttpClient client = new DefaultHttpClient();

HttpGet request = new HttpGet(url);

HttpResponse response;

InputStream is;

RandomAccessFile fos = null;

OutputStream output = null;

DownloadInfo downloadInfo = null;

try {

//本地文件

File file = new File(downloador.getDownloadPath() + File.separator + url.substring(url.lastIndexOf("/") + 1));

//获取之前下载保存的信息

downloadInfo = downloadInfoDAO.getDownloadInfoByTaskIdAndUrl(taskId, url);

//从之前结束的位置继续下载

//这里加了判断file.exists(),判断是否被用户删除了,如果文件没有下载完,但是已经被用户删除了,则重新下载

if(file.exists() && downloadInfo != null) {

if(downloadInfo.isDownloadSuccess() == 1) {

//下载完成直接结束

return null;

}

beginPosition = beginPosition + downloadInfo.getDownloadLength();

downloadLength = downloadInfo.getDownloadLength();

}

if(!file.exists()) {

//如果此task已经下载完,但是文件被用户删除,则需要重新设置已下载长度,重新下载

downloador.resetDownloadLength();

}

//设置下载的数据位置beginPosition字节到endPosition字节

Header header_size = new BasicHeader("Range", "bytes=" + beginPosition + "-" + endPosition);

request.addHeader(header_size);

//执行请求获取下载输入流

response = client.execute(request);

is = response.getEntity().getContent();

//创建文件输出流

fos = new RandomAccessFile(file, "rw");

//从文件的size以后的位置开始写入

fos.seek(beginPosition);

byte buffer [] = new byte[1024];

int inputSize = -1;

while((inputSize = is.read(buffer)) != -1) {

fos.write(buffer, 0, inputSize);

downloadLength += inputSize;

downloador.updateDownloadLength(inputSize);

//如果暂停了,需要将下载信息存入数据库

if (isCancelled()) {

if(downloadInfo == null) {

downloadInfo = new DownloadInfo();

}

downloadInfo.setUrl(url);

downloadInfo.setDownloadLength(downloadLength);

downloadInfo.setTaskId(taskId);

downloadInfo.setDownloadSuccess(0);

//保存下载信息到数据库

downloadInfoDAO.insertDownloadInfo(downloadInfo);

return null;

}

}

} catch (MalformedURLException e) {

Log.e(TAG, e.getMessage());

} catch (IOException e) {

Log.e(TAG, e.getMessage());

} finally{

try{

if (request != null) {

request.abort();

}

if(output != null) {

output.close();

}

if(fos != null) {

fos.close();

}

} catch(Exception e) {

e.printStackTrace();

}

}

//执行到这里,说明该task已经下载完了

if(downloadInfo == null) {

downloadInfo = new DownloadInfo();

}

downloadInfo.setUrl(url);

downloadInfo.setDownloadLength(downloadLength);

downloadInfo.setTaskId(taskId);

downloadInfo.setDownloadSuccess(1);

//保存下载信息到数据库

downloadInfoDAO.insertDownloadInfo(downloadInfo);

return null;

}

}

Downloador和DownloadTask只这个例子的核心代码,下面是关于数据库的,因为要实现断点续传必须要在暂停的时候将每个线程下载的位置记录下来,方便下次继续下载时读取。这里有两个表,一个是存放每个文件的下载状态的,一个是存放每个文件对应的每个线程的下载状态的。

3、DBHelper

package com.bbk.lling.multitaskdownload.db;

import android.content.Context;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

/**

* @Class: DBHelper

* @Description: 数据库帮助类

* @author: lling(www.cnblogs.com/liuling)

* @Date: 2015/10/14

*/

public class DBHelper extends SQLiteOpenHelper {

public DBHelper(Context context) {

super(context, "download.db", null, 1);

}

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL("create table download_info(_id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER, "

+ "download_length INTEGER, url VARCHAR(255), is_success INTEGER)");

db.execSQL("create table download_file(_id INTEGER PRIMARY KEY AUTOINCREMENT, app_name VARCHAR(255), "

+ "url VARCHAR(255), download_percent INTEGER, status INTEGER)");

}

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}

}

4、DownloadFileDAO,文件下载状态的数据库操作类

package com.bbk.lling.multitaskdownload.db;

import android.content.Context;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import android.text.TextUtils;

import android.util.Log;

import com.bbk.lling.multitaskdownload.beans.AppContent;

import java.util.ArrayList;

import java.util.List;

/**

* @Class: DownloadFileDAO

* @Description: 每个文件下载状态记录的数据库操作类

* @author: lling(www.cnblogs.com/liuling)

* @Date: 2015/10/13

*/

public class DownloadFileDAO {

private static final String TAG = "DownloadFileDAO";

private static DownloadFileDAO dao=null;

private Context context;

private DownloadFileDAO(Context context) {

this.context=context;

}

synchronized public static DownloadFileDAO getInstance(Context context){

if(dao==null){

dao=new DownloadFileDAO(context);

}

return dao;

}

/**

* 获取数据库连接

* @return

*/

public SQLiteDatabase getConnection() {

SQLiteDatabase sqliteDatabase = null;

try {

sqliteDatabase= new DBHelper(context).getReadableDatabase();

} catch (Exception e) {

Log.e(TAG, e.getMessage());

}

return sqliteDatabase;

}

/**

* 插入数据

* @param appContent

*/

public void insertDownloadFile(AppContent appContent) {

if(appContent == null) {

return;

}

//如果本地已经存在,直接修改

if(getAppContentByUrl(appContent.getUrl()) != null) {

updateDownloadFile(appContent);

return;

}

SQLiteDatabase database = getConnection();

try {

String sql = "insert into download_file(app_name, url, download_percent, status) values (?,?,?,?)";

Object[] bindArgs = { appContent.getName(), appContent.getUrl(), appContent.getDownloadPercent()

, appContent.getStatus().getValue()};

database.execSQL(sql, bindArgs);

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (null != database) {

database.close();

}

}

}

/**

* 根据url获取下载文件信息

* @param url

* @return

*/

public AppContent getAppContentByUrl(String url) {

if(TextUtils.isEmpty(url)) {

return null;

}

SQLiteDatabase database = getConnection();

AppContent appContent = null;

Cursor cursor = null;

try {

String sql = "select * from download_file where url=?";

cursor = database.rawQuery(sql, new String[] { url });

if (cursor.moveToNext()) {

appContent = new AppContent(cursor.getString(1), cursor.getString(2));

appContent.setDownloadPercent(cursor.getInt(3));

appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4)));

}

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (null != database) {

database.close();

}

if (null != cursor) {

cursor.close();

}

}

return appContent;

}

/**

* 更新下载信息

* @param appContent

*/

public void updateDownloadFile(AppContent appContent) {

if(appContent == null) {

return;

}

SQLiteDatabase database = getConnection();

try {

Log.e(TAG, "update download_file,app name:" + appContent.getName() + ",url:" + appContent.getUrl()

+ ",percent" + appContent.getDownloadPercent() + ",status:" + appContent.getStatus().getValue());

String sql = "update download_file set app_name=?, url=?, download_percent=?, status=? where url=?";

Object[] bindArgs = {appContent.getName(), appContent.getUrl(), appContent.getDownloadPercent()

, appContent.getStatus().getValue(), appContent.getUrl()};

database.execSQL(sql, bindArgs);

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (null != database) {

database.close();

}

}

}

/**

* 获取所有下载文件记录

* @return

*/

public List getAll() {

SQLiteDatabase database = getConnection();

List list = new ArrayList();

Cursor cursor = null;

try {

String sql = "select * from download_file";

cursor = database.rawQuery(sql, null);

while (cursor.moveToNext()) {

AppContent appContent = new AppContent(cursor.getString(1), cursor.getString(2));

appContent.setDownloadPercent(cursor.getInt(3));

appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4)));

list.add(appContent);

}

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (null != database) {

database.close();

}

if (null != cursor) {

cursor.close();

}

}

return list;

}

/**

* 根据url删除记录

* @param url

*/

public void delByUrl(String url) {

if(TextUtils.isEmpty(url)) {

return;

}

SQLiteDatabase database = getConnection();

try {

String sql = "delete from download_file where url=?";

Object[] bindArgs = { url };

database.execSQL(sql, bindArgs);

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (null != database) {

database.close();

}

}

}

}

5、DownloadInfoDAO,每个线程对应下载状态的数据库操作类

package com.bbk.lling.multitaskdownload.db;

import android.content.Context;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import android.text.TextUtils;

import android.util.Log;

import com.bbk.lling.multitaskdownload.beans.DownloadInfo;

import java.util.ArrayList;

import java.util.List;

/**

* @Class: DownloadInfoDAO

* @Description: 每个单独线程下载信息记录的数据库操作类

* @author: lling(www.cnblogs.com/liuling)

* @Date: 2015/10/13

*/

public class DownloadInfoDAO {

private static final String TAG = "DownloadInfoDAO";

private static DownloadInfoDAO dao=null;

private Context context;

private DownloadInfoDAO(Context context) {

this.context=context;

}

synchronized public static DownloadInfoDAO getInstance(Context context){

if(dao==null){

dao=new DownloadInfoDAO(context);

}

return dao;

}

/**

* 获取数据库连接

* @return

*/

public SQLiteDatabase getConnection() {

SQLiteDatabase sqliteDatabase = null;

try {

sqliteDatabase= new DBHelper(context).getReadableDatabase();

} catch (Exception e) {

Log.e(TAG, e.getMessage());

}

return sqliteDatabase;

}

/**

* 插入数据

* @param downloadInfo

*/

public void insertDownloadInfo(DownloadInfo downloadInfo) {

if(downloadInfo == null) {

return;

}

//如果本地已经存在,直接修改

if(getDownloadInfoByTaskIdAndUrl(downloadInfo.getTaskId(), downloadInfo.getUrl()) != null) {

updateDownloadInfo(downloadInfo);

return;

}

SQLiteDatabase database = getConnection();

try {

String sql = "insert into download_info(task_id, download_length, url, is_success) values (?,?,?,?)";

Object[] bindArgs = { downloadInfo.getTaskId(), downloadInfo.getDownloadLength(),

downloadInfo.getUrl(), downloadInfo.isDownloadSuccess()};

database.execSQL(sql, bindArgs);

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (null != database) {

database.close();

}

}

}

public List getDownloadInfosByUrl(String url) {

if(TextUtils.isEmpty(url)) {

return null;

}

SQLiteDatabase database = getConnection();

List list = new ArrayList();

Cursor cursor = null;

try {

String sql = "select * from download_info where url=?";

cursor = database.rawQuery(sql, new String[] { url });

while (cursor.moveToNext()) {

DownloadInfo info = new DownloadInfo();

info.setTaskId(cursor.getInt(1));

info.setDownloadLength(cursor.getLong(2));

info.setDownloadSuccess(cursor.getInt(4));

info.setUrl(cursor.getString(3));

list.add(info);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

if (null != database) {

database.close();

}

if (null != cursor) {

cursor.close();

}

}

return list;

}

/**

* 根据taskid和url获取下载信息

* @param taskId

* @param url

* @return

*/

public DownloadInfo getDownloadInfoByTaskIdAndUrl(int taskId, String url) {

if(TextUtils.isEmpty(url)) {

return null;

}

SQLiteDatabase database = getConnection();

DownloadInfo info = null;

Cursor cursor = null;

try {

String sql = "select * from download_info where url=? and task_id=?";

cursor = database.rawQuery(sql, new String[] { url, String.valueOf(taskId) });

if (cursor.moveToNext()) {

info = new DownloadInfo();

info.setTaskId(cursor.getInt(1));

info.setDownloadLength(cursor.getLong(2));

info.setDownloadSuccess(cursor.getInt(4));

info.setUrl(cursor.getString(3));

}

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (null != database) {

database.close();

}

if (null != cursor) {

cursor.close();

}

}

return info;

}

/**

* 更新下载信息

* @param downloadInfo

*/

public void updateDownloadInfo(DownloadInfo downloadInfo) {

if(downloadInfo == null) {

return;

}

SQLiteDatabase database = getConnection();

try {

String sql = "update download_info set download_length=?, is_success=? where task_id=? and url=?";

Object[] bindArgs = { downloadInfo.getDownloadLength(), downloadInfo.isDownloadSuccess(),

downloadInfo.getTaskId(), downloadInfo.getUrl() };

database.execSQL(sql, bindArgs);

} catch (Exception e) {

Log.e(TAG, e.getMessage());

} finally {

if (null != database) {

database.close();

}

}

}

}

具体的界面和使用代码我就不贴代码了,代码有点多。需要的可以下载Demo的源码看看。

因为还没有花太多时间去测,里面难免会有些bug,如果大家发现什么问题,欢迎留言探讨,谢谢!



推荐阅读
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了关于apache、phpmyadmin、mysql、php、emacs、path等知识点,以及如何搭建php环境。文章提供了详细的安装步骤和所需软件列表,希望能帮助读者解决与LAMP相关的技术问题。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
author-avatar
我们要疯_475
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有